Index: trunk/src/org/openstreetmap/josm/Main.java
===================================================================
--- trunk/src/org/openstreetmap/josm/Main.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/Main.java	(revision 14120)
@@ -7,5 +7,4 @@
 import java.awt.GraphicsEnvironment;
 import java.io.IOException;
-import java.lang.ref.WeakReference;
 import java.net.URL;
 import java.nio.file.InvalidPathException;
@@ -16,8 +15,6 @@
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.Callable;
-import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -25,5 +22,4 @@
 import java.util.concurrent.Future;
 
-import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.Preferences;
 import org.openstreetmap.josm.data.UndoRedoHandler;
@@ -38,4 +34,5 @@
 import org.openstreetmap.josm.data.projection.Projection;
 import org.openstreetmap.josm.data.projection.ProjectionChangeListener;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.io.FileWatcher;
 import org.openstreetmap.josm.io.OnlineResource;
@@ -364,24 +361,13 @@
     }
 
-    /* ----------------------------------------------------------------------------------------- */
-    /* 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.
-     * Use {@link #getProjection()} and {@link #setProjection(Projection)} for access.
-     * Use {@link #setProjection(Projection)} in order to trigger a projection change event.
-     */
-    private static volatile Projection proj;
-
     /**
      * Replies the current projection.
      *
      * @return the currently active projection
-     */
+     * @deprecated Use {@link ProjectionRegistry#getProjection}
+     */
+    @Deprecated
     public static Projection getProjection() {
-        return proj;
+        return ProjectionRegistry.getProjection();
     }
 
@@ -390,49 +376,9 @@
      *
      * @param p the projection
-     */
+     * @deprecated Use {@link ProjectionRegistry#setProjection}
+     */
+    @Deprecated
     public static void setProjection(Projection p) {
-        CheckParameterUtil.ensureParameterNotNull(p);
-        Projection oldValue = proj;
-        Bounds b = main != null ? main.getRealBounds() : null;
-        proj = p;
-        fireProjectionChanged(oldValue, proj, b);
-    }
-
-    /**
-     * Returns the bounds for the current projection. Used for projection events.
-     * @return the bounds for the current projection
-     * @see #restoreOldBounds
-     */
-    protected Bounds getRealBounds() {
-        // To be overriden
-        return null;
-    }
-
-    /**
-     * Restore clean state corresponding to old bounds after a projection change event.
-     * @param oldBounds bounds previously returned by {@link #getRealBounds}, before the change of projection
-     * @see #getRealBounds
-     */
-    protected void restoreOldBounds(Bounds oldBounds) {
-        // To be overriden
-    }
-
-    /*
-     * Keep WeakReferences to the listeners. This relieves clients from the burden of
-     * explicitly removing the listeners and allows us to transparently register every
-     * created dataset as projection change listener.
-     */
-    private static final List<WeakReference<ProjectionChangeListener>> listeners = new CopyOnWriteArrayList<>();
-
-    private static void fireProjectionChanged(Projection oldValue, Projection newValue, Bounds oldBounds) {
-        if ((newValue == null ^ oldValue == null)
-                || (newValue != null && oldValue != null && !Objects.equals(newValue.toCode(), oldValue.toCode()))) {
-            listeners.removeIf(x -> x.get() == null);
-            listeners.stream().map(WeakReference::get).filter(Objects::nonNull).forEach(x -> x.projectionChanged(oldValue, newValue));
-            if (newValue != null && oldBounds != null && main != null) {
-                main.restoreOldBounds(oldBounds);
-            }
-            /* TODO - remove layers with fixed projection */
-        }
+        ProjectionRegistry.setProjection(p);
     }
 
@@ -442,12 +388,9 @@
      *
      * @param listener the listener. Ignored if <code>null</code>.
-     */
+     * @deprecated Use {@link ProjectionRegistry#addProjectionChangeListener}
+     */
+    @Deprecated
     public static void addProjectionChangeListener(ProjectionChangeListener listener) {
-        if (listener == null) return;
-        for (WeakReference<ProjectionChangeListener> wr : listeners) {
-            // already registered ? => abort
-            if (wr.get() == listener) return;
-        }
-        listeners.add(new WeakReference<>(listener));
+        ProjectionRegistry.addProjectionChangeListener(listener);
     }
 
@@ -456,9 +399,9 @@
      *
      * @param listener the listener. Ignored if <code>null</code>.
-     */
+     * @deprecated Use {@link ProjectionRegistry#removeProjectionChangeListener}
+     */
+    @Deprecated
     public static void removeProjectionChangeListener(ProjectionChangeListener listener) {
-        if (listener == null) return;
-        // remove the listener - and any other listener which got garbage collected in the meantime
-        listeners.removeIf(wr -> wr.get() == null || wr.get() == listener);
+        ProjectionRegistry.removeProjectionChangeListener(listener);
     }
 
@@ -466,7 +409,9 @@
      * Remove all projection change listeners. For testing purposes only.
      * @since 13322
-     */
+     * @deprecated Use {@link ProjectionRegistry#clearProjectionChangeListeners}
+     */
+    @Deprecated
     public static void clearProjectionChangeListeners() {
-        listeners.clear();
+        ProjectionRegistry.clearProjectionChangeListeners();
     }
 
Index: trunk/src/org/openstreetmap/josm/actions/CreateCircleAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/CreateCircleAction.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/actions/CreateCircleAction.java	(revision 14120)
@@ -18,5 +18,4 @@
 import javax.swing.JOptionPane;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.command.AddCommand;
 import org.openstreetmap.josm.command.ChangeCommand;
@@ -30,4 +29,5 @@
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.Notification;
@@ -180,6 +180,6 @@
 
         // see #10777
-        LatLon ll1 = Main.getProjection().eastNorth2latlon(n1);
-        LatLon ll2 = Main.getProjection().eastNorth2latlon(center);
+        LatLon ll1 = ProjectionRegistry.getProjection().eastNorth2latlon(n1);
+        LatLon ll2 = ProjectionRegistry.getProjection().eastNorth2latlon(center);
 
         double radiusInMeters = ll1.greatCircleDistance(ll2);
@@ -218,5 +218,5 @@
                 double x = center.east() + r*Math.cos(alpha);
                 double y = center.north() + r*Math.sin(alpha);
-                LatLon ll = Main.getProjection().eastNorth2latlon(new EastNorth(x, y));
+                LatLon ll = ProjectionRegistry.getProjection().eastNorth2latlon(new EastNorth(x, y));
                 if (ll.isOutSideWorld()) {
                     notifyNodesNotOnCircle();
Index: trunk/src/org/openstreetmap/josm/actions/ImageryAdjustAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/ImageryAdjustAction.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/actions/ImageryAdjustAction.java	(revision 14120)
@@ -26,4 +26,5 @@
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.imagery.OffsetBookmark;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.ExtendedDialog;
 import org.openstreetmap.josm.gui.MainApplication;
@@ -74,13 +75,13 @@
         }
         old = layer.getDisplaySettings().getOffsetBookmark();
-        EastNorth curOff = old == null ? EastNorth.ZERO : old.getDisplacement(Main.getProjection());
+        EastNorth curOff = old == null ? EastNorth.ZERO : old.getDisplacement(ProjectionRegistry.getProjection());
         LatLon center;
         if (MainApplication.isDisplayingMapView()) {
-            center = Main.getProjection().eastNorth2latlon(MainApplication.getMap().mapView.getCenter());
+            center = ProjectionRegistry.getProjection().eastNorth2latlon(MainApplication.getMap().mapView.getCenter());
         } else {
             center = LatLon.ZERO;
         }
         tempOffset = new OffsetBookmark(
-                Main.getProjection().toCode(),
+                ProjectionRegistry.getProjection().toCode(),
                 layer.getInfo().getId(),
                 layer.getInfo().getName(),
@@ -258,5 +259,5 @@
                     "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.getProjection().toString())), GBC.eop());
+                    ProjectionRegistry.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));
@@ -313,5 +314,5 @@
             if (layer != null) {
                 // Support projections with very small numbers (e.g. 4326)
-                int precision = Main.getProjection().getDefaultZoomInPPD() >= 1.0 ? 2 : 7;
+                int precision = ProjectionRegistry.getProjection().getDefaultZoomInPPD() >= 1.0 ? 2 : 7;
                 // US locale to force decimal separator to be '.'
                 try (Formatter us = new Formatter(Locale.US)) {
Index: trunk/src/org/openstreetmap/josm/actions/JoinNodeWayAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/JoinNodeWayAction.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/actions/JoinNodeWayAction.java	(revision 14120)
@@ -19,5 +19,4 @@
 import java.util.TreeSet;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.command.ChangeCommand;
 import org.openstreetmap.josm.command.Command;
@@ -30,4 +29,5 @@
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.osm.WaySegment;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.MapView;
@@ -143,5 +143,5 @@
                                 node.getEastNorth());
                         MoveCommand c = new MoveCommand(
-                                node, Main.getProjection().eastNorth2latlon(newPosition));
+                                node, ProjectionRegistry.getProjection().eastNorth2latlon(newPosition));
                         // Avoid moving a given node several times at the same position in case of overlapping ways
                         if (!cmds.contains(c)) {
Index: trunk/src/org/openstreetmap/josm/actions/OrthogonalizeAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/OrthogonalizeAction.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/actions/OrthogonalizeAction.java	(revision 14120)
@@ -30,4 +30,5 @@
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
 import org.openstreetmap.josm.gui.MainApplication;
@@ -148,5 +149,5 @@
         if (!isEnabled())
             return;
-        if ("EPSG:4326".equals(Main.getProjection().toString())) {
+        if ("EPSG:4326".equals(ProjectionRegistry.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>" +
@@ -307,5 +308,5 @@
             throw new InvalidUserInputException("Unable to orthogonalize " + singleNode);
         }
-        return new MoveCommand(singleNode, Main.getProjection().eastNorth2latlon(Geometry.getCentroidEN(rightAnglePositions)));
+        return new MoveCommand(singleNode, ProjectionRegistry.getProjection().eastNorth2latlon(Geometry.getCentroidEN(rightAnglePositions)));
     }
 
Index: trunk/src/org/openstreetmap/josm/actions/SessionLoadAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/SessionLoadAction.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/actions/SessionLoadAction.java	(revision 14120)
@@ -20,4 +20,5 @@
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
 import org.openstreetmap.josm.gui.MainApplication;
@@ -147,5 +148,5 @@
                 }
                 if (noMap && viewport != null) {
-                    MainApplication.getMap().mapView.scheduleZoomTo(viewport.getEastNorthViewport(Main.getProjection()));
+                    MainApplication.getMap().mapView.scheduleZoomTo(viewport.getEastNorthViewport(ProjectionRegistry.getProjection()));
                 }
             }
Index: trunk/src/org/openstreetmap/josm/actions/mapmode/ExtrudeAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/mapmode/ExtrudeAction.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/actions/mapmode/ExtrudeAction.java	(revision 14120)
@@ -29,5 +29,4 @@
 import javax.swing.JMenuItem;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.JosmAction;
 import org.openstreetmap.josm.actions.MergeNodesAction;
@@ -46,4 +45,5 @@
 import org.openstreetmap.josm.data.osm.WaySegment;
 import org.openstreetmap.josm.data.preferences.NamedColorProperty;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.MainMenu;
@@ -655,10 +655,10 @@
             //move existing node
             Node n1Old = selectedSegment.getFirstNode();
-            cmds.add(new MoveCommand(n1Old, Main.getProjection().eastNorth2latlon(newN1en)));
+            cmds.add(new MoveCommand(n1Old, ProjectionRegistry.getProjection().eastNorth2latlon(newN1en)));
             changedNodes.add(n1Old);
         } else if (ignoreSharedNodes && segmentAngleZero && !alwaysCreateNodes && hasOtherWays) {
             // replace shared node with new one
             Node n1Old = selectedSegment.getFirstNode();
-            Node n1New = new Node(Main.getProjection().eastNorth2latlon(newN1en));
+            Node n1New = new Node(ProjectionRegistry.getProjection().eastNorth2latlon(newN1en));
             wnew.addNode(insertionPoint, n1New);
             wnew.removeNode(n1Old);
@@ -668,5 +668,5 @@
         } else {
             //introduce new node
-            Node n1New = new Node(Main.getProjection().eastNorth2latlon(newN1en));
+            Node n1New = new Node(ProjectionRegistry.getProjection().eastNorth2latlon(newN1en));
             wnew.addNode(insertionPoint, n1New);
             wayWasModified = true;
@@ -685,10 +685,10 @@
             //move existing node
             Node n2Old = selectedSegment.getSecondNode();
-            cmds.add(new MoveCommand(n2Old, Main.getProjection().eastNorth2latlon(newN2en)));
+            cmds.add(new MoveCommand(n2Old, ProjectionRegistry.getProjection().eastNorth2latlon(newN2en)));
             changedNodes.add(n2Old);
         } else if (ignoreSharedNodes && segmentAngleZero && !alwaysCreateNodes && hasOtherWays) {
             // replace shared node with new one
             Node n2Old = selectedSegment.getSecondNode();
-            Node n2New = new Node(Main.getProjection().eastNorth2latlon(newN2en));
+            Node n2New = new Node(ProjectionRegistry.getProjection().eastNorth2latlon(newN2en));
             wnew.addNode(insertionPoint, n2New);
             wnew.removeNode(n2Old);
@@ -698,5 +698,5 @@
         } else {
             //introduce new node
-            Node n2New = new Node(Main.getProjection().eastNorth2latlon(newN2en));
+            Node n2New = new Node(ProjectionRegistry.getProjection().eastNorth2latlon(newN2en));
             wnew.addNode(insertionPoint, n2New);
             wayWasModified = true;
@@ -937,6 +937,6 @@
 
         // find out the movement distance, in metres
-        double distance = Main.getProjection().eastNorth2latlon(initialN1en).greatCircleDistance(
-                Main.getProjection().eastNorth2latlon(n1movedEn));
+        double distance = ProjectionRegistry.getProjection().eastNorth2latlon(initialN1en).greatCircleDistance(
+                ProjectionRegistry.getProjection().eastNorth2latlon(n1movedEn));
         MainApplication.getMap().statusLine.setDist(distance);
         updateStatusLine();
Index: trunk/src/org/openstreetmap/josm/command/MoveCommand.java
===================================================================
--- trunk/src/org/openstreetmap/josm/command/MoveCommand.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/command/MoveCommand.java	(revision 14120)
@@ -14,5 +14,4 @@
 import javax.swing.Icon;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.coor.LatLon;
@@ -21,4 +20,5 @@
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.visitor.AllNodesVisitor;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.tools.ImageProvider;
 
@@ -73,5 +73,5 @@
     public MoveCommand(Node node, LatLon position) {
         this(Collections.singleton((OsmPrimitive) node),
-                Main.getProjection().latlon2eastNorth(position).subtract(node.getEastNorth()));
+                ProjectionRegistry.getProjection().latlon2eastNorth(position).subtract(node.getEastNorth()));
     }
 
Index: trunk/src/org/openstreetmap/josm/data/coor/CachedLatLon.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/coor/CachedLatLon.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/data/coor/CachedLatLon.java	(revision 14120)
@@ -4,7 +4,7 @@
 import java.util.Objects;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.projection.Projecting;
 import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 
 /**
@@ -46,5 +46,5 @@
      */
     public CachedLatLon(EastNorth eastNorth) {
-        this(eastNorth, Main.getProjection());
+        this(eastNorth, ProjectionRegistry.getProjection());
     }
 
Index: trunk/src/org/openstreetmap/josm/data/coor/LatLon.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/coor/LatLon.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/data/coor/LatLon.java	(revision 14120)
@@ -17,6 +17,6 @@
 import java.util.Objects;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.Utils;
@@ -195,5 +195,5 @@
      */
     public boolean isOutSideWorld() {
-        Bounds b = Main.getProjection().getWorldBoundsLatLon();
+        Bounds b = ProjectionRegistry.getProjection().getWorldBoundsLatLon();
         return lat() < b.getMinLat() || lat() > b.getMaxLat() ||
                 lon() < b.getMinLon() || lon() > b.getMaxLon();
Index: trunk/src/org/openstreetmap/josm/data/coor/conversion/ProjectedCoordinateFormat.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/coor/conversion/ProjectedCoordinateFormat.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/data/coor/conversion/ProjectedCoordinateFormat.java	(revision 14120)
@@ -4,6 +4,6 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.coor.ILatLon;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 
 /**
@@ -25,10 +25,10 @@
     @Override
     public String latToString(ILatLon ll) {
-        return cDdFormatter.format(ll.getEastNorth(Main.getProjection()).north());
+        return cDdFormatter.format(ll.getEastNorth(ProjectionRegistry.getProjection()).north());
     }
 
     @Override
     public String lonToString(ILatLon ll) {
-        return cDdFormatter.format(ll.getEastNorth(Main.getProjection()).east());
+        return cDdFormatter.format(ll.getEastNorth(ProjectionRegistry.getProjection()).east());
     }
 }
Index: trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java	(revision 14120)
@@ -20,5 +20,4 @@
 import java.util.stream.Stream;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.Data;
@@ -26,4 +25,5 @@
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.gpx.GpxTrack.GpxTrackChangeListener;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.layer.GpxLayer;
@@ -539,5 +539,5 @@
                 WayPoint r = null;
                 for (WayPoint wpSeg : seg.getWayPoints()) {
-                    EastNorth en = wpSeg.getEastNorth(Main.getProjection());
+                    EastNorth en = wpSeg.getEastNorth(ProjectionRegistry.getProjection());
                     if (r == null) {
                         r = wpSeg;
@@ -586,5 +586,5 @@
                 }
                 if (r != null) {
-                    EastNorth c = r.getEastNorth(Main.getProjection());
+                    EastNorth c = r.getEastNorth(ProjectionRegistry.getProjection());
                     /* if there is only one point in the seg, it will do this twice, but no matter */
                     rx = c.east();
@@ -603,5 +603,5 @@
         if (bestEN == null)
             return null;
-        WayPoint best = new WayPoint(Main.getProjection().eastNorth2latlon(bestEN));
+        WayPoint best = new WayPoint(ProjectionRegistry.getProjection().eastNorth2latlon(bestEN));
         best.time = bestTime;
         return best;
Index: trunk/src/org/openstreetmap/josm/data/imagery/OffsetBookmark.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/imagery/OffsetBookmark.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/data/imagery/OffsetBookmark.java	(revision 14120)
@@ -10,5 +10,4 @@
 import java.util.stream.Collectors;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.StructUtils;
 import org.openstreetmap.josm.data.StructUtils.StructEntry;
@@ -18,4 +17,5 @@
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.data.projection.Projections;
 import org.openstreetmap.josm.gui.MainApplication;
@@ -49,5 +49,5 @@
     public boolean isUsable(ImageryLayer layer) {
         if (projection_code == null) return false;
-        if (!Main.getProjection().toCode().equals(projection_code) && !hasCenter()) return false;
+        if (!ProjectionRegistry.getProjection().toCode().equals(projection_code) && !hasCenter()) return false;
         ImageryInfo info = layer.getInfo();
         return imagery_id != null ? Objects.equals(info.getId(), imagery_id) : Objects.equals(info.getName(), imagery_name);
@@ -363,10 +363,10 @@
         LatLon center;
         if (MainApplication.isDisplayingMapView()) {
-            center = Main.getProjection().eastNorth2latlon(MainApplication.getMap().mapView.getCenter());
+            center = ProjectionRegistry.getProjection().eastNorth2latlon(MainApplication.getMap().mapView.getCenter());
         } else {
             center = LatLon.ZERO;
         }
         OffsetBookmark nb = new OffsetBookmark(
-                Main.getProjection().toCode(), layer.getInfo().getId(), layer.getInfo().getName(),
+                ProjectionRegistry.getProjection().toCode(), layer.getInfo().getId(), layer.getInfo().getName(),
                 name, layer.getDisplaySettings().getDisplacement(), center);
         for (ListIterator<OffsetBookmark> it = allBookmarks.listIterator(); it.hasNext();) {
Index: trunk/src/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSource.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSource.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSource.java	(revision 14120)
@@ -16,7 +16,7 @@
 
 import org.openstreetmap.gui.jmapviewer.interfaces.TemplatedTileSource;
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.layer.WMSLayer;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
@@ -108,5 +108,5 @@
         } else if (baseUrl.toLowerCase(Locale.US).contains("crs=")) {
             // assume WMS 1.3.0
-            switchLatLon = Main.getProjection().switchXY();
+            switchLatLon = ProjectionRegistry.getProjection().switchXY();
         }
         String bbox = getBbox(zoom, tilex, tiley, switchLatLon);
Index: trunk/src/org/openstreetmap/josm/data/imagery/WMTSTileSource.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/imagery/WMTSTileSource.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/data/imagery/WMTSTileSource.java	(revision 14120)
@@ -50,4 +50,5 @@
 import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
 import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.data.projection.Projections;
 import org.openstreetmap.josm.gui.ExtendedDialog;
@@ -346,5 +347,5 @@
             List<Layer> ls = layerById.entrySet().iterator().next().getValue()
                     .stream().filter(
-                            u -> u.tileMatrixSet.crs.equals(Main.getProjection().toCode()))
+                            u -> u.tileMatrixSet.crs.equals(ProjectionRegistry.getProjection().toCode()))
                     .collect(Collectors.toList());
             if (ls.size() == 1) {
@@ -631,5 +632,5 @@
     private static TileMatrix parseTileMatrix(XMLStreamReader reader, String matrixCrs) throws XMLStreamException {
         Projection matrixProj = Optional.ofNullable(Projections.getProjectionByCode(matrixCrs))
-                .orElseGet(Main::getProjection); // use current projection if none found. Maybe user is using custom string
+                .orElseGet(ProjectionRegistry::getProjection); // use current projection if none found. Maybe user is using custom string
         TileMatrix ret = new TileMatrix();
         for (int event = reader.getEventType();
Index: trunk/src/org/openstreetmap/josm/data/osm/DataSet.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 14120)
@@ -25,5 +25,4 @@
 import java.util.stream.Stream;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.APIDataSet.APIOperation;
 import org.openstreetmap.josm.data.Bounds;
@@ -54,4 +53,5 @@
 import org.openstreetmap.josm.data.projection.Projection;
 import org.openstreetmap.josm.data.projection.ProjectionChangeListener;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.spi.preferences.Config;
@@ -175,5 +175,5 @@
         // Transparently register as projection change listener. No need to explicitly remove
         // the listener, projection change listeners are managed as WeakReferences.
-        Main.addProjectionChangeListener(this);
+        ProjectionRegistry.addProjectionChangeListener(this);
         addSelectionListener((DataSelectionListener) e -> fireSelectionChange(e.getSelection()));
     }
@@ -1045,5 +1045,5 @@
      */
     public void invalidateEastNorthCache() {
-        if (Main.getProjection() == null)
+        if (ProjectionRegistry.getProjection() == null)
             return; // sanity check
         beginUpdate();
Index: trunk/src/org/openstreetmap/josm/data/osm/INode.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/INode.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/data/osm/INode.java	(revision 14120)
@@ -6,4 +6,5 @@
 import org.openstreetmap.josm.data.coor.ILatLon;
 import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 
 /**
@@ -34,5 +35,5 @@
      */
     default EastNorth getEastNorth() {
-        return getEastNorth(Main.getProjection());
+        return getEastNorth(ProjectionRegistry.getProjection());
     }
 
Index: trunk/src/org/openstreetmap/josm/data/osm/Node.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/Node.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/data/osm/Node.java	(revision 14120)
@@ -10,5 +10,4 @@
 import java.util.function.Predicate;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.coor.LatLon;
@@ -16,4 +15,5 @@
 import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
 import org.openstreetmap.josm.data.projection.Projecting;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
 import org.openstreetmap.josm.tools.Utils;
@@ -114,10 +114,10 @@
             invalidateEastNorthCache();
         } else if (eastNorth != null) {
-            LatLon ll = Main.getProjection().eastNorth2latlon(eastNorth);
+            LatLon ll = ProjectionRegistry.getProjection().eastNorth2latlon(eastNorth);
             this.lat = ll.lat();
             this.lon = ll.lon();
             this.east = eastNorth.east();
             this.north = eastNorth.north();
-            this.eastNorthCacheKey = Main.getProjection().getCacheKey();
+            this.eastNorthCacheKey = ProjectionRegistry.getProjection().getCacheKey();
         } else {
             this.lat = Double.NaN;
Index: trunk/src/org/openstreetmap/josm/data/osm/NodeData.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/NodeData.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/data/osm/NodeData.java	(revision 14120)
@@ -2,8 +2,8 @@
 package org.openstreetmap.josm.data.osm;
 
-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.projection.ProjectionRegistry;
 
 /**
@@ -77,5 +77,5 @@
     @Override
     public void setEastNorth(EastNorth eastNorth) {
-        setCoor(Main.getProjection().eastNorth2latlon(eastNorth));
+        setCoor(ProjectionRegistry.getProjection().eastNorth2latlon(eastNorth));
     }
 
Index: trunk/src/org/openstreetmap/josm/data/osm/search/SearchCompiler.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/search/SearchCompiler.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/data/osm/search/SearchCompiler.java	(revision 14120)
@@ -23,5 +23,4 @@
 import java.util.stream.Collectors;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.LatLon;
@@ -36,4 +35,5 @@
 import org.openstreetmap.josm.data.osm.search.PushbackTokenizer.Range;
 import org.openstreetmap.josm.data.osm.search.PushbackTokenizer.Token;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.mappaint.Environment;
 import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
@@ -1895,5 +1895,6 @@
         protected Collection<Bounds> getBounds(OsmPrimitive primitive) {
             final Collection<Bounds> bounds = super.getBounds(primitive);
-            return bounds == null || bounds.isEmpty() ? Collections.singleton(Main.getProjection().getWorldBoundsLatLon()) : bounds;
+            return bounds == null || bounds.isEmpty() ?
+                    Collections.singleton(ProjectionRegistry.getProjection().getWorldBoundsLatLon()) : bounds;
         }
 
Index: trunk/src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java	(revision 14120)
@@ -4,5 +4,4 @@
 import java.util.Collection;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.ProjectionBounds;
@@ -19,4 +18,5 @@
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.MapFrame;
@@ -76,5 +76,5 @@
     public void visit(Bounds b) {
         if (b != null) {
-            Main.getProjection().visitOutline(b, this::visit);
+            ProjectionRegistry.getProjection().visitOutline(b, this::visit);
         }
     }
@@ -98,5 +98,5 @@
     public void visit(ILatLon latlon) {
         if (latlon != null) {
-            visit(latlon.getEastNorth(Main.getProjection()));
+            visit(latlon.getEastNorth(ProjectionRegistry.getProjection()));
         }
     }
@@ -159,12 +159,12 @@
         if (bounds == null)
             return;
-        LatLon minLatlon = Main.getProjection().eastNorth2latlon(bounds.getMin());
-        LatLon maxLatlon = Main.getProjection().eastNorth2latlon(bounds.getMax());
+        LatLon minLatlon = ProjectionRegistry.getProjection().eastNorth2latlon(bounds.getMin());
+        LatLon maxLatlon = ProjectionRegistry.getProjection().eastNorth2latlon(bounds.getMax());
         bounds = new ProjectionBounds(new LatLon(
                         Math.max(-90, minLatlon.lat() - enlargeDegree),
-                        Math.max(-180, minLatlon.lon() - enlargeDegree)).getEastNorth(Main.getProjection()),
+                        Math.max(-180, minLatlon.lon() - enlargeDegree)).getEastNorth(ProjectionRegistry.getProjection()),
                 new LatLon(
                         Math.min(90, maxLatlon.lat() + enlargeDegree),
-                        Math.min(180, maxLatlon.lon() + enlargeDegree)).getEastNorth(Main.getProjection()));
+                        Math.min(180, maxLatlon.lon() + enlargeDegree)).getEastNorth(ProjectionRegistry.getProjection()));
     }
 
Index: trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/relations/MultipolygonCache.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/relations/MultipolygonCache.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/relations/MultipolygonCache.java	(revision 14120)
@@ -9,5 +9,4 @@
 import java.util.concurrent.ConcurrentHashMap;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.osm.DataSelectionListener;
 import org.openstreetmap.josm.data.osm.DataSet;
@@ -29,4 +28,5 @@
 import org.openstreetmap.josm.data.projection.Projection;
 import org.openstreetmap.josm.data.projection.ProjectionChangeListener;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
@@ -49,5 +49,5 @@
 
     private MultipolygonCache() {
-        Main.addProjectionChangeListener(this);
+        ProjectionRegistry.addProjectionChangeListener(this);
         SelectionEventManager.getInstance().addSelectionListener(this);
         MainApplication.getLayerManager().addLayerChangeListener(this);
Index: trunk/src/org/openstreetmap/josm/data/projection/ProjectionBoundsProvider.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/ProjectionBoundsProvider.java	(revision 14120)
+++ trunk/src/org/openstreetmap/josm/data/projection/ProjectionBoundsProvider.java	(revision 14120)
@@ -0,0 +1,25 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.projection;
+
+import org.openstreetmap.josm.data.Bounds;
+
+/**
+ * Handles projection change events by keeping a clean state of current bounds.
+ * @since 14120
+ */
+public interface ProjectionBoundsProvider {
+
+    /**
+     * Returns the bounds for the current projection. Used for projection events.
+     * @return the bounds for the current projection
+     * @see #restoreOldBounds
+     */
+    Bounds getRealBounds();
+
+    /**
+     * Restore clean state corresponding to old bounds after a projection change event.
+     * @param oldBounds bounds previously returned by {@link #getRealBounds}, before the change of projection
+     * @see #getRealBounds
+     */
+    void restoreOldBounds(Bounds oldBounds);
+}
Index: trunk/src/org/openstreetmap/josm/data/projection/ProjectionRegistry.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/ProjectionRegistry.java	(revision 14120)
+++ trunk/src/org/openstreetmap/josm/data/projection/ProjectionRegistry.java	(revision 14120)
@@ -0,0 +1,120 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.projection;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+
+/**
+ * Registry for a single, global projection instance.
+ * @since 14120
+ */
+public final class ProjectionRegistry {
+
+    /**
+     * The projection method used.
+     * Use {@link #getProjection()} and {@link #setProjection(Projection)} for access.
+     * Use {@link #setProjection(Projection)} in order to trigger a projection change event.
+     */
+    private static volatile Projection proj;
+
+    private static ProjectionBoundsProvider boundsProvider;
+
+    /*
+     * Keep WeakReferences to the listeners. This relieves clients from the burden of
+     * explicitly removing the listeners and allows us to transparently register every
+     * created dataset as projection change listener.
+     */
+    private static final List<WeakReference<ProjectionChangeListener>> listeners = new CopyOnWriteArrayList<>();
+
+    private ProjectionRegistry() {
+        // hide constructor
+    }
+
+    /**
+     * Replies the current projection.
+     *
+     * @return the currently active projection
+     */
+    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;
+        Bounds b = boundsProvider != null ? boundsProvider.getRealBounds() : null;
+        proj = p;
+        fireProjectionChanged(oldValue, proj, b);
+    }
+
+    private static void fireProjectionChanged(Projection oldValue, Projection newValue, Bounds oldBounds) {
+        if ((newValue == null ^ oldValue == null)
+                || (newValue != null && oldValue != null && !Objects.equals(newValue.toCode(), oldValue.toCode()))) {
+            listeners.removeIf(x -> x.get() == null);
+            listeners.stream().map(WeakReference::get).filter(Objects::nonNull).forEach(x -> x.projectionChanged(oldValue, newValue));
+            if (newValue != null && oldBounds != null && boundsProvider != null) {
+                boundsProvider.restoreOldBounds(oldBounds);
+            }
+            /* TODO - remove layers with fixed projection */
+        }
+    }
+
+    /**
+     * Register a projection change listener.
+     * The listener is registered to be weak, so keep a reference of it if you want it to be preserved.
+     *
+     * @param listener the listener. Ignored if <code>null</code>.
+     */
+    public static void addProjectionChangeListener(ProjectionChangeListener listener) {
+        if (listener == null) return;
+        for (WeakReference<ProjectionChangeListener> wr : listeners) {
+            // already registered ? => abort
+            if (wr.get() == listener) return;
+        }
+        listeners.add(new WeakReference<>(listener));
+    }
+
+    /**
+     * Removes a projection change listener.
+     *
+     * @param listener the listener. Ignored if <code>null</code>.
+     */
+    public static void removeProjectionChangeListener(ProjectionChangeListener listener) {
+        if (listener == null) return;
+        // remove the listener - and any other listener which got garbage collected in the meantime
+        listeners.removeIf(wr -> wr.get() == null || wr.get() == listener);
+    }
+
+    /**
+     * Remove all projection change listeners. For testing purposes only.
+     */
+    public static void clearProjectionChangeListeners() {
+        listeners.clear();
+    }
+
+    /**
+     * Returns the bounds provider called in projection events.
+     * @return the bounds provider
+     */
+    public static ProjectionBoundsProvider getBoundsProvider() {
+        return boundsProvider;
+    }
+
+    /**
+     * Sets the bounds provider called in projection events. Must not be null
+     * @param provider the bounds provider
+     */
+    public static void setboundsProvider(ProjectionBoundsProvider provider) {
+        boundsProvider = Objects.requireNonNull(provider);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/data/validation/OsmValidator.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/validation/OsmValidator.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/data/validation/OsmValidator.java	(revision 14120)
@@ -31,4 +31,5 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.preferences.sources.ValidatorPrefHelper;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.data.validation.tests.Addresses;
 import org.openstreetmap.josm.data.validation.tests.ApiCapabilitiesTest;
@@ -352,5 +353,5 @@
      */
     public static void initializeGridDetail() {
-        String code = Main.getProjection().toCode();
+        String code = ProjectionRegistry.getProjection().toCode();
         if (Arrays.asList(ProjectionPreference.wgs84.allCodes()).contains(code)) {
             OsmValidator.griddetail = 10_000;
Index: trunk/src/org/openstreetmap/josm/gui/MainApplication.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 14120)
@@ -102,5 +102,7 @@
 import org.openstreetmap.josm.data.preferences.JosmUrls;
 import org.openstreetmap.josm.data.preferences.sources.SourceType;
+import org.openstreetmap.josm.data.projection.ProjectionBoundsProvider;
 import org.openstreetmap.josm.data.projection.ProjectionCLI;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.data.projection.datum.NTV2GridShiftFileSource;
 import org.openstreetmap.josm.data.projection.datum.NTV2GridShiftFileWrapper;
@@ -258,4 +260,18 @@
     };
 
+    private static final ProjectionBoundsProvider mainBoundsProvider = new ProjectionBoundsProvider() {
+        @Override
+        public Bounds getRealBounds() {
+            return isDisplayingMapView() ? map.mapView.getRealBounds() : null;
+        }
+
+        @Override
+        public void restoreOldBounds(Bounds oldBounds) {
+            if (isDisplayingMapView()) {
+                map.mapView.zoomTo(oldBounds);
+            }
+        }
+    };
+
     private static final List<CLIModule> cliModules = new ArrayList<>();
 
@@ -341,4 +357,5 @@
         undoRedo = super.undoRedo;
         getLayerManager().addLayerChangeListener(undoRedoCleaner);
+        ProjectionRegistry.setboundsProvider(mainBoundsProvider);
     }
 
@@ -511,16 +528,4 @@
         } catch (SecurityException e) {
             Logging.log(Logging.LEVEL_ERROR, "Unable to shutdown worker", e);
-        }
-    }
-
-    @Override
-    protected Bounds getRealBounds() {
-        return isDisplayingMapView() ? map.mapView.getRealBounds() : null;
-    }
-
-    @Override
-    protected void restoreOldBounds(Bounds oldBounds) {
-        if (isDisplayingMapView()) {
-            map.mapView.zoomTo(oldBounds);
         }
     }
Index: trunk/src/org/openstreetmap/josm/gui/MapView.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MapView.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/gui/MapView.java	(revision 14120)
@@ -37,5 +37,4 @@
 import javax.swing.SwingUtilities;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.mapmode.MapMode;
 import org.openstreetmap.josm.data.Bounds;
@@ -48,4 +47,5 @@
 import org.openstreetmap.josm.data.osm.visitor.paint.Rendering;
 import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.MapViewState.MapViewRectangle;
 import org.openstreetmap.josm.gui.autofilter.AutoFilterManager;
@@ -358,5 +358,5 @@
 
                 layer.addPropertyChangeListener(this);
-                Main.addProjectionChangeListener(layer);
+                ProjectionRegistry.addProjectionChangeListener(layer);
                 invalidatedListener.addTo(layer);
                 AudioPlayer.reset();
@@ -398,5 +398,5 @@
         }
         painter.detachFromMapView(new MapViewEvent(this, false));
-        Main.removeProjectionChangeListener(layer);
+        ProjectionRegistry.removeProjectionChangeListener(layer);
         layer.removePropertyChangeListener(this);
         invalidatedListener.removeFrom(layer);
Index: trunk/src/org/openstreetmap/josm/gui/MapViewState.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MapViewState.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/gui/MapViewState.java	(revision 14120)
@@ -16,5 +16,4 @@
 import javax.swing.JComponent;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.ProjectionBounds;
@@ -25,4 +24,5 @@
 import org.openstreetmap.josm.data.projection.Projecting;
 import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.download.DownloadDialog;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
@@ -394,5 +394,5 @@
      */
     public static MapViewState createDefaultState(int width, int height) {
-        Projection projection = Main.getProjection();
+        Projection projection = ProjectionRegistry.getProjection();
         double scale = projection.getDefaultZoomInPPD();
         MapViewState state = new MapViewState(projection, width, height, scale, new EastNorth(0, 0));
@@ -403,6 +403,6 @@
     private static EastNorth calculateDefaultCenter() {
         Bounds b = Optional.ofNullable(DownloadDialog.getSavedDownloadBounds()).orElseGet(
-                () -> Main.getProjection().getWorldBoundsLatLon());
-        return b.getCenter().getEastNorth(Main.getProjection());
+                () -> ProjectionRegistry.getProjection().getWorldBoundsLatLon());
+        return b.getCenter().getEastNorth(ProjectionRegistry.getProjection());
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java	(revision 14120)
@@ -32,5 +32,4 @@
 import javax.swing.SwingUtilities;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.ProjectionBounds;
@@ -53,4 +52,5 @@
 import org.openstreetmap.josm.data.projection.Projection;
 import org.openstreetmap.josm.data.projection.ProjectionChangeListener;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.help.Helpful;
 import org.openstreetmap.josm.gui.layer.NativeScaleLayer;
@@ -188,5 +188,5 @@
         setLayout(null);
         state = MapViewState.createDefaultState(getWidth(), getHeight());
-        Main.addProjectionChangeListener(projectionChangeListener);
+        ProjectionRegistry.addProjectionChangeListener(projectionChangeListener);
     }
 
@@ -334,5 +334,5 @@
      */
     public void fixProjection() {
-        state = state.usingProjection(Main.getProjection());
+        state = state.usingProjection(ProjectionRegistry.getProjection());
         repaint();
     }
@@ -492,5 +492,5 @@
      */
     public Bounds getLatLonBounds(Rectangle r) {
-        return Main.getProjection().getLatLonBoundsBox(getProjectionBounds(r));
+        return ProjectionRegistry.getProjection().getLatLonBoundsBox(getProjectionBounds(r));
     }
 
@@ -525,5 +525,5 @@
             return new Point();
         } else {
-            return getPoint2D(latlon.getEastNorth(Main.getProjection()));
+            return getPoint2D(latlon.getEastNorth(ProjectionRegistry.getProjection()));
         }
     }
Index: trunk/src/org/openstreetmap/josm/gui/datatransfer/importers/PrimitiveDataPaster.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/datatransfer/importers/PrimitiveDataPaster.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/gui/datatransfer/importers/PrimitiveDataPaster.java	(revision 14120)
@@ -23,4 +23,5 @@
 import org.openstreetmap.josm.data.osm.RelationMemberData;
 import org.openstreetmap.josm.data.osm.WayData;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.ExtendedDialog;
 import org.openstreetmap.josm.gui.MainApplication;
@@ -73,5 +74,5 @@
                 if (data instanceof NodeData) {
                     NodeData nodeData = (NodeData) data;
-                    nodeData.setEastNorth(nodeData.getEastNorth(Main.getProjection()).add(offset));
+                    nodeData.setEastNorth(nodeData.getEastNorth(ProjectionRegistry.getProjection()).add(offset));
                 } else if (data instanceof WayData) {
                     updateNodes(newIds.get(OsmPrimitiveType.NODE), data);
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/InspectPrimitiveDataText.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/InspectPrimitiveDataText.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/InspectPrimitiveDataText.java	(revision 14120)
@@ -8,5 +8,4 @@
 import java.util.List;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.conflict.Conflict;
 import org.openstreetmap.josm.data.coor.EastNorth;
@@ -22,4 +21,5 @@
 import org.openstreetmap.josm.data.osm.OsmData;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.data.projection.proj.TransverseMercator;
 import org.openstreetmap.josm.data.projection.proj.TransverseMercator.Hemisphere;
@@ -171,5 +171,5 @@
             addBbox(o);
             add(tr("Centroid: "),
-                    toStringCSV(", ", Main.getProjection().eastNorth2latlon(
+                    toStringCSV(", ", ProjectionRegistry.getProjection().eastNorth2latlon(
                             Geometry.getCentroid(((IWay<?>) o).getNodes()))));
             addWayNodes((IWay<?>) o);
@@ -203,6 +203,6 @@
         if (bbox != null) {
             add(tr("Bounding box: "), bbox.toStringCSV(", "));
-            EastNorth bottomRigth = bbox.getBottomRight().getEastNorth(Main.getProjection());
-            EastNorth topLeft = bbox.getTopLeft().getEastNorth(Main.getProjection());
+            EastNorth bottomRigth = bbox.getBottomRight().getEastNorth(ProjectionRegistry.getProjection());
+            EastNorth topLeft = bbox.getTopLeft().getEastNorth(ProjectionRegistry.getProjection());
             add(tr("Bounding box (projected): "),
                     Double.toString(topLeft.east()), ", ",
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/LatLonDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/LatLonDialog.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/LatLonDialog.java	(revision 14120)
@@ -23,9 +23,9 @@
 import javax.swing.event.DocumentListener;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.coor.conversion.CoordinateFormatManager;
 import org.openstreetmap.josm.data.coor.conversion.LatLonParser;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.ExtendedDialog;
 import org.openstreetmap.josm.gui.util.WindowGeometry;
@@ -196,5 +196,5 @@
         tfLatLon.setText(CoordinateFormatManager.getDefaultFormat().latToString(llc) + ' ' +
                          CoordinateFormatManager.getDefaultFormat().lonToString(llc));
-        EastNorth en = Main.getProjection().latlon2eastNorth(llc);
+        EastNorth en = ProjectionRegistry.getProjection().latlon2eastNorth(llc);
         tfEastNorth.setText(Double.toString(en.east()) + ' ' + Double.toString(en.north()));
         // Both latLonCoordinates and eastNorthCoordinates may have been reset to null if ll is out of the world
@@ -213,5 +213,5 @@
         } else {
             if (eastNorthCoordinates == null) return null;
-            return Main.getProjection().eastNorth2latlon(eastNorthCoordinates);
+            return ProjectionRegistry.getProjection().eastNorth2latlon(eastNorthCoordinates);
         }
     }
Index: trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java	(revision 14120)
@@ -91,4 +91,5 @@
 import org.openstreetmap.josm.data.preferences.IntegerProperty;
 import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.data.projection.Projections;
 import org.openstreetmap.josm.gui.ExtendedDialog;
@@ -328,5 +329,5 @@
         if (coordinateConverter.requiresReprojection()) {
             content.add(Arrays.asList(tr("Tile download projection"), tileSource.getServerCRS()));
-            content.add(Arrays.asList(tr("Tile display projection"), Main.getProjection().toCode()));
+            content.add(Arrays.asList(tr("Tile display projection"), ProjectionRegistry.getProjection().toCode()));
         }
         content.add(Arrays.asList(tr("Current zoom"), Integer.toString(currentZoomLevel)));
@@ -449,5 +450,5 @@
                     content.add(Arrays.asList(tr("Reprojection"),
                             tile.getTileSource().getServerCRS() +
-                            " -> " + Main.getProjection().toCode()));
+                            " -> " + ProjectionRegistry.getProjection().toCode()));
                     BufferedImage img = tile.getImage();
                     if (img != null) {
@@ -566,5 +567,5 @@
             }
             // check if projection is supported
-            projectionChanged(null, Main.getProjection());
+            projectionChanged(null, ProjectionRegistry.getProjection());
             initTileSource(this.tileSource);
         }
@@ -1412,5 +1413,5 @@
                     CoordinateConversion.projToEn(topLeftUnshifted),
                     CoordinateConversion.projToEn(botRightUnshifted));
