From 77f5b2b45b8c07f1b32922d324148a7452c30e43 Mon Sep 17 00:00:00 2001
From: Gubaer <karl.gugisberg@guggis.ch>
Date: Sun, 5 Jun 2011 11:01:23 +0200
Subject: [PATCH] Combined commits from cached-lat-lon, patch against JOSM 4115

---
 src/org/openstreetmap/josm/Main.java               |  116 ++++++++++++++++++-
 .../josm/actions/CreateCircleAction.java           |    4 +-
 .../josm/actions/ImageryAdjustAction.java          |    4 +-
 .../josm/actions/OrthogonalizeAction.java          |    2 +-
 .../josm/actions/mapmode/ExtrudeAction.java        |   10 +-
 .../openstreetmap/josm/command/MoveCommand.java    |    4 +-
 .../openstreetmap/josm/data/coor/CachedLatLon.java |   88 ++++++++++-----
 src/org/openstreetmap/josm/data/coor/LatLon.java   |    9 +-
 src/org/openstreetmap/josm/data/gpx/GpxTrack.java  |    1 -
 .../josm/data/gpx/ImmutableGpxTrackSegment.java    |    1 -
 src/org/openstreetmap/josm/data/gpx/WayPoint.java  |   73 +++++++++++-
 .../josm/data/imagery/OffsetBookmark.java          |    6 +-
 src/org/openstreetmap/josm/data/osm/DataSet.java   |   67 +++++++++++-
 src/org/openstreetmap/josm/data/osm/INode.java     |    3 +-
 src/org/openstreetmap/josm/data/osm/Node.java      |  119 +++++++++++++++----
 src/org/openstreetmap/josm/data/osm/NodeData.java  |   38 ++++--
 .../josm/data/osm/visitor/BoundingXYVisitor.java   |   18 +--
 .../data/projection/ProjectionChangeListener.java  |    6 +
 .../josm/data/projection/Projections.java          |   44 +++++---
 .../josm/data/validation/OsmValidator.java         |   57 +++++-----
 src/org/openstreetmap/josm/gui/MapView.java        |    4 +-
 .../josm/gui/NavigatableComponent.java             |   44 +++-----
 src/org/openstreetmap/josm/gui/layer/GpxLayer.java |   36 ++++++-
 .../openstreetmap/josm/gui/layer/ImageryLayer.java |    2 +-
 src/org/openstreetmap/josm/gui/layer/Layer.java    |   12 ++-
 .../openstreetmap/josm/gui/layer/OsmDataLayer.java |   12 ++-
 .../openstreetmap/josm/gui/layer/RawGpsLayer.java  |    2 +-
 src/org/openstreetmap/josm/gui/layer/TMSLayer.java |    4 +-
 .../josm/gui/layer/geoimage/ImageEntry.java        |   10 +-
 .../josm/gui/layer/markerlayer/Marker.java         |   92 +++++++--------
 .../josm/gui/preferences/ImageryPreference.java    |    2 +-
 .../josm/gui/preferences/ProjectionPreference.java |   46 +------
 src/org/openstreetmap/josm/io/imagery/Grabber.java |    2 +-
 .../openstreetmap/josm/io/imagery/HTMLGrabber.java |    2 +-
 .../josm/io/imagery/OsmosnimkiOffsetServer.java    |    4 +-
 .../openstreetmap/josm/io/imagery/WMSGrabber.java  |   18 ++--
 src/org/openstreetmap/josm/tools/Geometry.java     |    8 +-
 .../openstreetmap/josm/fixtures/JOSMFixture.java   |    2 +-
 .../pair/properties/PropertiesMergerTest.java      |    2 +-
 .../josm/gui/history/HistoryBrowserTest.java       |    2 +-
 .../josm/io/MultiFetchServerObjectReaderTest.java  |    2 +-
 .../josm/io/OsmServerBackreferenceReaderTest.java  |    2 +-
 .../data/osm/MapPaintVisitorPerformanceTest.java   |    2 +-
 .../josm/data/osm/DataSetMergerTest.java           |    2 +-
 .../openstreetmap/josm/data/osm/FilterTest.java    |    2 +-
 .../josm/data/osm/NodeProjectionTest.java          |   72 ++++++++++++
 .../josm/data/osm/OsmPrimitiveTest.java            |    2 +-
 .../josm/data/osm/QuadBucketsTest.java             |    4 +-
 .../openstreetmap/josm/data/osm/RelationTest.java  |    2 +-
 .../visitor/MergeSourceBuildingVisitorTest.java    |    2 +-
 .../josm/data/projection/SwissGridTest.java        |  122 +++++++++++++-------
 .../properties/PropertiesMergeModelTest.java       |    2 +-
 52 files changed, 826 insertions(+), 366 deletions(-)
 create mode 100644 src/org/openstreetmap/josm/data/projection/ProjectionChangeListener.java
 create mode 100644 test/unit/org/openstreetmap/josm/data/osm/NodeProjectionTest.java

diff --git a/src/org/openstreetmap/josm/Main.java b/src/org/openstreetmap/josm/Main.java
index c25d00b..4ddd9e0 100644
--- a/src/org/openstreetmap/josm/Main.java
+++ b/src/org/openstreetmap/josm/Main.java
@@ -15,10 +15,12 @@ import java.awt.event.KeyEvent;
 import java.awt.event.WindowAdapter;
 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;
 import java.util.StringTokenizer;
@@ -55,6 +57,7 @@ import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.DataSet;
 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;
 import org.openstreetmap.josm.gui.MainMenu;
@@ -72,6 +75,7 @@ import org.openstreetmap.josm.gui.preferences.TaggingPresetPreference;
 import org.openstreetmap.josm.gui.preferences.ToolbarPreferences;
 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;
 import org.openstreetmap.josm.tools.OsmUrlToBounds;
@@ -118,10 +122,8 @@ abstract public class Main {
      */
     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.
      */
@@ -212,7 +214,7 @@ abstract public class Main {
         isOpenjdk = System.getProperty("java.vm.name").toUpperCase().indexOf("OPENJDK") != -1;
         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.
         try {
@@ -767,4 +769,108 @@ abstract public class Main {
         }
         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();
+                }
+            }
+        }
+    }
 }
diff --git a/src/org/openstreetmap/josm/actions/CreateCircleAction.java b/src/org/openstreetmap/josm/actions/CreateCircleAction.java
index ebf7666..1afea76 100644
--- a/src/org/openstreetmap/josm/actions/CreateCircleAction.java
+++ b/src/org/openstreetmap/josm/actions/CreateCircleAction.java
@@ -152,7 +152,7 @@ public final class CreateCircleAction extends JosmAction {
                     // get the position of the new node and insert it
                     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));
                 }
@@ -244,7 +244,7 @@ public final class CreateCircleAction extends JosmAction {
                 // get the position of the new node and insert it
                 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));
             }
diff --git a/src/org/openstreetmap/josm/actions/ImageryAdjustAction.java b/src/org/openstreetmap/josm/actions/ImageryAdjustAction.java
index 9380fc1..3d5b844 100644
--- a/src/org/openstreetmap/josm/actions/ImageryAdjustAction.java
+++ b/src/org/openstreetmap/josm/actions/ImageryAdjustAction.java
@@ -161,7 +161,7 @@ public class ImageryAdjustAction extends MapMode implements MouseListener, Mouse
             JPanel pnl = new JPanel(new GridBagLayout());
             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));
             pnl.add(new JLabel(tr("Bookmark name: ")),GBC.std());
