Index: /trunk/src/org/openstreetmap/josm/data/projection/Projections.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/projection/Projections.java	(revision 9369)
+++ /trunk/src/org/openstreetmap/josm/data/projection/Projections.java	(revision 9370)
@@ -44,6 +44,8 @@
 
 /**
- * Class to handle projections
+ * Class to manage projections.
  *
+ * Use this class to query available projection or register new projections
+ * from a plugin.
  */
 public final class Projections {
@@ -157,4 +159,10 @@
     }
 
+    /**
+     * Convert from lat/lon to easting/northing using the current projection.
+     *
+     * @param ll the geographical point to convert (in WGS84 lat/lon)
+     * @return the corresponding east/north coordinates
+     */
     public static EastNorth project(LatLon ll) {
         if (ll == null) return null;
@@ -162,4 +170,10 @@
     }
 
+    /**
+     * Convert from easting/norting to lat/lon using the current projection.
+     *
+     * @param en the geographical point to convert (in projected coordinates)
+     * @return the corresponding lat/lon (WGS84)
+     */
     public static LatLon inverseProject(EastNorth en) {
         if (en == null) return null;
@@ -184,4 +198,10 @@
     }
 
+    /**
+     * Get a base projection by id.
+     *
+     * @param id the id, for example "lonlat" or "tmerc"
+     * @return the corresponding base projection if the id is known, null otherwise
+     */
     public static Proj getBaseProjection(String id) {
         ProjFactory fac = projs.get(id);
@@ -190,12 +210,29 @@
     }
 
+    /**
+     * Get an ellipsoid by id.
+     *
+     * @param id the id, for example "bessel" or "WGS84"
+     * @return the corresponding ellipsoid if the id is known, null otherwise
+     */
     public static Ellipsoid getEllipsoid(String id) {
         return ellipsoids.get(id);
     }
 
+    /**
+     * Get a geodetic datum by id.
+     *
+     * @param id the id, for example "potsdam" or "WGS84"
+     * @return the corresponding datum if the id is known, null otherwise
+     */
     public static Datum getDatum(String id) {
         return datums.get(id);
     }
 