-            ProjectionBounds bbox = projServer.getEastNorthBoundsBox(projBounds, Main.getProjection());
+            ProjectionBounds bbox = projServer.getEastNorthBoundsBox(projBounds, ProjectionRegistry.getProjection());
             t1 = tileSource.projectedToTileXY(CoordinateConversion.enToProj(bbox.getMin()), zoom);
             t2 = tileSource.projectedToTileXY(CoordinateConversion.enToProj(bbox.getMax()), zoom);
@@ -1962,5 +1963,5 @@
         @Override
         public boolean isActive() {
-            EastNorth offset = bookmark.getDisplacement(Main.getProjection());
+            EastNorth offset = bookmark.getDisplacement(ProjectionRegistry.getProjection());
             EastNorth active = getDisplaySettings().getDisplacement();
             return Utils.equalsEpsilon(offset.east(), active.east()) && Utils.equalsEpsilon(offset.north(), active.north());
Index: trunk/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java	(revision 14120)
@@ -29,8 +29,8 @@
 import javax.swing.JTextField;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.ProjectionBounds;
 import org.openstreetmap.josm.data.imagery.ImageryInfo;
 import org.openstreetmap.josm.data.preferences.IntegerProperty;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.MapView;
@@ -85,5 +85,5 @@
     public double getPPD() {
         if (!MainApplication.isDisplayingMapView())
-            return Main.getProjection().getDefaultZoomInPPD();
+            return ProjectionRegistry.getProjection().getDefaultZoomInPPD();
         MapView mapView = MainApplication.getMap().mapView;
         ProjectionBounds bounds = mapView.getProjectionBounds();
Index: trunk/src/org/openstreetmap/josm/gui/layer/WMSLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/WMSLayer.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/gui/layer/WMSLayer.java	(revision 14120)
@@ -16,5 +16,4 @@
 import org.apache.commons.jcs.access.CacheAccess;
 import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
 import org.openstreetmap.josm.data.coor.LatLon;
@@ -29,4 +28,5 @@
 import org.openstreetmap.josm.data.preferences.IntegerProperty;
 import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.data.projection.Projections;
 import org.openstreetmap.josm.gui.MainApplication;
@@ -96,5 +96,5 @@
         AbstractWMSTileSource tileSource;
         if (info.getImageryType() == ImageryType.WMS) {
-            tileSource = new TemplatedWMSTileSource(info, chooseProjection(Main.getProjection()));
+            tileSource = new TemplatedWMSTileSource(info, chooseProjection(ProjectionRegistry.getProjection()));
         } else {
             /*
@@ -107,7 +107,7 @@
              *  * we always use current definitions returned by server
              */
-            WMSEndpointTileSource endpointTileSource = new WMSEndpointTileSource(info, Main.getProjection());
+            WMSEndpointTileSource endpointTileSource = new WMSEndpointTileSource(info, ProjectionRegistry.getProjection());
             this.serverProjections = endpointTileSource.getServerProjections();
-            endpointTileSource.setTileProjection(chooseProjection(Main.getProjection()));
+            endpointTileSource.setTileProjection(chooseProjection(ProjectionRegistry.getProjection()));
             tileSource = endpointTileSource;
         }
@@ -182,5 +182,5 @@
         Logging.info(tr("Reprojecting layer {0} from {1} to {2}. For best image quality and performance,"
                 + " switch to one of the supported projections: {3}",
-                getName(), proj.toCode(), Main.getProjection().toCode(), Utils.join(", ", getNativeProjections())));
+                getName(), proj.toCode(), ProjectionRegistry.getProjection().toCode(), Utils.join(", ", getNativeProjections())));
         return proj;
     }
