Index: trunk/src/org/openstreetmap/josm/Main.java
===================================================================
--- trunk/src/org/openstreetmap/josm/Main.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/Main.java	(revision 4126)
@@ -16,8 +16,10 @@
 import java.awt.event.WindowEvent;
 import java.io.File;
+import java.lang.ref.WeakReference;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -56,4 +58,5 @@
 import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy;
 import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.ProjectionChangeListener;
 import org.openstreetmap.josm.data.validation.OsmValidator;
 import org.openstreetmap.josm.gui.GettingStarted;
@@ -73,4 +76,5 @@
 import org.openstreetmap.josm.io.OsmApi;
 import org.openstreetmap.josm.plugins.PluginHandler;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
 import org.openstreetmap.josm.tools.I18n;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -119,8 +123,5 @@
     public static PrimitiveDeepCopy pasteBuffer = new PrimitiveDeepCopy();
     public static Layer pasteSource;
-    /**
-     * The projection method used.
-     */
-    public static Projection proj;
+
     /**
      * The MapFrame. Use setMapFrame to set or clear it.
@@ -154,5 +155,5 @@
      */
     static public int debug_level = 1;
-    static public final void debug(String msg) {
+    static public void debug(String msg) {
         if (debug_level <= 0)
             return;
@@ -213,5 +214,5 @@
         platform.startupHook();
 
-        // We try to establish an API connection early, so that any API 
+        // We try to establish an API connection early, so that any API
         // capabilities are already known to the editor instance. However
         // if it goes wrong that's not critical at this stage.
@@ -768,3 +769,107 @@
         System.err.println("Error: Could not recognize Java Version: "+version);
     }
+
+    /* ----------------------------------------------------------------------------------------- */
+    /* projection handling  - Main is a registry for a single, global projection instance        */
+    /*                                                                                           */
+    /* TODO: For historical reasons the registry is implemented by Main. An alternative approach */
+    /* would be a singleton org.openstreetmap.josm.data.projection.ProjectionRegistry class.     */
+    /* ----------------------------------------------------------------------------------------- */
+    /**
+     * The projection method used.
+     * @deprecated use {@link #getProjection()} and {@link #setProjection(Projection)} instead.
+     * For the time being still publicly available, but avoid/migrate write access to it. Use
+     * {@link #setProjection(Projection)} in order to trigger a projection change event.
+     */
+    @Deprecated
+    public static Projection proj;
+
+    /**
+     * Replies the current projection.
+     * 
+     * @return
+     */
+    public static Projection getProjection() {
+        return proj;
+    }
+
+    /**
+     * Sets the current projection
+     * 
+     * @param p the projection
+     */
+    public static void setProjection(Projection p) {
+        CheckParameterUtil.ensureParameterNotNull(p);
+        Projection oldValue = proj;
+        proj = p;
+        fireProjectionChanged(oldValue, proj);
+    }
+
+    /*
+     * Keep WeakReferences to the listeners. This relieves clients from the burden of
+     * explicitly removing the listeners and allows us to transparently register every
+     * created dataset as projection change listener.
+     */
+    private static final ArrayList<WeakReference<ProjectionChangeListener>> listeners = new ArrayList<WeakReference<ProjectionChangeListener>>();
+
+    private static void fireProjectionChanged(Projection oldValue, Projection newValue) {
+        if (newValue == null ^ oldValue == null
+                || (newValue != null && oldValue != null && !newValue.getClass().getName().equals(oldValue.getClass().getName()))) {
+
+            synchronized(Main.class) {
+                Iterator<WeakReference<ProjectionChangeListener>> it = listeners.iterator();
+                while(it.hasNext()){
+                    WeakReference<ProjectionChangeListener> wr = it.next();
+                    if (wr.get() == null) {
+                        it.remove();
+                        continue;
+                    }
+                    wr.get().projectionChanged(oldValue, newValue);
+                }
+            }
+            if (newValue != null) {
+                Bounds b = (Main.map != null && Main.map.mapView != null) ? Main.map.mapView.getRealBounds() : null;
+                if (b != null){
+                    Main.map.mapView.zoomTo(b);
+                }
+            }
+            /* TODO - remove layers with fixed projection */
+        }
+    }
+
+    /**
+     * Register a projection change listener
+     * 
+     * @param listener the listener. Ignored if null.
+     */
+    public static void addProjectionChangeListener(ProjectionChangeListener listener) {
+        if (listener == null) return;
+        synchronized (Main.class) {
+            for (WeakReference<ProjectionChangeListener> wr : listeners) {
+                // already registered ? => abort
+                if (wr.get() == listener) return;
+            }
+        }
+        listeners.add(new WeakReference<ProjectionChangeListener>(listener));
+    }
+
+    /**
+     * Removes a projection change listener
+     * 
+     * @param listener the listener. Ignored if null.
+     */
+    public static void removeProjectionChangeListener(ProjectionChangeListener listener) {
+        if (listener == null) return;
+        synchronized(Main.class){
+            Iterator<WeakReference<ProjectionChangeListener>> it = listeners.iterator();
+            while(it.hasNext()){
+                WeakReference<ProjectionChangeListener> wr = it.next();
+                // remove the listener - and any other listener which god garbage
+                // collected in the meantime
+                if (wr.get() == null || wr.get() == listener) {
+                    it.remove();
+                }
+            }
+        }
+    }
 }
Index: trunk/src/org/openstreetmap/josm/actions/CreateCircleAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/CreateCircleAction.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/actions/CreateCircleAction.java	(revision 4126)
@@ -153,5 +153,5 @@
                     double x = xc + r*Math.cos(a);
                     double y = yc + r*Math.sin(a);
-                    Node n = new Node(Main.proj.eastNorth2latlon(new EastNorth(x,y)));
+                    Node n = new Node(Main.getProjection().eastNorth2latlon(new EastNorth(x,y)));
                     wayToAdd.add(n);
                     cmds.add(new AddCommand(n));
@@ -245,5 +245,5 @@
                 double x = xc + r*Math.cos(a);
                 double y = yc + r*Math.sin(a);
-                Node n = new Node(Main.proj.eastNorth2latlon(new EastNorth(x,y)));
+                Node n = new Node(Main.getProjection().eastNorth2latlon(new EastNorth(x,y)));
                 wayToAdd.add(n);
                 cmds.add(new AddCommand(n));
Index: trunk/src/org/openstreetmap/josm/actions/ImageryAdjustAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/ImageryAdjustAction.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/actions/ImageryAdjustAction.java	(revision 4126)
@@ -162,5 +162,5 @@
             pnl.add(new JMultilineLabel(tr("Use arrow keys or drag the imagery layer with mouse to adjust the imagery offset.\n" +
                     "You can also enter east and north offset in the {0} coordinates.\n" +
-                    "If you want to save the offset as bookmark, enter the bookmark name below",Main.proj.toString())), GBC.eop());
+                    "If you want to save the offset as bookmark, enter the bookmark name below",Main.getProjection().toString())), GBC.eop());
             pnl.add(new JLabel(tr("Offset: ")),GBC.std());
             pnl.add(tOffset,GBC.eol().fill(GBC.HORIZONTAL).insets(0,0,0,5));
