Index: OsmMercator.java
===================================================================
--- OsmMercator.java	(revision 26700)
+++ OsmMercator.java	(working copy)
@@ -18,6 +18,18 @@
     private static int TILE_SIZE = 256;
     public static final double MAX_LAT = 85.05112877980659;
     public static final double MIN_LAT = -85.05112877980659;
+    private static double EARTH_RADIUS_KM = 6371;
+
+    private static double KMTOM=1000;
+    private static double MTOKM=1/1000;
+
+    public static double kmToMeters(double kmeters) {
+        return kmeters*KMTOM;
+    }
+
+    public static double metersToKm(double meters) {
+        return meters*MTOKM;
+    }
 
     public static double radius(int aZoomlevel) {
         return (TILE_SIZE * (1 << aZoomlevel)) / (2.0 * Math.PI);
@@ -43,6 +55,50 @@
     }
 
     /**
+     * Transform pixelspace to coordinates and get the distance.
+     *
+     * @param x1 the first x coordinate
+     * @param y1 the first y coordinate
+     * @param x2 the second x coordinate
+     * @param y2 the second y coordinate
+     * 
+     * @param zoomLevel the zoom level
+     * @return the distance
+     * @author Jason Huntley
+     */
+    public static double getDistance(int x1, int y1, int x2, int y2, int zoomLevel) {
+        double la1 = YToLat(y1, zoomLevel);
+        double lo1 = XToLon(x1, zoomLevel);
+        double la2 = YToLat(y2, zoomLevel);
+        double lo2 = XToLon(x2, zoomLevel);
+
+        return getDistance(la1, lo1, la2, lo2);
+    }
+
+    /**
+     * Gets the distance using Spherical law of cosines.
+     *
+     * @param la1 the Latitude in degrees
+     * @param lo1 the Longitude in degrees
+     * @param la2 the Latitude from 2nd coordinate in degrees
+     * @param lo2 the Longitude from 2nd coordinate in degrees
+     * @return the distance
+     * @author Jason Huntley
+     */
+    public static double getDistance(double la1, double lo1, double la2, double lo2) {
+        double aStartLat = Math.toRadians(la1);
+        double aStartLong = Math.toRadians(lo1);
+        double aEndLat =Math.toRadians(la2);
+        double aEndLong = Math.toRadians(lo2);
+
+        double distance = Math.acos(Math.sin(aStartLat) * Math.sin(aEndLat)
+                + Math.cos(aStartLat) * Math.cos(aEndLat)
+                * Math.cos(aEndLong - aStartLong));
+
+        return (EARTH_RADIUS_KM * distance);		
+    }
+
+    /**
      * Transform longitude to pixelspace
      *
      * <p>
Index: JMapViewer.java
===================================================================
--- JMapViewer.java	(revision 26700)
+++ JMapViewer.java	(working copy)
@@ -25,7 +25,11 @@
 import javax.swing.JSlider;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
+import javax.swing.event.EventListenerList;
 
+import org.openstreetmap.gui.jmapviewer.events.JMVCommandEvent;
+import org.openstreetmap.gui.jmapviewer.events.JMVCommandEvent.COMMAND;
+import org.openstreetmap.gui.jmapviewer.interfaces.JMapViewerEventListener;
 import org.openstreetmap.gui.jmapviewer.interfaces.MapMarker;
 import org.openstreetmap.gui.jmapviewer.interfaces.MapPolygon;
 import org.openstreetmap.gui.jmapviewer.interfaces.MapRectangle;
@@ -36,12 +40,12 @@
 import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource;
 
 /**
- * 
+ *
  * Provides a simple panel that displays pre-rendered map tiles loaded from the
  * OpenStreetMap project.
- * 
+ *
  * @author Jan Peter Stotz
- * 
+ *
  */
 public class JMapViewer extends JPanel implements TileLoaderListener {
 
@@ -117,8 +121,8 @@
         tileSource = new OsmTileSource.Mapnik();
         tileController = new TileController(tileSource, tileCache, this);
         mapMarkerList = new LinkedList<MapMarker>();
-        mapRectangleList = new LinkedList<MapRectangle>();
         mapPolygonList = new LinkedList<MapPolygon>();
+        mapRectangleList = new LinkedList<MapRectangle>();
         mapMarkersVisible = true;
         mapRectanglesVisible = true;
         mapPolygonsVisible = true;
@@ -187,7 +191,7 @@
     /**
      * Changes the map pane so that it is centered on the specified coordinate
      * at the given zoom level.
-     * 
+     *
      * @param lat
      *            latitude of the specified coordinate
      * @param lon
@@ -203,7 +207,7 @@
      * Changes the map pane so that the specified coordinate at the given zoom
      * level is displayed on the map at the screen coordinate
      * <code>mapPoint</code>.
-     * 
+     *
      * @param mapPoint
      *            point on the map denoted in pixels where the coordinate should
      *            be set
@@ -264,13 +268,13 @@
             nbElemToCheck += mapPolygonList.size();
         if (nbElemToCheck == 0)
             return;
-        
+
         int x_min = Integer.MAX_VALUE;
         int y_min = Integer.MAX_VALUE;
         int x_max = Integer.MIN_VALUE;
         int y_max = Integer.MIN_VALUE;
         int mapZoomMax = tileController.getTileSource().getMaxZoom();
-        
+
         if (markers) {
             for (MapMarker marker : mapMarkerList) {
                 int x = OsmMercator.LonToX(marker.getLon(), mapZoomMax);
@@ -281,7 +285,7 @@
                 y_min = Math.min(y_min, y);
             }
         }
-        
+
         if (rectangles) {
             for (MapRectangle rectangle : mapRectangleList) {
                 x_max = Math.max(x_max, OsmMercator.LonToX(rectangle.getBottomRight().getLon(), mapZoomMax));
@@ -290,7 +294,7 @@
                 y_min = Math.min(y_min, OsmMercator.LatToY(rectangle.getBottomRight().getLat(), mapZoomMax));
             }
         }
-        
+
         if (polygons) {
             for (MapPolygon polygon : mapPolygonList) {
                 for (Coordinate c : polygon.getPoints()) {
@@ -303,7 +307,7 @@
                 }
             }
         }
-        
+
         int height = Math.max(0, getHeight());
         int width = Math.max(0, getWidth());
         int newZoom = mapZoomMax;
@@ -321,7 +325,8 @@
         y /= z;
         setDisplayPosition(x, y, newZoom);
     }
-    
+
+
     /**
      * Sets the displayed map pane and zoom level so that all map markers are
      * visible.
@@ -337,7 +342,7 @@
     public void setDisplayToFitMapRectangles() {
         setDisplayToFitMapElements(false, true, false);
     }
-    
+
     /**
      * Sets the displayed map pane and zoom level so that all map polygons are
      * visible.
@@ -347,9 +352,23 @@
     }
 
     /**
+     * @return the center
+     */
+    public Point getCenter() {
+        return center;
+    }
+
+    /**
+     * @param center the center to set
+     */
+    public void setCenter(Point center) {
+        this.center = center;
+    }
+
+    /**
      * Calculates the latitude/longitude coordinate of the center of the
      * currently displayed map area.
-     * 
+     *
      * @return latitude / longitude
      */
     public Coordinate getPosition() {
@@ -361,7 +380,7 @@
     /**
      * Converts the relative pixel coordinate (regarding the top left corner of
      * the displayed map) into a latitude / longitude coordinate
-     * 
+     *
      * @param mapPoint
      *            relative pixel coordinate regarding the top left corner of the
      *            displayed map
@@ -374,7 +393,7 @@
     /**
      * Converts the relative pixel coordinate (regarding the top left corner of
      * the displayed map) into a latitude / longitude coordinate
-     * 
+     *
      * @param mapPointX
      * @param mapPointY
      * @return
@@ -389,7 +408,7 @@
 
     /**
      * Calculates the position on the map of a given coordinate
-     * 
+     *
      * @param lat
      * @param lon
      * @param checkOutside
@@ -410,7 +429,7 @@
 
     /**
      * Calculates the position on the map of a given coordinate
-     * 
+     *
      * @param lat
      * @param lon
      * @return point on the map or <code>null</code> if the point is not visible
@@ -421,7 +440,7 @@
 
     /**
      * Calculates the position on the map of a given coordinate
-     * 
+     *
      * @param coord
      * @return point on the map or <code>null</code> if the point is not visible
      */
@@ -434,7 +453,7 @@
 
     /**
      * Calculates the position on the map of a given coordinate
-     * 
+     *
      * @param coord
      * @return point on the map or <code>null</code> if the point is not visible
      *         and checkOutside set to <code>true</code>
@@ -446,6 +465,29 @@
             return null;
     }
 
+    /**
+     * Gets the meter per pixel.
+     *
+     * @return the meter per pixel
+     * @author Jason Huntley
+     */
+    public double getMeterPerPixel() {
+        Point origin=new Point(5,5);
+        Point center=new Point(getWidth()/2, getHeight()/2);
+
+        double pDistance=center.distance(origin);
+
+        Coordinate originCoord=getPosition(origin);
+        Coordinate centerCoord=getPosition(center);
+
+        double kmDistance=OsmMercator.getDistance(originCoord.getLat(), originCoord.getLon(),
+                centerCoord.getLat(), centerCoord.getLon());
+
+        double mDistance=OsmMercator.kmToMeters(kmDistance);
+
+        return mDistance/pDistance;
+    }
+
     @Override
     protected void paintComponent(Graphics g) {
         super.paintComponent(g);
@@ -542,6 +584,7 @@
                 paintMarker(g, marker);
             }
         }
+
         paintAttribution(g);
     }
 