Index: trunk/src/org/openstreetmap/josm/gui/layer/WMTSLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/WMTSLayer.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/gui/layer/WMTSLayer.java	(revision 14120)
@@ -7,5 +7,4 @@
 import org.apache.commons.jcs.access.CacheAccess;
 import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
 import org.openstreetmap.josm.data.imagery.ImageryInfo;
@@ -15,4 +14,5 @@
 import org.openstreetmap.josm.data.imagery.WMTSTileSource.WMTSGetCapabilitiesException;
 import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.layer.imagery.TileSourceDisplaySettings;
@@ -81,5 +81,5 @@
         double displayScale = MainApplication.getMap().mapView.getScale();
         if (coordinateConverter.requiresReprojection()) {
-            displayScale *= Main.getProjection().getMetersPerUnit();
+            displayScale *= ProjectionRegistry.getProjection().getMetersPerUnit();
         }
         Scale snap = scaleList.getSnapScale(displayScale, false);
Index: trunk/src/org/openstreetmap/josm/gui/layer/gpx/ImportAudioAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/gpx/ImportAudioAction.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/gui/layer/gpx/ImportAudioAction.java	(revision 14120)
@@ -25,4 +25,5 @@
 import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
 import org.openstreetmap.josm.data.gpx.WayPoint;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
 import org.openstreetmap.josm.gui.MainApplication;
