Index: /applications/editors/josm/plugins/imagery_offset_db/src/iodb/CalibrationObject.java
===================================================================
--- /applications/editors/josm/plugins/imagery_offset_db/src/iodb/CalibrationObject.java	(revision 28007)
+++ /applications/editors/josm/plugins/imagery_offset_db/src/iodb/CalibrationObject.java	(revision 28008)
@@ -1,6 +1,6 @@
 package iodb;
 
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.User;
+import java.util.Map;
+import org.openstreetmap.josm.data.osm.*;
 
 /**
@@ -18,5 +18,5 @@
 
     public CalibrationObject(OsmPrimitive object) {
-        this(object, -1);
+        this(object, getLastUserId(object));
     }
 
@@ -28,3 +28,16 @@
         return object;
     }
+    
+    private static long getLastUserId( OsmPrimitive object ) {
+        return object.getUser() == null ? -1 : object.getUser().getId(); // todo?
+    }
+
+    @Override
+    public void putServerParams( Map<String, String> map ) {
+        super.putServerParams(map);
+        map.put("object", object instanceof Node ? "node" : "way");
+        map.put("id", String.valueOf(object.getId()));
+        map.put("lastuser", String.valueOf(lastUserId));
+    }
+    
 }
Index: /applications/editors/josm/plugins/imagery_offset_db/src/iodb/GetImageryOffsetAction.java
===================================================================
--- /applications/editors/josm/plugins/imagery_offset_db/src/iodb/GetImageryOffsetAction.java	(revision 28007)
+++ /applications/editors/josm/plugins/imagery_offset_db/src/iodb/GetImageryOffsetAction.java	(revision 28008)
@@ -37,6 +37,4 @@
     private List<ImageryOffsetBase> offsets;
     
-    private HashMap<String, String> imageryAliases;
-    
     public GetImageryOffsetAction() {
         super(tr("Get Imagery Offset..."), "getoffset", tr("Download offsets for current imagery from a server"),
@@ -48,7 +46,11 @@
         Projection proj = Main.map.mapView.getProjection();
         LatLon center = proj.eastNorth2latlon(Main.map.mapView.getCenter());
-        // todo: download a list of offsets for current bbox * N
-        List<ImageryOffsetBase> offsets = download(center); // todo: async
-        DownloadOffsets download = new DownloadOffsets();
+        ImageryLayer layer = ImageryOffsetTools.getTopImageryLayer();
+        String imagery = ImageryOffsetTools.getImageryID(layer);
+        if( imagery == null )
+            return;
+        
+        List<ImageryOffsetBase> offsets = download(center, imagery); // todo: async
+        /*DownloadOffsets download = new DownloadOffsets();
         Future<?> future = Main.worker.submit(download);
         try {
@@ -57,5 +59,5 @@
             ex.printStackTrace();
             return;
-        }
+        }*/
         
         // todo: show a dialog for selecting one of the offsets (without "update" flag)
@@ -63,14 +65,20 @@
         if( offset != null ) {
             // todo: use the chosen offset
+            if( offset instanceof ImageryOffset ) {
+                ImageryOffsetTools.applyLayerOffset(layer, (ImageryOffset)offset);
+            } else if( offset instanceof CalibrationObject ) {
+                // todo: select object
+            }
         }
     }
     