@@ -211,5 +211,5 @@
         public void updateOffsetIntl() {
             // Support projections with very small numbers (e.g. 4326)
-            int precision = Main.proj.getDefaultZoomInPPD() >= 1.0 ? 2 : 7;
+            int precision = Main.getProjection().getDefaultZoomInPPD() >= 1.0 ? 2 : 7;
             // US locale to force decimal separator to be '.'
             tOffset.setText(new java.util.Formatter(java.util.Locale.US).format(
Index: trunk/src/org/openstreetmap/josm/actions/OrthogonalizeAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/OrthogonalizeAction.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/actions/OrthogonalizeAction.java	(revision 4126)
@@ -117,5 +117,5 @@
         if (!isEnabled())
             return;
-        if ("EPSG:4326".equals(Main.proj.toString())) {
+        if ("EPSG:4326".equals(Main.getProjection().toString())) {
             String msg = tr("<html>You are using the EPSG:4326 projection which might lead<br>" +
                     "to undesirable results when doing rectangular alignments.<br>" +
Index: trunk/src/org/openstreetmap/josm/actions/mapmode/ExtrudeAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/mapmode/ExtrudeAction.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/actions/mapmode/ExtrudeAction.java	(revision 4126)
@@ -255,5 +255,5 @@
 
             // find out the movement distance, in metres
-            double distance = Main.proj.eastNorth2latlon(initialN1en).greatCircleDistance(Main.proj.eastNorth2latlon(newN1en));
+            double distance = Main.getProjection().eastNorth2latlon(initialN1en).greatCircleDistance(Main.getProjection().eastNorth2latlon(newN1en));
             Main.map.statusLine.setDist(distance);
             updateStatusLine();
@@ -309,8 +309,8 @@
                         //move existing node
                         Node n1Old = selectedSegment.getFirstNode();
-                        cmds.add(new MoveCommand(n1Old, Main.proj.eastNorth2latlon(newN1en)));
+                        cmds.add(new MoveCommand(n1Old, Main.getProjection().eastNorth2latlon(newN1en)));
                     } else {
                         //introduce new node
-                        Node n1New = new Node(Main.proj.eastNorth2latlon(newN1en));
+                        Node n1New = new Node(Main.getProjection().eastNorth2latlon(newN1en));
                         wnew.addNode(insertionPoint, n1New);
                         insertionPoint ++;
@@ -326,8 +326,8 @@
                         //move existing node
                         Node n2Old = selectedSegment.getSecondNode();
-                        cmds.add(new MoveCommand(n2Old, Main.proj.eastNorth2latlon(newN2en)));
+                        cmds.add(new MoveCommand(n2Old, Main.getProjection().eastNorth2latlon(newN2en)));
                     } else {
                         //introduce new node
-                        Node n2New = new Node(Main.proj.eastNorth2latlon(newN2en));
+                        Node n2New = new Node(Main.getProjection().eastNorth2latlon(newN2en));
                         wnew.addNode(insertionPoint, n2New);
                         insertionPoint ++;
Index: trunk/src/org/openstreetmap/josm/command/MoveCommand.java
===================================================================
--- trunk/src/org/openstreetmap/josm/command/MoveCommand.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/command/MoveCommand.java	(revision 4126)
@@ -12,5 +12,4 @@
 import javax.swing.JLabel;
 
-import org.openstreetmap.josm.data.coor.CachedLatLon;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.coor.LatLon;
@@ -18,4 +17,5 @@
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.visitor.AllNodesVisitor;
+import org.openstreetmap.josm.data.projection.Projections;
 import org.openstreetmap.josm.tools.ImageProvider;
 
@@ -59,5 +59,5 @@
 
     public MoveCommand(Node node, LatLon position) {
-        this(Collections.singleton((OsmPrimitive) node), node.getEastNorth().sub(new CachedLatLon(position).getEastNorth()));
+        this(Collections.singleton((OsmPrimitive) node), node.getEastNorth().sub(Projections.project(position)));
     }
 
Index: trunk/src/org/openstreetmap/josm/data/coor/CachedLatLon.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/coor/CachedLatLon.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/data/coor/CachedLatLon.java	(revision 4126)
@@ -5,4 +5,13 @@
 import org.openstreetmap.josm.data.projection.Projection;
 
+/**
+ * LatLon class that maintains a cache of projected EastNorth coordinates.
+ * 
+ * This class is convenient to use, but has relatively high memory costs.
+ * It keeps a pointer to the last known projection in order to detect projection 
+ * changes.
+ * 
+ * Node and WayPoint have another, optimized, cache for projected coordinates.
+ */
 public class CachedLatLon extends LatLon {
     private EastNorth eastNorth;
@@ -19,6 +28,6 @@
 
     public CachedLatLon(EastNorth eastNorth) {
-        super(Main.proj.eastNorth2latlon(eastNorth));
-        proj = Main.proj;
+        super(Main.getProjection().eastNorth2latlon(eastNorth));
+        proj = Main.getProjection();
         this.eastNorth = eastNorth;
     }
@@ -30,5 +39,5 @@
 
     public final void setEastNorth(EastNorth eastNorth) {
-        proj = Main.proj;
+        proj = Main.getProjection();
         this.eastNorth = eastNorth;
         LatLon l = proj.eastNorth2latlon(eastNorth);
@@ -36,8 +45,13 @@
     }
 
+    /**
+     * Replies the projected east/north coordinates.
+     * 
+     * @return the internally cached east/north coordinates. null, if the globally defined projection is null
+     */
     public final EastNorth getEastNorth() {
-        if(proj != Main.proj)
+        if(proj != Main.getProjection())
         {
-            proj = Main.proj;
+            proj = Main.getProjection();
             eastNorth = proj.latlon2eastNorth(this);
         }
Index: trunk/src/org/openstreetmap/josm/data/coor/LatLon.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/coor/LatLon.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/data/coor/LatLon.java	(revision 4126)
@@ -106,5 +106,5 @@
         case DEGREES_MINUTES_SECONDS: return dms(y) + ((y < 0) ? SOUTH : NORTH);
         case NAUTICAL: return dm(y) + ((y < 0) ? SOUTH : NORTH);
-        case EAST_NORTH: return cDdFormatter.format(Main.proj.latlon2eastNorth(this).north());
+        case EAST_NORTH: return cDdFormatter.format(Main.getProjection().latlon2eastNorth(this).north());
         default: return "ERR";
         }
@@ -122,5 +122,5 @@
         case DEGREES_MINUTES_SECONDS: return dms(x) + ((x < 0) ? WEST : EAST);
         case NAUTICAL: return dm(x) + ((x < 0) ? WEST : EAST);
-        case EAST_NORTH: return cDdFormatter.format(Main.proj.latlon2eastNorth(this).east());
+        case EAST_NORTH: return cDdFormatter.format(Main.getProjection().latlon2eastNorth(this).east());
         default: return "ERR";
         }
@@ -142,5 +142,5 @@
      */
     public boolean isOutSideWorld() {
-        Bounds b = Main.proj.getWorldBoundsLatLon();
+        Bounds b = Main.getProjection().getWorldBoundsLatLon();
         return lat() < b.getMin().lat() || lat() > b.getMax().lat() ||
         lon() < b.getMin().lon() || lon() > b.getMax().lon();
Index: trunk/src/org/openstreetmap/josm/data/gpx/WayPoint.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/gpx/WayPoint.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/data/gpx/WayPoint.java	(revision 4126)
@@ -7,7 +7,8 @@
 import java.util.Date;
 
-import org.openstreetmap.josm.data.coor.CachedLatLon;
+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.Projections;
 import org.openstreetmap.josm.tools.PrimaryDateParser;
 
@@ -26,20 +27,60 @@
 
     public WayPoint(LatLon ll) {
-        coor = new CachedLatLon(ll);
+        lat = ll.lat();
+        lon = ll.lon();
     }
 
-    private final CachedLatLon coor;
+    /*
+     * We "inline" lat/lon, rather than usinga LatLon internally => reduces memory overhead. Relevant
+     * because a lot of GPX waypoints are created when GPS tracks are downloaded from the OSM server.
+     */
+    private double lat = 0;
+    private double lon = 0;
+
+    /*
+     * internal cache of projected coordinates
+     */
+    private double east = Double.NaN;
+    private double north = Double.NaN;
+
+    /**
+     * Invalidate the internal cache of east/north coordinates.
+     */
+    public void invalidateEastNorthCache() {
+        this.east = Double.NaN;
+        this.north = Double.NaN;
+    }
 
     public final LatLon getCoor() {
-        return coor;
+        return new LatLon(lat,lon);
     }
 
+    /**
+     * <p>Replies the projected east/north coordinates.</p>
+     * 
+     * <p>Uses the {@link Main#getProjection() global projection} to project the lan/lon-coordinates.
+     * Internally caches the projected coordinates.</p>
+     *
+     * <p><strong>Caveat:</strong> doesn't listen to projection changes. Clients must
+     * {@link #reproject() trigger a reprojection} or {@link #invalidateEastNorthCache() invalidate the internal cache}.</p>
+     * 
+     * @return the east north coordinates or {@code null}
+     * @see #invalidateEastNorthCache()
+     * 
+     */
     public final EastNorth getEastNorth() {
-        return coor.getEastNorth();
+        if (Double.isNaN(east) || Double.isNaN(north)) {
+            // projected coordinates haven't been calculated yet,
+            // so fill the cache of the projected waypoint coordinates
+            EastNorth en = Projections.project(new LatLon(lat, lon));
+            this.east = en.east();
+            this.north = en.north();
+        }
+        return new EastNorth(east, north);
     }
 
     @Override
     public String toString() {
-        return "WayPoint (" + (attr.containsKey("name") ? attr.get("name") + ", " :"") + coor.toString() + ", " + attr + ")";
+        return "WayPoint (" + (attr.containsKey("name") ? attr.get("name") + ", " :"") + getCoor().toString() + ", " + attr + ")";
     }
 
@@ -57,6 +98,5 @@
     }
 
-    public int compareTo(WayPoint w)
-    {
+    public int compareTo(WayPoint w) {
         return Double.compare(time, w.time);
     }
Index: trunk/src/org/openstreetmap/josm/data/imagery/OffsetBookmark.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/imagery/OffsetBookmark.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/data/imagery/OffsetBookmark.java	(revision 4126)
@@ -30,5 +30,5 @@
     public boolean isUsable(ImageryLayer layer) {
         if (proj == null) return false;
-        if (!Main.proj.toCode().equals(proj.toCode())) return false;
+        if (!Main.getProjection().toCode().equals(proj.toCode())) return false;
         return layer.getInfo().getName().equals(layerName);
     }
@@ -122,10 +122,10 @@
         LatLon center;
         if (Main.map != null && Main.map.mapView != null) {
-            center = Main.proj.eastNorth2latlon(Main.map.mapView.getCenter());
+            center = Main.getProjection().eastNorth2latlon(Main.map.mapView.getCenter());
         } else {
             center = new LatLon(0,0);
         }
         OffsetBookmark nb = new OffsetBookmark(
-                Main.proj, layer.getInfo().getName(),
+                Main.getProjection(), layer.getInfo().getName(),
                 name, layer.getDx(), layer.getDy(), center.lon(), center.lat());
         for (ListIterator<OffsetBookmark> it = allBookmarks.listIterator();it.hasNext();) {
Index: trunk/src/org/openstreetmap/josm/data/osm/DataSet.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 4126)
@@ -20,4 +20,5 @@
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
+import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.SelectionChangedListener;
@@ -34,8 +35,11 @@
 import org.openstreetmap.josm.data.osm.event.TagsChangedEvent;
 import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent;
+import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.ProjectionChangeListener;
 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager;
 import org.openstreetmap.josm.tools.FilteredCollection;
 import org.openstreetmap.josm.tools.Predicate;
 import org.openstreetmap.josm.tools.SubclassFilteredCollection;
+import org.openstreetmap.josm.tools.Utils;
 
 /**
@@ -83,5 +87,5 @@
  * @author imi
  */
-public class DataSet implements Cloneable {
+public class DataSet implements Cloneable, ProjectionChangeListener {
 
     /**
@@ -120,4 +124,12 @@
     private final ReadWriteLock lock = new ReentrantReadWriteLock();
     private final Object selectionLock = new Object();
+
+    public DataSet() {
+        /*
+         * Transparently register as projection change lister. No need to explicitly remove the
+         * the listener, projection change listeners are managed as WeakReferences.
+         */
+        Main.addProjectionChangeListener(this);
+    }
 
     public Lock getReadLock() {
@@ -975,4 +987,23 @@
     }
 
+    /**
+     * Invalidates the internal cache of projected east/north coordinates.
+     * 
+     * This method can be invoked after the globally configured projection method
+     * changed. In contrast to {@link DataSet#reproject()} it only invalidates the
+     * cache and doesn't reproject the coordinates.
+     */
+    public void invalidateEastNorthCache() {
+        if (Main.getProjection() == null) return; // sanity check
+        try {
+            beginUpdate();
+            for (Node n: Utils.filteredCollection(allPrimitives, Node.class)) {
+                n.invalidateEastNorthCache();
+            }
+        } finally {
+            endUpdate();
+        }
+    }
+
     public void cleanupDeletedPrimitives() {
         beginUpdate();
@@ -1064,3 +1095,11 @@
         return ret;
     }
+
+    /* --------------------------------------------------------------------------------- */
+    /* interface ProjectionChangeListner                                                 */
+    /* --------------------------------------------------------------------------------- */
+    @Override
+    public void projectionChanged(Projection oldValue, Projection newValue) {
+        invalidateEastNorthCache();
+    }
 }
Index: trunk/src/org/openstreetmap/josm/data/osm/INode.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/INode.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/data/osm/INode.java	(revision 4126)
@@ -6,9 +6,8 @@
 
 public interface INode extends IPrimitive {
-    
+
     LatLon getCoor();
     void setCoor(LatLon coor);
     EastNorth getEastNorth();
     void setEastNorth(EastNorth eastNorth);
-
 }
Index: trunk/src/org/openstreetmap/josm/data/osm/Node.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/Node.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/data/osm/Node.java	(revision 4126)
@@ -2,9 +2,10 @@
 package org.openstreetmap.josm.data.osm;
 
-import org.openstreetmap.josm.data.coor.CachedLatLon;
+import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
 import org.openstreetmap.josm.data.osm.visitor.Visitor;
+import org.openstreetmap.josm.data.projection.Projections;
 
 /**
@@ -15,5 +16,20 @@
 public final class Node extends OsmPrimitive implements INode {
 
-    private CachedLatLon coor;
+    /*
+     * We "inline" lat/lon rather than using a LatLon-object => reduces memory footprint
+     */
+    static private final double COORDINATE_NOT_DEFINED = Double.NaN;
+    private double lat = COORDINATE_NOT_DEFINED;
+    private double lon = COORDINATE_NOT_DEFINED;
+
+    /*
+     * the cached projected coordinates
+     */
+    private double east = Double.NaN;
+    private double north = Double.NaN;
+
+    private boolean isLatLonKnown() {
+        return lat != COORDINATE_NOT_DEFINED && lon != COORDINATE_NOT_DEFINED;
+    }
 
     @Override
@@ -46,10 +62,40 @@
     @Override
     public final LatLon getCoor() {
-        return coor;
-    }
-
+        if (!isLatLonKnown()) return null;
+        return new LatLon(lat,lon);
+    }
+
+    /**
+     * <p>Replies the projected east/north coordinates.</p>
+     * 
+     * <p>Uses the {@link Main#getProjection() global projection} to project the lan/lon-coordinates.
+     * Internally caches the projected coordinates.</p>
+     *
+     * <p><strong>Caveat:</strong> doesn't listen to projection changes. Clients must
+     * {@link #invalidateEastNorthCache() invalidate the internal cache}.</p>
+     * 
+     * <p>Replies {@code null} if this node doesn't know lat/lon-coordinates, i.e. because it is an incomplete node.
+     * 
+     * @return the east north coordinates or {@code null}
+     * @see #invalidateEastNorthCache()
+     * 
+     */
     @Override
     public final EastNorth getEastNorth() {
-        return coor != null ? coor.getEastNorth() : null;
+        if (!isLatLonKnown()) return null;
+        
+        if (getDataSet() == null)
+            // there is no dataset that listens for projection changes
+            // and invalidates the cache, so we don't use the cache at all
+            return Projections.project(new LatLon(lat, lon));
+        
+        if (Double.isNaN(east) || Double.isNaN(north)) {
+            // projected coordinates haven't been calculated yet,
+            // so fill the cache of the projected node coordinates
+            EastNorth en = Projections.project(new LatLon(lat, lon));
+            this.east = en.east();
+            this.north = en.north();
+        }
+        return new EastNorth(east, north);
     }
 
@@ -58,17 +104,16 @@
      */
     protected void setCoorInternal(LatLon coor, EastNorth eastNorth) {
-        if(this.coor == null) {
-            if (eastNorth == null) {
-                this.coor = new CachedLatLon(coor);
-            } else {
-                this.coor = new CachedLatLon(eastNorth);
-            }
-        } else {
-            if (eastNorth == null) {
-                this.coor.setCoor(coor);
-            } else {
-                this.coor.setEastNorth(eastNorth);
-            }
-        }
+        if (coor != null) {
+            this.lat = coor.lat();
+            this.lon = coor.lon();
+            invalidateEastNorthCache();
+        } else if (eastNorth != null) {
+            LatLon ll = Projections.inverseProject(eastNorth);
+            this.lat = ll.lat();
+            this.lon = ll.lon();
+            this.east = eastNorth.east();
+            this.north = eastNorth.north();
+        } else
+            throw new IllegalArgumentException();
     }
 
@@ -150,5 +195,5 @@
         try {
             super.cloneFrom(osm);
-            setCoor(((Node)osm).coor);
+            setCoor(((Node)osm).getCoor());
         } finally {
             writeUnlock(locked);
@@ -173,5 +218,5 @@
             super.mergeFrom(other);
             if (!other.isIncomplete()) {
-                setCoor(new LatLon(((Node)other).coor));
+                setCoor(((Node)other).getCoor());
             }
         } finally {
@@ -200,5 +245,5 @@
 
     @Override public String toString() {
-        String coorDesc = coor == null?"":"lat="+coor.lat()+",lon="+coor.lon();
+        String coorDesc = isLatLonKnown() ? "lat="+lat+",lon="+lon : "";
         return "{Node id=" + getUniqueId() + " version=" + getVersion() + " " + getFlagsAsString() + " "  + coorDesc+"}";
     }
@@ -211,8 +256,10 @@
             return false;
         Node n = (Node)other;
-        if (coor == null && n.coor == null)
+        LatLon coor = getCoor();
+        LatLon otherCoor = n.getCoor();
+        if (coor == null && otherCoor == null)
             return true;
-        else if (coor != null && n.coor != null)
-            return coor.equalsEpsilon(n.coor);
+        else if (coor != null && otherCoor != null)
+            return coor.equalsEpsilon(otherCoor);
         else
             return false;
@@ -241,5 +288,4 @@
     @Override
     public void updatePosition() {
-        // TODO: replace CachedLatLon with simple doubles and update precalculated EastNorth value here
     }
 
@@ -253,9 +299,9 @@
         builder.append(toString());
         builder.append("\n");
-        if (coor == null) {
+        if (isLatLonKnown()) {
             builder.append("Coor is null\n");
         } else {
-            builder.append(String.format("EastNorth: %s\n", coor.getEastNorth()));
-            builder.append(coor.getProjection());
+            builder.append(String.format("EastNorth: %s\n", getEastNorth()));
+            builder.append(Main.getProjection());
             builder.append("\n");
         }
@@ -263,3 +309,13 @@
         return builder.toString();
     }
+
+    /**
+     * Invoke to invalidate the internal cache of projected east/north coordinates.
+     * Coordinates are reprojected on demand when the {@link #getEastNorth()} is invoked
+     * next time.
+     */
+    public void invalidateEastNorthCache() {
+        this.east = Double.NaN;
+        this.north = Double.NaN;
+    }
 }
Index: trunk/src/org/openstreetmap/josm/data/osm/NodeData.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/NodeData.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/data/osm/NodeData.java	(revision 4126)
@@ -2,16 +2,18 @@
 package org.openstreetmap.josm.data.osm;
 
-import org.openstreetmap.josm.data.coor.CachedLatLon;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
+import org.openstreetmap.josm.data.projection.Projections;
 
 public class NodeData extends PrimitiveData implements INode {
 
-    private final CachedLatLon coor = new CachedLatLon(0, 0);
+    /*
+     * we "inline" lat/lon coordinates instead of using a LatLon => reduces memory footprint
+     */
+    private double lat = Double.NaN;
+    private double lon = Double.NaN;
 
-    public NodeData() {
-
-    }
+    public NodeData() {}
 
     public NodeData(NodeData data) {
@@ -20,22 +22,37 @@
     }
 
+    private boolean isLatLonKnown() {
+        return lat != Double.NaN && lon != Double.NaN;
+    }
+    
     @Override
     public LatLon getCoor() {
-        return coor;
+        return isLatLonKnown() ? new LatLon(lat,lon) : null;
     }
 
     @Override
     public void setCoor(LatLon coor) {
-        this.coor.setCoor(coor);
+        if (coor == null) {
+            this.lat = Double.NaN;
+            this.lon = Double.NaN;
+        } else {
+            this.lat = coor.lat();
+            this.lon = coor.lon();
+        }
     }
 
     @Override
     public EastNorth getEastNorth() {
-        return this.coor.getEastNorth();
+        /*
+         * No internal caching of projected coordinates needed. In contrast to getEastNorth()
+         * on Node, this method is rarely used. Caching would be overkill.
+         */
+        return Projections.project(getCoor());
     }
 
     @Override
     public void setEastNorth(EastNorth eastNorth) {
-        this.coor.setEastNorth(eastNorth);
+        LatLon ll = Projections.inverseProject(eastNorth);
+        setCoor(ll);
     }
 
@@ -47,5 +64,5 @@
     @Override
     public String toString() {
-        return super.toString() + " NODE " + coor;
+        return super.toString() + " NODE " + getCoor();
     }
 
@@ -54,6 +71,6 @@
         return OsmPrimitiveType.NODE;
     }
-    
-    @Override 
+
+    @Override
     public void visit(PrimitiveVisitor visitor) {
         visitor.visit(this);
@@ -64,4 +81,3 @@
         return formatter.format(this);
     }
-
 }
Index: trunk/src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java	(revision 4126)
@@ -113,9 +113,9 @@
         if (bounds == null)
             return;
-        LatLon minLatlon = Main.proj.eastNorth2latlon(bounds.getMin());
-        LatLon maxLatlon = Main.proj.eastNorth2latlon(bounds.getMax());
+        LatLon minLatlon = Main.getProjection().eastNorth2latlon(bounds.getMin());
+        LatLon maxLatlon = Main.getProjection().eastNorth2latlon(bounds.getMax());
         bounds = new ProjectionBounds(
-                Main.proj.latlon2eastNorth(new LatLon(minLatlon.lat() - enlargeDegree, minLatlon.lon() - enlargeDegree)),
-                Main.proj.latlon2eastNorth(new LatLon(maxLatlon.lat() + enlargeDegree, maxLatlon.lon() + enlargeDegree)));
+                Main.getProjection().latlon2eastNorth(new LatLon(minLatlon.lat() - enlargeDegree, minLatlon.lon() - enlargeDegree)),
+                Main.getProjection().latlon2eastNorth(new LatLon(maxLatlon.lat() + enlargeDegree, maxLatlon.lon() + enlargeDegree)));
     }
 
Index: trunk/src/org/openstreetmap/josm/data/projection/ProjectionChangeListener.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/ProjectionChangeListener.java	(revision 4126)
+++ trunk/src/org/openstreetmap/josm/data/projection/ProjectionChangeListener.java	(revision 4126)
@@ -0,0 +1,6 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.projection;
+
+public interface ProjectionChangeListener {
+    void projectionChanged(Projection oldValue, Projection newValue);
+}
Index: trunk/src/org/openstreetmap/josm/data/projection/Projections.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/Projections.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/data/projection/Projections.java	(revision 4126)
@@ -2,6 +2,10 @@
 package org.openstreetmap.josm.data.projection;
 
+import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.ArrayList;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.coor.LatLon;
 
 /**
@@ -14,19 +18,19 @@
      */
     private static ArrayList<Projection> allProjections =
-    new ArrayList<Projection>(Arrays.asList(new Projection[] {
-        // global projections
-        new Epsg4326(),
-        new Mercator(),
-        new UTM(),
-        // regional - alphabetical order by country name
-        new LambertEST(), // Still needs proper default zoom
-        new Lambert(),    // Still needs proper default zoom
-        new LambertCC9Zones(),    // Still needs proper default zoom
-        new UTM_France_DOM(),
-        new TransverseMercatorLV(),
-        new Puwg(),
-        new Epsg3008(), // SWEREF99 13 30
-        new SwissGrid(),
-    }));
+        new ArrayList<Projection>(Arrays.asList(new Projection[] {
+                // global projections
+                new Epsg4326(),
+                new Mercator(),
+                new UTM(),
+                // regional - alphabetical order by country name
+                new LambertEST(), // Still needs proper default zoom
+                new Lambert(),    // Still needs proper default zoom
+                new LambertCC9Zones(),    // Still needs proper default zoom
+                new UTM_France_DOM(),
+                new TransverseMercatorLV(),
+                new Puwg(),
+                new Epsg3008(), // SWEREF99 13 30
+                new SwissGrid(),
+        }));
 
     public static ArrayList<Projection> getProjections() {
@@ -43,3 +47,13 @@
         allProjections.add(proj);
     }
+
+    static public EastNorth project(LatLon ll) {
+        if (ll == null) return null;
+        return Main.getProjection().latlon2eastNorth(ll);
+    }
+
+    static public LatLon inverseProject(EastNorth en) {
+        if (en == null) return null;
+        return Main.getProjection().eastNorth2latlon(en);
+    }
 }
Index: trunk/src/org/openstreetmap/josm/data/validation/OsmValidator.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/validation/OsmValidator.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/data/validation/OsmValidator.java	(revision 4126)
@@ -23,5 +23,4 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.ValidateAction;
-import org.openstreetmap.josm.actions.upload.ValidateUploadHook;
 import org.openstreetmap.josm.data.projection.Epsg4326;
 import org.openstreetmap.josm.data.projection.Lambert;
@@ -30,6 +29,6 @@
 import org.openstreetmap.josm.data.validation.tests.CrossingWays;
 import org.openstreetmap.josm.data.validation.tests.DuplicateNode;
+import org.openstreetmap.josm.data.validation.tests.DuplicateRelation;
 import org.openstreetmap.josm.data.validation.tests.DuplicateWay;
-import org.openstreetmap.josm.data.validation.tests.DuplicateRelation;
 import org.openstreetmap.josm.data.validation.tests.DuplicatedWayNodes;
 import org.openstreetmap.josm.data.validation.tests.MultipolygonTest;
@@ -48,7 +47,7 @@
 import org.openstreetmap.josm.data.validation.tests.WronglyOrderedWays;
 import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
-import org.openstreetmap.josm.gui.layer.ValidatorLayer;
 import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.layer.ValidatorLayer;
 import org.openstreetmap.josm.gui.preferences.ValidatorPreference;
 
@@ -77,24 +76,24 @@
     @SuppressWarnings("unchecked")
     public static Class<Test>[] allAvailableTests = new Class[] {
-            DuplicateNode.class, // ID    1 ..   99
-            OverlappingWays.class, // ID  101 ..  199
-            UntaggedNode.class, // ID  201 ..  299
-            UntaggedWay.class, // ID  301 ..  399
-            SelfIntersectingWay.class, // ID  401 ..  499
-            DuplicatedWayNodes.class, // ID  501 ..  599
-            CrossingWays.class, // ID  601 ..  699
-            SimilarNamedWays.class, // ID  701 ..  799
-            NodesWithSameName.class, // ID  801 ..  899
-            Coastlines.class, // ID  901 ..  999
-            WronglyOrderedWays.class, // ID 1001 .. 1099
-            UnclosedWays.class, // ID 1101 .. 1199
-            TagChecker.class, // ID 1201 .. 1299
-            UnconnectedWays.class, // ID 1301 .. 1399
-            DuplicateWay.class, // ID 1401 .. 1499
-            NameMismatch.class, // ID  1501 ..  1599
-            MultipolygonTest.class, // ID  1601 ..  1699
-            RelationChecker.class, // ID  1701 ..  1799
-            TurnrestrictionTest.class, // ID  1801 ..  1899
-            DuplicateRelation.class, // ID 1901 .. 1999
+        DuplicateNode.class, // ID    1 ..   99
+        OverlappingWays.class, // ID  101 ..  199
+        UntaggedNode.class, // ID  201 ..  299
+        UntaggedWay.class, // ID  301 ..  399
+        SelfIntersectingWay.class, // ID  401 ..  499
+        DuplicatedWayNodes.class, // ID  501 ..  599
+        CrossingWays.class, // ID  601 ..  699
+        SimilarNamedWays.class, // ID  701 ..  799
+        NodesWithSameName.class, // ID  801 ..  899
+        Coastlines.class, // ID  901 ..  999
+        WronglyOrderedWays.class, // ID 1001 .. 1099
+        UnclosedWays.class, // ID 1101 .. 1199
+        TagChecker.class, // ID 1201 .. 1299
+        UnconnectedWays.class, // ID 1301 .. 1399
+        DuplicateWay.class, // ID 1401 .. 1499
+        NameMismatch.class, // ID  1501 ..  1599
+        MultipolygonTest.class, // ID  1601 ..  1699
+        RelationChecker.class, // ID  1701 ..  1799
+        TurnrestrictionTest.class, // ID  1801 ..  1899
+        DuplicateRelation.class, // ID 1901 .. 1999
     };
 
@@ -242,9 +241,9 @@
      */
     public void initializeGridDetail() {
-        if (Main.proj.toString().equals(new Epsg4326().toString())) {
+        if (Main.getProjection().toString().equals(new Epsg4326().toString())) {
             OsmValidator.griddetail = 10000;
-        } else if (Main.proj.toString().equals(new Mercator().toString())) {
+        } else if (Main.getProjection().toString().equals(new Mercator().toString())) {
             OsmValidator.griddetail = 0.01;
-        } else if (Main.proj.toString().equals(new Lambert().toString())) {
+        } else if (Main.getProjection().toString().equals(new Lambert().toString())) {
             OsmValidator.griddetail = 0.1;
         }
@@ -265,7 +264,7 @@
                 JOptionPane.showMessageDialog(Main.parent,
                         tr("Error initializing test {0}:\n {1}", test.getClass()
-                        .getSimpleName(), e),
-                        tr("Error"),
-                        JOptionPane.ERROR_MESSAGE);
+                                .getSimpleName(), e),
+                                tr("Error"),
+                                JOptionPane.ERROR_MESSAGE);
             }
         }