@@ -587,10 +630,10 @@
             polygon.paint(g, points);
         }
     }
-    
+
     /**
      * Moves the visible map pane.
-     * 
+     *
      * @param x
      *            horizontal movement in pixel.
      * @param y
@@ -600,6 +643,7 @@
         center.x += x;
         center.y += y;
         repaint();
+        this.fireJMVEvent(new JMVCommandEvent(COMMAND.MOVE, this));
     }
 
     /**
@@ -645,6 +689,8 @@
         tileController.cancelOutstandingJobs(); // Clearing outstanding load
         // requests
         setDisplayPositionByLatLon(mapPoint, zoomPos.getLat(), zoomPos.getLon(), zoom);
+
+        this.fireJMVEvent(new JMVCommandEvent(COMMAND.ZOOM, this));
     }
 
     public void setZoom(int zoom) {
@@ -655,7 +701,7 @@
      * Every time the zoom level changes this method is called. Override it in
      * derived implementations for adapting zoom dependent values. The new zoom
      * level can be obtained via {@link #getZoom()}.
-     * 
+     *
      * @param oldZoom
      *            the previous zoom level
      */
@@ -682,7 +728,7 @@
 
     /**
      * Enables or disables painting of the {@link MapMarker}
-     * 
+     *
      * @param mapMarkersVisible
      * @see #addMapMarker(MapMarker)
      * @see #getMapMarkerList()
@@ -763,7 +809,7 @@
         mapPolygonList.clear();
         repaint();
     }
-    
+
     public void setZoomContolsVisible(boolean visible) {
         zoomSlider.setVisible(visible);
         zoomInButton.setVisible(visible);
@@ -808,7 +854,7 @@
 
     /**
      * Enables or disables painting of the {@link MapRectangle}
-     * 
+     *
      * @param mapRectanglesVisible
      * @see #addMapRectangle(MapRectangle)
      * @see #getMapRectangleList()
@@ -824,7 +870,7 @@
 
     /**
      * Enables or disables painting of the {@link MapPolygon}
-     * 
+     *
      * @param mapPolygonsVisible
      * @see #addMapPolygon(MapPolygon)
      * @see #getMapPolygonList()
@@ -836,7 +882,7 @@
 
     /*
      * (non-Javadoc)
-     * 
+     *
      * @see
      * org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener#getTileCache
      * ()
@@ -898,4 +944,34 @@
 
         g.setFont(font);
     }
+
+    protected EventListenerList listenerList = new EventListenerList();
+
+    /**
+     * @param listener to set
+     */
+    public void addJMVListener(JMapViewerEventListener listener) {
+        listenerList.add(JMapViewerEventListener.class, listener);
+    }
+
+    /**
+     * @param listener to remove
+     */
+    public void removeJMVListener(JMapViewerEventListener listener) {
+        listenerList.remove(JMapViewerEventListener.class, listener);
+    }
+
+    /**
+     * Send an update to all objects registered with viewer
+     * 
+     * @param event to dispatch
+     */
+    void fireJMVEvent(JMVCommandEvent evt) {
+        Object[] listeners = listenerList.getListenerList();
+        for (int i=0; i<listeners.length; i+=2) {
+            if (listeners[i]==JMapViewerEventListener.class) {
+                ((JMapViewerEventListener)listeners[i+1]).processCommand(evt);
+            }
+        }
+    }
 }