-    private List<ImageryOffsetBase> download( LatLon center ) {
-        String base = Main.pref.get("iodb.server.url", "http://textual.ru/iodb.php");
-        String query = "?action=get&lat=" + center.getX() + "&lon=" + center.getY();
+    private List<ImageryOffsetBase> download( LatLon center, String imagery ) {
+        String base = Main.pref.get("iodb.server.url", "http://offsets.textual.ru/");
+        String query = "get?lat=" + center.getX() + "&lon=" + center.getY();
         List<ImageryOffsetBase> result = null;
         try {
-            query = query + "&imagery=" + URLEncoder.encode(getImageryID(), "utf-8");
+            query = query + "&imagery=" + URLEncoder.encode(imagery, "utf-8");
             URL url = new URL(base + query);
+            System.out.println("url=" + url);
             HttpURLConnection connection = (HttpURLConnection)url.openConnection();
             connection.connect();
@@ -79,4 +87,5 @@
             if( inp != null ) {
                 result = new IODBReader(inp).parse();
+                System.out.println("result.size() = " + result.size());
             }
             connection.disconnect();
@@ -95,70 +104,4 @@
             result = new ArrayList<ImageryOffsetBase>();
         return result;
-    }
-    
-    private String getImageryID() {
-        List<ImageryLayer> layers = Main.map.mapView.getLayersOfType(ImageryLayer.class);
-        String url = null;
-        for( ImageryLayer layer : layers ) {
-            if( layer.isVisible() ) {
-                url = layer.getInfo().getUrl();
-                break;
-            }
-        }
-        if( url == null )
-            return null;
-        
-        if( imageryAliases == null )
-            loadImageryAliases();
-        for( String substr : imageryAliases.keySet() )
-            if( url.contains(substr) )
-                return imageryAliases.get(substr);
-        
-        return url; // todo: strip parametric parts, etc
-    }
-    
-    private void loadImageryAliases() {
-        if( imageryAliases == null )
-            imageryAliases = new HashMap<String, String>();
-        else
-            imageryAliases.clear();
-        
-        // { substring, alias }
-        imageryAliases.put("bing", "bing");
-        // todo: load from a resource?
-    }
-    
-    // Following three methods were snatched from TMSLayer
-    private double latToTileY(double lat, int zoom) {
-        double l = lat / 180 * Math.PI;
-        double pf = Math.log(Math.tan(l) + (1 / Math.cos(l)));
-        return Math.pow(2.0, zoom - 1) * (Math.PI - pf) / Math.PI;
-    }
-
-    private double lonToTileX(double lon, int zoom) {
-        return Math.pow(2.0, zoom - 3) * (lon + 180.0) / 45.0;
-    }
-
-    private int getCurrentZoom() {
-        if (Main.map == null || Main.map.mapView == null) {
-            return 1;
-        }
-        MapView mv = Main.map.mapView;
-        LatLon topLeft = mv.getLatLon(0, 0);
-        LatLon botRight = mv.getLatLon(mv.getWidth(), mv.getHeight());
-        double x1 = lonToTileX(topLeft.lon(), 1);
-        double y1 = latToTileY(topLeft.lat(), 1);
-        double x2 = lonToTileX(botRight.lon(), 1);
-        double y2 = latToTileY(botRight.lat(), 1);
-
-        int screenPixels = mv.getWidth() * mv.getHeight();
-        double tilePixels = Math.abs((y2 - y1) * (x2 - x1) * 256 * 256);
-        if (screenPixels == 0 || tilePixels == 0) {
-            return 1;
-        }
-        double factor = screenPixels / tilePixels;
-        double result = Math.log(factor) / Math.log(2) / 2 + 1;
-        int intResult = (int) Math.floor(result);
-        return intResult;
     }
     
Index: /applications/editors/josm/plugins/imagery_offset_db/src/iodb/ImageryOffset.java
===================================================================
--- /applications/editors/josm/plugins/imagery_offset_db/src/iodb/ImageryOffset.java	(revision 28007)
+++ /applications/editors/josm/plugins/imagery_offset_db/src/iodb/ImageryOffset.java	(revision 28008)
@@ -1,4 +1,6 @@
 package iodb;
 
+import java.util.Map;
+import org.openstreetmap.josm.data.coor.CoordinateFormat;
 import org.openstreetmap.josm.data.coor.LatLon;
 
@@ -19,5 +21,5 @@
         this.maxZoom = 30;
     }
-
+    
     public void setMaxZoom(int maxZoom) {
         this.maxZoom = maxZoom;
@@ -43,3 +45,15 @@
         return minZoom;
     }
+
+    @Override
+    public void putServerParams( Map<String, String> map ) {
+        super.putServerParams(map);
+        map.put("imagery", imagery);
+        map.put("imlat", imageryPos.latToString(CoordinateFormat.DECIMAL_DEGREES));
+        map.put("imlon", imageryPos.lonToString(CoordinateFormat.DECIMAL_DEGREES));
+        if( minZoom > 0 )
+            map.put("minzoom", String.valueOf(minZoom));
+        if( maxZoom < 30 )
+            map.put("maxzoom", String.valueOf(maxZoom));
+    }
 }
Index: /applications/editors/josm/plugins/imagery_offset_db/src/iodb/ImageryOffsetBase.java
===================================================================
--- /applications/editors/josm/plugins/imagery_offset_db/src/iodb/ImageryOffsetBase.java	(revision 28007)
+++ /applications/editors/josm/plugins/imagery_offset_db/src/iodb/ImageryOffsetBase.java	(revision 28008)
@@ -2,4 +2,6 @@
 
 import java.util.Date;
+import java.util.Map;
+import org.openstreetmap.josm.data.coor.CoordinateFormat;
 import org.openstreetmap.josm.data.coor.LatLon;
 
@@ -51,3 +53,10 @@
         return position;
     }