@@ -189,5 +190,5 @@
                     continue;
                 }
-                WayPoint wNear = layer.data.nearestPointOnTrack(w.getEastNorth(Main.getProjection()), snapDistance);
+                WayPoint wNear = layer.data.nearestPointOnTrack(w.getEastNorth(ProjectionRegistry.getProjection()), snapDistance);
                 if (wNear != null) {
                     WayPoint wc = new WayPoint(w.getCoor());
Index: trunk/src/org/openstreetmap/josm/gui/layer/imagery/ReprojectionTile.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/imagery/ReprojectionTile.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/gui/layer/imagery/ReprojectionTile.java	(revision 14120)
@@ -8,9 +8,9 @@
 import org.openstreetmap.gui.jmapviewer.Tile;
 import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.ProjectionBounds;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.imagery.CoordinateConversion;
 import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.data.projection.Projections;
 import org.openstreetmap.josm.gui.MainApplication;
@@ -122,5 +122,5 @@
         }
 
-        Projection projCurrent = Main.getProjection();
+        Projection projCurrent = ProjectionRegistry.getProjection();
         Projection projServer = Projections.getProjectionByCode(source.getServerCRS());
         EastNorth en00Server = tileToEastNorth(xtile, ytile, zoom);
Index: trunk/src/org/openstreetmap/josm/gui/layer/imagery/TileCoordinateConverter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/imagery/TileCoordinateConverter.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/gui/layer/imagery/TileCoordinateConverter.java	(revision 14120)
@@ -14,9 +14,9 @@
 import org.openstreetmap.gui.jmapviewer.interfaces.IProjected;
 import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.imagery.CoordinateConversion;
 import org.openstreetmap.josm.data.projection.Projecting;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.data.projection.ShiftedProjecting;
 import org.openstreetmap.josm.gui.MapView;