Index: trunk/src/org/openstreetmap/josm/gui/MapView.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MapView.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/gui/MapView.java	(revision 4126)
@@ -118,5 +118,5 @@
      *
      * @param listener the listener. Ignored if null or already registered.
-     * @param initialFire Fire an edit-layer-changed-event right after adding 
+     * @param initialFire Fire an edit-layer-changed-event right after adding
      * the listener in case there is an edit layer present
      */
@@ -285,4 +285,5 @@
         }
         layer.addPropertyChangeListener(this);
+        Main.addProjectionChangeListener(layer);
         AudioPlayer.reset();
         repaint();
@@ -359,4 +360,5 @@
 
         layers.remove(layer);
+        Main.removeProjectionChangeListener(layer);
         fireLayerRemoved(layer);
         layer.removePropertyChangeListener(this);
Index: trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java	(revision 4126)
@@ -39,4 +39,5 @@
 import org.openstreetmap.josm.data.preferences.IntegerProperty;
 import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.Projections;
 import org.openstreetmap.josm.gui.help.Helpful;
 import org.openstreetmap.josm.gui.preferences.ProjectionPreference;
@@ -96,5 +97,5 @@
      * northing/easting space of the projection.
      */
-    private double scale = Main.proj.getDefaultZoomInPPD();
+    private double scale = Main.getProjection().getDefaultZoomInPPD();
     /**
      * Center n/e coordinate of the desired screen center.
@@ -111,9 +112,9 @@
 
     private EastNorth calculateDefaultCenter() {
-        Bounds b = Main.proj.getWorldBoundsLatLon();
+        Bounds b = Main.getProjection().getWorldBoundsLatLon();
         double lat = (b.getMax().lat() + b.getMin().lat())/2;
         double lon = (b.getMax().lon() + b.getMin().lon())/2;
 
-        return Main.proj.latlon2eastNorth(new LatLon(lat, lon));
+        return Main.getProjection().latlon2eastNorth(new LatLon(lat, lon));
     }
 
@@ -209,5 +210,5 @@
         EastNorth p2 = getEastNorth(r.x + r.width, r.y + r.height);
 
-        Bounds result = new Bounds(Main.proj.eastNorth2latlon(p1));
+        Bounds result = new Bounds(Main.getProjection().eastNorth2latlon(p1));
 
         double eastMin = Math.min(p1.east(), p2.east());
@@ -219,8 +220,8 @@
 
         for (int i=0; i < 10; i++) {
-            result.extend(Main.proj.eastNorth2latlon(new EastNorth(eastMin + i * deltaEast, northMin)));
-            result.extend(Main.proj.eastNorth2latlon(new EastNorth(eastMin + i * deltaEast, northMax)));
-            result.extend(Main.proj.eastNorth2latlon(new EastNorth(eastMin, northMin  + i * deltaNorth)));
-            result.extend(Main.proj.eastNorth2latlon(new EastNorth(eastMax, northMin  + i * deltaNorth)));
+            result.extend(Main.getProjection().eastNorth2latlon(new EastNorth(eastMin + i * deltaEast, northMin)));
+            result.extend(Main.getProjection().eastNorth2latlon(new EastNorth(eastMin + i * deltaEast, northMax)));
+            result.extend(Main.getProjection().eastNorth2latlon(new EastNorth(eastMin, northMin  + i * deltaNorth)));
+            result.extend(Main.getProjection().eastNorth2latlon(new EastNorth(eastMax, northMin  + i * deltaNorth)));
         }
 
@@ -250,4 +251,5 @@
             return getPoint2D(getProjection().latlon2eastNorth(latlon));
     }
+
     public Point2D getPoint2D(Node n) {
         return getPoint2D(n.getEastNorth());
@@ -282,5 +284,5 @@
     public void zoomTo(EastNorth newCenter, double newScale) {
         Bounds b = getProjection().getWorldBoundsLatLon();
-        CachedLatLon cl = new CachedLatLon(newCenter);
+        LatLon cl = Projections.inverseProject(newCenter);
         boolean changed = false;
         double lat = cl.lat();
@@ -291,5 +293,5 @@
         else if(lon > b.getMax().lon()) {changed = true; lon = b.getMax().lon(); }
         if(changed) {
-            newCenter = new CachedLatLon(lat, lon).getEastNorth();
+            newCenter = Projections.project(new LatLon(lat,lon));
         }
         int width = getWidth()/2;
@@ -350,17 +352,9 @@
 
     public void zoomTo(LatLon newCenter) {
-        if(newCenter instanceof CachedLatLon) {
-            zoomTo(((CachedLatLon)newCenter).getEastNorth(), scale);
-        } else {
-            zoomTo(getProjection().latlon2eastNorth(newCenter), scale);
-        }
+        zoomTo(Projections.project(newCenter));
     }
 
     public void smoothScrollTo(LatLon newCenter) {
-        if (newCenter instanceof CachedLatLon) {
-            smoothScrollTo(((CachedLatLon)newCenter).getEastNorth());
-        } else {
-            smoothScrollTo(getProjection().latlon2eastNorth(newCenter));
-        }
+        smoothScrollTo(Projections.project(newCenter));
     }
 
@@ -441,5 +435,5 @@
 
         public ZoomData(EastNorth center, double scale) {
-            this.center = new CachedLatLon(center);
+            this.center = Projections.inverseProject(center);
             this.scale = scale;
         }
@@ -1145,5 +1139,5 @@
      */
     public Projection getProjection() {
-        return Main.proj;
+        return Main.getProjection();
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 4126)
@@ -52,4 +52,5 @@
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.data.gpx.GpxRoute;
 import org.openstreetmap.josm.data.gpx.GpxTrack;
 import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