+    
+    public void putServerParams( Map<String, String> map ) {
+        map.put("lat", position.latToString(CoordinateFormat.DECIMAL_DEGREES));
+        map.put("lon", position.lonToString(CoordinateFormat.DECIMAL_DEGREES));
+        map.put("author", author);
+        map.put("description", description);
+    }
 }
Index: /applications/editors/josm/plugins/imagery_offset_db/src/iodb/ImageryOffsetTools.java
===================================================================
--- /applications/editors/josm/plugins/imagery_offset_db/src/iodb/ImageryOffsetTools.java	(revision 28008)
+++ /applications/editors/josm/plugins/imagery_offset_db/src/iodb/ImageryOffsetTools.java	(revision 28008)
@@ -0,0 +1,112 @@
+package iodb;
+
+import java.util.HashMap;
+import java.util.List;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.layer.ImageryLayer;
+
+/**
+ * Some common static methods for querying imagery layers.
+ * 
+ * @author zverik
+ */
+public class ImageryOffsetTools {
+    private static HashMap<String, String> imageryAliases;
+    
+    public static ImageryLayer getTopImageryLayer() {
+        List<ImageryLayer> layers = Main.map.mapView.getLayersOfType(ImageryLayer.class);
+        for( ImageryLayer layer : layers ) {
+            if( layer.isVisible() ) {
+                return layer;
+            }
+        }
+        return null;
+    }
+    
+    private static LatLon getMapCenter() {
+        Projection proj = Main.getProjection();
+        return Main.map == null || Main.map.mapView == null
+                ? new LatLon(0, 0) : proj.eastNorth2latlon(Main.map.mapView.getCenter());
+    }
+    
+    public static LatLon getLayerOffset( ImageryLayer layer, LatLon center ) {
+        Projection proj = Main.getProjection();
+        EastNorth offsetCenter = proj.latlon2eastNorth(center);
+        EastNorth centerOffset = offsetCenter.add(layer.getDx(), layer.getDy()); // todo: add or substract?
+        LatLon offsetLL = proj.eastNorth2latlon(centerOffset);
+        return offsetLL;
+    }
+    
+    public static void applyLayerOffset( ImageryLayer layer, ImageryOffset offset ) {
+        Projection proj = Main.getProjection();
+        EastNorth center = proj.latlon2eastNorth(offset.getPosition());
+        EastNorth offsetPos = proj.latlon2eastNorth(offset.getImageryPos());
+        layer.setOffset(offsetPos.getX() - center.getX(), offsetPos.getY() - center.getY()); // todo: + or -?
+    }
+    
+    public static String getImageryID( ImageryLayer layer ) {
+        if( layer == null )
+            return null;
+        
+        String url = layer.getInfo().getUrl();
+        if( url == null )
+            return null;
+        
+        if( imageryAliases == null )
+            loadImageryAliases();
+        for( String substr : imageryAliases.keySet() )
+            if( url.contains(substr) )
+                return imageryAliases.get(substr);
+        
+        return url; // todo: strip parametric parts, etc
+    }
+    
+    private static void loadImageryAliases() {
+        if( imageryAliases == null )
+            imageryAliases = new HashMap<String, String>();
+        else
+            imageryAliases.clear();
+        
+        // { substring, alias }
+        imageryAliases.put("bing", "bing");
+        // todo: load from a resource?
+    }
+    
+    // Following three methods were snatched from TMSLayer
+    private static double latToTileY(double lat, int zoom) {
+        double l = lat / 180 * Math.PI;
+        double pf = Math.log(Math.tan(l) + (1 / Math.cos(l)));
+        return Math.pow(2.0, zoom - 1) * (Math.PI - pf) / Math.PI;
+    }
+
+    private static double lonToTileX(double lon, int zoom) {
+        return Math.pow(2.0, zoom - 3) * (lon + 180.0) / 45.0;
+    }
+
+    public static int getCurrentZoom() {
+        if (Main.map == null || Main.map.mapView == null) {
+            return 1;
+        }
+        MapView mv = Main.map.mapView;
+        LatLon topLeft = mv.getLatLon(0, 0);
+        LatLon botRight = mv.getLatLon(mv.getWidth(), mv.getHeight());
+        double x1 = lonToTileX(topLeft.lon(), 1);
+        double y1 = latToTileY(topLeft.lat(), 1);
+        double x2 = lonToTileX(botRight.lon(), 1);
+        double y2 = latToTileY(botRight.lat(), 1);
+
+        int screenPixels = mv.getWidth() * mv.getHeight();
+        double tilePixels = Math.abs((y2 - y1) * (x2 - x1) * 256 * 256);
+        if (screenPixels == 0 || tilePixels == 0) {
+            return 1;
+        }
+        double factor = screenPixels / tilePixels;
+        double result = Math.log(factor) / Math.log(2) / 2 + 1;
+        int intResult = (int) Math.floor(result);
+        return intResult;
+    }
+}
Index: /applications/editors/josm/plugins/imagery_offset_db/src/iodb/OffsetDialog.java
===================================================================
--- /applications/editors/josm/plugins/imagery_offset_db/src/iodb/OffsetDialog.java	(revision 28007)
+++ /applications/editors/josm/plugins/imagery_offset_db/src/iodb/OffsetDialog.java	(revision 28008)
@@ -14,7 +14,7 @@
  * @author zverik
  */