@@ -202,5 +202,5 @@
      */
     public boolean requiresReprojection() {
-        return !Objects.equals(tileSource.getServerCRS(), Main.getProjection().toCode());
+        return !Objects.equals(tileSource.getServerCRS(), ProjectionRegistry.getProjection().toCode());
     }
 }
Index: trunk/src/org/openstreetmap/josm/gui/layer/imagery/TileSourceDisplaySettings.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/imagery/TileSourceDisplaySettings.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/gui/layer/imagery/TileSourceDisplaySettings.java	(revision 14120)
@@ -7,8 +7,8 @@
 
 import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.imagery.OffsetBookmark;
 import org.openstreetmap.josm.data.preferences.BooleanProperty;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.layer.AbstractTileSourceLayer;
 import org.openstreetmap.josm.io.session.SessionAwareReadApply;
@@ -206,5 +206,5 @@
             setDisplacement(EastNorth.ZERO);
         } else {
-            setDisplacement(offsetBookmark.getDisplacement(Main.getProjection()));
+            setDisplacement(offsetBookmark.getDisplacement(ProjectionRegistry.getProjection()));
         }
     }
Index: trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/PlayHeadMarker.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/PlayHeadMarker.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/PlayHeadMarker.java	(revision 14120)
@@ -22,4 +22,5 @@
 import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
 import org.openstreetmap.josm.data.gpx.WayPoint;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.MapFrame;
