Index: trunk/src/org/openstreetmap/josm/data/projection/CustomProjection.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/CustomProjection.java	(revision 5226)
+++ trunk/src/org/openstreetmap/josm/data/projection/CustomProjection.java	(revision 5227)
@@ -5,7 +5,10 @@
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -13,4 +16,5 @@
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.projection.CustomProjection.Param;
 import org.openstreetmap.josm.data.projection.datum.CentricDatum;
 import org.openstreetmap.josm.data.projection.datum.Datum;
@@ -38,4 +42,44 @@
     protected String pref = null;
 
+    protected static class Param {
+        public String key;
+        public boolean hasValue;
+
+        public final static Param x_0 = new Param("x_0", true);
+        public final static Param y_0 = new Param("y_0", true);
+        public final static Param lon_0 = new Param("lon_0", true);
+        public final static Param k_0 = new Param("k_0", true);
+        public final static Param ellps = new Param("ellps", true);
+        public final static Param a = new Param("a", true);
+        public final static Param es = new Param("es", true);
+        public final static Param rf = new Param("rf", true);
+        public final static Param f = new Param("f", true);
+        public final static Param b = new Param("b", true);
+        public final static Param datum = new Param("datum", true);
+        public final static Param towgs84 = new Param("towgs84", true);
+        public final static Param nadgrids = new Param("nadgrids", true);
+        public final static Param proj = new Param("proj", true);
+        public final static Param lat_0 = new Param("lat_0", true);
+        public final static Param lat_1 = new Param("lat_1", true);
+        public final static Param lat_2 = new Param("lat_2", true);
+
+        public final static Set<Param> params = new HashSet<Param>(Arrays.asList(
+                x_0, y_0, lon_0, k_0, ellps, a, es, rf, f, b, datum, towgs84,
+                nadgrids, proj, lat_0, lat_1, lat_2
+        ));
+
+        public final static Map<String, Param> paramsByKey = new HashMap<String, Param>();
+        static {
+            for (Param p : params) {
+                paramsByKey.put(p.key, p);
+            }
+        }
+
+        public Param(String key, boolean hasValue) {
+            this.key = key;
+            this.hasValue = hasValue;
+        }
+    }
+
     public void update(String pref) throws ProjectionConfigurationException {
         if (pref == null) {
@@ -47,4 +91,7 @@
             Map<String, String> parameters = new HashMap<String, String>();
             String[] parts = pref.trim().split("\\s+");
+            if (pref.trim().isEmpty()) {
+                parts = new String[0];
+            }
             for (int i = 0; i < parts.length; i++) {
                 String part = parts[i];
@@ -58,4 +105,10 @@
                         value = m.group(3);
                     }
+                    if (!Param.paramsByKey.containsKey(key))
+                        throw new ProjectionConfigurationException(tr("Unkown parameter: ''{0}''.", key));
+                    if (Param.paramsByKey.get(key).hasValue && value == null)
+                        throw new ProjectionConfigurationException(tr("Value expected for parameter ''{0}''.", key));
+                    if (!Param.paramsByKey.get(key).hasValue && value != null)
+                        throw new ProjectionConfigurationException(tr("No value expected for parameter ''{0}''.", key));
                     parameters.put(key, value);
                 } else
@@ -65,19 +118,19 @@
             datum = parseDatum(parameters, ellps);
             proj = parseProjection(parameters, ellps);
-            String s = parameters.get("x_0");
+            String s = parameters.get(Param.x_0.key);
             if (s != null) {
-                this.x_0 = parseDouble(s, "x_0");
-            }
-            s = parameters.get("y_0");
+                this.x_0 = parseDouble(s, Param.x_0.key);
+            }
+            s = parameters.get(Param.y_0.key);
             if (s != null) {
-                this.y_0 = parseDouble(s, "y_0");
-            }
-            s = parameters.get("lon_0");
+                this.y_0 = parseDouble(s, Param.x_0.key);
+            }
+            s = parameters.get(Param.lon_0.key);
             if (s != null) {
-                this.lon_0 = parseAngle(s, "lon_0");
-            }
-            s = parameters.get("k_0");
+                this.lon_0 = parseAngle(s, Param.x_0.key);
+            }
+            s = parameters.get(Param.k_0.key);
             if (s != null) {
-                this.k_0 = parseDouble(s, "k_0");
+                this.k_0 = parseDouble(s, Param.x_0.key);
             }
             this.pref = pref;