Index: Demo.java
===================================================================
--- Demo.java	(revision 26700)
+++ Demo.java	(working copy)
@@ -16,6 +16,8 @@
 import javax.swing.JLabel;
 import javax.swing.JPanel;
 
+import org.openstreetmap.gui.jmapviewer.events.JMVCommandEvent;
+import org.openstreetmap.gui.jmapviewer.interfaces.JMapViewerEventListener;
 import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
 import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
 import org.openstreetmap.gui.jmapviewer.tilesources.BingAerialTileSource;
@@ -28,22 +30,44 @@
  * @author Jan Peter Stotz
  *
  */
-public class Demo extends JFrame {
+public class Demo extends JFrame implements JMapViewerEventListener  {
 
     private static final long serialVersionUID = 1L;
 
+    private JMapViewer map = null;
+
+    private JLabel zoomLabel=null;
+    private JLabel zoomValue=null;
+
+    private JLabel mperpLabelName=null;
+    private JLabel mperpLabelValue = null;
+
     public Demo() {
         super("JMapViewer Demo");
         setSize(400, 400);
-        final JMapViewer map = new JMapViewer();
+
+        map = new JMapViewer();
+
+        // Listen to the map viewer for user operations so components will
+        // recieve events and update
+        map.addJMVListener(this);
+
         // final JMapViewer map = new JMapViewer(new MemoryTileCache(),4);
         // map.setTileLoader(new OsmFileCacheTileLoader(map));
         // new DefaultMapController(map);
+
         setLayout(new BorderLayout());
         setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
         setExtendedState(JFrame.MAXIMIZED_BOTH);
         JPanel panel = new JPanel();
         JPanel helpPanel = new JPanel();
+
+        mperpLabelName=new JLabel("Meters/Pixels: ");
+        mperpLabelValue=new JLabel(String.format("%s",map.getMeterPerPixel()));
+
+        zoomLabel=new JLabel("Zoom: ");
+        zoomValue=new JLabel(String.format("%s", map.getZoom()));
+
         add(panel, BorderLayout.NORTH);
         add(helpPanel, BorderLayout.SOUTH);
         JLabel helpLabel = new JLabel("Use right mouse button to move,\n "
@@ -106,6 +130,12 @@
         });
         panel.add(showZoomControls);
         panel.add(button);
+
+        panel.add(zoomLabel);
+        panel.add(zoomValue);
+        panel.add(mperpLabelName);
+        panel.add(mperpLabelValue);
+
         add(map, BorderLayout.CENTER);
 
         //
@@ -129,4 +159,19 @@
         new Demo().setVisible(true);
     }
 
+    private void updateZoomParameters() {
+        if (mperpLabelValue!=null)
+            mperpLabelValue.setText(String.format("%s",map.getMeterPerPixel()));
+        if (zoomValue!=null)
+            zoomValue.setText(String.format("%s", map.getZoom()));
+    }
+
+    @Override
+    public void processCommand(JMVCommandEvent command) {
+        if (command.getCommand().equals(JMVCommandEvent.COMMAND.ZOOM) ||
+                command.getCommand().equals(JMVCommandEvent.COMMAND.MOVE)) {
+            updateZoomParameters();
+        }
+    }
+
 }