@@ -215,5 +216,5 @@
             for (Marker m : recent.parentLayer.data) {
                 if (m instanceof AudioMarker) {
-                    double distanceSquared = m.getEastNorth(Main.getProjection()).distanceSq(en);
+                    double distanceSquared = m.getEastNorth(ProjectionRegistry.getProjection()).distanceSq(en);
                     if (distanceSquared < closestAudioMarkerDistanceSquared) {
                         ca = (AudioMarker) m;
@@ -348,6 +349,6 @@
             return;
         setEastNorth(w2 == null ?
-                w1.getEastNorth(Main.getProjection()) :
-                    w1.getEastNorth(Main.getProjection()).interpolate(w2.getEastNorth(Main.getProjection()),
+                w1.getEastNorth(ProjectionRegistry.getProjection()) :
+                    w1.getEastNorth(ProjectionRegistry.getProjection()).interpolate(w2.getEastNorth(ProjectionRegistry.getProjection()),
                             (audioTime - w1.time)/(w2.time - w1.time)));
         time = audioTime;
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/RenderingCLI.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/RenderingCLI.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/RenderingCLI.java	(revision 14120)
@@ -21,5 +21,4 @@
 
 import org.openstreetmap.gui.jmapviewer.OsmMercator;
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.cli.CLIModule;
 import org.openstreetmap.josm.data.Bounds;
@@ -32,4 +31,5 @@
 import org.openstreetmap.josm.data.preferences.JosmUrls;
 import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.data.projection.Projections;
 import org.openstreetmap.josm.gui.mappaint.RenderingHelper.StyleData;
@@ -427,5 +427,5 @@
         Config.getPref().putBoolean("mappaint.auto_reload_local_styles", false); // unnecessary to listen for external changes
         String projCode = Optional.ofNullable(argProjection).orElse("epsg:3857");
-        Main.setProjection(Projections.getProjectionByCode(projCode.toUpperCase(Locale.US)));
+        ProjectionRegistry.setProjection(Projections.getProjectionByCode(projCode.toUpperCase(Locale.US)));
 
         RightAndLefthandTraffic.initialize();
@@ -449,5 +449,5 @@
     RenderingArea determineRenderingArea(DataSet ds) {
 
-        Projection proj = Main.getProjection();
+        Projection proj = ProjectionRegistry.getProjection();
         Double scale = null; // scale in east-north units per pixel
         if (argZoom != null) {
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/RenderingHelper.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/RenderingHelper.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/RenderingHelper.java	(revision 14120)
@@ -16,5 +16,4 @@
 import java.util.Optional;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.ProjectionBounds;
@@ -22,4 +21,5 @@
 import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
 import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.NavigatableComponent;
 import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
@@ -65,5 +65,5 @@
         this.scale = scale;
         this.styles = styles;
-        Projection proj = Main.getProjection();
+        Projection proj = ProjectionRegistry.getProjection();
         projBounds = new ProjectionBounds();
         projBounds.extend(proj.latlon2eastNorth(bounds.getMin()));
Index: trunk/src/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreference.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreference.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreference.java	(revision 14120)
@@ -62,4 +62,5 @@
 import org.openstreetmap.josm.data.imagery.Shape;
 import org.openstreetmap.josm.data.preferences.NamedColorProperty;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.download.DownloadDialog;
@@ -856,5 +857,5 @@
             JButton add = new JButton(tr("Add"));
             buttonPanel.add(add, GBC.std().insets(0, 5, 0, 0));
-            add.addActionListener(e -> model.addRow(new OffsetBookmark(Main.getProjection().toCode(), "", "", "", 0, 0)));
+            add.addActionListener(e -> model.addRow(new OffsetBookmark(ProjectionRegistry.getProjection().toCode(), "", "", "", 0, 0)));
 
             JButton delete = new JButton(tr("Delete"));
Index: trunk/src/org/openstreetmap/josm/gui/preferences/projection/ProjectionPreference.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/projection/ProjectionPreference.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/projection/ProjectionPreference.java	(revision 14120)
@@ -32,4 +32,5 @@
 import org.openstreetmap.josm.data.projection.CustomProjection;
 import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.data.projection.Projections;
 import org.openstreetmap.josm.gui.ExtendedDialog;
@@ -493,5 +494,5 @@
         pc.setPreferences(pref);
         Projection proj = pc.getProjection();
-        Main.setProjection(proj);
+        ProjectionRegistry.setProjection(proj);
     }
 
Index: trunk/src/org/openstreetmap/josm/io/session/SessionWriter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/session/SessionWriter.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/io/session/SessionWriter.java	(revision 14120)
@@ -26,7 +26,7 @@
 import javax.xml.transform.stream.StreamResult;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.MapView;
@@ -264,5 +264,5 @@
         MapView mapView = MainApplication.getMap().mapView;
         EastNorth center = mapView.getCenter();
-        LatLon centerLL = Main.getProjection().eastNorth2latlon(center);
+        LatLon centerLL = ProjectionRegistry.getProjection().eastNorth2latlon(center);
         centerEl.setAttribute("lat", Double.toString(centerLL.lat()));
         centerEl.setAttribute("lon", Double.toString(centerLL.lon()));
@@ -293,5 +293,5 @@
             }
         }
-        String code = Main.getProjection().toCode();
+        String code = ProjectionRegistry.getProjection().toCode();
         if (code != null) {
             Element codeEl = doc.createElement("code");
Index: trunk/src/org/openstreetmap/josm/tools/Geometry.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/Geometry.java	(revision 14119)
+++ trunk/src/org/openstreetmap/josm/tools/Geometry.java	(revision 14120)
@@ -37,4 +37,5 @@
 import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache;
 import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.data.projection.Projections;
 
@@ -164,5 +165,5 @@
                                 }
 
-                                Node newNode = new Node(Main.getProjection().eastNorth2latlon(intersection));
+                                Node newNode = new Node(ProjectionRegistry.getProjection().eastNorth2latlon(intersection));
                                 Node intNode = newNode;
                                 boolean insertInSeg1 = false;
@@ -1053,5 +1054,5 @@
         double area = 0;
         double perimeter = 0;
-        Projection useProjection = projection == null ? Main.getProjection() : projection;
+        Projection useProjection = projection == null ? ProjectionRegistry.getProjection() : projection;
 
         if (!nodes.isEmpty()) {