@@ -59,4 +60,5 @@
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+import org.openstreetmap.josm.data.projection.Projection;
 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
@@ -330,6 +332,6 @@
 
         g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
-        Main.pref.getBoolean("mappaint.gpx.use-antialiasing", false) ?
-                RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
+                Main.pref.getBoolean("mappaint.gpx.use-antialiasing", false) ?
+                        RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
 
         /****************************************************************
@@ -1231,5 +1233,5 @@
         if (bestEN == null)
             return null;
-        WayPoint best = new WayPoint(Main.proj.eastNorth2latlon(bestEN));
+        WayPoint best = new WayPoint(Main.getProjection().eastNorth2latlon(bestEN));
         best.time = bestTime;
         return best;
@@ -1490,5 +1492,33 @@
             importer.importDataHandleExceptions(files, NullProgressMonitor.INSTANCE);
         }
-
+    }
+
+    @Override
+    public void projectionChanged(Projection oldValue, Projection newValue) {
+        if (newValue == null) return;
+        if (data.waypoints != null) {
+            for (WayPoint wp : data.waypoints){
+                wp.invalidateEastNorthCache();
+            }
+        }
+        if (data.tracks != null){
+            for (GpxTrack track: data.tracks) {
+                for (GpxTrackSegment segment: track.getSegments()) {
+                    for (WayPoint wp: segment.getWayPoints()) {
+                        wp.invalidateEastNorthCache();
+                    }
+                }
+            }
+        }
+        if (data.routes != null) {
+            for (GpxRoute route: data.routes) {
+                if (route.routePoints == null) {
+                    continue;
+                }
+                for (WayPoint wp: route.routePoints) {
+                    wp.invalidateEastNorthCache();
+                }
+            }
+        }
     }
 }
Index: trunk/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java	(revision 4126)
@@ -83,5 +83,5 @@
 
     public double getPPD(){
-        if (Main.map == null || Main.map.mapView == null) return Main.proj.getDefaultZoomInPPD();
+        if (Main.map == null || Main.map.mapView == null) return Main.getProjection().getDefaultZoomInPPD();
         ProjectionBounds bounds = Main.map.mapView.getProjectionBounds();
         return Main.map.mapView.getWidth() / (bounds.maxEast - bounds.minEast);
Index: trunk/src/org/openstreetmap/josm/gui/layer/Layer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/Layer.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/gui/layer/Layer.java	(revision 4126)
@@ -23,4 +23,6 @@
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.ProjectionChangeListener;
 import org.openstreetmap.josm.gui.MapView;
 import org.openstreetmap.josm.tools.Destroyable;
@@ -42,5 +44,5 @@
  * @author imi
  */
