From 06a0e12528a747adf4a1b7c25e2f009f14a48789 Mon Sep 17 00:00:00 2001
From: Gubaer <karl.gugisberg@guggis.ch>
Date: Sat, 4 Jun 2011 13:02:07 +0200
Subject: [PATCH] Combined patch for reducing memory footprint in the two relevant classes Node and WayPoint

---
 src/org/openstreetmap/josm/Main.java               |   85 +++++-
 .../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 |   71 +++---
 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  |   34 ++-
 .../josm/data/imagery/OffsetBookmark.java          |    6 +-
 src/org/openstreetmap/josm/data/osm/DataSet.java   |   22 ++
 src/org/openstreetmap/josm/data/osm/Node.java      |   84 ++++--
 src/org/openstreetmap/josm/data/osm/NodeData.java  |   42 ++-
 .../josm/data/osm/visitor/BoundingXYVisitor.java   |   18 +-
 .../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 |   10 +-
 .../openstreetmap/josm/gui/layer/RawGpsLayer.java  |  304 --------------------
 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/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 +-
 49 files changed, 579 insertions(+), 669 deletions(-)
 delete mode 100644 src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java

diff --git a/src/org/openstreetmap/josm/Main.java b/src/org/openstreetmap/josm/Main.java
index c25d00b..47e3851 100644
--- a/src/org/openstreetmap/josm/Main.java
+++ b/src/org/openstreetmap/josm/Main.java
@@ -22,6 +22,7 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.StringTokenizer;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
@@ -55,6 +56,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 +74,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 +121,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 +213,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 +768,78 @@ 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);
+    }
+
+    private static CopyOnWriteArrayList<ProjectionChangeListener> listeners = new CopyOnWriteArrayList<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()))) {
+            for (ProjectionChangeListener listener: listeners) {
+                listener.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;
+        listeners.addIfAbsent(listener);
+    }
+
+    /**
+     * Removes a projection change listener
+     * 
+     * @param listener the listener. Ignored if null.
+     */
+    public static void removeProjectionChangeListener(ProjectionChangeListener listener) {
+        if (listener == null) return;
+        listeners.remove(listener);
+    }
 }
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..ec944af 100644
--- a/src/org/openstreetmap/josm/data/coor/CachedLatLon.java
+++ b/src/org/openstreetmap/josm/data/coor/CachedLatLon.java
@@ -2,53 +2,58 @@
 package org.openstreetmap.josm.data.coor;
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.data.projection.Projection;
-
+import org.openstreetmap.josm.data.gpx.WayPoint;
+import org.openstreetmap.josm.data.projection.Projections;
+
+/**
+ * 
+ * @deprecated despite its name, the caching functionality has been removed. 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;
 
+    /**
+     * 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;
-    }
-
-    public final void setCoor(LatLon coor) {
-        setLocation(coor.lon(), coor.lat());
-        proj = null;
-    }
-
-    public final void setEastNorth(EastNorth eastNorth) {
-        proj = Main.proj;
-        this.eastNorth = eastNorth;
-        LatLon l = proj.eastNorth2latlon(eastNorth);
-        setLocation(l.lon(), l.lat());
+        super(Main.getProjection().eastNorth2latlon(eastNorth = eastNorth == null ? new EastNorth(0, 0) : eastNorth));
     }
 
+    /**
+     * Replies the internally cached east/north coordinates without triggering a projection.
+     * 
+     * @return the internally cached east/north coordinates
+     */
     public final EastNorth getEastNorth() {
-        if(proj != Main.proj)
-        {
-            proj = Main.proj;
-            eastNorth = proj.latlon2eastNorth(this);
-        }
-        return eastNorth;
+        return Projections.project(this);
     }
+
     @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..ccec593 100644
--- a/src/org/openstreetmap/josm/data/gpx/WayPoint.java
+++ b/src/org/openstreetmap/josm/data/gpx/WayPoint.java
@@ -6,9 +6,9 @@ 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.data.coor.EastNorth;
 import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.projection.Projections;
 import org.openstreetmap.josm.tools.PrimaryDateParser;
 
 public class WayPoint extends WithAttributes implements Comparable<WayPoint> {
@@ -25,22 +25,44 @@ 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();
+        reproject();
     }
 