@@ -210,7 +210,7 @@ public class ImageryAdjustAction extends MapMode implements MouseListener, Mouse
 
         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(
                     "%1." + precision + "f; %1." + precision + "f",
diff --git a/src/org/openstreetmap/josm/actions/OrthogonalizeAction.java b/src/org/openstreetmap/josm/actions/OrthogonalizeAction.java
index d4b7714..a7df4da 100644
--- a/src/org/openstreetmap/josm/actions/OrthogonalizeAction.java
+++ b/src/org/openstreetmap/josm/actions/OrthogonalizeAction.java
@@ -116,7 +116,7 @@ public final class OrthogonalizeAction extends JosmAction {
     public void actionPerformed(ActionEvent e) {
         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>" +
                     "Change your projection to get rid of this warning.<br>" +
diff --git a/src/org/openstreetmap/josm/actions/mapmode/ExtrudeAction.java b/src/org/openstreetmap/josm/actions/mapmode/ExtrudeAction.java
index 252852c..63c8960 100644
--- a/src/org/openstreetmap/josm/actions/mapmode/ExtrudeAction.java
+++ b/src/org/openstreetmap/josm/actions/mapmode/ExtrudeAction.java
@@ -254,7 +254,7 @@ public class ExtrudeAction extends MapMode implements MapViewPaintable {
             newN2en = new EastNorth(initialN2en.getX() + bestMovement.getX(), initialN2en.getY() + bestMovement.getY());
 
             // 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();
 
@@ -308,10 +308,10 @@ public class ExtrudeAction extends MapMode implements MapViewPaintable {
                     if (nodeOverlapsSegment && !alwaysCreateNodes && !hasOtherWays) {
                         //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 ++;
                         cmds.add(new AddCommand(n1New));
@@ -325,10 +325,10 @@ public class ExtrudeAction extends MapMode implements MapViewPaintable {
                     if (nodeOverlapsSegment && !alwaysCreateNodes && !hasOtherWays) {
                         //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 ++;
                         cmds.add(new AddCommand(n2New));
diff --git a/src/org/openstreetmap/josm/command/MoveCommand.java b/src/org/openstreetmap/josm/command/MoveCommand.java
index b7b5d25..ba43d5d 100644
--- a/src/org/openstreetmap/josm/command/MoveCommand.java
+++ b/src/org/openstreetmap/josm/command/MoveCommand.java
@@ -11,12 +11,12 @@ import java.util.List;
 
 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;
 import org.openstreetmap.josm.data.osm.Node;
 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;
 
 /**
@@ -58,7 +58,7 @@ public class MoveCommand extends Command {
     }
 
     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)));
     }
 
     public MoveCommand(Collection<OsmPrimitive> objects, EastNorth offset) {
diff --git a/src/org/openstreetmap/josm/data/coor/CachedLatLon.java b/src/org/openstreetmap/josm/data/coor/CachedLatLon.java
index 6858705..f40c264 100644
--- a/src/org/openstreetmap/josm/data/coor/CachedLatLon.java
+++ b/src/org/openstreetmap/josm/data/coor/CachedLatLon.java
@@ -2,53 +2,83 @@
 package org.openstreetmap.josm.data.coor;
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.gpx.WayPoint;
 import org.openstreetmap.josm.data.projection.Projection;
 
+/**
+ * <p>Maintains an internal cache of the projected lat/lon coordinates.</p>
+ * 
+ * <p><strong>Caveat:</strong> instances do not listen to {@link ProjectionChangeEvents}s. Clients have to make
+ * sure that they invalidate the internal cache if the {@link Main#getProjection() global projection} changes.</p>
+ * 
+ * @deprecated Don't use anymore. If necessary, maintain a cache of projected coordinates elsewhere. See {@link Node} and {@link WayPoint} for examples.
+ * 
+ */
+@Deprecated
 public class CachedLatLon extends LatLon {
-    private EastNorth eastNorth;
-    private Projection proj;
 
+    private double east = java.lang.Double.NaN;
+    private double north = java.lang.Double.NaN;
+
+    /**
+     * Constructor
+     * 
+     * @param lat latitude
+     * @param lon longitude
+     */
     public CachedLatLon(double lat, double lon) {
-        super(lat, lon);
+        this(new LatLon(lat,lon));
     }
 
+    /**
+     * Constructor
+     * 
+     * @param coor lat/lon pair. If null, assumes lat/lon pair (0,0)
+     */
     public CachedLatLon(LatLon coor) {
-        super(coor.lat(), coor.lon());
-        proj = null;
+        super(coor == null ? new LatLon(0,0) : coor);
     }
 
+    /**
+     * Constructor
+     * 
+     * Inversely projects {@code eastNorth} using {@link Main#getProjection() the current projection}. Throws
+     * an assertion exception, if this projection isn't set.
+     * 
+     * @param eastNorth  the projected east/north coordinates. Assumes (0/0) if null.
+     */
     public CachedLatLon(EastNorth eastNorth) {
-        super(Main.proj.eastNorth2latlon(eastNorth));
-        proj = Main.proj;
-        this.eastNorth = eastNorth;
+        super(Main.getProjection().eastNorth2latlon(eastNorth = eastNorth == null ? new EastNorth(0, 0) : eastNorth));
+        this.east = eastNorth.east();
+        this.north = eastNorth.north();
     }
 
-    public final void setCoor(LatLon coor) {
-        setLocation(coor.lon(), coor.lat());
-        proj = null;
+    /**
+     * 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 (east == java.lang.Double.NaN && north == java.lang.Double.NaN) {
+            Projection p = Main.getProjection();
+            if (p == null) return null;
+            EastNorth en = p.latlon2eastNorth(this);
+            this.east = en.east();
+            this.north = en.north();
+            return en;
+        } else
+            return new EastNorth(east, north);
     }
 
-    public final void setEastNorth(EastNorth eastNorth) {
-        proj = Main.proj;
-        this.eastNorth = eastNorth;
-        LatLon l = proj.eastNorth2latlon(eastNorth);
-        setLocation(l.lon(), l.lat());
+    /**
+     * Invalidate the internal cache for east/north coordinates
+     */
+    public void invalidateEastNorthCache() {
+        this.east = java.lang.Double.NaN;
+        this.north = java.lang.Double.NaN;
     }
 
-    public final EastNorth getEastNorth() {
-        if(proj != Main.proj)
-        {
-            proj = Main.proj;
-            eastNorth = proj.latlon2eastNorth(this);
-        }
-        return eastNorth;
-    }
     @Override public String toString() {
         return "CachedLatLon[lat="+lat()+",lon="+lon()+"]";
     }
-
-    // Only for Node.get3892DebugInfo()
-    public Projection getProjection() {
-        return proj;
-    }
 }
diff --git a/src/org/openstreetmap/josm/data/coor/LatLon.java b/src/org/openstreetmap/josm/data/coor/LatLon.java
index 63ffdb5..084bc9c 100644
--- a/src/org/openstreetmap/josm/data/coor/LatLon.java
+++ b/src/org/openstreetmap/josm/data/coor/LatLon.java
@@ -1,14 +1,13 @@
 // License: GPL. Copyright 2007 by Immanuel Scholz and others
 package org.openstreetmap.josm.data.coor;
 
-import static org.openstreetmap.josm.tools.I18n.trc;
-
 import static java.lang.Math.PI;
 import static java.lang.Math.asin;
 import static java.lang.Math.cos;
 import static java.lang.Math.sin;
 import static java.lang.Math.sqrt;
 import static java.lang.Math.toRadians;
+import static org.openstreetmap.josm.tools.I18n.trc;
 
 import java.text.DecimalFormat;
 import java.text.NumberFormat;
@@ -105,7 +104,7 @@ public class LatLon extends Coordinate {
         case DECIMAL_DEGREES: return cDdFormatter.format(y);
         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";
         }
     }
@@ -121,7 +120,7 @@ public class LatLon extends Coordinate {
         case DECIMAL_DEGREES: return cDdFormatter.format(x);
         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";
         }
     }
@@ -141,7 +140,7 @@ public class LatLon extends Coordinate {
      * by using lat/lon.
      */
     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();
     }
diff --git a/src/org/openstreetmap/josm/data/gpx/GpxTrack.java b/src/org/openstreetmap/josm/data/gpx/GpxTrack.java
index db7c57d..ce837aa 100644
--- a/src/org/openstreetmap/josm/data/gpx/GpxTrack.java
+++ b/src/org/openstreetmap/josm/data/gpx/GpxTrack.java
@@ -25,5 +25,4 @@ public interface GpxTrack {
      * @return Number of times this track has been changed. Always 0 for read-only tracks
      */
     int getUpdateCount();
-
 }
diff --git a/src/org/openstreetmap/josm/data/gpx/ImmutableGpxTrackSegment.java b/src/org/openstreetmap/josm/data/gpx/ImmutableGpxTrackSegment.java
index 5a6eff6..6e76bf3 100644
--- a/src/org/openstreetmap/josm/data/gpx/ImmutableGpxTrackSegment.java
+++ b/src/org/openstreetmap/josm/data/gpx/ImmutableGpxTrackSegment.java
@@ -64,5 +64,4 @@ public class ImmutableGpxTrackSegment implements GpxTrackSegment {
     public int getUpdateCount() {
         return 0;
     }
-
 }
diff --git a/src/org/openstreetmap/josm/data/gpx/WayPoint.java b/src/org/openstreetmap/josm/data/gpx/WayPoint.java
index 94b41c6..3ec724d 100644
--- a/src/org/openstreetmap/josm/data/gpx/WayPoint.java
+++ b/src/org/openstreetmap/josm/data/gpx/WayPoint.java
@@ -6,9 +6,10 @@ package org.openstreetmap.josm.data.gpx;
 import java.awt.Color;
 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.Projection;
 import org.openstreetmap.josm.tools.PrimaryDateParser;
 
 public class WayPoint extends WithAttributes implements Comparable<WayPoint> {
@@ -25,22 +26,82 @@ public class WayPoint extends WithAttributes implements Comparable<WayPoint> {
     public int dir;
 
     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;
+
+    private boolean isAlreadyProjected() {
+        return ! Double.isNaN(east) && ! Double.isNaN(north);
+    }
+
+    /**
+     * <p>Reproject the coordinates and refresh  the internal cache of the projected coordinates.</p>
+     */
+    public void reproject() {
+        Projection p = Main.getProjection();
+        if (p == null){
+            east = Double.NaN;
+            north = Double.NaN;
+            return;
+        }
+        EastNorth en = p.latlon2eastNorth(getCoor());
+        east = en.east();
+        north = en.north();
+    }
+
+    /**
+     * 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>
+     * 
+     * <p>Replies {@code null}, if no global projection is defined.</p>
+     * 
+     * @return the east north coordinates or {@code null}
+     * @see #reproject()
+     * @see #invalidateEastNorthCache()
+     * 
+     */
     public final EastNorth getEastNorth() {
-        return coor.getEastNorth();
+        if (isAlreadyProjected())
+            return new EastNorth(east, north);
+        reproject();
+        if (!isAlreadyProjected()) return null;
+        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 + ")";
     }
 
     /**
diff --git a/src/org/openstreetmap/josm/data/imagery/OffsetBookmark.java b/src/org/openstreetmap/josm/data/imagery/OffsetBookmark.java
index d6d8c6c..838542a 100644
--- a/src/org/openstreetmap/josm/data/imagery/OffsetBookmark.java
+++ b/src/org/openstreetmap/josm/data/imagery/OffsetBookmark.java
@@ -29,7 +29,7 @@ public class OffsetBookmark {
 
     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);
     }
 
@@ -121,12 +121,12 @@ public class OffsetBookmark {
     public static void bookmarkOffset(String name, ImageryLayer layer) {
         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();) {
             OffsetBookmark b = it.next();
diff --git a/src/org/openstreetmap/josm/data/osm/DataSet.java b/src/org/openstreetmap/josm/data/osm/DataSet.java
index 0cde797..dae4fac 100644
--- a/src/org/openstreetmap/josm/data/osm/DataSet.java
+++ b/src/org/openstreetmap/josm/data/osm/DataSet.java
@@ -19,6 +19,7 @@ import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
+import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.SelectionChangedListener;
 import org.openstreetmap.josm.data.coor.EastNorth;
@@ -33,6 +34,8 @@ import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent;
 import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent;
 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;
@@ -82,7 +85,7 @@ import org.openstreetmap.josm.tools.SubclassFilteredCollection;
  *
  * @author imi
  */
-public class DataSet implements Cloneable {
+public class DataSet implements Cloneable, ProjectionChangeListener {
 
     /**
      * Maximum number of events that can be fired between beginUpdate/endUpdate to be send as single events (ie without DatasetChangedEvent)
@@ -120,6 +123,14 @@ public class DataSet implements Cloneable {
     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() {
         return lock.readLock();
     }
@@ -974,6 +985,52 @@ public class DataSet implements Cloneable {
         highlightUpdateCount++;
     }
 
+
+    /**
+     * Triggers a refresh of cached projected coordinates.
+     * 
+     * This method can be invoked after the globally configured projection method
+     * changed.
+     */
+    public void reproject() {
+        if (Main.getProjection() == null) return; // sanity check
+        try {
+            beginUpdate();
+            for (OsmPrimitive p: allPrimitives) {
+                if ( ! (p instanceof Node)) {
+                    continue;
+                }
+                Node n = (Node)p;
+                n.reproject();
+            }
+        } finally {
+            endUpdate();
+        }
+    }
+
+    /**
+     * 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 (OsmPrimitive p: allPrimitives) {
+                if ( ! (p instanceof Node)) {
+                    continue;
+                }
+                Node n = (Node)p;
+                n.invalidateEastNorthCache();
+            }
+        } finally {
+            endUpdate();
+        }
+    }
+
     public void cleanupDeletedPrimitives() {
         beginUpdate();
         try {
@@ -1063,4 +1120,12 @@ public class DataSet implements Cloneable {
         }
         return ret;
     }
+
+    /* --------------------------------------------------------------------------------- */
+    /* interface ProjectionChangeListner                                                 */
+    /* --------------------------------------------------------------------------------- */
+    @Override
+    public void projectionChanged(Projection oldValue, Projection newValue) {
+        invalidateEastNorthCache();
+    }
 }
diff --git a/src/org/openstreetmap/josm/data/osm/INode.java b/src/org/openstreetmap/josm/data/osm/INode.java
index 68eb01d..40d6cc1 100644
--- a/src/org/openstreetmap/josm/data/osm/INode.java
+++ b/src/org/openstreetmap/josm/data/osm/INode.java
@@ -5,10 +5,9 @@ import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.coor.LatLon;
 
 public interface INode extends IPrimitive {
-    
+
     LatLon getCoor();
     void setCoor(LatLon coor);
     EastNorth getEastNorth();
     void setEastNorth(EastNorth eastNorth);
-
 }
diff --git a/src/org/openstreetmap/josm/data/osm/Node.java b/src/org/openstreetmap/josm/data/osm/Node.java
index d11bc81..155f61e 100644
--- a/src/org/openstreetmap/josm/data/osm/Node.java
+++ b/src/org/openstreetmap/josm/data/osm/Node.java
@@ -1,12 +1,13 @@
 // License: GPL. Copyright 2007 by Immanuel Scholz and others
 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.Projection;
+import org.openstreetmap.josm.data.projection.Projections;
 /**
  * One node data, consisting of one world coordinate waypoint.
  *
@@ -14,7 +15,17 @@ import org.openstreetmap.josm.data.osm.visitor.Visitor;
  */
 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;
+
+
+    private boolean isLatLonKnown() {
+        return lat != COORDINATE_NOT_DEFINED && lon != COORDINATE_NOT_DEFINED;
+    }
 
     @Override
     public final void setCoor(LatLon coor) {
@@ -45,30 +56,52 @@ public final class Node extends OsmPrimitive implements INode {
 
     @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 #reproject() trigger a reprojection} or {@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. Also
+     * replies {@code null}, if no global projection is defined.</p>
+     * 
+     * @return the east north coordinates or {@code null}
+     * @see #reproject()
+     * @see #invalidateEastNorthCache()
+     * 
+     */
     @Override
     public final EastNorth getEastNorth() {
-        return coor != null ? coor.getEastNorth() : null;
+        if (!isLatLonKnown()) return null;
+        Projection p = Main.getProjection();
+        if (p == null) return null;
+        if (!isAlreadyProjected()) {
+            reproject();
+        }
+        return new EastNorth(east, north);
     }
 
     /**
      * To be used only by Dataset.reindexNode
      */
     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();
         }
     }
 
@@ -149,7 +182,7 @@ public final class Node extends OsmPrimitive implements INode {
         boolean locked = writeLock();
         try {
             super.cloneFrom(osm);
-            setCoor(((Node)osm).coor);
+            setCoor(((Node)osm).getCoor());
         } finally {
             writeUnlock(locked);
         }
@@ -172,7 +205,7 @@ public final class Node extends OsmPrimitive implements INode {
         try {
             super.mergeFrom(other);
             if (!other.isIncomplete()) {
-                setCoor(new LatLon(((Node)other).coor));
+                setCoor(new LatLon(((Node)other).getCoor()));
             }
         } finally {
             writeUnlock(locked);
@@ -199,6 +232,7 @@ public final class Node extends OsmPrimitive implements INode {
     }
 
     @Override public String toString() {
+        LatLon coor = getCoor();
         String coorDesc = coor == null?"":"lat="+coor.lat()+",lon="+coor.lon();
         return "{Node id=" + getUniqueId() + " version=" + getVersion() + " " + getFlagsAsString() + " "  + coorDesc+"}";
     }
@@ -210,10 +244,12 @@ public final class Node extends OsmPrimitive implements INode {
         if (! super.hasEqualSemanticAttributes(other))
             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;
     }
@@ -240,7 +276,6 @@ public final class Node extends OsmPrimitive implements INode {
 
     @Override
     public void updatePosition() {
-        // TODO: replace CachedLatLon with simple doubles and update precalculated EastNorth value here
     }
 
     public boolean isConnectionNode() {
@@ -252,14 +287,46 @@ public final class Node extends OsmPrimitive implements INode {
         builder.append("Unexpected error. Please report it to http://josm.openstreetmap.de/ticket/3892\n");
         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");
         }
 
         return builder.toString();
     }
+
+    /*
+     * the cached projected coordinates
+     */
+    private double east = Double.NaN;
+    private double north = Double.NaN;
+
+    private boolean isAlreadyProjected() {
+        return !Double.isNaN(east) && !Double.isNaN(north);
+    }
+
+    /**
+     * <p>Refresh the internal cache of the projected node coordinates.</p>
+     * 
+     * <p>Should be invoked after having switched to a new projection.</p>
+     */
+    public void reproject() {
+        if (!isLatLonKnown()) return;
+        EastNorth en = Projections.project(getCoor());
+        this.east = en.east();
+        this.north = en.north();
+    }
+
+    /**
+     * 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;
+    }
 }
diff --git a/src/org/openstreetmap/josm/data/osm/NodeData.java b/src/org/openstreetmap/josm/data/osm/NodeData.java
index 16d7f81..73abbb8 100644
--- a/src/org/openstreetmap/josm/data/osm/NodeData.java
+++ b/src/org/openstreetmap/josm/data/osm/NodeData.java
@@ -1,18 +1,20 @@
 // License: GPL. For details, see LICENSE file.
 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 = 0;
+    private double lon = 0;
 
-    public NodeData() {
-
-    }
+    public NodeData() {}
 
     public NodeData(NodeData data) {
         super(data);
@@ -21,22 +23,33 @@ public class NodeData extends PrimitiveData implements INode {
 
     @Override
     public LatLon getCoor() {
-        return coor;
+        return new LatLon(lat,lon);
     }
 
     @Override
     public void setCoor(LatLon coor) {
-        this.coor.setCoor(coor);
+        if (coor == null) {
+            this.lat = 0;
+            this.lon = 0;
+        } 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);
     }
 
     @Override
@@ -46,15 +59,15 @@ public class NodeData extends PrimitiveData implements INode {
 
     @Override
     public String toString() {
-        return super.toString() + " NODE " + coor;
+        return super.toString() + " NODE " + getCoor();
     }
 
     @Override
     public OsmPrimitiveType getType() {
         return OsmPrimitiveType.NODE;
     }
-    
-    @Override 
+
+    @Override
     public void visit(PrimitiveVisitor visitor) {
         visitor.visit(this);
     }
@@ -63,5 +76,4 @@ public class NodeData extends PrimitiveData implements INode {
     public String getDisplayName(NameFormatter formatter) {
         return formatter.format(this);
     }
-
 }
diff --git a/src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java b/src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java
index 2c29454..63f8903 100644
--- a/src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java
+++ b/src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java
@@ -6,7 +6,6 @@ import java.util.Collection;
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.ProjectionBounds;
-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.Node;
@@ -61,13 +60,8 @@ public class BoundingXYVisitor extends AbstractVisitor {
     }
 
     public void visit(LatLon latlon) {
-        if(latlon != null)
-        {
-            if(latlon instanceof CachedLatLon) {
-                visit(((CachedLatLon)latlon).getEastNorth());
-            } else {
-                visit(Main.proj.latlon2eastNorth(latlon));
-            }
+        if(latlon != null){
+            visit(Main.getProjection().latlon2eastNorth(latlon));
         }
     }
 
@@ -112,11 +106,11 @@ public class BoundingXYVisitor extends AbstractVisitor {
     public void enlargeBoundingBox(double enlargeDegree) {
         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)));
     }
 
     @Override public String toString() {
diff --git a/src/org/openstreetmap/josm/data/projection/ProjectionChangeListener.java b/src/org/openstreetmap/josm/data/projection/ProjectionChangeListener.java
new file mode 100644
index 0000000..f5accfc
--- /dev/null
+++ b/src/org/openstreetmap/josm/data/projection/ProjectionChangeListener.java
@@ -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);
+}
diff --git a/src/org/openstreetmap/josm/data/projection/Projections.java b/src/org/openstreetmap/josm/data/projection/Projections.java
index f31f7a9..10d0a2d 100644
--- a/src/org/openstreetmap/josm/data/projection/Projections.java
+++ b/src/org/openstreetmap/josm/data/projection/Projections.java
@@ -1,8 +1,12 @@
 // License: GPL. Copyright 2007 by Immanuel Scholz and others
 package org.openstreetmap.josm.data.projection;
 
-import java.util.Arrays;
 import java.util.ArrayList;
+import java.util.Arrays;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.coor.LatLon;
 
 /**
  * Class to handle projections
@@ -13,21 +17,21 @@ public class Projections {
      * List of all available projections.
      */
     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() {
         return allProjections;
@@ -42,4 +46,12 @@ public class Projections {
     public static void addProjection(Projection proj) {
         allProjections.add(proj);
     }
+
+    static public EastNorth project(LatLon ll) {
+        return Main.getProjection().latlon2eastNorth(ll);
+    }
+
+    static public LatLon inverseProject(EastNorth en) {
+        return Main.getProjection().eastNorth2latlon(en);
+    }
 }
diff --git a/src/org/openstreetmap/josm/data/validation/OsmValidator.java b/src/org/openstreetmap/josm/data/validation/OsmValidator.java
index ce77780..7817efe 100644
--- a/src/org/openstreetmap/josm/data/validation/OsmValidator.java
+++ b/src/org/openstreetmap/josm/data/validation/OsmValidator.java
@@ -22,15 +22,14 @@ import javax.swing.JOptionPane;
 
 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;
 import org.openstreetmap.josm.data.projection.Mercator;
 import org.openstreetmap.josm.data.validation.tests.Coastlines;
 import org.openstreetmap.josm.data.validation.tests.CrossingWays;
 import org.openstreetmap.josm.data.validation.tests.DuplicateNode;
-import org.openstreetmap.josm.data.validation.tests.DuplicateWay;
 import org.openstreetmap.josm.data.validation.tests.DuplicateRelation;
+import org.openstreetmap.josm.data.validation.tests.DuplicateWay;
 import org.openstreetmap.josm.data.validation.tests.DuplicatedWayNodes;
 import org.openstreetmap.josm.data.validation.tests.MultipolygonTest;
 import org.openstreetmap.josm.data.validation.tests.NameMismatch;
@@ -47,9 +46,9 @@ import org.openstreetmap.josm.data.validation.tests.UntaggedNode;
 import org.openstreetmap.josm.data.validation.tests.UntaggedWay;
 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;
 
 /**
@@ -76,26 +75,26 @@ public class OsmValidator implements LayerChangeListener {
      */
     @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
     };
 
     public OsmValidator() {
@@ -241,11 +240,11 @@ public class OsmValidator implements LayerChangeListener {
      * until most bugs were discovered while keeping the processing time reasonable)
      */
     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;
         }
     }
@@ -264,9 +263,9 @@ public class OsmValidator implements LayerChangeListener {
                 e.printStackTrace();
                 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);
             }
         }
     }
diff --git a/src/org/openstreetmap/josm/gui/MapView.java b/src/org/openstreetmap/josm/gui/MapView.java
index e4fe0db..0507b20 100644
--- a/src/org/openstreetmap/josm/gui/MapView.java
+++ b/src/org/openstreetmap/josm/gui/MapView.java
@@ -117,7 +117,7 @@ public class MapView extends NavigatableComponent implements PropertyChangeListe
      * Adds an edit layer change listener
      *
      * @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
      */
     public static void addEditLayerChangeListener(EditLayerChangeListener listener, boolean initialFire) {
@@ -284,6 +284,7 @@ public class MapView extends NavigatableComponent implements PropertyChangeListe
             setActiveLayer(layer);
         }
         layer.addPropertyChangeListener(this);
+        Main.addProjectionChangeListener(layer);
         AudioPlayer.reset();
         repaint();
     }
@@ -358,6 +359,7 @@ public class MapView extends NavigatableComponent implements PropertyChangeListe
         }
 
         layers.remove(layer);
+        Main.removeProjectionChangeListener(layer);
         fireLayerRemoved(layer);
         layer.removePropertyChangeListener(this);
         layer.destroy();
diff --git a/src/org/openstreetmap/josm/gui/NavigatableComponent.java b/src/org/openstreetmap/josm/gui/NavigatableComponent.java
index 32484c4..a1a6328 100644
--- a/src/org/openstreetmap/josm/gui/NavigatableComponent.java
+++ b/src/org/openstreetmap/josm/gui/NavigatableComponent.java
@@ -27,7 +27,6 @@ import javax.swing.JComponent;
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.ProjectionBounds;
-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.BBox;
@@ -38,6 +37,7 @@ import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.osm.WaySegment;
 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;
 import org.openstreetmap.josm.tools.Predicate;
@@ -95,7 +95,7 @@ public class NavigatableComponent extends JComponent implements Helpful {
      * every physical pixel on screen are 10 x or 10 y units in the
      * 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.
      */
@@ -110,11 +110,11 @@ public class NavigatableComponent extends JComponent implements Helpful {
     }
 
     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));
     }
 
     public static String getDistText(double dist) {
@@ -208,7 +208,7 @@ public class NavigatableComponent extends JComponent implements Helpful {
         EastNorth p1 = getEastNorth(r.x, r.y);
         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());
         double eastMax = Math.max(p1.east(), p2.east());
@@ -218,10 +218,10 @@ public class NavigatableComponent extends JComponent implements Helpful {
         double deltaNorth = (northMax - northMin) / 10;
 
         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)));
         }
 
         return result;
@@ -244,11 +244,9 @@ public class NavigatableComponent extends JComponent implements Helpful {
     public Point2D getPoint2D(LatLon latlon) {
         if (latlon == null)
             return new Point();
-        else if (latlon instanceof CachedLatLon)
-            return getPoint2D(((CachedLatLon)latlon).getEastNorth());
-        else
-            return getPoint2D(getProjection().latlon2eastNorth(latlon));
+        return getPoint2D(Projections.project(latlon));
     }
+
     public Point2D getPoint2D(Node n) {
         return getPoint2D(n.getEastNorth());
     }
@@ -281,7 +279,7 @@ public class NavigatableComponent extends JComponent implements Helpful {
      */
     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();
         double lon = cl.lon();
@@ -290,7 +288,7 @@ public class NavigatableComponent extends JComponent implements Helpful {
         if(lon < b.getMin().lon()) {changed = true; lon = b.getMin().lon(); }
         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;
         int height = getHeight()/2;
@@ -349,19 +347,11 @@ public class NavigatableComponent extends JComponent implements Helpful {
     }
 
     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));
     }
 
     /**
@@ -440,7 +430,7 @@ public class NavigatableComponent extends JComponent implements Helpful {
         double scale;
 
         public ZoomData(EastNorth center, double scale) {
-            this.center = new CachedLatLon(center);
+            this.center = Projections.inverseProject(center);
             this.scale = scale;
         }
 
@@ -1144,7 +1134,7 @@ public class NavigatableComponent extends JComponent implements Helpful {
      * @return The projection to be used in calculating stuff.
      */
     public Projection getProjection() {
-        return Main.proj;
+        return Main.getProjection();
     }
 
     public String helpTopic() {
diff --git a/src/org/openstreetmap/josm/gui/layer/GpxLayer.java b/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
index 83e9df8..e734736 100644
--- a/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
+++ b/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
@@ -51,6 +51,7 @@ import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.EastNorth;
 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;
 import org.openstreetmap.josm.data.gpx.WayPoint;
@@ -58,6 +59,7 @@ import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.Node;
 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;
 import org.openstreetmap.josm.gui.MapView;
@@ -329,8 +331,8 @@ public class GpxLayer extends Layer {
         lastTracks.addAll(data.tracks);
 
         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);
 
         /****************************************************************
          ********** STEP 1 - GET CONFIG VALUES **************************
@@ -1230,7 +1232,7 @@ public class GpxLayer extends Layer {
         }
         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;
     }
@@ -1489,6 +1491,34 @@ public class GpxLayer extends Layer {
             addRecursiveFiles(files, sel);
             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();
+                }
+            }
+        }
     }
 }
\ No newline at end of file
diff --git a/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java b/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java
index 6520532..497cff9 100644
--- a/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java
+++ b/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java
@@ -82,7 +82,7 @@ public abstract class ImageryLayer extends Layer {
     }
 
     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);
     }
diff --git a/src/org/openstreetmap/josm/gui/layer/Layer.java b/src/org/openstreetmap/josm/gui/layer/Layer.java
index f6ee43c..0bf99a0 100644
--- a/src/org/openstreetmap/josm/gui/layer/Layer.java
+++ b/src/org/openstreetmap/josm/gui/layer/Layer.java
@@ -22,6 +22,8 @@ import org.openstreetmap.josm.actions.SaveAction;
 import org.openstreetmap.josm.actions.SaveAsAction;
 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;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -41,7 +43,7 @@ import org.openstreetmap.josm.tools.ImageProvider;
  *
  * @author imi
  */
-abstract public class Layer implements Destroyable, MapViewPaintable {
+abstract public class Layer implements Destroyable, MapViewPaintable, ProjectionChangeListener {
 
     public interface LayerAction {
         boolean supportLayers(List<Layer> layers);
@@ -361,4 +363,12 @@ abstract public class Layer implements Destroyable, MapViewPaintable {
             new GpxExportAction().export(layer);
         }
     }
+
+    /* --------------------------------------------------------------------------------- */
+    /* interface ProjectionChangeListener                                                */
+    /* --------------------------------------------------------------------------------- */
+    @Override
+    public void projectionChanged(Projection oldValue, Projection newValue) {
+        // default implementation does nothing - override in subclasses
+    }
 }
diff --git a/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java b/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
index 32fda21..7725a92 100644
--- a/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
+++ b/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
@@ -61,6 +61,7 @@ import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
 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;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
@@ -79,6 +80,8 @@ import org.openstreetmap.josm.tools.ImageProvider;
  * @author imi
  */
 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";
 
@@ -657,7 +660,6 @@ public class OsmDataLayer extends Layer implements Listener, SelectionChangedLis
                 JOptionPane.showMessageDialog(Main.parent, p, tr("Warning"), JOptionPane.WARNING_MESSAGE);
             }
         }
-
     }
 
     @Override
@@ -674,4 +676,12 @@ public class OsmDataLayer extends Layer implements Listener, SelectionChangedLis
     public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
         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.
+         */
+    }
 }
diff --git a/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java b/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java
index bb41ddc..4acce3f 100644
--- a/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java
+++ b/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java
@@ -94,7 +94,7 @@ public class RawGpsLayer extends Layer implements PreferenceChangedListener {
         public final String time;
         public GpsPoint(LatLon ll, String t) {
             latlon = ll;
-            eastNorth = Main.proj.latlon2eastNorth(ll);
+            eastNorth = Main.getProjection().latlon2eastNorth(ll);
             time = t;
         }
     }
diff --git a/src/org/openstreetmap/josm/gui/layer/TMSLayer.java b/src/org/openstreetmap/josm/gui/layer/TMSLayer.java
index 360a6fc..3423be1 100644
--- a/src/org/openstreetmap/josm/gui/layer/TMSLayer.java
+++ b/src/org/openstreetmap/josm/gui/layer/TMSLayer.java
@@ -870,7 +870,7 @@ public class TMSLayer extends ImageryLayer implements ImageObserver, TileLoaderL
     }
 
     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) {
         double lon = tileSource.tileXToLon(t.getXtile(), t.getZoom());
@@ -878,7 +878,7 @@ public class TMSLayer extends ImageryLayer implements ImageObserver, TileLoaderL
         return pixelPos(tmpLL);
     }
     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) {
         LatLon ll = getShiftedLatLon(en);
diff --git a/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java b/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java
index 5731554..9de7e28 100644
--- a/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java
+++ b/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java
@@ -9,7 +9,6 @@ import java.awt.Image;
 import java.io.File;
 import java.util.Date;
 
-import org.openstreetmap.josm.data.coor.CachedLatLon;
 import org.openstreetmap.josm.data.coor.LatLon;
 
 /*
@@ -24,7 +23,7 @@ final public class ImageEntry implements Comparable<ImageEntry>, Cloneable {
     Image thumbnail;
 
     /** The following values are computed from the correlation with the gpx track */
-    private CachedLatLon pos;
+    private LatLon pos;
     /** Speed in kilometer per second */
     private Double speed;
     /** Elevation (altitude) in meters */
@@ -45,7 +44,7 @@ final public class ImageEntry implements Comparable<ImageEntry>, Cloneable {
     /**
      * getter methods that refer to the temporary value
      */
-    public CachedLatLon getPos() {
+    public LatLon getPos() {
         if (tmp != null)
             return tmp.pos;
         return pos;
@@ -85,11 +84,8 @@ final public class ImageEntry implements Comparable<ImageEntry>, Cloneable {
     /**
      * setter methods
      */
-    public void setPos(CachedLatLon pos) {
-        this.pos = pos;
-    }
     public void setPos(LatLon pos) {
-        this.pos = new CachedLatLon(pos);
+        this.pos = pos;
     }
     public void setSpeed(Double speed) {
         this.speed = speed;
diff --git a/src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java b/src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java
index 6236ea3..e35a75d 100644
--- a/src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java
+++ b/src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java
@@ -15,13 +15,13 @@ import java.util.Map;
 
 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;
 import org.openstreetmap.josm.data.gpx.GpxData;
 import org.openstreetmap.josm.data.gpx.GpxLink;
 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;
 
@@ -69,14 +69,10 @@ public class Marker implements ActionListener {
     public double offset; /* time offset in seconds from the gpx point from which it was derived,
                              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);
     }
 
     public final LatLon getCoor() {
@@ -84,11 +80,11 @@ public class Marker implements ActionListener {
     }
 
     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);
     }
 
     /**
@@ -263,12 +259,10 @@ public class Marker implements ActionListener {
      * @return Text
      */
     public String getText() {
-        if (this.text != null ) {
+        if (this.text != null )
             return this.text;
-        }
-        else {
+        else
             return getText(this.textMap);
-        }
     }
 
     /**
@@ -284,51 +278,51 @@ public class Marker implements ActionListener {
         if (textMap != null && !textMap.isEmpty()) {
             switch(PROP_LABEL.get())
             {
-                // name
-                case 1:
-                {
-                    if (textMap.containsKey("name")) {
-                        text = textMap.get("name");
-                    }
-                    break;
+            // name
+            case 1:
+            {
+                if (textMap.containsKey("name")) {
+                    text = textMap.get("name");
                 }
+                break;
+            }
 
-                // desc
-                case 2:
-                {
-                    if (textMap.containsKey("desc")) {
-                        text = textMap.get("desc");
-                    }
-                    break;
+            // desc
+            case 2:
+            {
+                if (textMap.containsKey("desc")) {
+                    text = textMap.get("desc");
                 }
+                break;
+            }
 
-                // auto
-                case 0:
+            // auto
+            case 0:
                 // both
-                case 3:
-                {
-                    if (textMap.containsKey("name")) {
-                        text = textMap.get("name");
-
-                        if (textMap.containsKey("desc")) {
-                            if (PROP_LABEL.get() != 0 || !text.equals(textMap.get("desc"))) {
-                                text += " - " + textMap.get("desc");
-                            }
+            case 3:
+            {
+                if (textMap.containsKey("name")) {
+                    text = textMap.get("name");
+
+                    if (textMap.containsKey("desc")) {
+                        if (PROP_LABEL.get() != 0 || !text.equals(textMap.get("desc"))) {
+                            text += " - " + textMap.get("desc");
                         }
                     }
-                    else if (textMap.containsKey("desc")) {
-                        text = textMap.get("desc");
-                    }
-                    break;
                 }
-
-                // none
-                case 4:
-                default:
-                {
-                    text = "";
-                    break;
+                else if (textMap.containsKey("desc")) {
+                    text = textMap.get("desc");
                 }
+                break;
+            }
+
+            // none
+            case 4:
+            default:
+            {
+                text = "";
+                break;
+            }
             }
         }
 
diff --git a/src/org/openstreetmap/josm/gui/preferences/ImageryPreference.java b/src/org/openstreetmap/josm/gui/preferences/ImageryPreference.java
index e5fa737..60502e7 100644
--- a/src/org/openstreetmap/josm/gui/preferences/ImageryPreference.java
+++ b/src/org/openstreetmap/josm/gui/preferences/ImageryPreference.java
@@ -733,7 +733,7 @@ public class ImageryPreference implements PreferenceSetting {
             add.addActionListener(new ActionListener() {
                 @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);
                 }
             });
diff --git a/src/org/openstreetmap/josm/gui/preferences/ProjectionPreference.java b/src/org/openstreetmap/josm/gui/preferences/ProjectionPreference.java
index 9d0b41e..b834a77 100644
--- a/src/org/openstreetmap/josm/gui/preferences/ProjectionPreference.java
+++ b/src/org/openstreetmap/josm/gui/preferences/ProjectionPreference.java
@@ -8,7 +8,6 @@ import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.concurrent.CopyOnWriteArrayList;
 
 import javax.swing.BorderFactory;
 import javax.swing.JComboBox;
@@ -26,8 +25,8 @@ import org.openstreetmap.josm.data.preferences.ParametrizedCollectionProperty;
 import org.openstreetmap.josm.data.preferences.StringProperty;
 import org.openstreetmap.josm.data.projection.Mercator;
 import org.openstreetmap.josm.data.projection.Projection;
-import org.openstreetmap.josm.data.projection.Projections;
 import org.openstreetmap.josm.data.projection.ProjectionSubPrefs;
+import org.openstreetmap.josm.data.projection.Projections;
 import org.openstreetmap.josm.gui.NavigatableComponent;
 import org.openstreetmap.josm.plugins.PluginHandler;
 import org.openstreetmap.josm.tools.GBC;
@@ -40,10 +39,6 @@ public class ProjectionPreference implements PreferenceSetting {
         }
     }
 
-    public interface ProjectionChangedListener {
-        void projectionChanged();
-    }
-
     private static final StringProperty PROP_PROJECTION = new StringProperty("projection", Mercator.class.getName());
     private static final StringProperty PROP_COORDINATES = new StringProperty("coordinates", null);
     private static final CollectionProperty PROP_SUB_PROJECTION = new CollectionProperty("projection.sub", null);
@@ -64,24 +59,6 @@ public class ProjectionPreference implements PreferenceSetting {
         }
     }
 
-    //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
      */
@@ -160,7 +137,7 @@ public class ProjectionPreference implements PreferenceSetting {
         JScrollPane scrollpane = new JScrollPane(projPanel);
         gui.mapcontent.addTab(tr("Map Projection"), scrollpane);
 
-        updateMeta(Main.proj);
+        updateMeta(Main.getProjection());
     }
 
     private void updateMeta(Projection proj)
@@ -200,9 +177,6 @@ public class ProjectionPreference implements PreferenceSetting {
 
     static public void setProjection(String name, Collection<String> coll)
     {
-        Bounds b = (Main.map != null && Main.map.mapView != null) ? Main.map.mapView.getRealBounds() : null;
-        Projection oldProj = Main.proj;
-
         Projection p = null;
         for (ClassLoader cl : PluginHandler.getResourceClassLoaders()) {
             try {
@@ -210,7 +184,6 @@ public class ProjectionPreference implements PreferenceSetting {
             } catch (final Exception e) {
             }
             if (p != null) {
-                Main.proj = p;
                 break;
             }
         }
@@ -222,20 +195,15 @@ public class ProjectionPreference implements PreferenceSetting {
                     JOptionPane.ERROR_MESSAGE
             );
             coll = null;
-            Main.proj = new Mercator();
-            name = Main.proj.getClass().getName();
+            p = 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()))
-        {
-            Main.map.mapView.zoomTo(b);
-            /* TODO - remove layers with fixed projection */
+        if(Main.getProjection() instanceof ProjectionSubPrefs) {
+            ((ProjectionSubPrefs) Main.getProjection()).setPreferences(coll);
         }
+        Main.setProjection(p);
     }
 
     private class SBPanel extends JPanel implements ActionListener
diff --git a/src/org/openstreetmap/josm/io/imagery/Grabber.java b/src/org/openstreetmap/josm/io/imagery/Grabber.java
index 648cb8b..5a49fc3 100644
--- a/src/org/openstreetmap/josm/io/imagery/Grabber.java
+++ b/src/org/openstreetmap/josm/io/imagery/Grabber.java
@@ -40,7 +40,7 @@ abstract public class Grabber implements Runnable {
                             b.maxNorth + northCoef * northSize));
         }
 
-        this.proj = Main.proj;
+        this.proj = Main.getProjection();
         this.pixelPerDegree = request.getPixelPerDegree();
         this.request = request;
     }
diff --git a/src/org/openstreetmap/josm/io/imagery/HTMLGrabber.java b/src/org/openstreetmap/josm/io/imagery/HTMLGrabber.java
index d12619d..40aac55 100644
--- a/src/org/openstreetmap/josm/io/imagery/HTMLGrabber.java
+++ b/src/org/openstreetmap/josm/io/imagery/HTMLGrabber.java
@@ -51,7 +51,7 @@ public class HTMLGrabber extends WMSGrabber {
         ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
         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;
     }
diff --git a/src/org/openstreetmap/josm/io/imagery/OsmosnimkiOffsetServer.java b/src/org/openstreetmap/josm/io/imagery/OsmosnimkiOffsetServer.java
index 02d9ee5..fc31932 100644
--- a/src/org/openstreetmap/josm/io/imagery/OsmosnimkiOffsetServer.java
+++ b/src/org/openstreetmap/josm/io/imagery/OsmosnimkiOffsetServer.java
@@ -39,7 +39,7 @@ public class OsmosnimkiOffsetServer implements OffsetServer {
 
     @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"));
             System.out.println(tr("Querying offset: {0}", url.toString()));
@@ -49,7 +49,7 @@ public class OsmosnimkiOffsetServer implements OffsetServer {
             if (i == -1) return null;
             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();
             return null;
diff --git a/src/org/openstreetmap/josm/io/imagery/WMSGrabber.java b/src/org/openstreetmap/josm/io/imagery/WMSGrabber.java
index 048370e..d13bfa3 100644
--- a/src/org/openstreetmap/josm/io/imagery/WMSGrabber.java
+++ b/src/org/openstreetmap/josm/io/imagery/WMSGrabber.java
@@ -71,11 +71,11 @@ public class WMSGrabber extends Grabber {
 
     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();
             w = sw.lon();
@@ -113,8 +113,8 @@ public class WMSGrabber extends Grabber {
 
     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";
         }
         String res = "";
@@ -145,13 +145,13 @@ public class WMSGrabber extends Grabber {
 
     @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) {
             request.finish(State.IMAGE, cached);
             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);
                 return true;
@@ -193,7 +193,7 @@ public class WMSGrabber extends Grabber {
         ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
         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;
     }
 
diff --git a/src/org/openstreetmap/josm/tools/Geometry.java b/src/org/openstreetmap/josm/tools/Geometry.java
index ade8359..210f6d4 100644
--- a/src/org/openstreetmap/josm/tools/Geometry.java
+++ b/src/org/openstreetmap/josm/tools/Geometry.java
@@ -111,7 +111,7 @@ public class Geometry {
                                     return intersectionNodes;
                                 }
 
-                                Node newNode = new Node(Main.proj.eastNorth2latlon(intersection));
+                                Node newNode = new Node(Main.getProjection().eastNorth2latlon(intersection));
                                 Node intNode = newNode;
                                 boolean insertInSeg1 = false;
                                 boolean insertInSeg2 = false;
@@ -457,7 +457,7 @@ public class Geometry {
 
         return inside;
     }
-    
+
     /**
      * returns area of a closed way in square meters
      * (approximate(?), but should be OK for small areas)
@@ -477,7 +477,7 @@ public class Geometry {
         }
         return Math.abs(area/2);
     }
-    
+
     protected static double calcX(Node p1){
         double lat1, lon1, lat2, lon2;
         double dlon, dlat;
@@ -494,7 +494,7 @@ public class Geometry {
         double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
         return 6367000 * c;
     }
-    
+
     protected static double calcY(Node p1){
         double lat1, lon1, lat2, lon2;
         double dlon, dlat;
diff --git a/test/functional/org/openstreetmap/josm/fixtures/JOSMFixture.java b/test/functional/org/openstreetmap/josm/fixtures/JOSMFixture.java
index e976723..adadcbd 100644
--- a/test/functional/org/openstreetmap/josm/fixtures/JOSMFixture.java
+++ b/test/functional/org/openstreetmap/josm/fixtures/JOSMFixture.java
@@ -67,7 +67,7 @@ public class JOSMFixture {
         Main.pref.init(false);
 
         // init projection
-        Main.proj = new Mercator();
+        Main.setProjection(new Mercator());
 
         // make sure we don't upload to or test against production
         //
diff --git a/test/functional/org/openstreetmap/josm/gui/conflict/pair/properties/PropertiesMergerTest.java b/test/functional/org/openstreetmap/josm/gui/conflict/pair/properties/PropertiesMergerTest.java
index f7ac131..09146d4 100644
--- a/test/functional/org/openstreetmap/josm/gui/conflict/pair/properties/PropertiesMergerTest.java
+++ b/test/functional/org/openstreetmap/josm/gui/conflict/pair/properties/PropertiesMergerTest.java
@@ -17,7 +17,7 @@ public class PropertiesMergerTest extends JFrame{
     private PropertiesMerger merger;
 
     protected void build() {
-        Main.proj = new Epsg4326();
+        Main.setProjection(new Epsg4326());
 
         setLayout(new BorderLayout());
         add(merger = new PropertiesMerger(), BorderLayout.CENTER);
diff --git a/test/functional/org/openstreetmap/josm/gui/history/HistoryBrowserTest.java b/test/functional/org/openstreetmap/josm/gui/history/HistoryBrowserTest.java
index e343a20..020b8c1 100644
--- a/test/functional/org/openstreetmap/josm/gui/history/HistoryBrowserTest.java
+++ b/test/functional/org/openstreetmap/josm/gui/history/HistoryBrowserTest.java
@@ -57,7 +57,7 @@ public class HistoryBrowserTest extends JFrame {
         Main.pref.init(false);
 
         // init projection
-        Main.proj = new Mercator();
+        Main.setProjection(new Mercator());
     }
 
     private HistoryBrowser browser;
diff --git a/test/functional/org/openstreetmap/josm/io/MultiFetchServerObjectReaderTest.java b/test/functional/org/openstreetmap/josm/io/MultiFetchServerObjectReaderTest.java
index cba8559..9a71296 100644
--- a/test/functional/org/openstreetmap/josm/io/MultiFetchServerObjectReaderTest.java
+++ b/test/functional/org/openstreetmap/josm/io/MultiFetchServerObjectReaderTest.java
@@ -175,7 +175,7 @@ public class MultiFetchServerObjectReaderTest {
         // don't use atomic upload, the test API server can't cope with large diff uploads
         //
         Main.pref.put("osm-server.atomic-upload", false);
-        Main.proj = new Mercator();
+        Main.setProjection(new Mercator());
 
         File dataSetCacheOutputFile = new File(tempOutputDir, MultiFetchServerObjectReaderTest.class.getName() + ".dataset");
 
diff --git a/test/functional/org/openstreetmap/josm/io/OsmServerBackreferenceReaderTest.java b/test/functional/org/openstreetmap/josm/io/OsmServerBackreferenceReaderTest.java
index 354af96..92d206c 100644
--- a/test/functional/org/openstreetmap/josm/io/OsmServerBackreferenceReaderTest.java
+++ b/test/functional/org/openstreetmap/josm/io/OsmServerBackreferenceReaderTest.java
@@ -182,7 +182,7 @@ public class OsmServerBackreferenceReaderTest {
         // don't use atomic upload, the test API server can't cope with large diff uploads
         //
         Main.pref.put("osm-server.atomic-upload", false);
-        Main.proj = new Mercator();
+        Main.setProjection(new Mercator());
 
         File dataSetCacheOutputFile = new File(tempOutputDir, MultiFetchServerObjectReaderTest.class.getName() + ".dataset");
 
diff --git a/test/performance/org/openstreetmap/josm/data/osm/MapPaintVisitorPerformanceTest.java b/test/performance/org/openstreetmap/josm/data/osm/MapPaintVisitorPerformanceTest.java
index bb702e2..d74cce7 100644
--- a/test/performance/org/openstreetmap/josm/data/osm/MapPaintVisitorPerformanceTest.java
+++ b/test/performance/org/openstreetmap/josm/data/osm/MapPaintVisitorPerformanceTest.java
@@ -31,7 +31,7 @@ public class MapPaintVisitorPerformanceTest {
 
     @BeforeClass
     public static void load() throws Exception {
-        Main.proj = new Mercator();
+        Main.setProjection(new Mercator());
         img = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, BufferedImage.TYPE_3BYTE_BGR);
         g = (Graphics2D)img.getGraphics();
         nc = new NavigatableComponent();
diff --git a/test/unit/org/openstreetmap/josm/data/osm/DataSetMergerTest.java b/test/unit/org/openstreetmap/josm/data/osm/DataSetMergerTest.java
index e40437e..58a1a7b 100644
--- a/test/unit/org/openstreetmap/josm/data/osm/DataSetMergerTest.java
+++ b/test/unit/org/openstreetmap/josm/data/osm/DataSetMergerTest.java
@@ -75,7 +75,7 @@ public class DataSetMergerTest {
         my.setVersion("0.6");
         their = new DataSet();
         their.setVersion("0.6");
-        Main.proj = new Mercator();
+        Main.setProjection(new Mercator());
     }
 
     private void runConsistencyTests(DataSet ds) throws Exception {
diff --git a/test/unit/org/openstreetmap/josm/data/osm/FilterTest.java b/test/unit/org/openstreetmap/josm/data/osm/FilterTest.java
index 596ccb5..9317599 100644
--- a/test/unit/org/openstreetmap/josm/data/osm/FilterTest.java
+++ b/test/unit/org/openstreetmap/josm/data/osm/FilterTest.java
@@ -28,7 +28,7 @@ public class FilterTest {
 
     @BeforeClass
     public static void setUp() {
-        Main.proj = new Mercator();
+        Main.setProjection(new Mercator());
         Main.pref = new Preferences();
     }
 
diff --git a/test/unit/org/openstreetmap/josm/data/osm/NodeProjectionTest.java b/test/unit/org/openstreetmap/josm/data/osm/NodeProjectionTest.java
new file mode 100644
index 0000000..a1e4186
--- /dev/null
+++ b/test/unit/org/openstreetmap/josm/data/osm/NodeProjectionTest.java
@@ -0,0 +1,72 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Preferences;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.projection.Epsg4326;
+import org.openstreetmap.josm.data.projection.Mercator;
+public class NodeProjectionTest {
+
+
+    @Before
+    public void setUp(){
+        Main.pref  = new Preferences();
+        Main.setProjection(new Mercator());
+    }
+
+    /**
+     * If a node is isolated (i.e. if it isn't part of a data set), {@link Node#getEastNorth()} should always
+     * reply projected east/north coordinates compliant with the global projection given
+     * by {@link Main#getProjection()}.
+     */
+    @Test
+    public void projectIsolatedNode() {
+        Node n = new Node(1,1);
+        n.setCoor(new LatLon(0,0));
+        EastNorth en = Main.getProjection().latlon2eastNorth(new LatLon(0,0));
+        assertEquals(en, n.getEastNorth());
+
+        /* change the projection ... */
+        Main.setProjection(new Epsg4326());
+        en = Main.getProjection().latlon2eastNorth(new LatLon(0,0));
+
+        /* n should now reply east/north coordinates for this projection
+         * although it is isolated (neither added to a dataset, nor to  - indirectly -
+         * a layer).
+         */
+        assertEquals(en, n.getEastNorth());
+    }
+
+    /**
+     * If a node isn't isolated (i.e. if it *is* part of a data set), {@link Node#getEastNorth()} should always
+     * reply projected east/north coordinates compliant with the global projection given
+     * by {@link Main#getProjection()}, *although* it internally caches the projected
+     * coordinates. In other words: it should refresh the internal cache if the projection
+     * changes.
+     */
+
+    @Test
+    public void projectNodeBelongingToAnIsolatedDataSet() {
+        DataSet ds  = new DataSet();
+        Node n = new Node(1,1);
+        ds.addPrimitive(n);
+        n.setCoor(new LatLon(0,0));
+        EastNorth en = Main.getProjection().latlon2eastNorth(new LatLon(0,0));
+        assertEquals(en, n.getEastNorth());
+
+        /* change the projection ... */
+        Main.setProjection(new Epsg4326());
+        en = Main.getProjection().latlon2eastNorth(new LatLon(0,0));
+
+        /* n should now reply east/north coordinates for this projection
+         * although it is part of a dataset which isn't connected to a layer
+         */
+        assertEquals(en, n.getEastNorth());
+    }
+}
diff --git a/test/unit/org/openstreetmap/josm/data/osm/OsmPrimitiveTest.java b/test/unit/org/openstreetmap/josm/data/osm/OsmPrimitiveTest.java
index 9a2bc7b..5172817 100644
--- a/test/unit/org/openstreetmap/josm/data/osm/OsmPrimitiveTest.java
+++ b/test/unit/org/openstreetmap/josm/data/osm/OsmPrimitiveTest.java
@@ -28,7 +28,7 @@ public class OsmPrimitiveTest {
 
     @BeforeClass
     public static void setUp() {
-        Main.proj = new Mercator();
+        Main.setProjection(new Mercator());
     }
 
     @Test
diff --git a/test/unit/org/openstreetmap/josm/data/osm/QuadBucketsTest.java b/test/unit/org/openstreetmap/josm/data/osm/QuadBucketsTest.java
index c0d9597..9f1fc44 100644
--- a/test/unit/org/openstreetmap/josm/data/osm/QuadBucketsTest.java
+++ b/test/unit/org/openstreetmap/josm/data/osm/QuadBucketsTest.java
@@ -65,14 +65,14 @@ public class QuadBucketsTest {
 
     @Test
     public void testRemove() throws Exception {
-        Main.proj = new Mercator();
+        Main.setProjection(new Mercator());
         DataSet ds = OsmReader.parseDataSet(new FileInputStream("data_nodist/restriction.osm"), NullProgressMonitor.INSTANCE);
         removeAllTest(ds);
     }
 
     @Test
     public void testMove() throws Exception {
-        Main.proj = new Mercator();
+        Main.setProjection(new Mercator());
         DataSet ds = OsmReader.parseDataSet(new FileInputStream("data_nodist/restriction.osm"), NullProgressMonitor.INSTANCE);
 
         for (Node n: ds.getNodes()) {
diff --git a/test/unit/org/openstreetmap/josm/data/osm/RelationTest.java b/test/unit/org/openstreetmap/josm/data/osm/RelationTest.java
index 2687fd8..1bf7122 100644
--- a/test/unit/org/openstreetmap/josm/data/osm/RelationTest.java
+++ b/test/unit/org/openstreetmap/josm/data/osm/RelationTest.java
@@ -15,7 +15,7 @@ public class RelationTest {
 
     @BeforeClass
     public static void setUp() {
-        Main.proj = new Mercator();
+        Main.setProjection(new Mercator());
         Main.pref = new Preferences();
     }
 
diff --git a/test/unit/org/openstreetmap/josm/data/osm/visitor/MergeSourceBuildingVisitorTest.java b/test/unit/org/openstreetmap/josm/data/osm/visitor/MergeSourceBuildingVisitorTest.java
index 7b9aee8..fef5177 100644
--- a/test/unit/org/openstreetmap/josm/data/osm/visitor/MergeSourceBuildingVisitorTest.java
+++ b/test/unit/org/openstreetmap/josm/data/osm/visitor/MergeSourceBuildingVisitorTest.java
@@ -36,7 +36,7 @@ public class MergeSourceBuildingVisitorTest {
 
     @BeforeClass
     public static void setUp() {
-        Main.proj = new Mercator();
+        Main.setProjection(new Mercator());
         Main.pref = new Preferences();
     }
 
diff --git a/test/unit/org/openstreetmap/josm/data/projection/SwissGridTest.java b/test/unit/org/openstreetmap/josm/data/projection/SwissGridTest.java
index 4b44434..e369652 100644
--- a/test/unit/org/openstreetmap/josm/data/projection/SwissGridTest.java
+++ b/test/unit/org/openstreetmap/josm/data/projection/SwissGridTest.java
@@ -14,7 +14,7 @@ public class SwissGridTest {
 
     @BeforeClass
     public static void setUp() {
-        Main.proj = new SwissGrid();
+        Main.setProjection(new SwissGrid());
     }
 
     /**
@@ -61,39 +61,49 @@ public class SwissGridTest {
     public void a_latlon2eastNorth_test() {
         {
             LatLon ll = new LatLon(46.518, 6.567);
-            EastNorth en = Main.proj.latlon2eastNorth(ll);
-            if (debug) System.out.println(en);
+            EastNorth en = Main.getProjection().latlon2eastNorth(ll);
+            if (debug) {
+                System.out.println(en);
+            }
             assertTrue("Lausanne", Math.abs(en.east() - 533111.69) < 0.1);
             assertTrue("Lausanne", Math.abs(en.north() - 152227.85) < 0.1);
         }
 
         {
             LatLon ll = new LatLon(47.78, 8.58);
-            EastNorth en = Main.proj.latlon2eastNorth(ll);
-            if (debug) System.out.println(en);
+            EastNorth en = Main.getProjection().latlon2eastNorth(ll);
+            if (debug) {
+                System.out.println(en);
+            }
             assertTrue("Schafouse", Math.abs(en.east() - 685544.16) < 0.1);
             assertTrue("Schafouse", Math.abs(en.north() - 292782.91) < 0.1);
         }
 
         {
             LatLon ll = new LatLon(46.58, 10.48);
-            EastNorth en = Main.proj.latlon2eastNorth(ll);
-            if (debug) System.out.println(en);
+            EastNorth en = Main.getProjection().latlon2eastNorth(ll);
+            if (debug) {
+                System.out.println(en);
+            }
             assertTrue("Grinson", Math.abs(en.east() - 833068.04) < 0.1);
             assertTrue("Grinson", Math.abs(en.north() - 163265.39) < 0.1);
         }
 
         {
             LatLon ll = new LatLon(46.0 + 57.0 / 60 + 3.89813884505 / 3600, 7.0 + 26.0 / 60 + 19.076595154147 / 3600);
-            EastNorth en = Main.proj.latlon2eastNorth(ll);
-            if (debug) System.out.println(en);
+            EastNorth en = Main.getProjection().latlon2eastNorth(ll);
+            if (debug) {
+                System.out.println(en);
+            }
             assertTrue("Berne", Math.abs(en.east() - 600000.0) < 0.1);
             assertTrue("Berne", Math.abs(en.north() - 200000.0) < 0.1);
         }
         {
             LatLon ll = new LatLon(46.0 + 2.0 / 60 + 38.87 / 3600, 8.0 + 43.0 / 60 + 49.79 / 3600);
-            EastNorth en = Main.proj.latlon2eastNorth(ll);
-            if (debug) System.out.println(en);
+            EastNorth en = Main.getProjection().latlon2eastNorth(ll);
+            if (debug) {
+                System.out.println(en);
+            }
             assertTrue("Ref", Math.abs(en.east() - 700000.0) < 0.1);
             assertTrue("Ref", Math.abs(en.north() - 100000.0) < 0.1);
         }
@@ -104,40 +114,50 @@ public class SwissGridTest {
     public void b_eastNorth2latlon_test() {
         {
             EastNorth en = new EastNorth(533111.69, 152227.85);
-            LatLon ll = Main.proj.eastNorth2latlon(en);
-            if (debug) System.out.println(ll);
+            LatLon ll = Main.getProjection().eastNorth2latlon(en);
+            if (debug) {
+                System.out.println(ll);
+            }
             assertTrue("Lausanne", Math.abs(ll.lat() - 46.518) < 0.00001);
             assertTrue("Lausanne", Math.abs(ll.lon() - 6.567) < 0.00001);
         }
 
         {
             EastNorth en = new EastNorth(685544.16, 292782.91);
-            LatLon ll = Main.proj.eastNorth2latlon(en);
-            if (debug) System.out.println(ll);
+            LatLon ll = Main.getProjection().eastNorth2latlon(en);
+            if (debug) {
+                System.out.println(ll);
+            }
             assertTrue("Schafouse", Math.abs(ll.lat() - 47.78) < 0.00001);
             assertTrue("Schafouse", Math.abs(ll.lon() - 8.58) < 0.00001);
         }
 
         {
             EastNorth en = new EastNorth(833068.04, 163265.39);
-            LatLon ll = Main.proj.eastNorth2latlon(en);
-            if (debug) System.out.println(ll);
+            LatLon ll = Main.getProjection().eastNorth2latlon(en);
+            if (debug) {
+                System.out.println(ll);
+            }
             assertTrue("Grinson", Math.abs(ll.lat() - 46.58) < 0.00001);
             assertTrue("Grinson", Math.abs(ll.lon() - 10.48) < 0.00001);
         }
 
         {
             EastNorth en = new EastNorth(600000.0, 200000.0);
-            LatLon ll = Main.proj.eastNorth2latlon(en);
-            if (debug) System.out.println(ll);
+            LatLon ll = Main.getProjection().eastNorth2latlon(en);
+            if (debug) {
+                System.out.println(ll);
+            }
             assertTrue("Berne", Math.abs(ll.lat() - (46.0 + 57.0 / 60 + 3.89813884505 / 3600)) < 0.00001);
             assertTrue("Berne", Math.abs(ll.lon() - (7.0 + 26.0 / 60 + 19.076595154147 / 3600)) < 0.00001);
         }
 
         {
             EastNorth en = new EastNorth(700000.0, 100000.0);
-            LatLon ll = Main.proj.eastNorth2latlon(en);
-            if (debug) System.out.println(ll);
+            LatLon ll = Main.getProjection().eastNorth2latlon(en);
+            if (debug) {
+                System.out.println(ll);
+            }
             assertTrue("Ref", Math.abs(ll.lat() - (46.0 + 2.0 / 60 + 38.87 / 3600)) < 0.00001);
             assertTrue("Ref", Math.abs(ll.lon() - (8.0 + 43.0 / 60 + 49.79 / 3600)) < 0.00001);
         }
@@ -151,50 +171,70 @@ public class SwissGridTest {
     public void c_sendandreturn_test() {
         {
             EastNorth en = new EastNorth(533111.69, 152227.85);
-            LatLon ll = Main.proj.eastNorth2latlon(en);
-            EastNorth en2 = Main.proj.latlon2eastNorth(ll);
-            if (debug) System.out.println(en.east() - en2.east());
-            if (debug) System.out.println(en.north() - en2.north());
+            LatLon ll = Main.getProjection().eastNorth2latlon(en);
+            EastNorth en2 = Main.getProjection().latlon2eastNorth(ll);
+            if (debug) {
+                System.out.println(en.east() - en2.east());
+            }
+            if (debug) {
+                System.out.println(en.north() - en2.north());
+            }
             assertTrue("Lausanne", Math.abs(en.east() - en2.east()) < 0.002);
             assertTrue("Lausanne", Math.abs(en.north() - en2.north()) < 0.002);
         }
 
         {
             EastNorth en = new EastNorth(685544.16, 292782.91);
-            LatLon ll = Main.proj.eastNorth2latlon(en);
-            EastNorth en2 = Main.proj.latlon2eastNorth(ll);
-            if (debug) System.out.println(en.east() - en2.east());
-            if (debug) System.out.println(en.north() - en2.north());
+            LatLon ll = Main.getProjection().eastNorth2latlon(en);
+            EastNorth en2 = Main.getProjection().latlon2eastNorth(ll);
+            if (debug) {
+                System.out.println(en.east() - en2.east());
+            }
+            if (debug) {
+                System.out.println(en.north() - en2.north());
+            }
             assertTrue("Schafouse", Math.abs(en.east() - en2.east()) < 0.002);
             assertTrue("Schafouse", Math.abs(en.north() - en2.north()) < 0.002);
         }
 
         {
             EastNorth en = new EastNorth(833068.04, 163265.39);
-            LatLon ll = Main.proj.eastNorth2latlon(en);
-            EastNorth en2 = Main.proj.latlon2eastNorth(ll);
-            if (debug) System.out.println(en.east() - en2.east());
-            if (debug) System.out.println(en.north() - en2.north());
+            LatLon ll = Main.getProjection().eastNorth2latlon(en);
+            EastNorth en2 = Main.getProjection().latlon2eastNorth(ll);
+            if (debug) {
+                System.out.println(en.east() - en2.east());
+            }
+            if (debug) {
+                System.out.println(en.north() - en2.north());
+            }
             assertTrue("Grinson", Math.abs(en.east() - en2.east()) < 0.002);
             assertTrue("Grinson", Math.abs(en.north() - en2.north()) < 0.002);
         }
 
         {
             EastNorth en = new EastNorth(600000.0, 200000.0);
-            LatLon ll = Main.proj.eastNorth2latlon(en);
-            EastNorth en2 = Main.proj.latlon2eastNorth(ll);
-            if (debug) System.out.println(en.east() - en2.east());
-            if (debug) System.out.println(en.north() - en2.north());
+            LatLon ll = Main.getProjection().eastNorth2latlon(en);
+            EastNorth en2 = Main.getProjection().latlon2eastNorth(ll);
+            if (debug) {
+                System.out.println(en.east() - en2.east());
+            }
+            if (debug) {
+                System.out.println(en.north() - en2.north());
+            }
             assertTrue("Berne", Math.abs(en.east() - en2.east()) < 0.002);
             assertTrue("Berne", Math.abs(en.north() - en2.north()) < 0.002);
         }
 
         {
             EastNorth en = new EastNorth(700000.0, 100000.0);
-            LatLon ll = Main.proj.eastNorth2latlon(en);
-            EastNorth en2 = Main.proj.latlon2eastNorth(ll);
-            if (debug) System.out.println(en.east() - en2.east());
-            if (debug) System.out.println(en.north() - en2.north());
+            LatLon ll = Main.getProjection().eastNorth2latlon(en);
+            EastNorth en2 = Main.getProjection().latlon2eastNorth(ll);
+            if (debug) {
+                System.out.println(en.east() - en2.east());
+            }
+            if (debug) {
+                System.out.println(en.north() - en2.north());
+            }
             assertTrue("Ref", Math.abs(en.east() - en2.east()) < 0.002);
             assertTrue("Ref", Math.abs(en.north() - en2.north()) < 0.002);
         }
diff --git a/test/unit/org/openstreetmap/josm/gui/conflict/properties/PropertiesMergeModelTest.java b/test/unit/org/openstreetmap/josm/gui/conflict/properties/PropertiesMergeModelTest.java
index 7e9ad9b..7f9ffae 100644
--- a/test/unit/org/openstreetmap/josm/gui/conflict/properties/PropertiesMergeModelTest.java
+++ b/test/unit/org/openstreetmap/josm/gui/conflict/properties/PropertiesMergeModelTest.java
@@ -46,7 +46,7 @@ public class PropertiesMergeModelTest {
 
     @BeforeClass
     public static void init() {
-        Main.proj = new Epsg4326();
+        Main.setProjection(new Epsg4326());
         Main.pref = new Preferences();
     }
 
-- 
1.7.4.msysgit.0