-abstract public class Layer implements Destroyable, MapViewPaintable {
+abstract public class Layer implements Destroyable, MapViewPaintable, ProjectionChangeListener {
 
     public interface LayerAction {
@@ -362,3 +364,11 @@
         }
     }
+
+    /* --------------------------------------------------------------------------------- */
+    /* interface ProjectionChangeListener                                                */
+    /* --------------------------------------------------------------------------------- */
+    @Override
+    public void projectionChanged(Projection oldValue, Projection newValue) {
+        // default implementation does nothing - override in subclasses
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 4126)
@@ -62,4 +62,5 @@
 import org.openstreetmap.josm.data.osm.visitor.paint.MapRendererFactory;
 import org.openstreetmap.josm.data.osm.visitor.paint.Rendering;
+import org.openstreetmap.josm.data.projection.Projection;
 import org.openstreetmap.josm.data.validation.TestError;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
@@ -80,4 +81,6 @@
  */
 public class OsmDataLayer extends Layer implements Listener, SelectionChangedListener {
+    // static private final Logger logger = Logger.getLogger(OsmDataLayer.class.getName());
+
     static public final String REQUIRES_SAVE_TO_DISK_PROP = OsmDataLayer.class.getName() + ".requiresSaveToDisk";
     static public final String REQUIRES_UPLOAD_TO_SERVER_PROP = OsmDataLayer.class.getName() + ".requiresUploadToServer";
@@ -658,5 +661,4 @@
             }
         }
