Index: trunk/src/org/openstreetmap/josm/data/projection/Ellipsoid.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/Ellipsoid.java	(revision 2506)
+++ trunk/src/org/openstreetmap/josm/data/projection/Ellipsoid.java	(revision 2507)
@@ -165,5 +165,2 @@
     }
 }
-
-
-
Index: trunk/src/org/openstreetmap/josm/data/projection/GaussLaborde_Reunion.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/GaussLaborde_Reunion.java	(revision 2506)
+++ trunk/src/org/openstreetmap/josm/data/projection/GaussLaborde_Reunion.java	(revision 2507)
@@ -217,6 +217,6 @@
 
     public double getDefaultZoomInPPD() {
-        // this will set the map scaler to about 1000 m
-        return 10.02;
+        // this will set the map scaler to about 1000 m (in default scale, 1 pixel will be 10 meters)
+        return 10.0;
     }
 }
Index: trunk/src/org/openstreetmap/josm/data/projection/Lambert.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/Lambert.java	(revision 2506)
+++ trunk/src/org/openstreetmap/josm/data/projection/Lambert.java	(revision 2507)
@@ -1,11 +1,16 @@
-//License: GPL. For details, see LICENSE file.
-//Thanks to Johan Montagnat and its geoconv java converter application
-//(http://www.i3s.unice.fr/~johan/gps/ , published under GPL license)
-//from which some code and constants have been reused here.
+// License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.data.projection;
 
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import javax.swing.JOptionPane;
+import java.awt.GridBagLayout;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
 
 import org.openstreetmap.josm.Main;