Index: events/JMVCommandEvent.java
===================================================================
--- events/JMVCommandEvent.java	(revision 0)
+++ events/JMVCommandEvent.java	(revision 0)
@@ -0,0 +1,50 @@
+/**
+ * 
+ */
+package org.openstreetmap.gui.jmapviewer.events;
+
+import java.util.EventObject;
+
+/**
+ * Used for passing events between UI components and other
+ * objects that register as a JMapViewerEventListener
+ * 
+ * @author Jason Huntley
+ *
+ */
+public class JMVCommandEvent extends EventObject {
+    public static enum COMMAND {
+        MOVE,
+        ZOOM
+    }
+
+    private COMMAND command;
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 8701544867914969620L;
+
+    public JMVCommandEvent(COMMAND cmd, Object source) {
+        super(source);
+
+        setCommand(cmd);
+    }
+
+    public JMVCommandEvent(Object source) {
+        super(source);
+    }
+
+    /**
+     * @return the command
+     */
+    public COMMAND getCommand() {
+        return command;
+    }
+
+    /**
+     * @param command the command to set
+     */
+    public void setCommand(COMMAND command) {
+        this.command = command;
+    }
+}

Property changes on: events\JMVCommandEvent.java
___________________________________________________________________
Added: svn:eol-style
   + native

Index: interfaces/JMapViewerEventListener.java
===================================================================
--- interfaces/JMapViewerEventListener.java	(revision 0)
+++ interfaces/JMapViewerEventListener.java	(revision 0)
@@ -0,0 +1,19 @@
+/**
+ * 
+ */
+package org.openstreetmap.gui.jmapviewer.interfaces;
+
+import java.util.EventListener;
+
+import org.openstreetmap.gui.jmapviewer.events.JMVCommandEvent;
+
+/**
+ * Must be implemented for processing commands while user
+ * interacts with map viewer.
+ * 
+ * @author Jason Huntley
+ *
+ */
+public interface JMapViewerEventListener extends EventListener {
+	public void processCommand(JMVCommandEvent command);
+}

Property changes on: interfaces\JMapViewerEventListener.java
___________________________________________________________________
Added: svn:eol-style
   + native