-
     }
 
@@ -675,3 +677,11 @@
         isChanged = true;
     }
+
+    @Override
+    public void projectionChanged(Projection oldValue, Projection newValue) {
+        /*
+         * No reprojection required. The dataset itself is registered as projection
+         * change listener and already got notified.
+         */
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java	(revision 4126)
@@ -95,5 +95,5 @@
         public GpsPoint(LatLon ll, String t) {
             latlon = ll;
-            eastNorth = Main.proj.latlon2eastNorth(ll);
+            eastNorth = Main.getProjection().latlon2eastNorth(ll);
             time = t;
         }
Index: trunk/src/org/openstreetmap/josm/gui/layer/TMSLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/TMSLayer.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/gui/layer/TMSLayer.java	(revision 4126)
@@ -871,5 +871,5 @@
 
     private Point pixelPos(LatLon ll) {
-        return Main.map.mapView.getPoint(Main.proj.latlon2eastNorth(ll).add(getDx(), getDy()));
+        return Main.map.mapView.getPoint(Main.getProjection().latlon2eastNorth(ll).add(getDx(), getDy()));
     }
     private Point pixelPos(Tile t) {
@@ -879,5 +879,5 @@
     }
     private LatLon getShiftedLatLon(EastNorth en) {
-        return Main.proj.eastNorth2latlon(en.add(-getDx(), -getDy()));
+        return Main.getProjection().eastNorth2latlon(en.add(-getDx(), -getDy()));
     }
     private Coordinate getShiftedCoord(EastNorth en) {
Index: trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java	(revision 4126)
@@ -16,5 +16,4 @@
 import javax.swing.Icon;
 
-import org.openstreetmap.josm.data.coor.CachedLatLon;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.coor.LatLon;
@@ -23,4 +22,5 @@
 import org.openstreetmap.josm.data.gpx.WayPoint;
 import org.openstreetmap.josm.data.preferences.IntegerProperty;
+import org.openstreetmap.josm.data.projection.Projections;
 import org.openstreetmap.josm.gui.MapView;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -70,12 +70,8 @@
                              may be adjusted later to sync with other data, so not final */
 
-    private CachedLatLon coor;
+    private LatLon coor;
 
     public final void setCoor(LatLon coor) {
-        if(this.coor == null) {
-            this.coor = new CachedLatLon(coor);
-        } else {
-            this.coor.setCoor(coor);
-        }
+        this.coor = new LatLon(coor);
     }
 
@@ -85,9 +81,9 @@
 
     public final void setEastNorth(EastNorth eastNorth) {
-        coor.setEastNorth(eastNorth);
+        this.coor = Projections.inverseProject(eastNorth);
     }
 
     public final EastNorth getEastNorth() {
-        return coor.getEastNorth();
+        return Projections.project(this.coor);
     }
 
@@ -264,10 +260,8 @@
      */
     public String getText() {
-        if (this.text != null ) {
+        if (this.text != null )
             return this.text;
-        }
-        else {
+        else
             return getText(this.textMap);
-        }
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/preferences/ImageryPreference.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/ImageryPreference.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/ImageryPreference.java	(revision 4126)
@@ -734,5 +734,5 @@
                 @Override
                 public void actionPerformed(ActionEvent e) {
-                    OffsetBookmark b = new OffsetBookmark(Main.proj,"","",0,0);
+                    OffsetBookmark b = new OffsetBookmark(Main.getProjection(),"","",0,0);
                     model.addRow(b);
                 }
Index: trunk/src/org/openstreetmap/josm/gui/preferences/ProjectionPreference.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/ProjectionPreference.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/ProjectionPreference.java	(revision 4126)
@@ -9,5 +9,4 @@
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.concurrent.CopyOnWriteArrayList;
 
 import javax.swing.BorderFactory;
@@ -27,6 +26,6 @@
 import org.openstreetmap.josm.data.projection.Mercator;
 import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.ProjectionSubPrefs;
 import org.openstreetmap.josm.data.projection.Projections;
-import org.openstreetmap.josm.data.projection.ProjectionSubPrefs;
 import org.openstreetmap.josm.gui.NavigatableComponent;
 import org.openstreetmap.josm.plugins.PluginHandler;
@@ -39,8 +38,4 @@
             return new ProjectionPreference();
         }
-    }
-
-    public interface ProjectionChangedListener {
-        void projectionChanged();
     }
 
@@ -65,22 +60,4 @@
     }
 