@@ -13,6 +18,15 @@
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.coor.LatLon;
-
-public class Lambert implements Projection {
+import org.openstreetmap.josm.tools.GBC;
+
+/**
+ * This class provides the two methods <code>latlon2eastNorth</code> and <code>eastNorth2latlon</code>
+ * converting the JOSM LatLon coordinates in WGS84 system (GPS) to and from East North values in
+ * the projection Lambert conic conform 4 zones using the French geodetic system NTF.
+ * This newer version uses the grid translation NTF<->RGF93 provided by IGN for a submillimetric accuracy.
+ * (RGF93 is the French geodetic system similar to WGS84 but not mathematically equal)
+ * @author Pieren
+ */
+public class Lambert implements Projection, ProjectionSubPrefs {
     /**
      * Lambert I, II, III, and IV projection exponents
@@ -49,93 +63,69 @@
      * France is divided in 4 Lambert projection zones (1,2,3 + 4th for Corsica)
      */
-    public static final double cMaxLatZone1 = Math.toRadians(57 * 0.9);
-
-    public static final double zoneLimits[] = { Math.toRadians(53.5 * 0.9), // between Zone 1 and Zone 2 (in grad *0.9)
-        Math.toRadians(50.5 * 0.9), // between Zone 2 and Zone 3
-        Math.toRadians(47.51963 * 0.9), // between Zone 3 and Zone 4
-        Math.toRadians(46.17821 * 0.9) };// lowest latitude of Zone 4
-
-    public static final double cMinLonZones = Math.toRadians(-4.9074074074074059 * 0.9);
-
-    public static final double cMaxLonZones = Math.toRadians(10.2 * 0.9);
-
-    /**
-     *  Because josm cannot work correctly if two zones are displayed, we allow some overlapping
-     */
-    public static final double cMaxOverlappingZones = Math.toRadians(1.5 * 0.9);
-
-    public static int layoutZone = -1;
-
-    private static int currentZone = 0;
+    public static final double cMaxLatZone1Radian = Math.toRadians(57 * 0.9);
+    public static final double cMinLatZone1Radian = Math.toRadians(46.1 * 0.9);// lowest latitude of Zone 4 (South Corsica)
+
+    public static final double zoneLimitsDegree[][] = {
+        {Math.toDegrees(cMaxLatZone1Radian), (53.5 * 0.9)}, // Zone 1  (reference values in grad *0.9)
+        {(53.5 * 0.9), (50.5 * 0.9)}, // Zone 2
+        {(50.5 * 0.9), (47.0 * 0.9)}, // Zone 3
+        {(47.51963 * 0.9), Math.toDegrees(cMinLatZone1Radian)} // Zone 4
+    };
+
+    public static final double cMinLonZonesRadian = Math.toRadians(-4.9074074074074059 * 0.9);
+
+    public static final double cMaxLonZonesRadian = Math.toRadians(10.2 * 0.9);
+
+    /**
+     *  Allow some extension beyond the theoretical limits
+     */
+    public static final double cMaxOverlappingZonesDegree = 1.5;
+
+    public static final int DEFAULT_ZONE = 0;
+
+    private static int layoutZone = DEFAULT_ZONE;
+
+    private static NTV2GridShiftFile ntf_rgf93Grid = null;
+
+    public static NTV2GridShiftFile getNtf_rgf93Grid() {
+        return ntf_rgf93Grid;
+    }
+
+    public Lambert() {
+        if (ntf_rgf93Grid == null) {
+            try {
+                String gridFileName = "ntf_r93_b.gsb";
+                InputStream is = Main.class.getResourceAsStream("/data/"+gridFileName);
+                ntf_rgf93Grid = new NTV2GridShiftFile();
+                ntf_rgf93Grid.loadGridShiftFile(is, false);
+                //System.out.println("NTF<->RGF93 grid loaded.");
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
 
     /**
      * @param p  WGS84 lat/lon (ellipsoid GRS80) (in degree)
      * @return eastnorth projection in Lambert Zone (ellipsoid Clark)
+     * @throws IOException
      */
     public EastNorth latlon2eastNorth(LatLon p) {
         // translate ellipsoid GRS80 (WGS83) => Clark
-        LatLon geo = GRS802Clark(p);
-        double lt = geo.lat(); // in radian
-        double lg = geo.lon();
+        LatLon geo = WGS84_to_NTF(p);
+        double lt = Math.toRadians(geo.lat()); // in radian
+        double lg = Math.toRadians(geo.lon());
 
         // check if longitude and latitude are inside the French Lambert zones
-        currentZone = 0;
-        boolean outOfLambertZones = false;
-        if (lt >= zoneLimits[3] && lt <= cMaxLatZone1 && lg >= cMinLonZones && lg <= cMaxLonZones) {
-            // zone I
-            if (lt > zoneLimits[0]) {
-                currentZone = 0;
-            } else if (lt > zoneLimits[1]) {
-                currentZone = 1;
-            } else if (lt > zoneLimits[2]) {
-                currentZone = 2;
-            } else if (lt > zoneLimits[3])
-                // Note: zone IV is dedicated to Corsica island and extends from 47.8 to
-                // 45.9 degrees of latitude. There is an overlap with zone III that can be
-                // solved only with longitude (covers Corsica if lon > 7.2 degree)
-                if (lg < Math.toRadians(8 * 0.9)) {
-                    currentZone = 2;
-                } else {
-                    currentZone = 3;
-                }
-        } else {
-            outOfLambertZones = true; // possible when MAX_LAT is used
-        }
-        if (!outOfLambertZones) {
-            if (layoutZone == -1) {
-                layoutZone = currentZone;
-            } else if (layoutZone != currentZone) {
-                if (farawayFromLambertZoneFrance(lt,lg)) {
-                    JOptionPane.showMessageDialog(Main.parent,
-                            tr("IMPORTANT : data positioned far away from\n"
-                                    + "the current Lambert zone limits.\n"
-                                    + "Do not upload any data after this message.\n"
-                                    + "Undo your last action, save your work\n"
-                                    + "and start a new layer on the new zone."),
-                                    tr("Warning"),
-                                    JOptionPane.WARNING_MESSAGE);
-                    layoutZone = -1;
-                } else {
-                    System.out.println("temporarily extend Lambert zone " + layoutZone + " projection at lat,lon:"
-                            + lt + "," + lg);
-                }
-            }
-        }
-        if (layoutZone == -1)
-            return ConicProjection(lt, lg, Xs[currentZone], Ys[currentZone], c[currentZone], n[currentZone]);
-        return ConicProjection(lt, lg, Xs[layoutZone], Ys[layoutZone], c[layoutZone], n[layoutZone]);
+        if (lt >= cMinLatZone1Radian && lt <= cMaxLatZone1Radian && lg >= cMinLonZonesRadian && lg <= cMaxLonZonesRadian)
+            return ConicProjection(lt, lg, Xs[layoutZone], Ys[layoutZone], c[layoutZone], n[layoutZone]);
+        return ConicProjection(lt, lg, Xs[0], Ys[0], c[0], n[0]);
     }
 
     public LatLon eastNorth2latlon(EastNorth p) {
         LatLon geo;
-        if (layoutZone == -1) {
-            // possible until the Lambert zone is determined by latlon2eastNorth() with a valid LatLon
-            geo = Geographic(p, Xs[currentZone], Ys[currentZone], c[currentZone], n[currentZone]);
-        } else {
-            geo = Geographic(p, Xs[layoutZone], Ys[layoutZone], c[layoutZone], n[layoutZone]);
-        }
-        // translate ellipsoid Clark => GRS80 (WGS83)
-        LatLon wgs = Clark2GRS80(geo);
-        return new LatLon(Math.toDegrees(wgs.lat()), Math.toDegrees(wgs.lon()));
+        geo = Geographic(p, Xs[layoutZone], Ys[layoutZone], c[layoutZone], n[layoutZone]);
+        // translate geodetic system from NTF (ellipsoid Clark) to RGF93/WGS84 (ellipsoid GRS80)
+        return NTF_to_WGS84(geo);
     }
 
@@ -145,5 +135,5 @@
 
     public String toCode() {
-        return "EPSG:"+(27561+currentZone);
+        return "EPSG:"+(27561+layoutZone);
     }
 
@@ -182,5 +172,5 @@
      * @param c         projection constant
      * @param n         projection exponent
-     * @return LatLon in radian
+     * @return LatLon in degree
      */
     private LatLon Geographic(EastNorth eastNorth, double Xs, double Ys, double c, double n) {
@@ -201,5 +191,5 @@
             lat = nlt;
         }
-        return new LatLon(lat, lon); // in radian
+        return new LatLon(Math.toDegrees(lat), Math.toDegrees(lon)); // in radian
     }
 
@@ -207,90 +197,98 @@
      * Translate latitude/longitude in WGS84, (ellipsoid GRS80) to Lambert
      * geographic, (ellipsoid Clark)
-     */
-    private LatLon GRS802Clark(LatLon wgs) {
-        double lat = Math.toRadians(wgs.lat()); // degree to radian
-        double lon = Math.toRadians(wgs.lon());
-        // WGS84 geographic => WGS84 cartesian
-        double N = Ellipsoid.GRS80.a / (Math.sqrt(1.0 - Ellipsoid.GRS80.e2 * Math.sin(lat) * Math.sin(lat)));
-        double X = (N/* +height */) * Math.cos(lat) * Math.cos(lon);
-        double Y = (N/* +height */) * Math.cos(lat) * Math.sin(lon);
-        double Z = (N * (1.0 - Ellipsoid.GRS80.e2)/* + height */) * Math.sin(lat);
-        // WGS84 => Lambert ellipsoide similarity
-        X += 168.0;
-        Y += 60.0;
-        Z += -320.0;
-        // Lambert cartesian => Lambert geographic
-        return Geographic(X, Y, Z, Ellipsoid.clarke);
-    }
-
-    private LatLon Clark2GRS80(LatLon lambert) {
-        double lat = lambert.lat(); // in radian
-        double lon = lambert.lon();
-        // Lambert geographic => Lambert cartesian
-        double N = Ellipsoid.clarke.a / (Math.sqrt(1.0 - Ellipsoid.clarke.e2 * Math.sin(lat) * Math.sin(lat)));
-        double X = (N/* +height */) * Math.cos(lat) * Math.cos(lon);
-        double Y = (N/* +height */) * Math.cos(lat) * Math.sin(lon);
-        double Z = (N * (1.0 - Ellipsoid.clarke.e2)/* + height */) * Math.sin(lat);
-        // Lambert => WGS84 ellipsoide similarity
-        X += -168.0;
-        Y += -60.0;
-        Z += 320.0;
-        // WGS84 cartesian => WGS84 geographic
-        return Geographic(X, Y, Z, Ellipsoid.GRS80);
-    }
-
-    /**
-     * initializes from cartesian coordinates
-     *
-     * @param X
-     *            1st coordinate in meters
-     * @param Y
-     *            2nd coordinate in meters
-     * @param Z
-     *            3rd coordinate in meters
-     * @param ell
-     *            reference ellipsoid
-     */
-    private LatLon Geographic(double X, double Y, double Z, Ellipsoid ell) {
-        double norm = Math.sqrt(X * X + Y * Y);
-        double lg = 2.0 * Math.atan(Y / (X + norm));
-        double lt = Math.atan(Z / (norm * (1.0 - (ell.a * ell.e2 / Math.sqrt(X * X + Y * Y + Z * Z)))));
-        double delta = 1.0;
-        while (delta > epsilon) {
-            double s2 = Math.sin(lt);
-            s2 *= s2;
-            double l = Math.atan((Z / norm)
-                    / (1.0 - (ell.a * ell.e2 * Math.cos(lt) / (norm * Math.sqrt(1.0 - ell.e2 * s2)))));
-            delta = Math.abs(l - lt);
-            lt = l;
-        }
-        double s2 = Math.sin(lt);
-        s2 *= s2;
-        // h = norm / Math.cos(lt) - ell.a / Math.sqrt(1.0 - ell.e2 * s2);
-        return new LatLon(lt, lg);
-    }
-
-    private boolean farawayFromLambertZoneFrance(double lat, double lon) {
-        if (lat < (zoneLimits[3] - cMaxOverlappingZones) || (lat > (cMaxLatZone1 + cMaxOverlappingZones))
-                || (lon < (cMinLonZones - cMaxOverlappingZones)) || (lon > (cMaxLonZones + cMaxOverlappingZones)))
-            return true;
-        return false;
+     * @throws IOException
+     */
+    private LatLon WGS84_to_NTF(LatLon wgs) {
+        NTV2GridShift gs = new NTV2GridShift(wgs);
+        if (ntf_rgf93Grid != null) {
+            ntf_rgf93Grid.gridShiftReverse(gs);
+            return new LatLon(wgs.lat()+gs.getLatShiftDegrees(), wgs.lon()+gs.getLonShiftPositiveEastDegrees());
+        }
+        return new LatLon(0,0);
+    }
+
+    private LatLon NTF_to_WGS84(LatLon ntf) {
+        NTV2GridShift gs = new NTV2GridShift(ntf);
+        if (ntf_rgf93Grid != null) {
+            ntf_rgf93Grid.gridShiftForward(gs);
+            return new LatLon(ntf.lat()+gs.getLatShiftDegrees(), ntf.lon()+gs.getLonShiftPositiveEastDegrees());
+        }
+        return new LatLon(0,0);
     }
 
     public Bounds getWorldBoundsLatLon()
     {
-        // These are not the Lambert Zone boundaries but we keep these values until coordinates outside the
-        // projection boundaries are handled correctly.
-        return new Bounds(
-                new LatLon(-85.05112877980659, -180.0),
-                new LatLon(85.05112877980659, 180.0));
-        /*return new Bounds(
-                new LatLon(45.0, -4.9074074074074059),
-                new LatLon(57.0, 10.2));*/
-    }
-
+        Bounds b= new Bounds(
+                new LatLon(zoneLimitsDegree[layoutZone][1] - cMaxOverlappingZonesDegree, -4.9074074074074059),
+                new LatLon(zoneLimitsDegree[layoutZone][0] + cMaxOverlappingZonesDegree, 10.2));
+        return b;
+    }
+
+    /**
+     * Returns the default zoom scale in pixel per degree ({@see #NavigatableComponent#scale}))
+     */
     public double getDefaultZoomInPPD() {
-        // TODO FIXME
-        return 0;
-    }
+        // this will set the map scaler to about 1000 m (in default scale, 1 pixel will be 10 meters)
+        return 10.0;
+    }
+
+    public int getLayoutZone() {
+        return layoutZone;
+    }
+
+    public static String[] lambert4zones = {
+        tr("{0} ({1} to {2} degrees)", 1,"51.30","48.15"),
+        tr("{0} ({1} to {2} degrees)", 1,"48.15","45.45"),
+        tr("{0} ({1} to {2} degrees)", 1,"45.45","42.76"),
+        tr("{0} (Corsica)", 4)
+    };
+
+    public void setupPreferencePanel(JPanel p) {
+        JComboBox prefcb = new JComboBox(lambert4zones);
+
+        prefcb.setSelectedIndex(layoutZone);
+        p.setLayout(new GridBagLayout());
+        p.add(new JLabel(tr("Lambert CC Zone")), GBC.std().insets(5,5,0,5));
+        p.add(GBC.glue(1, 0), GBC.std().fill(GBC.HORIZONTAL));
+        /* Note: we use component position 2 below to find this again */
+        p.add(prefcb, GBC.eop().fill(GBC.HORIZONTAL));
+        p.add(GBC.glue(1, 1), GBC.eol().fill(GBC.BOTH));
+    }
+
+    public Collection<String> getPreferences(JPanel p) {
+        Object prefcb = p.getComponent(2);
+        if(!(prefcb instanceof JComboBox))
+            return null;
+        layoutZone = ((JComboBox)prefcb).getSelectedIndex();
+        return Collections.singleton(Integer.toString(layoutZone+1));
+    }
+
+    public void setPreferences(Collection<String> args) {
+        layoutZone = DEFAULT_ZONE;
+        if (args != null) {
+            try {
+                for(String s : args)
+                {
+                    layoutZone = Integer.parseInt(s)-1;
+                    if(layoutZone < 0 || layoutZone > 3) {
+                        layoutZone = DEFAULT_ZONE;
+                    }
+                    break;
+                }
+            } catch(NumberFormatException e) {}
+        }
+    }
+
+    public Collection<String> getPreferencesFromCode(String code) {
+        if (code.startsWith("EPSG:2756") && code.length() == 9) {
+            try {
+                String zonestring = code.substring(9);
+                int zoneval = Integer.parseInt(zonestring);
+                if(zoneval >= 1 && zoneval <= 4)
+                    return Collections.singleton(zonestring);
+            } catch(NumberFormatException e) {}
+        }
+        return null;
+    }
+
 }
Index: trunk/src/org/openstreetmap/josm/data/projection/LambertCC9Zones.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/LambertCC9Zones.java	(revision 2506)
+++ trunk/src/org/openstreetmap/josm/data/projection/LambertCC9Zones.java	(revision 2507)
@@ -4,7 +4,12 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import javax.swing.JOptionPane;
-
-import org.openstreetmap.josm.Main;
+import java.awt.GridBagLayout;
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.EastNorth;
@@ -12,4 +17,6 @@
 import org.openstreetmap.josm.data.projection.Projection;
 import org.openstreetmap.josm.data.projection.Ellipsoid;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.ImageProvider;
 
 /**
@@ -19,5 +26,5 @@
  *
  */
-public class LambertCC9Zones implements Projection {
+public class LambertCC9Zones implements Projection, ProjectionSubPrefs {
 
     /**
@@ -58,11 +65,12 @@
      * France is divided in 9 Lambert projection zones, CC42 to CC50.
      */
-    public static final double cMaxLatZones = Math.toRadians(51.1);
-
-    public static final double cMinLatZones = Math.toRadians(41.0);
-
-    public static final double cMinLonZones = Math.toRadians(-5.0);
-
-    public static final double cMaxLonZones = Math.toRadians(10.2);
+    public static final double cMaxLatZonesRadian = Math.toRadians(51.1);
+
+    public static final double cMinLatZonesDegree = 41.0;
+    public static final double cMinLatZonesRadian = Math.toRadians(cMinLatZonesDegree);
+
+    public static final double cMinLonZonesRadian = Math.toRadians(-5.0);
+
+    public static final double cMaxLonZonesRadian = Math.toRadians(10.2);
 
     public static final double lambda0 = Math.toRadians(3);
@@ -70,10 +78,10 @@
     public static final double e2 =Ellipsoid.GRS80.e2;
     public static final double a = Ellipsoid.GRS80.a;
-    /**
-     *  Because josm cannot work correctly if two zones are displayed, we allow some overlapping
-     */
-    public static final double cMaxOverlappingZones = Math.toRadians(1.5);
-
-    public static int layoutZone = -1;
+
+    public static final double cMaxOverlappingZones = 1.5;
+
+    public static final int DEFAULT_ZONE = 0;
+
+    private static int layoutZone = DEFAULT_ZONE;
 
     private double L(double phi, double e) {
@@ -89,51 +97,11 @@
         double lt = Math.toRadians(p.lat());
         double lg = Math.toRadians(p.lon());
-        // check if longitude and latitude are inside the French Lambert zones and seek a zone number
-        // if it is not already defined in layoutZone
-        int possibleZone = 0;
-        boolean outOfLambertZones = false;
-        if (lt >= cMinLatZones && lt <= cMaxLatZones && lg >= cMinLonZones && lg <= cMaxLonZones) {
-            /* with Lambert CC9 zones, each latitude is present in two zones. If the layout
-               zone is not already set, we choose arbitrary the first possible zone */
-            possibleZone = (int)p.lat()-42;
-            if (possibleZone > 8) {
-                possibleZone = 8;
-            }
-            if (possibleZone < 0) {
-                possibleZone = 0;
-            }
-        } else {
-            outOfLambertZones = true; // possible when MAX_LAT is used
-        }
-        if (!outOfLambertZones) {
-            if (layoutZone == -1) {
-                if (layoutZone != possibleZone) {
-                    System.out.println("change Lambert zone from "+layoutZone+" to "+possibleZone);
-                }
-                layoutZone = possibleZone;
-            } else if (Math.abs(layoutZone - possibleZone) > 1) {
-                if (farawayFromLambertZoneFrance(lt, lg)) {
-                    JOptionPane.showMessageDialog(Main.parent,
-                            tr("IMPORTANT : data positioned far away from\n"
-                                    + "the current Lambert zone limits.\n"
-                                    + "Do not upload any data after this message.\n"
-                                    + "Undo your last action, save your work\n"
-                                    + "and start a new layer on the new zone."),
-                                    tr("Warning"),
-                                    JOptionPane.WARNING_MESSAGE);
-                    layoutZone = -1;
-                } else {
-                    System.out.println("temporarily extend Lambert zone " + layoutZone + " projection at lat,lon:"
-                            + lt + "," + lg);
-                }
-            }
-        }
-        if (layoutZone == -1)
-            return ConicProjection(lt, lg, possibleZone);
-        return ConicProjection(lt, lg, layoutZone);
-    }
-
-    /**
-     * 
+        if (lt >= cMinLatZonesRadian && lt <= cMaxLatZonesRadian && lg >= cMinLonZonesRadian && lg <= cMaxLonZonesRadian)
+            return ConicProjection(lt, lg, layoutZone);
+        return ConicProjection(lt, lg, 0);
+    }
+
+    /**
+     *
      * @param lat latitude in grad
      * @param lon longitude in grad
@@ -150,5 +118,4 @@
 
     public LatLon eastNorth2latlon(EastNorth p) {
-        layoutZone  = north2ZoneNumber(p.north());
         return Geographic(p, layoutZone);
     }
@@ -174,16 +141,6 @@
     }
 
-    public static boolean isInL9CCZones(LatLon p) {
-        double lt = Math.toRadians(p.lat());
-        double lg = Math.toRadians(p.lon());
-        if (lg >= cMinLonZones && lg <= cMaxLonZones && lt >= cMinLatZones && lt <= cMaxLatZones)
-            return true;
-        return false;
-    }
-
     public String toCode() {
-        if (layoutZone == -1)
-            return "EPSG:"+(3942);
-        return "EPSG:"+(3942+layoutZone); //CC42 is EPSG:3842 (up to EPSG:3950 for CC50)
+        return "EPSG:"+(3942+layoutZone); //CC42 is EPSG:3942 (up to EPSG:3950 for CC50)
     }
 
@@ -192,28 +149,92 @@
     }
 
-    private boolean farawayFromLambertZoneFrance(double lat, double lon) {
-        if (lat < (cMinLatZones - cMaxOverlappingZones) || (lat > (cMaxLatZones + cMaxOverlappingZones))
-                || (lon < (cMinLonZones - cMaxOverlappingZones)) || (lon > (cMaxLonZones + cMaxOverlappingZones)))
-            return true;
-        return false;
-    }
-
+    /**
+     * Returns the default zoom scale in pixel per degree ({@see #NavigatableComponent#scale}))
+     */
     public double getDefaultZoomInPPD() {
-        // TODO Auto-generated method stub
-        return 0;
+        // this will set the map scaler to about 1000 m (in default scale, 1 pixel will be 10 meters)
+        return 10.0;
     }
 
     public Bounds getWorldBoundsLatLon()
     {
-        // These are not the Lambert Zone boundaries but we keep these values until coordinates outside the
-        // projection boundaries are handled correctly.
+        double medLatZone = cMinLatZonesDegree + (layoutZone+1);
         return new Bounds(
-                new LatLon(-85.05112877980659, -180.0),
-                new LatLon(85.05112877980659, 180.0));
-        /*return new Bounds(
-                new LatLon(45.0, -4.9074074074074059),
-                new LatLon(57.0, 10.2));*/
-    }
-
+                new LatLon(medLatZone - 1.0 - cMaxOverlappingZones, -4.9),
+                new LatLon(medLatZone + 1.0 + cMaxOverlappingZones, 10.2));
+    }
+
+    public int getLayoutZone() {
+        return layoutZone;
+    }
+
+    private static String[] lambert9zones = {
+        tr("{0} ({1} to {2} degrees)", 1,41,43),
+        tr("{0} ({1} to {2} degrees)", 2,42,44),
+        tr("{0} ({1} to {2} degrees)", 3,43,45),
+        tr("{0} ({1} to {2} degrees)", 4,44,46),
+        tr("{0} ({1} to {2} degrees)", 5,45,47),
+        tr("{0} ({1} to {2} degrees)", 6,46,48),
+        tr("{0} ({1} to {2} degrees)", 7,47,49),
+        tr("{0} ({1} to {2} degrees)", 8,48,50),
+        tr("{0} ({1} to {2} degrees)", 9,49,51)
+    };
+
+    public void setupPreferencePanel(JPanel p) {
+        JComboBox prefcb = new JComboBox(lambert9zones);
+
+        prefcb.setSelectedIndex(layoutZone);
+        p.setLayout(new GridBagLayout());
+        p.add(new JLabel(tr("Lambert CC Zone")), GBC.std().insets(5,5,0,5));
+        p.add(GBC.glue(1, 0), GBC.std().fill(GBC.HORIZONTAL));
+        /* Note: we use component position 2 below to find this again */
+        p.add(prefcb, GBC.eop().fill(GBC.HORIZONTAL));
+        p.add(new JLabel(ImageProvider.get("data/projection", "LambertCC9Zones.png")), GBC.eol().fill(GBC.HORIZONTAL));
+        p.add(GBC.glue(1, 1), GBC.eol().fill(GBC.BOTH));
+    }
+
+    public Collection<String> getPreferences(JPanel p) {
+        Object prefcb = p.getComponent(2);
+        if(!(prefcb instanceof JComboBox))
+            return null;
+        layoutZone = ((JComboBox)prefcb).getSelectedIndex();
+        if (layoutZone == 0) {
+            layoutZone = layoutZone +1 -1;
+        }
+        return Collections.singleton(Integer.toString(layoutZone+1));
+    }
+
+    public void setPreferences(Collection<String> args)
+    {
+        layoutZone = DEFAULT_ZONE;
+        if (args != null) {
+            try {
+                for(String s : args)
+                {
+                    layoutZone = Integer.parseInt(s)-1;
+                    if(layoutZone < 0 || layoutZone > 8) {
+                        layoutZone = DEFAULT_ZONE;
+                    }
+                    break;
+                }
+            } catch(NumberFormatException e) {}
+        }
+        if (layoutZone == 0) {
+            layoutZone = layoutZone +1 -1;
+        }
+    }
+
+    public Collection<String> getPreferencesFromCode(String code)
+    {
+        //zone 1=CC42=EPSG:3942 up to zone 9=CC50=EPSG:3950
+        if (code.startsWith("EPSG:39") && code.length() == 9) {
+            try {
+                String zonestring = code.substring(5,4);
+                int zoneval = Integer.parseInt(zonestring)-3942;
+                if(zoneval >= 0 && zoneval <= 8)
+                    return Collections.singleton(zonestring);
+            } catch(NumberFormatException e) {}
+        }
+        return null;
+    }
 }
-
Index: trunk/src/org/openstreetmap/josm/data/projection/NTV2GridShift.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/NTV2GridShift.java	(revision 2507)
+++ trunk/src/org/openstreetmap/josm/data/projection/NTV2GridShift.java	(revision 2507)
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2003 Objectix Pty Ltd  All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL OBJECTIX PTY LTD BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openstreetmap.josm.data.projection;
+
+import java.io.Serializable;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+
+/**
+ * A value object for storing Longitude and Latitude of a point, the
+ * Lon and Lat shift values to get from one datum to another, and the
+ * Lon and Lat accuracy of the shift values.
+ * <p>All values are stored as Positive West Seconds, but accessors
+ * are also provided for Positive East Degrees.
+ *
+ * @author Peter Yuill
+ * Modifified for JOSM :
+ * - add a constructor for JOSM LatLon (Pieren)
+ */
+public class NTV2GridShift implements Serializable {
+
+    private static final double METRE_PER_SECOND = 2.0 * Math.PI * 6378137.0 / 3600.0 / 360.0;
+    private static final double RADIANS_PER_SECOND = 2.0 * Math.PI / 3600.0 / 360.0;
+    private double lon;
+    private double lat;
+    private double lonShift;
+    private double latShift;
+    private double lonAccuracy;
+    private double latAccuracy;
+    boolean latAccuracyAvailable;
+    boolean lonAccuracyAvailable;
+    private String subGridName;
+
+    public NTV2GridShift() {
+    }
+
+    public NTV2GridShift(LatLon p) {
+        setLatDegrees(p.lat());
+        setLonPositiveEastDegrees(p.lon());
+    }
+
+    /**
+     * @return
+     */
+    public double getLatSeconds() {
+        return lat;
+    }
+
+    /**
+     * @return
+     */
+    public double getLatDegrees() {
+        return lat / 3600.0;
+    }
+
+    /**
+     * @return
+     */
+    public double getLatShiftSeconds() {
+        return latShift;
+    }
+
+    /**
+     * @return
+     */
+    public double getLatShiftDegrees() {
+        return latShift / 3600.0;
+    }
+
+    /**
+     * @return
+     */
+    public double getShiftedLatSeconds() {
+        return lat + latShift;
+    }
+
+    /**
+     * @return
+     */
+    public double getShiftedLatDegrees() {
+        return (lat + latShift) / 3600.0;
+    }
+
+    /**
+     * @return
+     */
+    public boolean isLatAccuracyAvailable() {
+        return latAccuracyAvailable;
+    }
+
+    /**
+     * @return
+     */
+    public double getLatAccuracySeconds() {
+        if (!latAccuracyAvailable)
+            throw new IllegalStateException("Latitude Accuracy not available");
+        return latAccuracy;
+    }
+
+    /**
+     * @return
+     */
+    public double getLatAccuracyDegrees() {
+        if (!latAccuracyAvailable)
+            throw new IllegalStateException("Latitude Accuracy not available");
+        return latAccuracy / 3600.0;
+    }
+
+    /**
+     * @return
+     */
+    public double getLatAccuracyMetres() {
+        if (!latAccuracyAvailable)
+            throw new IllegalStateException("Latitude Accuracy not available");
+        return latAccuracy * METRE_PER_SECOND;
+    }
+
+    /**
+     * @return
+     */
+    public double getLonPositiveWestSeconds() {
+        return lon;
+    }
+
+    /**
+     * @return
+     */
+    public double getLonPositiveEastDegrees() {
+        return lon / -3600.0;
+    }
+
+    /**
+     * @return
+     */
+    public double getLonShiftPositiveWestSeconds() {
+        return lonShift;
+    }
+
+    /**
+     * @return
+     */
+    public double getLonShiftPositiveEastDegrees() {
+        return lonShift / -3600.0;
+    }
+
+    /**
+     * @return
+     */
+    public double getShiftedLonPositiveWestSeconds() {
+        return lon + lonShift;
+    }
+
+    /**
+     * @return
+     */
+    public double getShiftedLonPositiveEastDegrees() {
+        return (lon + lonShift) / -3600.0;
+    }
+
+    /**
+     * @return
+     */
+    public boolean isLonAccuracyAvailable() {
+        return lonAccuracyAvailable;
+    }
+
+    /**
+     * @return
+     */
+    public double getLonAccuracySeconds() {
+        if (!lonAccuracyAvailable)
+            throw new IllegalStateException("Longitude Accuracy not available");
+        return lonAccuracy;
+    }
+
+    /**
+     * @return
+     */
+    public double getLonAccuracyDegrees() {
+        if (!lonAccuracyAvailable)
+            throw new IllegalStateException("Longitude Accuracy not available");
+        return lonAccuracy / 3600.0;
+    }
+
+    /**
+     * @return
+     */
+    public double getLonAccuracyMetres() {
+        if (!lonAccuracyAvailable)
+            throw new IllegalStateException("Longitude Accuracy not available");
+        return lonAccuracy * METRE_PER_SECOND * Math.cos(RADIANS_PER_SECOND * lat);
+    }
+
+    /**
+     * @param d
+     */
+    public void setLatSeconds(double d) {
+        lat = d;
+    }
+
+    /**
+     * @param d
+     */
+    public void setLatDegrees(double d) {
+        lat = d * 3600.0;
+    }
+
+    /**
+     * @param b
+     */
+    public void setLatAccuracyAvailable(boolean b) {
+        latAccuracyAvailable = b;
+    }
+
+    /**
+     * @param d
+     */
+    public void setLatAccuracySeconds(double d) {
+        latAccuracy = d;
+    }
+
+    /**
+     * @param d
+     */
+    public void setLatShiftSeconds(double d) {
+        latShift = d;
+    }
+
+    /**
+     * @param d
+     */
+    public void setLonPositiveWestSeconds(double d) {
+        lon = d;
+    }
+
+    /**
+     * @param d
+     */
+    public void setLonPositiveEastDegrees(double d) {
+        lon = d * -3600.0;
+    }
+
+    /**
+     * @param b
+     */
+    public void setLonAccuracyAvailable(boolean b) {
+        lonAccuracyAvailable = b;
+    }
+
+    /**
+     * @param d
+     */
+    public void setLonAccuracySeconds(double d) {
+        lonAccuracy = d;
+    }
+
+    /**
+     * @param d
+     */
+    public void setLonShiftPositiveWestSeconds(double d) {
+        lonShift = d;
+    }
+
+    /**
+     * @return
+     */
+    public String getSubGridName() {
+        return subGridName;
+    }
+
+    /**
+     * @param string
+     */
+    public void setSubGridName(String string) {
+        subGridName = string;
+    }
+
+    /**
+     * Make this object a copy of the supplied GridShift
+     * @param gs
+     */
+    public void copy(NTV2GridShift gs) {
+        this.lon = gs.lon;
+        this.lat = gs.lat;
+        this.lonShift = gs.lonShift;
+        this.latShift = gs.latShift;
+        this.lonAccuracy = gs.lonAccuracy;
+        this.latAccuracy = gs.latAccuracy;
+        this.latAccuracyAvailable = gs.latAccuracyAvailable;
+        this.lonAccuracyAvailable = gs.lonAccuracyAvailable;
+        this.subGridName = gs.subGridName;
+    }
+
+}
Index: trunk/src/org/openstreetmap/josm/data/projection/NTV2GridShiftFile.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/NTV2GridShiftFile.java	(revision 2507)
+++ trunk/src/org/openstreetmap/josm/data/projection/NTV2GridShiftFile.java	(revision 2507)
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 2003 Objectix Pty Ltd  All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL OBJECTIX PTY LTD BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openstreetmap.josm.data.projection;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Models the NTv2 format Grid Shift File and exposes methods to shift
+ * coordinate values using the Sub Grids contained in the file.
+ * <p>The principal reference for the alogrithms used is the
+ * 'GDAit Software Architecture Manual' produced by the <a
+ * href='http://www.sli.unimelb.edu.au/gda94'>Geomatics
+ * Department of the University of Melbourne</a>
+ * <p>This library reads binary NTv2 Grid Shift files in Big Endian
+ * (Canadian standard) or Little Endian (Australian Standard) format.
+ * The older 'Australian' binary format is not supported, only the
+ * official Canadian format, which is now also used for the national
+ * Australian Grid.
+ * <p>Grid Shift files can be read as InputStreams or RandomAccessFiles.
+ * Loading an InputStream places all the required node information
+ * (accuracy data is optional) into heap based Java arrays. This is the
+ * highest perfomance option, and is useful for large volume transformations.
+ * Non-file data sources (eg using an SQL Blob) are also supported through
+ * InputStream. The RandonAccessFile option has a much smaller memory
+ * footprint as only the Sub Grid headers are stored in memory, but
+ * transformation is slower because the file must be read a number of
+ * times for each transformation.
+ * <p>Coordinates may be shifted Forward (ie from and to the Datums specified
+ * in the Grid Shift File header) or Reverse. The reverse transformation
+ * uses an iterative approach to approximate the Grid Shift, as the
+ * precise transformation is based on 'from' datum coordinates.
+ * <p>Coordinates may be specified
+ * either in Seconds using Positive West Longitude (the original NTv2
+ * arrangement) or in decimal Degrees using Positive East Longitude.
+ *
+ * @author Peter Yuill
+ * Modifified for JOSM :
+ * - removed the RandomAccessFile mode (Pieren)
+ */
+public class NTV2GridShiftFile implements Serializable {
+
+    private static final int REC_SIZE = 16;
+    private String overviewHeaderCountId;
+    private int overviewHeaderCount;
+    private int subGridHeaderCount;
+    private int subGridCount;
+    private String shiftType;
+    private String version;
+    private String fromEllipsoid = "";
+    private String toEllipsoid = "";
+    private double fromSemiMajorAxis;
+    private double fromSemiMinorAxis;
+    private double toSemiMajorAxis;
+    private double toSemiMinorAxis;
+
+    private NTV2SubGrid[] topLevelSubGrid;
+    private NTV2SubGrid lastSubGrid;
+
+    public NTV2GridShiftFile() {
+    }
+
+    /**
+     * Load a Grid Shift File from an InputStream. The Grid Shift node
+     * data is stored in Java arrays, which will occupy about the same memory
+     * as the original file with accuracy data included, and about half that
+     * with accuracy data excluded. The size of the Australian national file
+     * is 4.5MB, and the Canadian national file is 13.5MB
+     * <p>The InputStream is closed by this method.
+     *
+     * @param in Grid Shift File InputStream
+     * @param loadAccuracy is Accuracy data to be loaded as well as shift data?
+     * @throws Exception
+     */
+    public void loadGridShiftFile(InputStream in, boolean loadAccuracy ) throws IOException {
+        byte[] b8 = new byte[8];
+        boolean bigEndian = true;
+        fromEllipsoid = "";
+        toEllipsoid = "";
+        topLevelSubGrid = null;
+        in.read(b8);
+        overviewHeaderCountId = new String(b8);
+        if (!"NUM_OREC".equals(overviewHeaderCountId))
+            throw new IllegalArgumentException("Input file is not an NTv2 grid shift file");
+        in.read(b8);
+        overviewHeaderCount = NTV2Util.getIntBE(b8, 0);
+        if (overviewHeaderCount == 11) {
+            bigEndian = true;
+        } else {
+            overviewHeaderCount = NTV2Util.getIntLE(b8, 0);
+            if (overviewHeaderCount == 11) {
+                bigEndian = false;
+            } else
+                throw new IllegalArgumentException("Input file is not an NTv2 grid shift file");
+        }
+        in.read(b8);
+        in.read(b8);
+        subGridHeaderCount = NTV2Util.getInt(b8, bigEndian);
+        in.read(b8);
+        in.read(b8);
+        subGridCount = NTV2Util.getInt(b8, bigEndian);
+        NTV2SubGrid[] subGrid = new NTV2SubGrid[subGridCount];
+        in.read(b8);
+        in.read(b8);
+        shiftType = new String(b8);
+        in.read(b8);
+        in.read(b8);
+        version = new String(b8);
+        in.read(b8);
+        in.read(b8);
+        fromEllipsoid = new String(b8);
+        in.read(b8);
+        in.read(b8);
+        toEllipsoid = new String(b8);
+        in.read(b8);
+        in.read(b8);
+        fromSemiMajorAxis = NTV2Util.getDouble(b8, bigEndian);
+        in.read(b8);
+        in.read(b8);
+        fromSemiMinorAxis = NTV2Util.getDouble(b8, bigEndian);
+        in.read(b8);
+        in.read(b8);
+        toSemiMajorAxis = NTV2Util.getDouble(b8, bigEndian);
+        in.read(b8);
+        in.read(b8);
+        toSemiMinorAxis = NTV2Util.getDouble(b8, bigEndian);
+
+        for (int i = 0; i < subGridCount; i++) {
+            subGrid[i] = new NTV2SubGrid(in, bigEndian, loadAccuracy);
+        }
+        topLevelSubGrid = createSubGridTree(subGrid);
+        lastSubGrid = topLevelSubGrid[0];
+
+        in.close();
+    }
+
+    /**
+     * Create a tree of Sub Grids by adding each Sub Grid to its parent (where
+     * it has one), and returning an array of the top level Sub Grids
+     * @param subGrid an array of all Sub Grids
+     * @return an array of top level Sub Grids with lower level Sub Grids set.
+     */
+    private NTV2SubGrid[] createSubGridTree(NTV2SubGrid[] subGrid) {
+        int topLevelCount = 0;
+        HashMap<String, ArrayList<NTV2SubGrid>> subGridMap = new HashMap<String, ArrayList<NTV2SubGrid>>();
+        for (int i = 0; i < subGrid.length; i++) {
+            if (subGrid[i].getParentSubGridName().equalsIgnoreCase("NONE")) {
+                topLevelCount++;
+            }
+            subGridMap.put(subGrid[i].getSubGridName(), new ArrayList<NTV2SubGrid>());
+        }
+        NTV2SubGrid[] topLevelSubGrid = new NTV2SubGrid[topLevelCount];
+        topLevelCount = 0;
+        for (int i = 0; i < subGrid.length; i++) {
+            if (subGrid[i].getParentSubGridName().equalsIgnoreCase("NONE")) {
+                topLevelSubGrid[topLevelCount++] = subGrid[i];
+            } else {
+                ArrayList<NTV2SubGrid> parent = subGridMap.get(subGrid[i].getParentSubGridName());
+                parent.add(subGrid[i]);
+            }
+        }
+        NTV2SubGrid[] nullArray = new NTV2SubGrid[0];
+        for (int i = 0; i < subGrid.length; i++) {
+            ArrayList<NTV2SubGrid> subSubGrids = subGridMap.get(subGrid[i].getSubGridName());
+            if (subSubGrids.size() > 0) {
+                NTV2SubGrid[] subGridArray = subSubGrids.toArray(nullArray);
+                subGrid[i].setSubGridArray(subGridArray);
+            }
+        }
+        return topLevelSubGrid;
+    }
+
+    /**
+     * Shift a coordinate in the Forward direction of the Grid Shift File.
+     *
+     * @param gs A GridShift object containing the coordinate to shift
+     * @return True if the coordinate is within a Sub Grid, false if not
+     * @throws IOException
+     */
+    public boolean gridShiftForward(NTV2GridShift gs) {
+        // Try the last sub grid first, big chance the coord is still within it
+        NTV2SubGrid subGrid = lastSubGrid.getSubGridForCoord(gs.getLonPositiveWestSeconds(), gs.getLatSeconds());
+        if (subGrid == null) {
+            subGrid = getSubGrid(gs.getLonPositiveWestSeconds(), gs.getLatSeconds());
+        }
+        if (subGrid == null)
+            return false;
+        else {
+            subGrid.interpolateGridShift(gs);
+            gs.setSubGridName(subGrid.getSubGridName());
+            lastSubGrid = subGrid;
+            return true;
+        }
+    }
+
+    /**
+     * Shift a coordinate in the Reverse direction of the Grid Shift File.
+     *
+     * @param gs A GridShift object containing the coordinate to shift
+     * @return True if the coordinate is within a Sub Grid, false if not
+     * @throws IOException
+     */
+    public boolean gridShiftReverse(NTV2GridShift gs) {
+        // set up the first estimate
+        NTV2GridShift forwardGs = new NTV2GridShift();
+        forwardGs.setLonPositiveWestSeconds(gs.getLonPositiveWestSeconds());
+        forwardGs.setLatSeconds(gs.getLatSeconds());
+        for (int i = 0; i < 4; i++) {
+            if (!gridShiftForward(forwardGs))
+                return false;
+            forwardGs.setLonPositiveWestSeconds(
+                    gs.getLonPositiveWestSeconds() - forwardGs.getLonShiftPositiveWestSeconds());
+            forwardGs.setLatSeconds(gs.getLatSeconds() - forwardGs.getLatShiftSeconds());
+        }
+        gs.setLonShiftPositiveWestSeconds(-forwardGs.getLonShiftPositiveWestSeconds());
+        gs.setLatShiftSeconds(-forwardGs.getLatShiftSeconds());
+        gs.setLonAccuracyAvailable(forwardGs.isLonAccuracyAvailable());
+        if (forwardGs.isLonAccuracyAvailable()) {
+            gs.setLonAccuracySeconds(forwardGs.getLonAccuracySeconds());
+        }
+        gs.setLatAccuracyAvailable(forwardGs.isLatAccuracyAvailable());
+        if (forwardGs.isLatAccuracyAvailable()) {
+            gs.setLatAccuracySeconds(forwardGs.getLatAccuracySeconds());
+        }
+        return true;
+    }
+
+    /**
+     * Find the finest SubGrid containing the coordinate, specified
+     * in Positive West Seconds
+     *
+     * @param lon Longitude in Positive West Seconds
+     * @param lat Latitude in Seconds
+     * @return The SubGrid found or null
+     */
+    private NTV2SubGrid getSubGrid(double lon, double lat) {
+        NTV2SubGrid sub = null;
+        for (int i = 0; i < topLevelSubGrid.length; i++) {
+            sub = topLevelSubGrid[i].getSubGridForCoord(lon, lat);
+            if (sub != null) {
+                break;
+            }
+        }
+        return sub;
+    }
+
+    public boolean isLoaded() {
+        return (topLevelSubGrid != null);
+    }
+
+    public void unload() throws IOException {
+        topLevelSubGrid = null;
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer buf = new StringBuffer("Headers  : ");
+        buf.append(overviewHeaderCount);
+        buf.append("\nSub Hdrs : ");
+        buf.append(subGridHeaderCount);
+        buf.append("\nSub Grids: ");
+        buf.append(subGridCount);
+        buf.append("\nType     : ");
+        buf.append(shiftType);
+        buf.append("\nVersion  : ");
+        buf.append(version);
+        buf.append("\nFr Ellpsd: ");
+        buf.append(fromEllipsoid);
+        buf.append("\nTo Ellpsd: ");
+        buf.append(toEllipsoid);
+        buf.append("\nFr Maj Ax: ");
+        buf.append(fromSemiMajorAxis);
+        buf.append("\nFr Min Ax: ");
+        buf.append(fromSemiMinorAxis);
+        buf.append("\nTo Maj Ax: ");
+        buf.append(toSemiMajorAxis);
+        buf.append("\nTo Min Ax: ");
+        buf.append(toSemiMinorAxis);
+        return buf.toString();
+    }
+
+    /**
+     * Get a copy of the SubGrid tree for this file.
+     *
+     * @return a deep clone of the current SubGrid tree
+     */
+    public NTV2SubGrid[] getSubGridTree() {
+        NTV2SubGrid[] clone = new NTV2SubGrid[topLevelSubGrid.length];
+        for (int i = 0; i < topLevelSubGrid.length; i++) {
+            clone[i] = (NTV2SubGrid)topLevelSubGrid[i].clone();
+        }
+        return clone;
+    }
+
+    public String getFromEllipsoid() {
+        return fromEllipsoid;
+    }
+
+    public String getToEllipsoid() {
+        return toEllipsoid;
+    }
+
+}
Index: trunk/src/org/openstreetmap/josm/data/projection/NTV2SubGrid.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/NTV2SubGrid.java	(revision 2507)
+++ trunk/src/org/openstreetmap/josm/data/projection/NTV2SubGrid.java	(revision 2507)
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2003 Objectix Pty Ltd  All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL OBJECTIX PTY LTD BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openstreetmap.josm.data.projection;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.Serializable;
+
+/**
+ * Models the NTv2 Sub Grid within a Grid Shift File
+ *
+ * @author Peter Yuill
+ * Modifified for JOSM :
+ * - removed the RandomAccessFile mode (Pieren)
+ * - read grid file by single bytes. Workaround for a bug in some VM not supporting
+ *   file reading by group of 4 bytes from a jar file.
+ */
+public class NTV2SubGrid implements Cloneable, Serializable {
+
+    private static final int REC_SIZE = 16;
+
+    private String subGridName;
+    private String parentSubGridName;
+    private String created;
+    private String updated;
+    private double minLat;
+    private double maxLat;
+    private double minLon;
+    private double maxLon;
+    private double latInterval;
+    private double lonInterval;
+    private int nodeCount;
+
+    private int lonColumnCount;
+    private int latRowCount;
+    private float[] latShift;
+    private float[] lonShift;
+    private float[] latAccuracy;
+    private float[] lonAccuracy;
+
+    boolean bigEndian;
+    private NTV2SubGrid[] subGrid;
+
+    /**
+     * Construct a Sub Grid from an InputStream, loading the node data into
+     * arrays in this object.
+     *
+     * @param in GridShiftFile InputStream
+     * @param bigEndian is the file bigEndian?
+     * @param loadAccuracy is the node Accuracy data to be loaded?
+     * @throws Exception
+     */
+    public NTV2SubGrid(InputStream in, boolean bigEndian, boolean loadAccuracy) throws IOException {
+        byte[] b8 = new byte[8];
+        byte[] b4 = new byte[4];
+        byte[] b1 = new byte[1];
+        in.read(b8);
+        in.read(b8);
+        subGridName = new String(b8).trim();
+        in.read(b8);
+        in.read(b8);
+        parentSubGridName = new String(b8).trim();
+        in.read(b8);
+        in.read(b8);
+        created = new String(b8);
+        in.read(b8);
+        in.read(b8);
+        updated = new String(b8);
+        in.read(b8);
+        in.read(b8);
+        minLat = NTV2Util.getDouble(b8, bigEndian);
+        in.read(b8);
+        in.read(b8);
+        maxLat = NTV2Util.getDouble(b8, bigEndian);
+        in.read(b8);
+        in.read(b8);
+        minLon = NTV2Util.getDouble(b8, bigEndian);
+        in.read(b8);
+        in.read(b8);
+        maxLon = NTV2Util.getDouble(b8, bigEndian);
+        in.read(b8);
+        in.read(b8);
+        latInterval = NTV2Util.getDouble(b8, bigEndian);
+        in.read(b8);
+        in.read(b8);
+        lonInterval = NTV2Util.getDouble(b8, bigEndian);
+        lonColumnCount = 1 + (int)((maxLon - minLon) / lonInterval);
+        latRowCount = 1 + (int)((maxLat - minLat) / latInterval);
+        in.read(b8);
+        in.read(b8);
+        nodeCount = NTV2Util.getInt(b8, bigEndian);
+        if (nodeCount != lonColumnCount * latRowCount)
+            throw new IllegalStateException("SubGrid " + subGridName + " has inconsistent grid dimesions");
+        latShift = new float[nodeCount];
+        lonShift = new float[nodeCount];
+        if (loadAccuracy) {
+            latAccuracy = new float[nodeCount];
+            lonAccuracy = new float[nodeCount];
+        }
+
+        for (int i = 0; i < nodeCount; i++) {
+            // Read the grid file byte after byte. This is a workaround about a bug in
+            // certain VM which are not able to read byte blocks when the resource file is
+            // in a .jar file (Pieren)
+            in.read(b1); b4[0] = b1[0];
+            in.read(b1); b4[1] = b1[0];
+            in.read(b1); b4[2] = b1[0];
+            in.read(b1); b4[3] = b1[0];
+            latShift[i] = NTV2Util.getFloat(b4, bigEndian);
+            in.read(b1); b4[0] = b1[0];
+            in.read(b1); b4[1] = b1[0];
+            in.read(b1); b4[2] = b1[0];
+            in.read(b1); b4[3] = b1[0];
+            lonShift[i] = NTV2Util.getFloat(b4, bigEndian);
+            in.read(b1); b4[0] = b1[0];
+            in.read(b1); b4[1] = b1[0];
+            in.read(b1); b4[2] = b1[0];
+            in.read(b1); b4[3] = b1[0];
+            if (loadAccuracy) {
+                latAccuracy[i] = NTV2Util.getFloat(b4, bigEndian);
+            }
+            in.read(b1); b4[0] = b1[0];
+            in.read(b1); b4[1] = b1[0];
+            in.read(b1); b4[2] = b1[0];
+            in.read(b1); b4[3] = b1[0];
+            if (loadAccuracy) {
+                lonAccuracy[i] = NTV2Util.getFloat(b4, bigEndian);
+            }
+        }
+    }
+
+    /**
+     * Tests if a specified coordinate is within this Sub Grid
+     * or one of its Sub Grids. If the coordinate is outside
+     * this Sub Grid, null is returned. If the coordinate is
+     * within this Sub Grid, but not within any of its Sub Grids,
+     * this Sub Grid is returned. If the coordinate is within
+     * one of this Sub Grid's Sub Grids, the method is called
+     * recursively on the child Sub Grid.
+     *
+     * @param lon Longitude in Positive West Seconds
+     * @param lat Latitude in Seconds
+     * @return the Sub Grid containing the Coordinate or null
+     */
+    public NTV2SubGrid getSubGridForCoord(double lon, double lat) {
+        if (isCoordWithin(lon, lat)) {
+            if (subGrid == null)
+                return this;
+            else {
+                for (int i = 0; i < subGrid.length; i++) {
+                    if (subGrid[i].isCoordWithin(lon, lat))
+                        return subGrid[i].getSubGridForCoord(lon, lat);
+                }
+                return this;
+            }
+        } else
+            return null;
+    }
+
+    /**
+     * Tests if a specified coordinate is within this Sub Grid.
+     * A coordinate on either outer edge (maximum Latitude or
+     * maximum Longitude) is deemed to be outside the grid.
+     *
+     * @param lon Longitude in Positive West Seconds
+     * @param lat Latitude in Seconds
+     * @return true or false
+     */
+    private boolean isCoordWithin(double lon, double lat) {
+        if ((lon >= minLon) && (lon < maxLon) && (lat >= minLat) && (lat < maxLat))
+            return true;
+        else
+            return false;
+    }
+
+    /**
+     * Bi-Linear interpolation of four nearest node values as described in
+     * 'GDAit Software Architecture Manual' produced by the <a
+     * href='http://www.sli.unimelb.edu.au/gda94'>Geomatics
+     * Department of the University of Melbourne</a>
+     * @param a value at the A node
+     * @param b value at the B node
+     * @param c value at the C node
+     * @param d value at the D node
+     * @param X Longitude factor
+     * @param Y Latitude factor
+     * @return interpolated value
+     */
+    private final double interpolate(float a, float b, float c, float d, double X, double Y) {
+        return a + (((double)b - (double)a) * X) + (((double)c - (double)a) * Y) +
+        (((double)a + (double)d - b - c) * X * Y);
+    }
+
+    /**
+     * Interpolate shift and accuracy values for a coordinate in the 'from' datum
+     * of the GridShiftFile. The algorithm is described in
+     * 'GDAit Software Architecture Manual' produced by the <a
+     * href='http://www.sli.unimelb.edu.au/gda94'>Geomatics
+     * Department of the University of Melbourne</a>
+     * <p>This method is thread safe for both memory based and file based node data.
+     * @param gs GridShift object containing the coordinate to shift and the shift values
+     * @return the GridShift object supplied, with values updated.
+     * @throws IOException
+     */
+    public NTV2GridShift interpolateGridShift(NTV2GridShift gs) {
+        int lonIndex = (int)((gs.getLonPositiveWestSeconds() - minLon) / lonInterval);
+        int latIndex = (int)((gs.getLatSeconds() - minLat) / latInterval);
+
+        double X = (gs.getLonPositiveWestSeconds() - (minLon + (lonInterval * lonIndex))) / lonInterval;
+        double Y = (gs.getLatSeconds() - (minLat + (latInterval * latIndex))) / latInterval;
+
+        // Find the nodes at the four corners of the cell
+
+        int indexA = lonIndex + (latIndex * lonColumnCount);
+        int indexB = indexA + 1;
+        int indexC = indexA + lonColumnCount;
+        int indexD = indexC + 1;
+
+        gs.setLonShiftPositiveWestSeconds(interpolate(
+                lonShift[indexA], lonShift[indexB], lonShift[indexC], lonShift[indexD], X, Y));
+
+        gs.setLatShiftSeconds(interpolate(
+                latShift[indexA], latShift[indexB], latShift[indexC], latShift[indexD], X, Y));
+
+        if (lonAccuracy == null) {
+            gs.setLonAccuracyAvailable(false);
+        } else {
+            gs.setLonAccuracyAvailable(true);
+            gs.setLonAccuracySeconds(interpolate(
+                    lonAccuracy[indexA], lonAccuracy[indexB], lonAccuracy[indexC], lonAccuracy[indexD], X, Y));
+        }
+
+        if (latAccuracy == null) {
+            gs.setLatAccuracyAvailable(false);
+        } else {
+            gs.setLatAccuracyAvailable(true);
+            gs.setLatAccuracySeconds(interpolate(
+                    latAccuracy[indexA], latAccuracy[indexB], latAccuracy[indexC], latAccuracy[indexD], X, Y));
+        }
+        return gs;
+    }
+
+    public String getParentSubGridName() {
+        return parentSubGridName;
+    }
+
+    public String getSubGridName() {
+        return subGridName;
+    }
+
+    public int getNodeCount() {
+        return nodeCount;
+    }
+
+    public int getSubGridCount() {
+        return (subGrid == null) ? 0 : subGrid.length;
+    }
+
+    public NTV2SubGrid getSubGrid(int index) {
+        return (subGrid == null) ? null : subGrid[index];
+    }
+
+    /**
+     * Set an array of Sub Grids of this sub grid
+     * @param subGrid
+     */
+    public void setSubGridArray(NTV2SubGrid[] subGrid) {
+        this.subGrid = subGrid;
+    }
+
+    @Override
+    public String toString() {
+        return subGridName;
+    }
+
+    public String getDetails() {
+        StringBuffer buf = new StringBuffer("Sub Grid : ");
+        buf.append(subGridName);
+        buf.append("\nParent   : ");
+        buf.append(parentSubGridName);
+        buf.append("\nCreated  : ");
+        buf.append(created);
+        buf.append("\nUpdated  : ");
+        buf.append(updated);
+        buf.append("\nMin Lat  : ");
+        buf.append(minLat);
+        buf.append("\nMax Lat  : ");
+        buf.append(maxLat);
+        buf.append("\nMin Lon  : ");
+        buf.append(minLon);
+        buf.append("\nMax Lon  : ");
+        buf.append(maxLon);
+        buf.append("\nLat Intvl: ");
+        buf.append(latInterval);
+        buf.append("\nLon Intvl: ");
+        buf.append(lonInterval);
+        buf.append("\nNode Cnt : ");
+        buf.append(nodeCount);
+        return buf.toString();
+    }
+
+    /**
+     * Make a deep clone of this Sub Grid
+     */
+    @Override
+    public Object clone() {
+        NTV2SubGrid clone = null;
+        try {
+            clone = (NTV2SubGrid)super.clone();
+        } catch (CloneNotSupportedException cnse) {
+        }
+        // Do a deep clone of the sub grids
+        if (subGrid != null) {
+            clone.subGrid = new NTV2SubGrid[subGrid.length];
+            for (int i = 0; i < subGrid.length; i++) {
+                clone.subGrid[i] = (NTV2SubGrid)subGrid[i].clone();
+            }
+        }
+        return clone;
+    }
+    /**
+     * @return
+     */
+    public double getMaxLat() {
+        return maxLat;
+    }
+
+    /**
+     * @return
+     */
+    public double getMaxLon() {
+        return maxLon;
+    }
+
+    /**
+     * @return
+     */
+    public double getMinLat() {
+        return minLat;
+    }
+
+    /**
+     * @return
+     */
+    public double getMinLon() {
+        return minLon;
+    }
+
+}
Index: trunk/src/org/openstreetmap/josm/data/projection/NTV2Util.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/NTV2Util.java	(revision 2507)
+++ trunk/src/org/openstreetmap/josm/data/projection/NTV2Util.java	(revision 2507)
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2003 Objectix Pty Ltd  All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL OBJECTIX PTY LTD BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openstreetmap.josm.data.projection;
+
+/**
+ * A set of static utility methods for reading the NTv2 file format
+ *
+ * @author Peter Yuill
+ */
+public class NTV2Util {
+
+    private NTV2Util() {
+    }
+
+    /**
+     * Get a Little Endian int from four bytes of a byte array
+     * @param b the byte array
+     * @param i the index of the first data byte in the array
+     * @return the int
+     */
+    public static final int getIntLE(byte[] b, int i) {
+        return (b[i++] & 0x000000FF) | ((b[i++] << 8) & 0x0000FF00) | ((b[i++] << 16) & 0x00FF0000) | (b[i] << 24);
+    }
+
+    /**
+     * Get a Big Endian int from four bytes of a byte array
+     * @param b the byte array
+     * @param i the index of the first data byte in the array
+     * @return the int
+     */
+    public static final int getIntBE(byte[] b, int i) {
+        return (b[i++] << 24) | ((b[i++] << 16) & 0x00FF0000) | ((b[i++] << 8) & 0x0000FF00) | (b[i] & 0x000000FF);
+    }
+
+    /**
+     * Get an int from the first 4 bytes of a byte array,
+     * in either Big Endian or Little Endian format.
+     * @param b the byte array
+     * @param bigEndian is the byte array Big Endian?
+     * @return the int
+     */
+    public static final int getInt(byte[] b, boolean bigEndian) {
+        if (bigEndian)
+            return getIntBE(b, 0);
+        else
+            return getIntLE(b, 0);
+    }
+
+    /**
+     * Get a float from the first 4 bytes of a byte array,
+     * in either Big Endian or Little Endian format.
+     * @param b the byte array
+     * @param bigEndian is the byte array Big Endian?
+     * @return the float
+     */
+    public static final float getFloat(byte[] b, boolean bigEndian) {
+        int i = 0;
+        if (bigEndian) {
+            i = getIntBE(b, 0);
+        } else {
+            i = getIntLE(b, 0);
+        }
+        return Float.intBitsToFloat(i);
+    }
+
+
+    /**
+     * Get a double from the first 8 bytes of a byte array,
+     * in either Big Endian or Little Endian format.
+     * @param b the byte array
+     * @param bigEndian is the byte array Big Endian?
+     * @return the double
+     */
+    public static final double getDouble(byte[] b, boolean bigEndian) {
+        int i = 0;
+        int j = 0;
+        if (bigEndian) {
+            i = getIntBE(b, 0);
+            j = getIntBE(b, 4);
+        } else {
+            i = getIntLE(b, 4);
+            j = getIntLE(b, 0);
+        }
+        long l = ((long)i << 32) |
+        (j & 0x00000000FFFFFFFFL);
+        return Double.longBitsToDouble(l);
+    }
+
+    /**
+     * Does the current VM support the New IO api
+     * @return true or false
+     */
+    public static boolean isNioAvailable() {
+        boolean nioAvailable = false;
+        try {
+            Class.forName("java.nio.channels.FileChannel");
+            nioAvailable = true;
+        } catch (ClassNotFoundException cnfe) {}
+        return nioAvailable;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/data/projection/Projection.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/Projection.java	(revision 2506)
+++ trunk/src/org/openstreetmap/josm/data/projection/Projection.java	(revision 2507)
@@ -28,7 +28,5 @@
         new SwissGrid(),
         new UTM(),
-        new UTM_20N_Guadeloupe_Ste_Anne(),
-        new UTM_20N_Guadeloupe_Fort_Marigot(),
-        new UTM_20N_Martinique_Fort_Desaix(),
+        new UTM_20N_France_DOM(),
         new GaussLaborde_Reunion(),
         new LambertCC9Zones()    // Still needs proper default zoom
Index: trunk/src/org/openstreetmap/josm/data/projection/ProjectionSubPrefs.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/ProjectionSubPrefs.java	(revision 2506)
+++ trunk/src/org/openstreetmap/josm/data/projection/ProjectionSubPrefs.java	(revision 2507)
@@ -10,16 +10,4 @@
      * Generates the GUI for the given preference and packs them in a JPanel
      * so they may be displayed if the projection is selected.
-     *
-     * Implementation hints:
-     * <ul>
-     *      <li>Do not return <code>null</code> as it is assumed that if this
-     *      interface is implemented the projection actually has prefs to
-     *      display/save.</li>
-     *      <li>Cache the JPanel in a local variable so that changes are
-     *      persistent even if the user chooses another projection in between.
-     *      Destroy the panel on destroyCachedPanel() so that the pre-selected
-     *      settings may update the preferences are updated from the outside</li>
-     *      </li>
-     * @return
      */
     public void setupPreferencePanel(JPanel p);
Index: trunk/src/org/openstreetmap/josm/data/projection/UTM_20N_France_DOM.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/UTM_20N_France_DOM.java	(revision 2506)
+++ trunk/src/org/openstreetmap/josm/data/projection/UTM_20N_France_DOM.java	(revision 2507)
@@ -3,12 +3,38 @@
 
 /**
- * This class is not a direct implementation of Projection. It collects all methods used
- * for the projection of the French departements in the Caribbean Sea UTM zone 20N
- * but using each time different local geodesic settings for the 7 parameters transformation algorithm.
+ * This class implements all projections for French departements in the Caribbean Sea using
+ * UTM zone 20N transvers Mercator and specific geodesic settings (7 parameters transformation algorithm).
  */
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagLayout;
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.coor.LatLon;
-
-public class UTM_20N_France_DOM {
+import org.openstreetmap.josm.tools.GBC;
+
+public class UTM_20N_France_DOM implements Projection, ProjectionSubPrefs {
+
+    private static String FortMarigotName = tr("Guadeloupe Fort-Marigot 1949");
+    private static String SainteAnneName = tr("Guadeloupe Ste-Anne 1948");
+    private static String MartiniqueName = tr("Martinique Fort Desaix 1952");
+    public static String[] utmGeodesicsNames = { FortMarigotName, SainteAnneName, MartiniqueName};
+
+    private Bounds FortMarigotBounds = new Bounds( new LatLon(17.6,-63.25), new LatLon(18.5,-62.5));
+    private Bounds SainteAnneBounds = new Bounds( new LatLon(15.8,-61.9), new LatLon(16.6,-60.9));
+    private Bounds MartiniqueBounds = new Bounds( new LatLon(14.25,-61.25), new LatLon(15.025,-60.725));
+    private Bounds[] utmBounds = { FortMarigotBounds, SainteAnneBounds, MartiniqueBounds};
+
+    private String FortMarigotEPSG = "EPSG::2969";
+    private String SainteAnneEPSG = "EPSG::2970";
+    private String MartiniqueEPSG = "EPSG::2973";
+    private String[] utmEPSGs = { FortMarigotEPSG, SainteAnneEPSG, MartiniqueEPSG};
 
     /**
@@ -28,19 +54,24 @@
      * UTM zone (from 1 to 60)
      */
-    int zone = 20;
+    private static int ZONE = 20;
     /**
      * whether north or south hemisphere
      */
     private boolean isNorth = true;
+
+    public static final int DEFAULT_GEODESIC = 0;
+
+    private static int currentGeodesic = DEFAULT_GEODESIC;
+
     /**
      * 7 parameters transformation
      */
-    double tx = 0.0;
-    double ty = 0.0;
-    double tz = 0.0;
-    double rx = 0;
-    double ry = 0;
-    double rz = 0;
-    double scaleDiff = 0;
+    private static double tx = 0.0;
+    private static double ty = 0.0;
+    private static double tz = 0.0;
+    private static double rx = 0;
+    private static double ry = 0;
+    private static double rz = 0;
+    private static double scaleDiff = 0;
     /**
      * precision in iterative schema
@@ -50,13 +81,31 @@
     public final static double RAD_TO_DEG = 180 / Math.PI;
 
-    public UTM_20N_France_DOM(double[] translation, double[] rotation, double scaleDiff) {
-        this.tx = translation[0];
-        this.ty = translation[1];
-        this.tz = translation[2];
-        this.rx = rotation[0]/206264.806247096355; // seconds to radian
-        this.ry = rotation[1]/206264.806247096355;
-        this.rz = rotation[2]/206264.806247096355;
-        this.scaleDiff = scaleDiff;
-    }
+    private void refresh7ParametersTranslation() {
+        //System.out.println("Current UTM geodesic system: " + utmGeodesicsNames[currentGeodesic]);
+        if (currentGeodesic == 0) { // UTM_20N_Guadeloupe_Fort_Marigot
+            set7ParametersTranslation(new double[]{136.596, 248.148, -429.789},
+                    new double[]{0, 0, 0},
+                    0);
+        } else if (currentGeodesic == 1) { // UTM_20N_Guadeloupe_Ste_Anne
+            set7ParametersTranslation(new double[]{-472.29, -5.63, -304.12},
+                    new double[]{0.4362, -0.8374, 0.2563},
+                    1.8984E-6);
+        } else { // UTM_20N_Martinique_Fort_Desaix
+            set7ParametersTranslation(new double[]{126.926, 547.939, 130.409},
+                    new double[]{-2.78670, 5.16124,  -0.85844},
+                    13.82265E-6);
+        }
+    }
+
+    private void set7ParametersTranslation(double[] translation, double[] rotation, double scalediff) {
+        tx = translation[0];
+        ty = translation[1];
+        tz = translation[2];
+        rx = rotation[0]/206264.806247096355; // seconds to radian
+        ry = rotation[1]/206264.806247096355;
+        rz = rotation[2]/206264.806247096355;
+        scaleDiff = scalediff;
+    }
+
     public EastNorth latlon2eastNorth(LatLon p) {
         // translate ellipsoid GRS80 (WGS83) => reference ellipsoid geographic
@@ -128,5 +177,5 @@
         double r6d = Math.PI / 30.0;
         //zone = (int) Math.floor((coord.lon() + Math.PI) / r6d) + 1;
-        lg0 = r6d * (zone - 0.5) - Math.PI;
+        lg0 = r6d * (ZONE - 0.5) - Math.PI;
         double e2 = e * e;
         double e4 = e2 * e2;
@@ -169,9 +218,8 @@
 
     public LatLon eastNorth2latlon(EastNorth p) {
-        MTProjection(p.east(), p.north(), zone, isNorth);
+        MTProjection(p.east(), p.north(), ZONE, isNorth);
         LatLon geo = Geographic(p, Ellipsoid.hayford.a, Ellipsoid.hayford.e, 0.0 /* z */);
 
         // reference ellipsoid geographic => reference ellipsoid cartesian
-        //Hayford2GRS80(ellipsoid, geo);
         double N = Ellipsoid.hayford.a / (Math.sqrt(1.0 - Ellipsoid.hayford.e2 * Math.sin(geo.lat()) * Math.sin(geo.lat())));
         double X = (N /*+ h*/) * Math.cos(geo.lat()) * Math.cos(geo.lon());
@@ -310,3 +358,75 @@
     }
 
+    public String getCacheDirectoryName() {
+        return this.toString();
+    }
+
+    /**
+     * Returns the default zoom scale in pixel per degree ({@see #NavigatableComponent#scale}))
+     */
+    public double getDefaultZoomInPPD() {
+        // this will set the map scaler to about 1000 m (in default scale, 1 pixel will be 10 meters)
+        return 10.0;
+    }
+
+    public Bounds getWorldBoundsLatLon() {
+        return utmBounds[currentGeodesic];
+    }
+
+    public String toCode() {
+        return utmEPSGs[currentGeodesic];
+    }
+
+    @Override public String toString() {
+        return (tr("UTM 20N (France)"));
+    }
+
+    public int getCurrentGeodesic() {
+        return currentGeodesic;
+    }
+
+    public void setupPreferencePanel(JPanel p) {
+        JComboBox prefcb = new JComboBox(utmGeodesicsNames);
+
+        prefcb.setSelectedIndex(currentGeodesic);
+        p.setLayout(new GridBagLayout());
+        p.add(new JLabel(tr("UTM20 North Geodesic system")), GBC.std().insets(5,5,0,5));
+        p.add(GBC.glue(1, 0), GBC.std().fill(GBC.HORIZONTAL));
+        p.add(prefcb, GBC.eop().fill(GBC.HORIZONTAL));
+        p.add(GBC.glue(1, 1), GBC.eol().fill(GBC.BOTH));
+    }
+
+    public Collection<String> getPreferences(JPanel p) {
+        Object prefcb = p.getComponent(2);
+        if(!(prefcb instanceof JComboBox))
+            return null;
+        currentGeodesic = ((JComboBox)prefcb).getSelectedIndex();
+        refresh7ParametersTranslation();
+        return Collections.singleton(Integer.toString(currentGeodesic+1));
+    }
+
+    public Collection<String> getPreferencesFromCode(String code) {
+        for (int i=0; i < utmEPSGs.length; i++ )
+            if (utmEPSGs[i].endsWith(code))
+                return Collections.singleton(Integer.toString(i));
+        return null;
+    }
+
+    public void setPreferences(Collection<String> args) {
+        currentGeodesic = DEFAULT_GEODESIC;
+        if (args != null) {
+            try {
+                for(String s : args)
+                {
+                    currentGeodesic = Integer.parseInt(s)-1;
+                    if(currentGeodesic < 0 || currentGeodesic > 2) {
+                        currentGeodesic = DEFAULT_GEODESIC;
+                    }
+                    break;
+                }
+            } catch(NumberFormatException e) {}
+        }
+        refresh7ParametersTranslation();
+    }
+
 }
Index: trunk/src/org/openstreetmap/josm/data/projection/UTM_20N_Guadeloupe_Fort_Marigot.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/UTM_20N_Guadeloupe_Fort_Marigot.java	(revision 2506)
+++ 	(revision )
@@ -1,42 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.data.projection;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import org.openstreetmap.josm.data.Bounds;
-import org.openstreetmap.josm.data.coor.LatLon;
-
-/*
- * Local geodisic system with UTM zone 20N projection.
- * Apply to Guadeloupe, France - St Martin and St Barthelemy islands
- */
-public class UTM_20N_Guadeloupe_Fort_Marigot extends UTM_20N_France_DOM implements Projection {
-    public UTM_20N_Guadeloupe_Fort_Marigot() {
-        super(new double[]{136.596, 248.148, -429.789},
-                new double[]{0, 0, 0},
-                0);
-    }
-
-    public String getCacheDirectoryName() {
-        return this.toString();
-    }
-
-    public Bounds getWorldBoundsLatLon() {
-        return new Bounds(
-                new LatLon(17.6,-63.25),
-                new LatLon(18.5,-62.5));
-    }
-
-    public String toCode() {
-        return "EPSG::2969";
-    }
-
-    @Override public String toString() {
-        return tr("UTM20N Guadeloupe Fort-Marigot 1949");
-    }
-
-    public double getDefaultZoomInPPD() {
-        // this will set the map scaler to about 1000 m
-        return 10;
-    }
-}
Index: trunk/src/org/openstreetmap/josm/data/projection/UTM_20N_Guadeloupe_Ste_Anne.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/UTM_20N_Guadeloupe_Ste_Anne.java	(revision 2506)
+++ 	(revision )
@@ -1,42 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.data.projection;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import org.openstreetmap.josm.data.Bounds;
-import org.openstreetmap.josm.data.coor.LatLon;
-
-/*
- * Local geodisic system with UTM zone 20N projection.
- * Apply to Guadeloupe, France - Grande-Terre and surrounding islands.
- */
-public class UTM_20N_Guadeloupe_Ste_Anne extends UTM_20N_France_DOM implements Projection {
-    public UTM_20N_Guadeloupe_Ste_Anne() {
-        super (new double[]{-472.29, -5.63, -304.12},
-                new double[]{0.4362, -0.8374, 0.2563},
-                1.8984E-6);
-    }
-
-    public String getCacheDirectoryName() {
-        return this.toString();
-    }
-
-    public Bounds getWorldBoundsLatLon() {
-        return new Bounds(
-                new LatLon(15.8,-61.9),
-                new LatLon(16.6,-60.9));
-    }
-
-    public String toCode() {
-        return "EPSG::2970";
-    }
-
-    @Override public String toString() {
-        return tr("UTM20N Guadeloupe Ste-Anne 1948");
-    }
-
-    public double getDefaultZoomInPPD() {
-        // this will set the map scaler to about 1000 m
-        return 10.01;
-    }
-}
Index: trunk/src/org/openstreetmap/josm/data/projection/UTM_20N_Martinique_Fort_Desaix.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/UTM_20N_Martinique_Fort_Desaix.java	(revision 2506)
+++ 	(revision )
@@ -1,42 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.data.projection;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import org.openstreetmap.josm.data.Bounds;
-import org.openstreetmap.josm.data.coor.LatLon;
-
-/*
- * Local geodisic system with UTM zone 20N projection.
- * Apply to Martinique, France and surrounding islands
- */
-public class UTM_20N_Martinique_Fort_Desaix extends UTM_20N_France_DOM implements Projection {
-    public UTM_20N_Martinique_Fort_Desaix() {
-        super(new double[]{126.926, 547.939, 130.409},
-                new double[]{-2.78670, 5.16124,  -0.85844},
-                13.82265E-6);
-    }
-
-    public String getCacheDirectoryName() {
-        return this.toString();
-    }
-
-    public Bounds getWorldBoundsLatLon() {
-        return new Bounds(
-                new LatLon(14.25,-61.25),
-                new LatLon(15.025,-60.725));
-    }
-
-    public String toCode() {
-        return "EPSG::2973";
-    }
-
-    @Override public String toString() {
-        return tr("UTM20N Martinique Fort Desaix 1952");
-    }
-
-    public double getDefaultZoomInPPD() {
-        // this will set the map scaler to about 1000 m
-        return 10.01;
-    }
-}