-    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 = 0;
+    private double north = 0;
+
+    /**
+     * <p>Reproject the coordinates and refresh  the internal cache of the projected coordinates.</p>
+     */
+    public void reproject() {
+        EastNorth en = Projections.project(getCoor());
+        east = en.east();
+        north = en.north();
+    }
 
     public final LatLon getCoor() {
-        return coor;
+        return new LatLon(lat,lon);
     }
 
     public final EastNorth getEastNorth() {
-        return coor.getEastNorth();
+        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..42b8b8f 100644
--- a/src/org/openstreetmap/josm/data/osm/DataSet.java
+++ b/src/org/openstreetmap/josm/data/osm/DataSet.java
@@ -974,6 +974,28 @@ public class DataSet implements Cloneable {
         highlightUpdateCount++;
     }
 
+
+    /**
+     * Triggers a refresh of cached projected coordinates.
+     * 
+     * This method should be invoked after the globally configured projection method
+     * changed.
+     */
+    public void reproject() {
+        try {
+            beginUpdate();
+            for (OsmPrimitive p: allPrimitives) {
+                if ( ! (p instanceof Node)) {
+                    continue;
+                }
+                Node n = (Node)p;
+                n.reproject();
+            }
+        } finally {
+            endUpdate();
+        }
+    }
+
     public void cleanupDeletedPrimitives() {
         beginUpdate();
         try {
diff --git a/src/org/openstreetmap/josm/data/osm/Node.java b/src/org/openstreetmap/josm/data/osm/Node.java
index d11bc81..87e872d 100644
--- a/src/org/openstreetmap/josm/data/osm/Node.java
+++ b/src/org/openstreetmap/josm/data/osm/Node.java
@@ -1,12 +1,12 @@
 // 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.Projections;
 /**
  * One node data, consisting of one world coordinate waypoint.
  *
@@ -14,7 +14,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 +55,32 @@ public final class Node extends OsmPrimitive implements INode {
 
     @Override
     public final LatLon getCoor() {
-        return coor;
+        if (!isLatLonKnown()) return null;
+        return new LatLon(lat,lon);
     }
 
     @Override
     public final EastNorth getEastNorth() {
-        return coor != null ? coor.getEastNorth() : null;
+        if (!isLatLonKnown()) return null;
+        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();
+            eastNorth = Projections.project(coor);
+            this.east = eastNorth.east();
+            this.north = eastNorth.north();
+        } 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 +161,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 +184,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 +211,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 +223,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 +255,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 +266,32 @@ 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;
+
+    /**
+     * <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();
+    }
 }
diff --git a/src/org/openstreetmap/josm/data/osm/NodeData.java b/src/org/openstreetmap/josm/data/osm/NodeData.java
index 16d7f81..72fe560 100644
--- a/src/org/openstreetmap/josm/data/osm/NodeData.java
+++ b/src/org/openstreetmap/josm/data/osm/NodeData.java
@@ -1,19 +1,31 @@
 // 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);
-
-    public NodeData() {
+    /*
+     * OSM uses 7-digit precision doubles for lat and lon. Both latitude and longitude
+     * therefore fit into an int. Internally we use ints instead of doubles in order to
+     * reduce the memory footprint of an OSM node.
+     * 
+     */
+    private int lat = 0;
+    private int lon = 0;
 
+    private int toInternal(double latOrLon){
+        return (int)(latOrLon * 1E7d);
+    }
+    private double fromInternal(int latOrLon){
+        return latOrLon / 1E7d;
     }
 
+    public NodeData() {}
+
     public NodeData(NodeData data) {
         super(data);
         setCoor(data.getCoor());
@@ -21,22 +33,29 @@ public class NodeData extends PrimitiveData implements INode {
 
     @Override
     public LatLon getCoor() {
-        return coor;
+        return new LatLon(fromInternal(lat), fromInternal(lon));
     }
 
     @Override
     public void setCoor(LatLon coor) {
-        this.coor.setCoor(coor);
+        if (coor == null) {
+            this.lat = 0;
+            this.lon = 0;
+        } else {
+            this.lat = toInternal(coor.lat());
+            this.lon = toInternal(coor.lon());
+        }
     }
 
     @Override
     public EastNorth getEastNorth() {
-        return this.coor.getEastNorth();
+        return Projections.project(getCoor());
     }
 
     @Override
     public void setEastNorth(EastNorth eastNorth) {
-        this.coor.setEastNorth(eastNorth);
+        LatLon ll = Projections.inverseProject(eastNorth);
+        setCoor(ll);
     }
 
     @Override
@@ -46,15 +65,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 +82,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/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..ef8595f 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.reproject();
+            }
+        }
+        if (data.tracks != null){
+            for (GpxTrack track: data.tracks) {
+                for (GpxTrackSegment segment: track.getSegments()) {
+                    for (WayPoint wp: segment.getWayPoints()) {
+                        wp.reproject();
+                    }
+                }
+            }
+        }
+        if (data.routes != null) {
+            for (GpxRoute route: data.routes) {
+                if (route.routePoints == null) {
+                    continue;
+                }
+                for (WayPoint wp: route.routePoints) {
+                    wp.reproject();
+                }
+            }
+        }
     }
 }
\ 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..ec387df 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,10 @@ 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) {
+        if (newValue == null) return;
+        data.reproject();
+    }
 }