-    //TODO This is not nice place for a listener code but probably only Dataset will want to listen for projection changes so it's acceptable
-    private static CopyOnWriteArrayList<ProjectionChangedListener> listeners = new CopyOnWriteArrayList<ProjectionChangedListener>();
-
-    public static void addProjectionChangedListener(ProjectionChangedListener listener) {
-        listeners.addIfAbsent(listener);
-    }
-
-    public static void removeProjectionChangedListener(ProjectionChangedListener listener) {
-        listeners.remove(listener);
-    }
-
-    private static void fireProjectionChanged() {
-        for (ProjectionChangedListener listener: listeners) {
-            listener.projectionChanged();
-        }
-    }
-
-
     /**
      * Combobox with all projections available
@@ -161,5 +138,5 @@
         gui.mapcontent.addTab(tr("Map Projection"), scrollpane);
 
-        updateMeta(Main.proj);
+        updateMeta(Main.getProjection());
     }
 
@@ -202,18 +179,16 @@
     {
         Bounds b = (Main.map != null && Main.map.mapView != null) ? Main.map.mapView.getRealBounds() : null;
-        Projection oldProj = Main.proj;
-
-        Projection p = null;
+
+        Projection proj = null;
         for (ClassLoader cl : PluginHandler.getResourceClassLoaders()) {
             try {
-                p = (Projection) Class.forName(name, true, cl).newInstance();
+                proj = (Projection) Class.forName(name, true, cl).newInstance();
             } catch (final Exception e) {
             }
-            if (p != null) {
-                Main.proj = p;
-                break;
-            }
-        }
-        if (p == null) {
+            if (proj != null) {
+                break;
+            }
+        }
+        if (proj == null) {
             JOptionPane.showMessageDialog(
                     Main.parent,
@@ -223,14 +198,15 @@
             );
             coll = null;
-            Main.proj = new Mercator();
-            name = Main.proj.getClass().getName();
+            proj = new Mercator();
+            name = Main.getProjection().getClass().getName();
         }
         PROP_SUB_PROJECTION.put(coll);
         PROP_PROJECTION_SUBPROJECTION.put(coll, name);
-        if(Main.proj instanceof ProjectionSubPrefs) {
-            ((ProjectionSubPrefs) Main.proj).setPreferences(coll);
-        }
-        fireProjectionChanged(); // This should be probably called from the if bellow, but hashCode condition doesn't look sure enough
-        if(b != null && (!Main.proj.getClass().getName().equals(oldProj.getClass().getName()) || Main.proj.hashCode() != oldProj.hashCode()))
+        if(proj instanceof ProjectionSubPrefs) {
+            ((ProjectionSubPrefs) proj).setPreferences(coll);
+        }
+        Projection oldProj = Main.getProjection();
+        Main.setProjection(proj);
+        if(b != null && (!proj.getClass().getName().equals(oldProj.getClass().getName()) || proj.hashCode() != oldProj.hashCode()))
         {
             Main.map.mapView.zoomTo(b);
Index: trunk/src/org/openstreetmap/josm/io/imagery/Grabber.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/imagery/Grabber.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/io/imagery/Grabber.java	(revision 4126)
@@ -41,5 +41,5 @@
         }
 
-        this.proj = Main.proj;
+        this.proj = Main.getProjection();
         this.pixelPerDegree = request.getPixelPerDegree();
         this.request = request;
Index: trunk/src/org/openstreetmap/josm/io/imagery/HTMLGrabber.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/imagery/HTMLGrabber.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/io/imagery/HTMLGrabber.java	(revision 4126)
@@ -52,5 +52,5 @@
         BufferedImage img = layer.normalizeImage(ImageIO.read(bais));
         bais.reset();
-        layer.cache.saveToCache(layer.isOverlapEnabled()?img:null, bais, Main.proj, pixelPerDegree, b.minEast, b.minNorth);
+        layer.cache.saveToCache(layer.isOverlapEnabled()?img:null, bais, Main.getProjection(), pixelPerDegree, b.minEast, b.minNorth);
 
         return img;
Index: trunk/src/org/openstreetmap/josm/io/imagery/OsmosnimkiOffsetServer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/imagery/OsmosnimkiOffsetServer.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/io/imagery/OsmosnimkiOffsetServer.java	(revision 4126)
@@ -40,5 +40,5 @@
     @Override
     public EastNorth getOffset(ImageryInfo info, EastNorth en) {
-        LatLon ll = Main.proj.eastNorth2latlon(en);
+        LatLon ll = Main.getProjection().eastNorth2latlon(en);
         try {
             URL url = new URL(this.url + "action=GetOffsetForPoint&lat=" + ll.lat() + "&lon=" + ll.lon() + "&id=" + URLEncoder.encode(info.getFullUrl(), "UTF-8"));
@@ -50,5 +50,5 @@
             String sLon = s.substring(1,i);
             String sLat = s.substring(i+1,s.length()-1);
-            return Main.proj.latlon2eastNorth(new LatLon(Double.valueOf(sLat),Double.valueOf(sLon))).sub(en);
+            return Main.getProjection().latlon2eastNorth(new LatLon(Double.valueOf(sLat),Double.valueOf(sLon))).sub(en);
         } catch (Exception e) {
             e.printStackTrace();
Index: trunk/src/org/openstreetmap/josm/io/imagery/WMSGrabber.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/imagery/WMSGrabber.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/io/imagery/WMSGrabber.java	(revision 4126)
@@ -72,9 +72,9 @@
     protected URL getURL(double w, double s,double e,double n,
             int wi, int ht) throws MalformedURLException {
-        String myProj = Main.proj.toCode();
-        if(Main.proj instanceof Mercator) // don't use mercator code directly
+        String myProj = Main.getProjection().toCode();
+        if(Main.getProjection() instanceof Mercator) // don't use mercator code directly
         {
-            LatLon sw = Main.proj.eastNorth2latlon(new EastNorth(w, s));
-            LatLon ne = Main.proj.eastNorth2latlon(new EastNorth(e, n));
+            LatLon sw = Main.getProjection().eastNorth2latlon(new EastNorth(w, s));
+            LatLon ne = Main.getProjection().eastNorth2latlon(new EastNorth(e, n));
             myProj = "EPSG:4326";
             s = sw.lat();
@@ -114,6 +114,6 @@
     static public String getProjection(String baseURL, Boolean warn)
     {
-        String projname = Main.proj.toCode();
-        if(Main.proj instanceof Mercator) {
+        String projname = Main.getProjection().toCode();
+        if(Main.getProjection() instanceof Mercator) {
             projname = "EPSG:4326";
         }
@@ -146,5 +146,5 @@
     @Override
     public boolean loadFromCache(WMSRequest request) {
-        BufferedImage cached = layer.cache.getExactMatch(Main.proj, pixelPerDegree, b.minEast, b.minNorth);
+        BufferedImage cached = layer.cache.getExactMatch(Main.getProjection(), pixelPerDegree, b.minEast, b.minNorth);
 
         if (cached != null) {
@@ -152,5 +152,5 @@
             return true;
         } else if (request.isAllowPartialCacheMatch()) {
-            BufferedImage partialMatch = layer.cache.getPartialMatch(Main.proj, pixelPerDegree, b.minEast, b.minNorth);
+            BufferedImage partialMatch = layer.cache.getPartialMatch(Main.getProjection(), pixelPerDegree, b.minEast, b.minNorth);
             if (partialMatch != null) {
                 request.finish(State.PARTLY_IN_CACHE, partialMatch);
@@ -194,5 +194,5 @@
         BufferedImage img = layer.normalizeImage(ImageIO.read(bais));
         bais.reset();
-        layer.cache.saveToCache(layer.isOverlapEnabled()?img:null, bais, Main.proj, pixelPerDegree, b.minEast, b.minNorth);
+        layer.cache.saveToCache(layer.isOverlapEnabled()?img:null, bais, Main.getProjection(), pixelPerDegree, b.minEast, b.minNorth);
         return img;
     }
Index: trunk/src/org/openstreetmap/josm/tools/Geometry.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/Geometry.java	(revision 4120)
+++ trunk/src/org/openstreetmap/josm/tools/Geometry.java	(revision 4126)
@@ -112,5 +112,5 @@
                                 }
 
-                                Node newNode = new Node(Main.proj.eastNorth2latlon(intersection));
+                                Node newNode = new Node(Main.getProjection().eastNorth2latlon(intersection));
                                 Node intNode = newNode;
                                 boolean insertInSeg1 = false;
@@ -458,5 +458,5 @@
         return inside;
     }
-    
+
     /**
      * returns area of a closed way in square meters
@@ -478,5 +478,5 @@
         return Math.abs(area/2);
     }
-    
+
     protected static double calcX(Node p1){
         double lat1, lon1, lat2, lon2;
@@ -495,5 +495,5 @@
         return 6367000 * c;
     }
-    
+
     protected static double calcY(Node p1){
         double lat1, lon1, lat2, lon2;