@@ -86,5 +139,5 @@
 
     public Ellipsoid parseEllipsoid(Map<String, String> parameters) throws ProjectionConfigurationException {
-        String code = parameters.get("ellps");
+        String code = parameters.get(Param.ellps.key);
         if (code != null) {
             Ellipsoid ellipsoid = Projections.getEllipsoid(code);
@@ -95,29 +148,29 @@
             }
         }
-        String s = parameters.get("a");
+        String s = parameters.get(Param.a.key);
         if (s != null) {
-            double a = parseDouble(s, "a");
-            if (parameters.get("es") != null) {
-                double es = parseDouble(parameters, "es");
+            double a = parseDouble(s, Param.a.key);
+            if (parameters.get(Param.es.key) != null) {
+                double es = parseDouble(parameters, Param.es.key);
                 return Ellipsoid.create_a_es(a, es);
             }
-            if (parameters.get("rf") != null) {
-                double rf = parseDouble(parameters, "rf");
+            if (parameters.get(Param.rf.key) != null) {
+                double rf = parseDouble(parameters, Param.rf.key);
                 return Ellipsoid.create_a_rf(a, rf);
             }
-            if (parameters.get("f") != null) {
-                double f = parseDouble(parameters, "f");
+            if (parameters.get(Param.f.key) != null) {
+                double f = parseDouble(parameters, Param.f.key);
                 return Ellipsoid.create_a_f(a, f);
             }
-            if (parameters.get("b") != null) {
-                double b = parseDouble(parameters, "b");
+            if (parameters.get(Param.b.key) != null) {
+                double b = parseDouble(parameters, Param.b.key);
                 return Ellipsoid.create_a_b(a, b);
             }
         }
-        if (parameters.containsKey("a") ||
-                parameters.containsKey("es") ||
-                parameters.containsKey("rf") ||
-                parameters.containsKey("f") ||
-                parameters.containsKey("b"))
+        if (parameters.containsKey(Param.a.key) ||
+                parameters.containsKey(Param.es.key) ||
+                parameters.containsKey(Param.rf.key) ||
+                parameters.containsKey(Param.f.key) ||
+                parameters.containsKey(Param.b.key))
             throw new ProjectionConfigurationException(tr("Combination of ellipsoid parameters is not supported."));
         // nothing specified, use WGS84 as default
@@ -126,5 +179,5 @@
 
     public Datum parseDatum(Map<String, String> parameters, Ellipsoid ellps) throws ProjectionConfigurationException {
-        String nadgridsId = parameters.get("nadgrids");
+        String nadgridsId = parameters.get(Param.nadgrids.key);
         if (nadgridsId != null) {
             NTV2GridShiftFileWrapper nadgrids = Projections.getNadgrids(nadgridsId);
@@ -134,9 +187,9 @@
         }
 
-        String towgs84 = parameters.get("towgs84");
+        String towgs84 = parameters.get(Param.towgs84.key);
         if (towgs84 != null)
             return parseToWGS84(towgs84, ellps);
 
-        String datumId = parameters.get("datum");
+        String datumId = parameters.get(Param.datum.key);
         if (datumId != null) {
             Datum datum = Projections.getDatum(datumId);
@@ -189,8 +242,8 @@
 
     public Proj parseProjection(Map<String, String> parameters, Ellipsoid ellps) throws ProjectionConfigurationException {
-        String id = (String) parameters.get("proj");
+        String id = (String) parameters.get(Param.proj.key);
         if (id == null) throw new ProjectionConfigurationException(tr("Projection required (+proj=*)"));
 
-        Proj proj =  Projections.getProjection(id);
+        Proj proj =  Projections.getBaseProjection(id);
         if (proj == null) throw new ProjectionConfigurationException(tr("Unkown projection identifier: ''{0}''", id));
 
@@ -200,15 +253,15 @@
 
         String s;
-        s = parameters.get("lat_0");
+        s = parameters.get(Param.lat_0.key);
         if (s != null) {
-            projParams.lat_0 = parseAngle(s, "lat_0");
-        }
-        s = parameters.get("lat_1");
+            projParams.lat_0 = parseAngle(s, Param.lat_0.key);
+        }
+        s = parameters.get(Param.lat_1.key);
         if (s != null) {
-            projParams.lat_1 = parseAngle(s, "lat_1");
-        }
-        s = parameters.get("lat_2");
+            projParams.lat_1 = parseAngle(s, Param.lat_1.key);
+        }
+        s = parameters.get(Param.lat_2.key);
         if (s != null) {
-            projParams.lat_2 = parseAngle(s, "lat_2");
+            projParams.lat_2 = parseAngle(s, Param.lat_2.key);
         }
         proj.initialize(projParams);
@@ -328,5 +381,5 @@
     @Override
     public String toString() {
-        return tr("Custom Projection (from PROJ.4 string)");
+        return tr("Custom Projection");
     }
 }
Index: trunk/src/org/openstreetmap/josm/data/projection/CustomProjectionPrefGui.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/CustomProjectionPrefGui.java	(revision 5226)
+++ trunk/src/org/openstreetmap/josm/data/projection/CustomProjectionPrefGui.java	(revision 5227)
@@ -9,14 +9,17 @@
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.logging.Level;
-import java.util.logging.Logger;
+import java.util.List;
+import java.util.Map;
 
 import javax.swing.JButton;
+import javax.swing.JComponent;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
 import javax.swing.JTextField;
 
+import org.openstreetmap.josm.gui.ExtendedDialog;
 import org.openstreetmap.josm.gui.preferences.projection.SubPrefsOptions;
 import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
@@ -24,4 +27,5 @@
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.Utils;
 
 public class CustomProjectionPrefGui extends CustomProjection implements ProjectionSubPrefs, SubPrefsOptions {
@@ -29,69 +33,137 @@
     @Override
     public void setupPreferencePanel(JPanel p, ActionListener listener) {
-        final JTextField input = new JTextField(pref == null ? "" : pref, 30);
-        final HtmlPanel errorsPanel = new HtmlPanel();
-        errorsPanel.setVisible(false);
-        final JLabel valStatus = new JLabel();
-        valStatus.setVisible(false);
-
-        final AbstractTextComponentValidator val = new AbstractTextComponentValidator(input, false, false, false) {
-
-            private String error;
-
-            @Override
-            public void validate() {
-                if (!isValid()) {
-                    feedbackInvalid(tr("Invalid projection configuration: {0}",error));
-                } else {
-                    feedbackValid(tr("Projection configuration is valid."));
-                }
-            }
-
-            @Override
-            public boolean isValid() {
-                try {
-                    CustomProjection test = new CustomProjection();
-                    test.update(input.getText());
-                } catch (ProjectionConfigurationException ex) {
-                    error = ex.getMessage();
-                    valStatus.setIcon(ImageProvider.get("data", "error.png"));
+        JPanel inner = new PreferencePanel();
+        p.setLayout(new GridBagLayout());
+        p.add(inner, GBC.std().fill(GBC.HORIZONTAL));
+    }
+
+    private class PreferencePanel extends JPanel {
+
+        public final JTextField input;
+
+        public PreferencePanel() {
+            input = new JTextField(pref == null ? "" : pref, 30);
+            build();
+        }
+
+        private void build() {
+            final HtmlPanel errorsPanel = new HtmlPanel();
+            errorsPanel.setVisible(false);
+            final JLabel valStatus = new JLabel();
+            valStatus.setVisible(false);
+
+            final AbstractTextComponentValidator val = new AbstractTextComponentValidator(input, false, false, false) {
+
+                private String error;
+
+                @Override
+                public void validate() {
+                    if (!isValid()) {
+                        feedbackInvalid(tr("Invalid projection configuration: {0}",error));
+                    } else {
+                        feedbackValid(tr("Projection configuration is valid."));
+                    }
+                }
+
+                @Override
+                public boolean isValid() {
+                    try {
+                        CustomProjection test = new CustomProjection();
+                        test.update(input.getText());
+                    } catch (ProjectionConfigurationException ex) {
+                        error = ex.getMessage();
+                        valStatus.setIcon(ImageProvider.get("data", "error.png"));
+                        valStatus.setVisible(true);
+                        errorsPanel.setText(error);
+                        errorsPanel.setVisible(true);
+                        return false;
+                    }
+                    errorsPanel.setVisible(false);
+                    valStatus.setIcon(ImageProvider.get("misc", "green_check.png"));
                     valStatus.setVisible(true);
-                    errorsPanel.setText(error);
-                    errorsPanel.setVisible(true);
-                    return false;
-                }
-                errorsPanel.setVisible(false);
-                valStatus.setIcon(ImageProvider.get("misc", "green_check.png"));
-                valStatus.setVisible(true);
-                return true;
-            }
-
-        };
-
-        JButton btnCheck = new JButton(tr("Validate"));
-        btnCheck.addActionListener(new ActionListener() {
-            @Override
-            public void actionPerformed(ActionEvent e) {
-                val.validate();
-            }
-        });
-        btnCheck.setLayout(new BorderLayout());
-        btnCheck.setMargin(new Insets(-1,0,-1,0));
-
-        p.setLayout(new GridBagLayout());
-        p.add(input, GBC.std().fill(GBC.HORIZONTAL).insets(0, 0, 5, 5));
-        p.add(btnCheck, GBC.eol());
-        JPanel p2 = new JPanel(new GridBagLayout());
-        p2.add(valStatus, GBC.std().anchor(GBC.WEST).weight(0.0001, 0));
-        p2.add(errorsPanel, GBC.eol().fill(GBC.HORIZONTAL));
-        p.add(p2, GBC.eol().fill(GBC.HORIZONTAL));
+                    return true;
+                }
+
+            };
+
+            JButton btnCheck = new JButton(tr("Validate"));
+            btnCheck.addActionListener(new ActionListener() {
+                @Override
+                public void actionPerformed(ActionEvent e) {
+                    val.validate();
+                }
+            });
+            btnCheck.setLayout(new BorderLayout());
+            btnCheck.setMargin(new Insets(-1,0,-1,0));
+
+            JButton btnInfo = new JButton(tr("Parameter information..."));
+            btnInfo.addActionListener(new ActionListener() {
+                @Override
+                public void actionPerformed(ActionEvent e) {
+                    ParameterInfoDialog dlg = new ParameterInfoDialog();
+                    dlg.showDialog();
+                    dlg.toFront();
+                }
+            });
+
+            this.setLayout(new GridBagLayout());
+            JPanel p2 = new JPanel(new GridBagLayout());
+            p2.add(input, GBC.std().fill(GBC.HORIZONTAL).insets(0, 20, 5, 5));
+            p2.add(btnCheck, GBC.eol().insets(0, 20, 0, 5));
+            this.add(p2, GBC.eol().fill(GBC.HORIZONTAL));
+            p2 = new JPanel(new GridBagLayout());
+            p2.add(valStatus, GBC.std().anchor(GBC.WEST).weight(0.0001, 0));
+            p2.add(errorsPanel, GBC.eol().fill(GBC.HORIZONTAL));
+            this.add(p2, GBC.eol().fill(GBC.HORIZONTAL));
+            p2 = new JPanel(new GridBagLayout());
+            p2.add(btnInfo, GBC.std().insets(0, 20, 0, 0));
+            p2.add(GBC.glue(1, 0), GBC.eol().fill(GBC.HORIZONTAL));
+            this.add(p2, GBC.eol().fill(GBC.HORIZONTAL));
+        }
+    }
+
+    public class ParameterInfoDialog extends ExtendedDialog {
+
+        public ParameterInfoDialog() {
+            super(null, tr("Parameter information"), new String[] { tr("Close") }, false);
+            setContent(build());
+        }
+
+        private JComponent build() {
+            StringBuilder s = new StringBuilder();
+            s.append("<b>+proj=...</b> - <i>"+tr("Projection name")+"</i><br>");
+            s.append("&nbsp;&nbsp;&nbsp;&nbsp;"+tr("Supported values:")+" ");
+            s.append(listKeys(Projections.projs)+"<br>");
+            s.append("<b>+lat_0=..., +lat_1=..., +lat_2=...</b> - <i>"+tr("Projection parameters")+"</i><br>");
+            s.append("<b>+x_0=..., +y_0=...</b> - <i>"+tr("False easting and false northing")+"</i><br>");
+            s.append("<b>+lon_0=...</b> - <i>"+tr("Central meridian")+"</i><br>");
+            s.append("<b>+k_0=...</b> - <i>"+tr("Scaling factor")+"</i><br>");
+            s.append("<b>+ellps=...</b> - <i>"+tr("Ellipsoid name")+"</i><br>");
+            s.append("&nbsp;&nbsp;&nbsp;&nbsp;"+tr("Supported values:")+" ");
+            s.append(listKeys(Projections.ellipsoids)+"<br>");
+            s.append("<b>+a=..., +b=..., +rf=..., +f=..., +es=...</b> - <i>"+tr("Ellipsoid parameters")+"</i><br>");
+            s.append("<b>+datum=...</b> - <i>"+tr("Datum name")+"</i><br>");
+            s.append("&nbsp;&nbsp;&nbsp;&nbsp;"+tr("Supported values:")+" ");
+            s.append(listKeys(Projections.datums)+"<br>");
+            s.append("<b>+towgs84=...</b> - <i>"+tr("3 or 7 term datum transform parameters")+"</i><br>");
+            s.append("<b>+nadgrids=...</b> - <i>"+tr("NTv2 grid file")+"</i><br>");
+            s.append("&nbsp;&nbsp;&nbsp;&nbsp;"+tr("Build-in:")+" ");
+            s.append(listKeys(Projections.nadgrids)+"<br>");
+
+            HtmlPanel info = new HtmlPanel(s.toString());
+            return info;
+        }
+
+        private String listKeys(Map<String, ?> map) {
+            List<String> keys = new ArrayList<String>(map.keySet());
+            Collections.sort(keys);
+            return Utils.join(", ", keys);
+        }
     }
 
     @Override
     public Collection<String> getPreferences(JPanel p) {
-        Object prefTf = p.getComponent(0);
-        if (!(prefTf instanceof JTextField))
-            return null;
-        String pref = ((JTextField) prefTf).getText();
+        PreferencePanel prefPanel = (PreferencePanel) p.getComponent(0);
+        String pref = prefPanel.input.getText();
         return Collections.singleton(pref);
     }
@@ -100,4 +172,5 @@
     public void setPreferences(Collection<String> args) {
         try {
+            if (args == null || args.isEmpty()) throw new ProjectionConfigurationException();
             update(args.iterator().next());
         } catch (ProjectionConfigurationException ex) {
Index: trunk/src/org/openstreetmap/josm/data/projection/Projections.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/Projections.java	(revision 5226)
+++ trunk/src/org/openstreetmap/josm/data/projection/Projections.java	(revision 5227)
@@ -13,6 +13,9 @@
 import org.openstreetmap.josm.data.projection.datum.NTV2GridShiftFileWrapper;
 import org.openstreetmap.josm.data.projection.datum.WGS84Datum;
+import org.openstreetmap.josm.data.projection.proj.ClassProjFactory;
 import org.openstreetmap.josm.data.projection.proj.LambertConformalConic;
+import org.openstreetmap.josm.data.projection.proj.LonLat;
 import org.openstreetmap.josm.data.projection.proj.Proj;
+import org.openstreetmap.josm.data.projection.proj.ProjFactory;
 import org.openstreetmap.josm.data.projection.proj.SwissObliqueMercator;
 import org.openstreetmap.josm.data.projection.proj.TransverseMercator;
@@ -45,11 +48,6 @@
                 new Puwg(),                 // PL
                 new Epsg3008(),             // SE
+                new CustomProjectionPrefGui()
         }));
-
-    static {
-        if (Main.pref.getBoolean("customprojection")) {
-            addProjection(new CustomProjectionPrefGui());
-        }
-    }
 
     public static ArrayList<Projection> getProjections() {
@@ -82,20 +80,20 @@
      * should be compatible to PROJ.4
      */
-
-    private static Map<String, Ellipsoid> ellipsoids = new HashMap<String, Ellipsoid>();
-    private static Map<String, Class<? extends Proj>> projs = new HashMap<String, Class<? extends Proj>>();
-    private static Map<String, Datum> datums = new HashMap<String, Datum>();
-    private static Map<String, NTV2GridShiftFileWrapper> nadgrids = new HashMap<String, NTV2GridShiftFileWrapper>();
+    public static Map<String, Ellipsoid> ellipsoids = new HashMap<String, Ellipsoid>();
+    public static Map<String, ProjFactory> projs = new HashMap<String, ProjFactory>();
+    public static Map<String, Datum> datums = new HashMap<String, Datum>();
+    public static Map<String, NTV2GridShiftFileWrapper> nadgrids = new HashMap<String, NTV2GridShiftFileWrapper>();
 
     static {
+        registerBaseProjection("lonlat", LonLat.class, "core");
+        registerBaseProjection("merc", org.openstreetmap.josm.data.projection.proj.Mercator.class, "core");
+        registerBaseProjection("lcc", LambertConformalConic.class, "core");
+        registerBaseProjection("somerc", SwissObliqueMercator.class, "core");
+        registerBaseProjection("tmerc", TransverseMercator.class, "core");
+
         ellipsoids.put("intl", Ellipsoid.hayford);
         ellipsoids.put("GRS80", Ellipsoid.GRS80);
         ellipsoids.put("WGS84", Ellipsoid.WGS84);
         ellipsoids.put("bessel", Ellipsoid.Bessel1841);
-
-        projs.put("merc", org.openstreetmap.josm.data.projection.proj.Mercator.class);
-        projs.put("lcc", LambertConformalConic.class);
-        projs.put("somerc", SwissObliqueMercator.class);
-        projs.put("tmerc", TransverseMercator.class);
 
         datums.put("WGS84", WGS84Datum.INSTANCE);
@@ -105,20 +103,29 @@
     }
 
+    /**
+     * Plugins can register additional base projections.
+     *
+     * @param id The "official" PROJ.4 id. In case the projection is not supported
+     * by PROJ.4, use some prefix, e.g. josm:myproj or gdal:otherproj.
+     * @param fac The base projection factory.
+     * @param origin Multiple plugins may implement the same base projection.
+     * Provide plugin name or similar string, so it be differentiated.
+     */
+    public static void registerBaseProjection(String id, ProjFactory fac, String origin) {
+        projs.put(id, fac);
+    }
+
+    public static void registerBaseProjection(String id, Class<? extends Proj> projClass, String origin) {
+        registerBaseProjection(id, new ClassProjFactory(projClass), origin);
+    }
+
+    public static Proj getBaseProjection(String id) {
+        ProjFactory fac = projs.get(id);
+        if (fac == null) return null;
+        return fac.createInstance();
+    }
+
     public static Ellipsoid getEllipsoid(String id) {
         return ellipsoids.get(id);
-    }
-
-    public static Proj getProjection(String id) {
-        Class<? extends Proj> projClass = projs.get(id);
-        if (projClass == null) return null;
-        Proj proj = null;
-        try {
-            proj = projClass.newInstance();
-        } catch (InstantiationException e) {
-            throw new RuntimeException(e);
-        } catch (IllegalAccessException e) {
-            throw new RuntimeException(e);
-        }
-        return proj;
     }
 
Index: trunk/src/org/openstreetmap/josm/data/projection/proj/ClassProjFactory.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/proj/ClassProjFactory.java	(revision 5227)
+++ trunk/src/org/openstreetmap/josm/data/projection/proj/ClassProjFactory.java	(revision 5227)
@@ -0,0 +1,27 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.projection.proj;
+
+/**
+ * Proj Factory that creates instances from a given class.
+ */
+public class ClassProjFactory implements ProjFactory {
+
+    private Class<? extends Proj> projClass;
+
+    public ClassProjFactory(Class<? extends Proj> projClass) {
+        this.projClass = projClass;
+    }
+
+    @Override
+    public Proj createInstance() {
+        Proj proj = null;
+        try {
+            proj = projClass.newInstance();
+        } catch (InstantiationException e) {
+            throw new RuntimeException(e);
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+        return proj;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/data/projection/proj/LonLat.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/proj/LonLat.java	(revision 5227)
+++ trunk/src/org/openstreetmap/josm/data/projection/proj/LonLat.java	(revision 5227)
@@ -0,0 +1,39 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.projection.proj;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import org.openstreetmap.josm.data.projection.ProjectionConfigurationException;
+
+/**
+ * Simple Lat/Lon (pseudo-)projection.
+ */
+public class LonLat implements Proj {
+
+    private double a;
+
+    @Override
+    public String getName() {
+        return tr("Lat/lon (Geodetic)");
+    }
+
+    @Override
+    public String getProj4Id() {
+        return "lonlat";
+    }
+
+    @Override
+    public void initialize(ProjParameters params) throws ProjectionConfigurationException {
+        a = params.ellps.a;
+    }
+
+    @Override
+    public double[] project(double lat_rad, double lon_rad) {
+        return new double[] { Math.toDegrees(lon_rad) / a, Math.toDegrees(lat_rad) / a };
+    }
+
+    @Override
+    public double[] invproject(double east, double north) {
+        return new double[] { Math.toRadians(north * a), Math.toRadians(east * a) };
+    }
+}
Index: trunk/src/org/openstreetmap/josm/data/projection/proj/ProjFactory.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/proj/ProjFactory.java	(revision 5227)
+++ trunk/src/org/openstreetmap/josm/data/projection/proj/ProjFactory.java	(revision 5227)
@@ -0,0 +1,9 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.projection.proj;
+
+/**
+ * Factory class that provides a Proj instance.
+ */
+public interface ProjFactory {
+    Proj createInstance();
+}