-public class OffsetDialog extends JDialog {
+public class OffsetDialog extends JDialog implements ActionListener {
     private List<ImageryOffsetBase> offsets;
-    private int selectedOffset;
+    private ImageryOffsetBase selectedOffset;
 
     public OffsetDialog( List<ImageryOffsetBase> offsets ) {
@@ -27,13 +27,10 @@
         JPanel buttonPanel = new JPanel(new GridLayout(offsets.size() + 1, 1));
         for( ImageryOffsetBase offset : offsets ) {
-            buttonPanel.add(new OffsetDialogButton(offset));
+            OffsetDialogButton button = new OffsetDialogButton(offset);
+            button.addActionListener(this);
+            buttonPanel.add(button);
         }
         JButton cancelButton = new JButton("Cancel");
-        cancelButton.addActionListener(new ActionListener() {
-            public void actionPerformed(ActionEvent e) {
-                selectedOffset = -1;
-                OffsetDialog.this.setVisible(false);
-            }
-        });
+        cancelButton.addActionListener(this);
         buttonPanel.add(cancelButton); // todo: proper button
         setContentPane(buttonPanel);
@@ -43,8 +40,16 @@
     
     public ImageryOffsetBase showDialog() {
-        selectedOffset = -1;
+        selectedOffset = null;
         prepareDialog();
         setVisible(true);
-        return selectedOffset < 0 ? null : offsets.get(selectedOffset);
+        return selectedOffset;
+    }
+
+    public void actionPerformed( ActionEvent e ) {
+        if( e.getSource() instanceof OffsetDialogButton ) {
+            selectedOffset = ((OffsetDialogButton)e.getSource()).getOffset();
+        } else
+            selectedOffset = null;
+        setVisible(false);
     }
 }
Index: /applications/editors/josm/plugins/imagery_offset_db/src/iodb/OffsetDialogButton.java
===================================================================
--- /applications/editors/josm/plugins/imagery_offset_db/src/iodb/OffsetDialogButton.java	(revision 28007)
+++ /applications/editors/josm/plugins/imagery_offset_db/src/iodb/OffsetDialogButton.java	(revision 28008)
@@ -9,7 +9,14 @@
  */
 public class OffsetDialogButton extends JButton {
+    
+    private ImageryOffsetBase offset;
 
     public OffsetDialogButton( ImageryOffsetBase offset ) {
         super(offset.getDescription() + " (" + offset.getPosition().lat() + ", " + offset.getPosition().lon() + ")");
+        this.offset = offset;
+    }
+
+    public ImageryOffsetBase getOffset() {
+        return offset;
     }
     
Index: /applications/editors/josm/plugins/imagery_offset_db/src/iodb/StoreImageryOffsetAction.java
===================================================================
--- /applications/editors/josm/plugins/imagery_offset_db/src/iodb/StoreImageryOffsetAction.java	(revision 28007)
+++ /applications/editors/josm/plugins/imagery_offset_db/src/iodb/StoreImageryOffsetAction.java	(revision 28008)
@@ -2,4 +2,6 @@
 
 import java.awt.event.ActionEvent;
+import java.util.HashMap;
+import java.util.Map;
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.JosmAction;
@@ -20,4 +22,6 @@
 
     public void actionPerformed(ActionEvent e) {
+        // todo: check that there is an imagery
+        // and that it is moved
         Projection proj = Main.map.mapView.getProjection();
         LatLon center = proj.eastNorth2latlon(Main.map.mapView.getCenter());
@@ -27,3 +31,10 @@
         // todo: upload object info to server
     }
+    
+    private static void upload( ImageryOffsetBase offset ) {
+        String base = Main.pref.get("iodb.server.url", "http://offsets.textual.ru/");
+        Map<String, String> params = new HashMap<String, String>();
+        offset.putServerParams(params);
+        // todo
+    }
 }