diff --git a/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java b/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java
deleted file mode 100644
index bb41ddc..0000000
--- a/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java
+++ /dev/null
@@ -1,304 +0,0 @@
-// License: GPL. Copyright 2007 by Immanuel Scholz and others
-package org.openstreetmap.josm.gui.layer;
-
-import static org.openstreetmap.josm.tools.I18n.marktr;
-import static org.openstreetmap.josm.tools.I18n.tr;
-import static org.openstreetmap.josm.tools.I18n.trn;
-import static org.openstreetmap.josm.tools.I18n.trnc;
-
-import java.awt.Color;
-import java.awt.Graphics2D;
-import java.awt.GridBagLayout;
-import java.awt.Point;
-import java.awt.event.ActionEvent;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import javax.swing.AbstractAction;
-import javax.swing.Action;
-import javax.swing.Box;
-import javax.swing.ButtonGroup;
-import javax.swing.Icon;
-import javax.swing.JColorChooser;
-import javax.swing.JLabel;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JRadioButton;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.actions.RenameLayerAction;
-import org.openstreetmap.josm.data.Bounds;
-import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent;
-import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
-import org.openstreetmap.josm.data.coor.EastNorth;
-import org.openstreetmap.josm.data.coor.LatLon;
-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.gui.ConditionalOptionPaneUtil;
-import org.openstreetmap.josm.gui.MapView;
-import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
-import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
-import org.openstreetmap.josm.tools.GBC;
-import org.openstreetmap.josm.tools.ImageProvider;
-import org.openstreetmap.josm.tools.UrlLabel;
-
-/**
- * A layer holding data from a gps source.
- * The data is read only.
- *
- * @author imi
- */
-public class RawGpsLayer extends Layer implements PreferenceChangedListener {
-
-    public class ConvertToDataLayerAction extends AbstractAction {
-        public ConvertToDataLayerAction() {
-            super(tr("Convert to data layer"), ImageProvider.get("converttoosm"));
-        }
-        public void actionPerformed(ActionEvent e) {
-            JPanel msg = new JPanel(new GridBagLayout());
-            msg.add(new JLabel(tr("<html>Upload of unprocessed GPS data as map data is considered harmful.<br>If you want to upload traces, look here:</html>")), GBC.eol());
-            msg.add(new UrlLabel(tr("http://www.openstreetmap.org/traces")), GBC.eop());
-            if (!ConditionalOptionPaneUtil.showConfirmationDialog(
-                    "convert_to_data",
-                    Main.parent,
-                    msg,
-                    tr("Warning"),
-                    JOptionPane.OK_CANCEL_OPTION,
-                    JOptionPane.WARNING_MESSAGE,
-                    JOptionPane.OK_OPTION))
-                return;
-            DataSet ds = new DataSet();
-            for (Collection<GpsPoint> c : data) {
-                List<Node> nodes = new ArrayList<Node>();
-                for (GpsPoint p : c) {
-                    Node n = new Node(p.latlon);
-                    ds.addPrimitive(n);
-                    nodes.add(n);
-                }
-                Way w = new Way();
-                w.setNodes(nodes);
-                ds.addPrimitive(w);
-            }
-            Main.main.addLayer(new OsmDataLayer(ds, tr("Converted from: {0}", RawGpsLayer.this.getName()), null));
-            Main.main.removeLayer(RawGpsLayer.this);
-        }
-    }
-
-    public static class GpsPoint {
-        public final LatLon latlon;
-        public final EastNorth eastNorth;
-        public final String time;
-        public GpsPoint(LatLon ll, String t) {
-            latlon = ll;
-            eastNorth = Main.proj.latlon2eastNorth(ll);
-            time = t;
-        }
-    }
-
-    /**
-     * A list of ways which containing a list of points.
-     */
-    public final Collection<Collection<GpsPoint>> data;
-    public final boolean fromServer;
-
-    public RawGpsLayer(boolean fromServer, Collection<Collection<GpsPoint>> data, String name, File associatedFile) {
-        super(name);
-        this.fromServer = fromServer;
-        setAssociatedFile(associatedFile);
-        this.data = data;
-        Main.pref.addPreferenceChangeListener(this);
-    }
-
-    /**
-     * Return a static icon.
-     */
-    @Override public Icon getIcon() {
-        return ImageProvider.get("layer", "rawgps_small");
-    }
-
-    @Override public void paint(Graphics2D g, MapView mv, Bounds box) {
-        g.setColor(Main.pref.getColor(marktr("gps point"), "layer "+getName(), Color.gray));
-        Point old = null;
-
-        boolean force = Main.pref.getBoolean("draw.rawgps.lines.force");
-        boolean lines = Main.pref.getBoolean("draw.rawgps.lines", true);
-        String linesKey = "draw.rawgps.lines.layer "+getName();
-        if (Main.pref.hasKey(linesKey)) {
-            lines = Main.pref.getBoolean(linesKey);
-        }
-        boolean large = Main.pref.getBoolean("draw.rawgps.large");
-        for (Collection<GpsPoint> c : data) {
-            if (!force) {
-                old = null;
-            }
-            for (GpsPoint p : c) {
-                Point screen = mv.getPoint(p.eastNorth);
-                if (lines && old != null) {
-                    g.drawLine(old.x, old.y, screen.x, screen.y);
-                } else if (!large) {
-                    g.drawRect(screen.x, screen.y, 0, 0);
-                }
-                if (large) {
-                    g.fillRect(screen.x-1, screen.y-1, 3, 3);
-                }
-                old = screen;
-            }
-        }
-    }
-
-    @Override public String getToolTipText() {
-        int points = 0;
-        for (Collection<GpsPoint> c : data) {
-            points += c.size();
-        }
-        String tool = data.size()+" "+trnc("gps", "track", "tracks", data.size())
-        +" "+points+" "+trn("point", "points", points);
-        File f = getAssociatedFile();
-        if (f != null) {
-            tool = "<html>"+tool+"<br>"+f.getPath()+"</html>";
-        }
-        return tool;
-    }
-
-    @Override public void mergeFrom(Layer from) {
-        RawGpsLayer layer = (RawGpsLayer)from;
-        data.addAll(layer.data);
-    }
-
-    @Override public boolean isMergable(Layer other) {
-        return other instanceof RawGpsLayer;
-    }
-
-    @Override public void visitBoundingBox(BoundingXYVisitor v) {
-        for (Collection<GpsPoint> c : data) {
-            for (GpsPoint p : c) {
-                v.visit(p.eastNorth);
-            }
-        }
-    }
-
-    @Override public Object getInfoComponent() {
-        StringBuilder b = new StringBuilder();
-        int points = 0;
-        for (Collection<GpsPoint> c : data) {
-            b.append("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"+trn("a track with {0} point","a track with {0} points", c.size(), c.size())+"<br>");
-            points += c.size();
-        }
-        b.append("</html>");
-        return "<html>"+trn("{0} consists of {1} track", "{0} consists of {1} tracks", data.size(), getName(), data.size())+" ("+trn("{0} point", "{0} points", points, points)+")<br>"+b.toString();
-    }
-
-    @Override public Action[] getMenuEntries() {
-        if (Main.applet)
-            return new Action[]{
-                LayerListDialog.getInstance().createShowHideLayerAction(),
-                LayerListDialog.getInstance().createDeleteLayerAction(),
-                SeparatorLayerAction.INSTANCE,
-                new CustomizeColor(),
-                new CustomizeLineDrawing(),
-                new ConvertToDataLayerAction(),
-                SeparatorLayerAction.INSTANCE,
-                new RenameLayerAction(getAssociatedFile(), this),
-                SeparatorLayerAction.INSTANCE,
-                new LayerListPopup.InfoAction(this)};
-        else
-            return new Action[]{
-                LayerListDialog.getInstance().createShowHideLayerAction(),
-                LayerListDialog.getInstance().createDeleteLayerAction(),
-                SeparatorLayerAction.INSTANCE,
-                new LayerGpxExportAction(this),
-                new CustomizeColor(),
-                new CustomizeLineDrawing(),
-                new ConvertToDataLayerAction(),
-                SeparatorLayerAction.INSTANCE,
-                new RenameLayerAction(getAssociatedFile(), this),
-                SeparatorLayerAction.INSTANCE,
-                new LayerListPopup.InfoAction(this)};
-    }
-
-    public void preferenceChanged(PreferenceChangeEvent e) {
-        if (Main.map != null && (e.getKey().equals("draw.rawgps.lines") || e.getKey().equals("draw.rawgps.lines.force"))) {
-            Main.map.repaint();
-        }
-    }
-
-    @Override public void destroy() {
-        Main.pref.removePreferenceChangeListener(this);
-    }
-
-    private class CustomizeLineDrawing extends AbstractAction {
-
-        public CustomizeLineDrawing() {
-            super(tr("Customize line drawing"), ImageProvider.get("mapmode/addsegment"));
-        }
-
-        @Override
-        public void actionPerformed(ActionEvent e) {
-            JRadioButton[] r = new JRadioButton[3];
-            r[0] = new JRadioButton(tr("Use global settings."));
-            r[1] = new JRadioButton(tr("Draw lines between points for this layer."));
-            r[2] = new JRadioButton(tr("Do not draw lines between points for this layer."));
-            ButtonGroup group = new ButtonGroup();
-            Box panel = Box.createVerticalBox();
-            for (JRadioButton b : r) {
-                group.add(b);
-                panel.add(b);
-            }
-            String propName = "draw.rawgps.lines.layer "+getName();
-            if (Main.pref.hasKey(propName)) {
-                group.setSelected(r[Main.pref.getBoolean(propName) ? 1:2].getModel(), true);
-            } else {
-                group.setSelected(r[0].getModel(), true);
-            }
-            int answer = JOptionPane.showConfirmDialog(
-                    Main.parent,
-                    panel,
-                    tr("Select line drawing options"),
-                    JOptionPane.OK_CANCEL_OPTION,
-                    JOptionPane.PLAIN_MESSAGE
-            );
-            if (answer == JOptionPane.CANCEL_OPTION)
-                return;
-            if (group.getSelection() == r[0].getModel()) {
-                Main.pref.put(propName, null);
-            } else {
-                Main.pref.put(propName, group.getSelection() == r[1].getModel());
-            }
-            Main.map.repaint();
-        }
-    }
-
-    private class CustomizeColor extends AbstractAction {
-
-        public CustomizeColor() {
-            super(tr("Customize Color"), ImageProvider.get("colorchooser"));
-        }
-
-        @Override
-        public void actionPerformed(ActionEvent e) {
-            JColorChooser c = new JColorChooser(Main.pref.getColor(marktr("gps point"), "layer "+getName(), Color.gray));
-            Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")};
-            int answer = JOptionPane.showOptionDialog(
-                    Main.parent,
-                    c,
-                    tr("Choose a color"),
-                    JOptionPane.OK_CANCEL_OPTION,
-                    JOptionPane.PLAIN_MESSAGE, null,options, options[0]);
-            switch (answer) {
-            case 0:
-                Main.pref.putColor("layer "+getName(), c.getColor());
-                break;
-            case 1:
-                return;
-            case 2:
-                Main.pref.putColor("layer "+getName(), null);
-                break;
-            }
-            Main.map.repaint();
-        }
-    }
-}
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/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