+    /**
+     * Get a NTV2 grid database by id.
+     * @param id the id
+     * @return the corresponding NTV2 grid if the id is known, null otherwise
+     */
     public static NTV2GridShiftFileWrapper getNTV2Grid(String id) {
         return nadgrids.get(id);
@@ -203,11 +240,11 @@
 
     /**
-     * Get the projection definition string for the given id.
-     * @param id the id
+     * Get the projection definition string for the given code.
+     * @param code the code
      * @return the string that can be processed by #{link CustomProjection}.
-     * Null, if the id isn't supported.
-     */
-    public static String getInit(String id) {
-        ProjectionDefinition pd = inits.get(id.toUpperCase(Locale.ENGLISH));
+     * Null, if the code isn't supported.
+     */
+    public static String getInit(String code) {
+        ProjectionDefinition pd = inits.get(code.toUpperCase(Locale.ENGLISH));
         if (pd == null) return null;
         return pd.definition;
@@ -260,4 +297,9 @@
     }
 
+    /**
+     * Get a projection by code.
+     * @param code the code, e.g. "EPSG:2026"
+     * @return the corresponding projection, if the code is known, null otherwise
+     */
     public static Projection getProjectionByCode(String code) {
         Projection proj = projectionsByCode_cache.get(code);
@@ -293,4 +335,14 @@
     }
 
+    /**
+     * Get a list of ids of all registered base projections.
+     *
+     * @return all registered base projection ids
+     * @see #getBaseProjection(java.lang.String)
+     */
+    public static Collection<String> getAllBaseProjectionIds() {
+        return projs.keySet();
+    }
+
     private static String listKeys(Map<String, ?> map) {
         List<String> keys = new ArrayList<>(map.keySet());
Index: /trunk/test/unit/org/openstreetmap/josm/data/projection/ProjectionTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/data/projection/ProjectionTest.java	(revision 9369)
+++ /trunk/test/unit/org/openstreetmap/josm/data/projection/ProjectionTest.java	(revision 9370)
@@ -3,4 +3,6 @@
 
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
 import java.util.Random;
 
@@ -19,33 +21,33 @@
 
     @Test
-    public void proj() {
+    public void projections() {
         error = false;
         text = "";
 
-        testProj(Projections.getProjectionByCode("EPSG:4326")); // WGS 84
-        testProj(Projections.getProjectionByCode("EPSG:3857")); // Mercator
-        testProj(Projections.getProjectionByCode("EPSG:3301")); // Lambert EST
+        testProjection(Projections.getProjectionByCode("EPSG:4326")); // WGS 84
+        testProjection(Projections.getProjectionByCode("EPSG:3857")); // Mercator
+        testProjection(Projections.getProjectionByCode("EPSG:3301")); // Lambert EST
 
         for (int i = 0; i <= 3; ++i) {
-            testProj(Projections.getProjectionByCode("EPSG:"+Integer.toString(27561+i))); // Lambert 4 Zones France
+            testProjection(Projections.getProjectionByCode("EPSG:"+Integer.toString(27561+i))); // Lambert 4 Zones France
         }
 
         for (int i = 0; i <= 4; ++i) {
-            testProj(Projections.getProjectionByCode("EPSG:"+Integer.toString(2176+i))); // PUWG Poland
+            testProjection(Projections.getProjectionByCode("EPSG:"+Integer.toString(2176+i))); // PUWG Poland
         }
 
-        testProj(Projections.getProjectionByCode("EPSG:21781")); // Swiss grid
+        testProjection(Projections.getProjectionByCode("EPSG:21781")); // Swiss grid
 
         for (int i = 0; i <= 60; ++i) {
-            testProj(Projections.getProjectionByCode("EPSG:"+Integer.toString(32601+i))); // UTM North
-            testProj(Projections.getProjectionByCode("EPSG:"+Integer.toString(32701+i))); // UTM South
+            testProjection(Projections.getProjectionByCode("EPSG:"+Integer.toString(32601+i))); // UTM North
+            testProjection(Projections.getProjectionByCode("EPSG:"+Integer.toString(32701+i))); // UTM South
         }
 
         for (String c : Arrays.asList("2969", "2970", "2972", "2973")) {
-            testProj(Projections.getProjectionByCode("EPSG:"+c)); // UTM France DOM
+            testProjection(Projections.getProjectionByCode("EPSG:"+c)); // UTM France DOM
         }
 
         for (int i = 0; i <= 8; ++i) {
-            testProj(Projections.getProjectionByCode("EPSG:"+Integer.toString(3942+i))); // Lambert CC9 Zones France
+            testProjection(Projections.getProjectionByCode("EPSG:"+Integer.toString(3942+i))); // Lambert CC9 Zones France
         }
 
@@ -56,5 +58,5 @@
     }
 
-    private void testProj(Projection p) {
+    private void testProjection(Projection p) {
         if (p != null) {
             double maxErrLat = 0, maxErrLon = 0;
@@ -64,8 +66,6 @@
             for (int num = 0; num < 1000; ++num) {
 
-                double lat = rand.nextDouble() * (b.getMax().lat() - b.getMin().lat()) + b.getMin().lat();
-                double lon = rand.nextDouble() * (b.getMax().lon() - b.getMin().lon()) + b.getMin().lon();
-
-                LatLon ll = new LatLon(lat, lon);
+                LatLon ll0 = random(b);
+                LatLon ll = ll0;
 
                 for (int i = 0; i < 10; ++i) {
@@ -73,6 +73,6 @@
                     ll = p.eastNorth2latlon(en);
                 }
-                maxErrLat = Math.max(maxErrLat, Math.abs(lat - ll.lat()));
-                maxErrLon = Math.max(maxErrLon, Math.abs(lon - ll.lon()));
+                maxErrLat = Math.max(maxErrLat, Math.abs(ll0.lat() - ll.lat()));
+                maxErrLon = Math.max(maxErrLon, Math.abs(ll0.lon() - ll.lon()));
             }
 
@@ -85,3 +85,65 @@
         }
     }
+
+    private LatLon random(Bounds b) {
+        for (int i=0; i<20; i++) {
+            double lat = rand.nextDouble() * (b.getMax().lat() - b.getMin().lat()) + b.getMin().lat();
+            double lon = rand.nextDouble() * (b.getMax().lon() - b.getMin().lon()) + b.getMin().lon();
+            LatLon result = new LatLon(lat, lon);
+            if (result.isValid()) return result;
+        }
+        throw new RuntimeException();
+    }
+
+    boolean error2;
+    String text2;
+    Collection<String> projIds;
+
+    @Test
+    public void projs() {
+        error2 = false;
+        text2 = "";
+
+        projIds = new HashSet<>(Projections.getAllBaseProjectionIds());
+
+        final double EPS = 1e-6;
+        testProj("lonlat", EPS, "");
+        testProj("josm:smerc", EPS, "");
+        testProj("lcc", EPS, "+lat_0=34");
+        testProj("lcc", EPS, "+lat_1=87 +lat_2=83.6 +lat_0=85.43");
+        testProj("somerc", EPS, "+lat_0=47");
+        testProj("tmerc", 2e-3, "");
+        testProj("sterea", EPS, "+lat_0=52");
+
+        if (error2) {
+            System.err.println(text2);
+            Assert.fail();
+        }
+        Assert.assertTrue("missing test: "+projIds, projIds.isEmpty());
+    }
+
+    private void testProj(String id, double eps, String prefAdd) {
+        final int NUM_IT = 1000;
+        projIds.remove(id);
+        String pref = String.format("+proj=%s +ellps=WGS84 +nadgrids=null "+prefAdd, id);
+        CustomProjection p = new CustomProjection();
+        try {
+            p.update(pref);
+        } catch (ProjectionConfigurationException ex) {
+            throw new RuntimeException(ex);
+        }
+        Bounds b = p.getWorldBoundsLatLon();
+        for (int i=0; i<NUM_IT; i++) {
+            LatLon ll1 = random(b);
+            EastNorth en = p.latlon2eastNorth(ll1);
+            LatLon ll2 = p.eastNorth2latlon(en);
+            Assert.assertTrue(p.toCode() + " at " + ll1 + " is " + ll2, ll2.isValid());
+            double dist = ll1.greatCircleDistance(ll2);
+            if (dist > eps) {
+                error2 = true;
+                text2 += id + ": dist " + dist + " at " + ll1 + "\n";
+                return;
+            }
+        }
+    }
 }
