Index: utils/josm/plugins/wmsplugin/README
===================================================================
--- utils/josm/plugins/wmsplugin/README	(revision 2121)
+++ utils/josm/plugins/wmsplugin/README	(revision 2121)
@@ -0,0 +1,7 @@
+A plugin for JOSM that can query any WMS server for background images.
+Also supports Metacarta's map rectifier.
+Supersedes the "Landsat" plugin, as the WMS plugin can do everything the
+landsat plugin could plus more.
+
+See also utils/ywms for a way to get Yahoo satellite images with this
+plugin (Linux required for ywms).
Index: utils/josm/plugins/wmsplugin/build.xml
===================================================================
--- utils/josm/plugins/wmsplugin/build.xml	(revision 2121)
+++ utils/josm/plugins/wmsplugin/build.xml	(revision 2121)
@@ -0,0 +1,41 @@
+<project name="wmsplugin" default="build" basedir=".">
+
+	<!-- point to your JOSM directory -->
+	<property name="josm" location="C:\Documents and Settings\tim\java\josm\dist\josm-custom.jar" />
+
+
+
+	<target name="init">
+		<mkdir dir="build"></mkdir>
+		<mkdir dir="dist"></mkdir>
+	</target>
+
+	<target name="compile" depends="init">
+		<javac srcdir="src" classpath="${josm}" debug="true" destdir="build">
+			<include name="**/*.java" />
+		</javac>
+	</target>
+
+	<target name="build" depends="compile">
+		<copy todir="build/images" >
+			<fileset dir="images" />
+		</copy>
+		<jar destfile="dist/wmsplugin.jar" basedir="build">
+			<manifest>
+				<attribute name="Plugin-Class" value="wmsplugin.WMSPlugin" />
+				<attribute name="Plugin-Description" value="Allows external WMS resources to be displayed,&lt;br&gt; i.e from Metacarta's Map Rectifier, Landsat, NPE.&lt;br&gt;
+					Change url via menu item, or wmsplugin.url in advanced prefs. v2.1" />
+			</manifest>
+		</jar>
+	</target>
+
+	<target name="clean">
+		<delete dir="build" />
+		<delete dir="dist" />
+	</target>
+
+	<target name="install" depends="build">
+		<copy file="dist/wmsplugin.jar" todir="${user.home}/.josm/plugins"/>
+	</target>
+
+</project>
Index: utils/josm/plugins/wmsplugin/src/META-INF/MANIFEST.MF
===================================================================
--- utils/josm/plugins/wmsplugin/src/META-INF/MANIFEST.MF	(revision 2121)
+++ utils/josm/plugins/wmsplugin/src/META-INF/MANIFEST.MF	(revision 2121)
@@ -0,0 +1,8 @@
+Manifest-Version: 1.0
+Ant-Version: Apache Ant 1.6.5
+Created-By: 1.5.0_06-b05 (Sun Microsystems Inc.)
+Plugin-Class: wmsplugin.WMSPlugin
+Plugin-Description: Allows external WMS resources to be displayed,<br>
+  i.e from Metacarta's Map Rectifier, Landsat, NPE.<br>      Change ur
+ l via menu item, or wmsplugin.url in advanced prefs. v1.5
+
Index: utils/josm/plugins/wmsplugin/src/readme.txt
===================================================================
--- utils/josm/plugins/wmsplugin/src/readme.txt	(revision 2121)
+++ utils/josm/plugins/wmsplugin/src/readme.txt	(revision 2121)
@@ -0,0 +1,46 @@
+--------------------------------------------------------------------------
+ Jcoord
+ readme.txt
+ 
+ (c) 2006 Jonathan Stott
+ 
+ Created on 11-Feb-2006
+ 
+ 1.0 - 11 Feb 2006
+  - Initial version created from PHPcoord v2.2
+--------------------------------------------------------------------------
+
+Jcoord is a collection of Java classes that provide functions for handling
+various co-ordinate systems and converting between them. Currently, OSGB
+(Ordnance Survey of Great Britain) grid references, UTM (Universal Transverse
+Mercator) references and latitude/longitude are supported. A function is 
+also provided to find the surface distance between two points of latitude
+and longitude.
+
+When using the OSGB conversions, the majority of applications use the
+WGS84 datum rather than the OSGB36 datum. Conversions between the two
+data were added in v1.1 - the conversions should be accurate to within
+5m or so. If accuracy is not important (i.e. to within 200m or so),
+then it isn't necessary to perform the conversions.
+
+Examples of how to use the classes in Jcoord can be found in the
+uk.me.jstott.jcoord.Test class
+
+See http://www.jstott.me.uk/jcoord/ for latest releases and information.
+
+
+DISCLAIMER
+
+Accuracy of the co-ordinate conversions contained within the Jcoord
+package is not guaranteed. Use of the conversions is entirely at your
+own risk and I cannot be held responsible for any consequences of
+errors created by the conversions. I do not recommend using the package
+for mission-critical applications.
+
+
+LICENSING
+
+This software product is available under the GNU General Public License
+(GPL). Terms of the GPL can be read at http://www.jstott.me.uk/gpl/.
+Any commercial use requires the purchase of a license - contact me at
+jcoord@jstott.me.uk for details.
Index: utils/josm/plugins/wmsplugin/src/uk/me/jstott/jcoord/LatLng.java
===================================================================
--- utils/josm/plugins/wmsplugin/src/uk/me/jstott/jcoord/LatLng.java	(revision 2121)
+++ utils/josm/plugins/wmsplugin/src/uk/me/jstott/jcoord/LatLng.java	(revision 2121)
@@ -0,0 +1,354 @@
+package uk.me.jstott.jcoord;
+
+/**
+ * Class to represent a latitude/longitude pair.
+ * 
+ * (c) 2006 Jonathan Stott
+ * 
+ * Created on 11-02-2006
+ * 
+ * @author Jonathan Stott
+ * @version 1.0
+ * @since 1.0
+ */
+public class LatLng {
+
+  /**
+   * Latitude in degrees.
+   */
+  private double lat;
+
+  /**
+   * Longitude in degrees.
+   */
+  private double lng;
+
+
+  /**
+   * Create a new LatLng object to represent a latitude/longitude pair.
+   * 
+   * @param lat
+   *          the latitude in degrees
+   * @param lng
+   *          the longitude in degrees
+   * @since 1.0
+   */
+  public LatLng(double lat, double lng) {
+    this.lat = lat;
+    this.lng = lng;
+  }
+
+
+  /**
+   * Get a String representation of this LatLng object.
+   * 
+   * @return a String representation of this LatLng object.
+   * @since 1.0
+   */
+  public String toString() {
+    return "(" + this.lat + ", " + this.lng + ")";
+  }
+
+
+  /**
+   * Convert this latitude and longitude into an OSGB (Ordnance Survey of Great
+   * Britain) grid reference.
+   * 
+   * @return the converted OSGB grid reference
+   * @since 1.0
+   */
+  public OSRef toOSRef() {
+    RefEll airy1830 = new RefEll(6377563.396, 6356256.909);
+    double OSGB_F0 = 0.9996012717;
+    double N0 = -100000.0;
+    double E0 = 400000.0;
+    double phi0 = Math.toRadians(49.0);
+    double lambda0 = Math.toRadians(-2.0);
+    double a = airy1830.getMaj();
+    double b = airy1830.getMin();
+    double eSquared = airy1830.getEcc();
+    double phi = Math.toRadians(getLat());
+    double lambda = Math.toRadians(getLng());
+    double E = 0.0;
+    double N = 0.0;
+    double n = (a - b) / (a + b);
+    double v =
+        a * OSGB_F0 * Math.pow(1.0 - eSquared * Util.sinSquared(phi), -0.5);
+    double rho =
+        a * OSGB_F0 * (1.0 - eSquared)
+            * Math.pow(1.0 - eSquared * Util.sinSquared(phi), -1.5);
+    double etaSquared = (v / rho) - 1.0;
+    double M =
+        (b * OSGB_F0)
+            * (((1 + n + ((5.0 / 4.0) * n * n) + ((5.0 / 4.0) * n * n * n)) * (phi - phi0))
+                - (((3 * n) + (3 * n * n) + ((21.0 / 8.0) * n * n * n))
+                    * Math.sin(phi - phi0) * Math.cos(phi + phi0))
+                + ((((15.0 / 8.0) * n * n) + ((15.0 / 8.0) * n * n * n))
+                    * Math.sin(2.0 * (phi - phi0)) * Math
+                    .cos(2.0 * (phi + phi0))) - (((35.0 / 24.0) * n * n * n)
+                * Math.sin(3.0 * (phi - phi0)) * Math.cos(3.0 * (phi + phi0))));
+    double I = M + N0;
+    double II = (v / 2.0) * Math.sin(phi) * Math.cos(phi);
+    double III =
+        (v / 24.0) * Math.sin(phi) * Math.pow(Math.cos(phi), 3.0)
+            * (5.0 - Util.tanSquared(phi) + (9.0 * etaSquared));
+    double IIIA =
+        (v / 720.0)
+            * Math.sin(phi)
+            * Math.pow(Math.cos(phi), 5.0)
+            * (61.0 - (58.0 * Util.tanSquared(phi)) + Math.pow(Math.tan(phi),
+                4.0));
+    double IV = v * Math.cos(phi);
+    double V =
+        (v / 6.0) * Math.pow(Math.cos(phi), 3.0)
+            * ((v / rho) - Util.tanSquared(phi));
+    double VI =
+        (v / 120.0)
+            * Math.pow(Math.cos(phi), 5.0)
+            * (5.0 - (18.0 * Util.tanSquared(phi))
+                + (Math.pow(Math.tan(phi), 4.0)) + (14 * etaSquared) - (58 * Util
+                .tanSquared(phi) * etaSquared));
+
+    N =
+        I + (II * Math.pow(lambda - lambda0, 2.0))
+            + (III * Math.pow(lambda - lambda0, 4.0))
+            + (IIIA * Math.pow(lambda - lambda0, 6.0));
+    E =
+        E0 + (IV * (lambda - lambda0)) + (V * Math.pow(lambda - lambda0, 3.0))
+            + (VI * Math.pow(lambda - lambda0, 5.0));
+
+    return new OSRef(E, N);
+  }
+
+
+  /**
+   * Convert this latitude and longitude to a UTM reference.
+   * 
+   * @return the converted UTM reference
+   * @since 1.0
+   */
+  public UTMRef toUTMRef() {
+    double UTM_F0 = 0.9996;
+    double a = RefEll.WGS84.getMaj();
+    double eSquared = RefEll.WGS84.getEcc();
+    double longitude = this.lng;
+    double latitude = this.lat;
+
+    double latitudeRad = latitude * (Math.PI / 180.0);
+    double longitudeRad = longitude * (Math.PI / 180.0);
+    int longitudeZone = (int) Math.floor((longitude + 180.0) / 6.0) + 1;
+
+    // Special zone for Norway
+    if (latitude >= 56.0 && latitude < 64.0 && longitude >= 3.0
+        && longitude < 12.0) {
+      longitudeZone = 32;
+    }
+
+    // Special zones for Svalbard
+    if (latitude >= 72.0 && latitude < 84.0) {
+      if (longitude >= 0.0 && longitude < 9.0) {
+        longitudeZone = 31;
+      } else if (longitude >= 9.0 && longitude < 21.0) {
+        longitudeZone = 33;
+      } else if (longitude >= 21.0 && longitude < 33.0) {
+        longitudeZone = 35;
+      } else if (longitude >= 33.0 && longitude < 42.0) {
+        longitudeZone = 37;
+      }
+    }
+
+    double longitudeOrigin = (longitudeZone - 1) * 6 - 180 + 3;
+    double longitudeOriginRad = longitudeOrigin * (Math.PI / 180.0);
+
+    char UTMZone = UTMRef.getUTMLatitudeZoneLetter(latitude);
+
+    double ePrimeSquared = (eSquared) / (1 - eSquared);
+
+    double n =
+        a
+            / Math.sqrt(1 - eSquared * Math.sin(latitudeRad)
+                * Math.sin(latitudeRad));
+    double t = Math.tan(latitudeRad) * Math.tan(latitudeRad);
+    double c = ePrimeSquared * Math.cos(latitudeRad) * Math.cos(latitudeRad);
+    double A = Math.cos(latitudeRad) * (longitudeRad - longitudeOriginRad);
+
+    double M =
+        a
+            * ((1 - eSquared / 4 - 3 * eSquared * eSquared / 64 - 5 * eSquared
+                * eSquared * eSquared / 256)
+                * latitudeRad
+                - (3 * eSquared / 8 + 3 * eSquared * eSquared / 32 + 45
+                    * eSquared * eSquared * eSquared / 1024)
+                * Math.sin(2 * latitudeRad)
+                + (15 * eSquared * eSquared / 256 + 45 * eSquared * eSquared
+                    * eSquared / 1024) * Math.sin(4 * latitudeRad) - (35
+                * eSquared * eSquared * eSquared / 3072)
+                * Math.sin(6 * latitudeRad));
+
+    double UTMEasting =
+        (UTM_F0
+            * n
+            * (A + (1 - t + c) * Math.pow(A, 3.0) / 6 + (5 - 18 * t + t * t
+                + 72 * c - 58 * ePrimeSquared)
+                * Math.pow(A, 5.0) / 120) + 500000.0);
+
+    double UTMNorthing =
+        (UTM_F0 * (M + n
+            * Math.tan(latitudeRad)
+            * (A * A / 2 + (5 - t + (9 * c) + (4 * c * c)) * Math.pow(A, 4.0)
+                / 24 + (61 - (58 * t) + (t * t) + (600 * c) - (330 * ePrimeSquared))
+                * Math.pow(A, 6.0) / 720)));
+
+    // Adjust for the southern hemisphere
+    if (latitude < 0) {
+      UTMNorthing += 10000000.0;
+    }
+
+    return new UTMRef(UTMEasting, UTMNorthing, UTMZone, longitudeZone);
+  }
+
+
+  /**
+   * Convert this LatLng from the OSGB36 datum to the WGS84 datum using an
+   * approximate Helmert transformation.
+   * 
+   * @since 1.0
+   */
+  public void toWGS84() {
+    double a = RefEll.AIRY_1830.getMaj();
+    double eSquared = RefEll.AIRY_1830.getEcc();
+    double phi = Math.toRadians(lat);
+    double lambda = Math.toRadians(lng);
+    double v = a / (Math.sqrt(1 - eSquared * Util.sinSquared(phi)));
+    double H = 0; // height
+    double x = (v + H) * Math.cos(phi) * Math.cos(lambda);
+    double y = (v + H) * Math.cos(phi) * Math.sin(lambda);
+    double z = ((1 - eSquared) * v + H) * Math.sin(phi);
+
+    double tx = 446.448;
+    double ty = -124.157;
+    double tz = 542.060;
+    double s = -0.0000204894;
+    double rx = Math.toRadians(0.00004172222);
+    double ry = Math.toRadians(0.00006861111);
+    double rz = Math.toRadians(0.00023391666);
+
+    double xB = tx + (x * (1 + s)) + (-rx * y) + (ry * z);
+    double yB = ty + (rz * x) + (y * (1 + s)) + (-rx * z);
+    double zB = tz + (-ry * x) + (rx * y) + (z * (1 + s));
+
+    a = RefEll.WGS84.getMaj();
+    eSquared = RefEll.WGS84.getEcc();
+
+    double lambdaB = Math.toDegrees(Math.atan(yB / xB));
+    double p = Math.sqrt((xB * xB) + (yB * yB));
+    double phiN = Math.atan(zB / (p * (1 - eSquared)));
+    for (int i = 1; i < 10; i++) {
+      v = a / (Math.sqrt(1 - eSquared * Util.sinSquared(phiN)));
+      double phiN1 = Math.atan((zB + (eSquared * v * Math.sin(phiN))) / p);
+      phiN = phiN1;
+    }
+
+    double phiB = Math.toDegrees(phiN);
+
+    lat = phiB;
+    lng = lambdaB;
+  }
+
+
+  /**
+   * Convert this LatLng from the WGS84 datum to the OSGB36 datum using an
+   * approximate Helmert transformation.
+   * 
+   * @since 1.0
+   */
+  public void toOSGB36() {
+    RefEll wgs84 = new RefEll(6378137.000, 6356752.3141);
+    double a = wgs84.getMaj();
+    double eSquared = wgs84.getEcc();
+    double phi = Math.toRadians(this.lat);
+    double lambda = Math.toRadians(this.lng);
+    double v = a / (Math.sqrt(1 - eSquared * Util.sinSquared(phi)));
+    double H = 0; // height
+    double x = (v + H) * Math.cos(phi) * Math.cos(lambda);
+    double y = (v + H) * Math.cos(phi) * Math.sin(lambda);
+    double z = ((1 - eSquared) * v + H) * Math.sin(phi);
+
+    double tx = -446.448;
+    double ty = 124.157;
+    double tz = -542.060;
+    double s = 0.0000204894;
+    double rx = Math.toRadians(-0.00004172222);
+    double ry = Math.toRadians(-0.00006861111);
+    double rz = Math.toRadians(-0.00023391666);
+
+    double xB = tx + (x * (1 + s)) + (-rx * y) + (ry * z);
+    double yB = ty + (rz * x) + (y * (1 + s)) + (-rx * z);
+    double zB = tz + (-ry * x) + (rx * y) + (z * (1 + s));
+
+    RefEll airy1830 = new RefEll(6377563.396, 6356256.909);
+    a = airy1830.getMaj();
+    eSquared = airy1830.getEcc();
+
+    double lambdaB = Math.toDegrees(Math.atan(yB / xB));
+    double p = Math.sqrt((xB * xB) + (yB * yB));
+    double phiN = Math.atan(zB / (p * (1 - eSquared)));
+    for (int i = 1; i < 10; i++) {
+      v = a / (Math.sqrt(1 - eSquared * Util.sinSquared(phiN)));
+      double phiN1 = Math.atan((zB + (eSquared * v * Math.sin(phiN))) / p);
+      phiN = phiN1;
+    }
+
+    double phiB = Math.toDegrees(phiN);
+
+    lat = phiB;
+    lng = lambdaB;
+  }
+
+
+  /**
+   * Calculate the surface distance in kilometres from the this LatLng to the
+   * given LatLng.
+   * 
+   * @param ll
+   * @return the surface distance in km
+   * @since 1.0
+   */
+  public double distance(LatLng ll) {
+    double er = 6366.707;
+
+    double latFrom = Math.toRadians(getLat());
+    double latTo = Math.toRadians(ll.getLat());
+    double lngFrom = Math.toRadians(getLng());
+    double lngTo = Math.toRadians(ll.getLng());
+
+    double d =
+        Math.acos(Math.sin(latFrom) * Math.sin(latTo) + Math.cos(latFrom)
+            * Math.cos(latTo) * Math.cos(lngTo - lngFrom))
+            * er;
+
+    return d;
+  }
+
+
+  /**
+   * Return the latitude in degrees.
+   * 
+   * @return the latitude in degrees
+   * @since 1.0
+   */
+  public double getLat() {
+    return lat;
+  }
+
+
+  /**
+   * Return the longitude in degrees.
+   * 
+   * @return the longitude in degrees
+   * @since 1.0
+   */
+  public double getLng() {
+    return lng;
+  }
+}
Index: utils/josm/plugins/wmsplugin/src/uk/me/jstott/jcoord/OSRef.java
===================================================================
--- utils/josm/plugins/wmsplugin/src/uk/me/jstott/jcoord/OSRef.java	(revision 2121)
+++ utils/josm/plugins/wmsplugin/src/uk/me/jstott/jcoord/OSRef.java	(revision 2121)
@@ -0,0 +1,245 @@
+package uk.me.jstott.jcoord;
+
+/**
+ * Class to represent an Ordnance Survey grid reference
+ * 
+ * (c) 2006 Jonathan Stott
+ * 
+ * Created on 11-02-2006
+ * 
+ * @author Jonathan Stott
+ * @version 1.0
+ * @since 1.0
+ */
+public class OSRef {
+
+  /**
+   * Easting
+   */
+  private double easting;
+
+  /**
+   * Northing
+   */
+  private double northing;
+
+
+  /**
+   * Create a new Ordnance Survey grid reference.
+   * 
+   * @param easting
+   *          the easting in metres
+   * @param northing
+   *          the northing in metres
+   * @since 1.0
+   */
+  public OSRef(double easting, double northing) {
+    this.easting = easting;
+    this.northing = northing;
+  }
+
+
+  /**
+   * Take a string formatted as a six-figure OS grid reference (e.g. "TG514131")
+   * and create a new OSRef object that represents that grid reference. The
+   * first character must be H, N, S, O or T. The second character can be any
+   * uppercase character from A through Z excluding I.
+   * 
+   * @param ref
+   *          a String representing a six-figure Ordnance Survey grid reference
+   *          in the form XY123456
+   * @throws IllegalArgumentException
+   *           if ref is not of the form XY123456
+   * @since 1.0
+   */
+  public OSRef(String ref) throws IllegalArgumentException {
+    // if (ref.matches(""))
+    char char1 = ref.charAt(0);
+    char char2 = ref.charAt(1);
+    // Thanks to Nick Holloway for pointing out the radix bug here
+    int east = Integer.parseInt(ref.substring(2, 5)) * 100;
+    int north = Integer.parseInt(ref.substring(5, 8)) * 100;
+    if (char1 == 'H') {
+      north += 1000000;
+    } else if (char1 == 'N') {
+      north += 500000;
+    } else if (char1 == 'O') {
+      north += 500000;
+      east += 500000;
+    } else if (char1 == 'T') {
+      east += 500000;
+    }
+    int char2ord = char2;
+    if (char2ord > 73)
+      char2ord--; // Adjust for no I
+    double nx = ((char2ord - 65) % 5) * 100000;
+    double ny = (4 - Math.floor((char2ord - 65) / 5)) * 100000;
+    easting = east + nx;
+    northing = north + ny;
+  }
+
+
+  /**
+   * Return a String representation of this OSGB grid reference showing the
+   * easting and northing.
+   * 
+   * @return a String represenation of this OSGB grid reference
+   * @since 1.0
+   */
+  public String toString() {
+    return "(" + easting + ", " + northing + ")";
+  }
+
+
+  /**
+   * Return a String representation of this OSGB grid reference using the
+   * six-figure notation in the form XY123456
+   * 
+   * @return a String representing this OSGB grid reference in six-figure
+   *         notation
+   * @since 1.0
+   */
+  public String toSixFigureString() {
+    int hundredkmE = (int) Math.floor(easting / 100000);
+    int hundredkmN = (int) Math.floor(northing / 100000);
+    String firstLetter;
+    if (hundredkmN < 5) {
+      if (hundredkmE < 5) {
+        firstLetter = "S";
+      } else {
+        firstLetter = "T";
+      }
+    } else if (hundredkmN < 10) {
+      if (hundredkmE < 5) {
+        firstLetter = "N";
+      } else {
+        firstLetter = "O";
+      }
+    } else {
+      firstLetter = "H";
+    }
+
+    int index = 65 + ((4 - (hundredkmN % 5)) * 5) + (hundredkmE % 5);
+    // int ti = index;
+    if (index >= 73)
+      index++;
+    String secondLetter = Character.toString((char) index);
+
+    int e = (int) Math.floor((easting - (100000 * hundredkmE)) / 100);
+    int n = (int) Math.floor((northing - (100000 * hundredkmN)) / 100);
+    String es = "" + e;
+    if (e < 100)
+      es = "0" + es;
+    if (e < 10)
+      es = "0" + es;
+    String ns = "" + n;
+    if (n < 100)
+      ns = "0" + ns;
+    if (n < 10)
+      ns = "0" + ns;
+
+    return firstLetter + secondLetter + es + ns;
+  }
+
+
+  /**
+   * Convert this OSGB grid reference to a latitude/longitude pair using the
+   * OSGB36 datum. Note that, the LatLng object may need to be converted to the
+   * WGS84 datum depending on the application.
+   * 
+   * @return a LatLng object representing this OSGB grid reference using the
+   *         OSGB36 datum
+   * @since 1.0
+   */
+  public LatLng toLatLng() {
+    double OSGB_F0 = 0.9996012717;
+    double N0 = -100000.0;
+    double E0 = 400000.0;
+    double phi0 = Math.toRadians(49.0);
+    double lambda0 = Math.toRadians(-2.0);
+    double a = RefEll.AIRY_1830.getMaj();
+    double b = RefEll.AIRY_1830.getMin();
+    double eSquared = RefEll.AIRY_1830.getEcc();
+    double phi = 0.0;
+    double lambda = 0.0;
+    double E = this.easting;
+    double N = this.northing;
+    double n = (a - b) / (a + b);
+    double M = 0.0;
+    double phiPrime = ((N - N0) / (a * OSGB_F0)) + phi0;
+    do {
+      M =
+          (b * OSGB_F0)
+              * (((1 + n + ((5.0 / 4.0) * n * n) + ((5.0 / 4.0) * n * n * n)) * (phiPrime - phi0))
+                  - (((3 * n) + (3 * n * n) + ((21.0 / 8.0) * n * n * n))
+                      * Math.sin(phiPrime - phi0) * Math.cos(phiPrime + phi0))
+                  + ((((15.0 / 8.0) * n * n) + ((15.0 / 8.0) * n * n * n))
+                      * Math.sin(2.0 * (phiPrime - phi0)) * Math
+                      .cos(2.0 * (phiPrime + phi0))) - (((35.0 / 24.0) * n * n * n)
+                  * Math.sin(3.0 * (phiPrime - phi0)) * Math
+                  .cos(3.0 * (phiPrime + phi0))));
+      phiPrime += (N - N0 - M) / (a * OSGB_F0);
+    } while ((N - N0 - M) >= 0.001);
+    double v =
+        a * OSGB_F0
+            * Math.pow(1.0 - eSquared * Util.sinSquared(phiPrime), -0.5);
+    double rho =
+        a * OSGB_F0 * (1.0 - eSquared)
+            * Math.pow(1.0 - eSquared * Util.sinSquared(phiPrime), -1.5);
+    double etaSquared = (v / rho) - 1.0;
+    double VII = Math.tan(phiPrime) / (2 * rho * v);
+    double VIII =
+        (Math.tan(phiPrime) / (24.0 * rho * Math.pow(v, 3.0)))
+            * (5.0 + (3.0 * Util.tanSquared(phiPrime)) + etaSquared - (9.0 * Util
+                .tanSquared(phiPrime) * etaSquared));
+    double IX =
+        (Math.tan(phiPrime) / (720.0 * rho * Math.pow(v, 5.0)))
+            * (61.0 + (90.0 * Util.tanSquared(phiPrime)) + (45.0 * Util
+                .tanSquared(phiPrime) * Util.tanSquared(phiPrime)));
+    double X = Util.sec(phiPrime) / v;
+    double XI =
+        (Util.sec(phiPrime) / (6.0 * v * v * v))
+            * ((v / rho) + (2 * Util.tanSquared(phiPrime)));
+    double XII =
+        (Util.sec(phiPrime) / (120.0 * Math.pow(v, 5.0)))
+            * (5.0 + (28.0 * Util.tanSquared(phiPrime)) + (24.0 * Util
+                .tanSquared(phiPrime) * Util.tanSquared(phiPrime)));
+    double XIIA =
+        (Util.sec(phiPrime) / (5040.0 * Math.pow(v, 7.0)))
+            * (61.0
+                + (662.0 * Util.tanSquared(phiPrime))
+                + (1320.0 * Util.tanSquared(phiPrime) * Util
+                    .tanSquared(phiPrime)) + (720.0 * Util.tanSquared(phiPrime)
+                * Util.tanSquared(phiPrime) * Util.tanSquared(phiPrime)));
+    phi =
+        phiPrime - (VII * Math.pow(E - E0, 2.0))
+            + (VIII * Math.pow(E - E0, 4.0)) - (IX * Math.pow(E - E0, 6.0));
+    lambda =
+        lambda0 + (X * (E - E0)) - (XI * Math.pow(E - E0, 3.0))
+            + (XII * Math.pow(E - E0, 5.0)) - (XIIA * Math.pow(E - E0, 7.0));
+
+    return new LatLng(Math.toDegrees(phi), Math.toDegrees(lambda));
+  }
+
+
+  /**
+   * Get the easting.
+   * 
+   * @return the easting in metres
+   * @since 1.0
+   */
+  public double getEasting() {
+    return easting;
+  }
+
+
+  /**
+   * Get the northing.
+   * 
+   * @return the northing in metres
+   * @since 1.0
+   */
+  public double getNorthing() {
+    return northing;
+  }
+}
Index: utils/josm/plugins/wmsplugin/src/uk/me/jstott/jcoord/RefEll.java
===================================================================
--- utils/josm/plugins/wmsplugin/src/uk/me/jstott/jcoord/RefEll.java	(revision 2121)
+++ utils/josm/plugins/wmsplugin/src/uk/me/jstott/jcoord/RefEll.java	(revision 2121)
@@ -0,0 +1,90 @@
+package uk.me.jstott.jcoord;
+
+/**
+ * Class to represent a reference ellipsoid. Also provides a number of
+ * pre-determined reference ellipsoids as constants.
+ * 
+ * (c) 2006 Jonathan Stott
+ * 
+ * Created on 11-Feb-2006
+ * 
+ * @author Jonathan Stott
+ * @version 1.0
+ * @since 1.0
+ */
+public class RefEll {
+
+  /**
+   * Airy 1830 Reference Ellipsoid
+   */
+  public static final RefEll AIRY_1830 = new RefEll(6377563.396, 6356256.909);
+
+  /**
+   * WGS84 Reference Ellipsoid
+   */
+  public static final RefEll WGS84     = new RefEll(6378137.000, 6356752.3141);
+
+  /**
+   * Semi-major axis
+   */
+  private double             maj;
+
+  /**
+   * Semi-minor axis
+   */
+  private double             min;
+
+  /**
+   * Eccentricity
+   */
+  private double             ecc;
+
+
+  /**
+   * Create a new reference ellipsoid
+   * 
+   * @param maj
+   *          semi-major axis
+   * @param min
+   *          semi-minor axis
+   * @since 1.0
+   */
+  public RefEll(double maj, double min) {
+    this.maj = maj;
+    this.min = min;
+    this.ecc = ((maj * maj) - (min * min)) / (maj * maj);
+  }
+
+
+  /**
+   * Return the semi-major axis.
+   * 
+   * @return the semi-major axis
+   * @since 1.0
+   */
+  public double getMaj() {
+    return maj;
+  }
+
+
+  /**
+   * Return the semi-minor axis
+   * 
+   * @return the semi-minor axis
+   * @since 1.0
+   */
+  public double getMin() {
+    return min;
+  }
+
+
+  /**
+   * Return the eccentricity.
+   * 
+   * @return the eccentricity
+   * @since 1.0
+   */
+  public double getEcc() {
+    return ecc;
+  }
+}
Index: utils/josm/plugins/wmsplugin/src/uk/me/jstott/jcoord/Test.java
===================================================================
--- utils/josm/plugins/wmsplugin/src/uk/me/jstott/jcoord/Test.java	(revision 2121)
+++ utils/josm/plugins/wmsplugin/src/uk/me/jstott/jcoord/Test.java	(revision 2121)
@@ -0,0 +1,145 @@
+package uk.me.jstott.jcoord;
+
+/**
+ * Class to illustrate the use of the various functions of the classes in the
+ * Jcoord package.
+ * 
+ * (c) 2006 Jonathan Stott
+ * 
+ * Created on 11-Feb-2006
+ * 
+ * @author Jonathan Stott
+ * @version 1.0
+ * @since 1.0
+ */
+public class Test {
+
+  /**
+   * Main method
+   * 
+   * @param args
+   *          not used
+   * @since 1.0
+   */
+  public static void main(String[] args) {
+
+    /*
+     * Calculate Surface Distance between two Latitudes/Longitudes
+     * 
+     * The distance() function takes a reference to a LatLng object as a
+     * parameter and calculates the surface distance between the the given
+     * object and this object in kilometres:
+     */
+
+    System.out
+        .println("Calculate Surface Distance between two Latitudes/Longitudes");
+    LatLng lld1 = new LatLng(40.718119, -73.995667); // New York
+    System.out.println("New York Lat/Long: " + lld1.toString());
+    LatLng lld2 = new LatLng(51.499981, -0.125313); // London
+    System.out.println("London Lat/Long: " + lld2.toString());
+    double d = lld1.distance(lld2);
+    System.out.println("Surface Distance between New York and London: " + d
+        + "km");
+    System.out.println();
+
+    /*
+     * Convert OS Grid Reference to Latitude/Longitude
+     * 
+     * Note that the OSGB-Latitude/Longitude conversions use the OSGB36 datum by
+     * default. The majority of applications use the WGS84 datum, for which the
+     * appropriate conversions need to be added. See the examples below to see
+     * the difference between the two data.
+     */
+
+    System.out.println("Convert OS Grid Reference to Latitude/Longitude");
+    // Using OSGB36 (convert an OSGB grid reference to a latitude and longitude
+    // using the OSGB36 datum):
+    System.out.println("Using OSGB36");
+    OSRef os1 = new OSRef(651409.903, 313177.270);
+    System.out.println("OS Grid Reference: " + os1.toString() + " - "
+        + os1.toSixFigureString());
+    LatLng ll1 = os1.toLatLng();
+    System.out.println("Converted to Lat/Long: " + ll1.toString());
+    System.out.println();
+
+    // Using WGS84 (convert an OSGB grid reference to a latitude and longitude
+    // using the WGS84 datum):
+    System.out.println("Using WGS84");
+    OSRef os1w = new OSRef(651409.903, 313177.270);
+    System.out.println("OS Grid Reference: " + os1w.toString() + " - "
+        + os1w.toSixFigureString());
+    LatLng ll1w = os1w.toLatLng();
+    ll1w.toWGS84();
+    System.out.println("Converted to Lat/Long: " + ll1w.toString());
+    System.out.println();
+
+    /*
+     * Convert Latitude/Longitude to OS Grid Reference
+     * 
+     * Note that the OSGB-Latitude/Longitude conversions use the OSGB36 datum by
+     * default. The majority of applications use the WGS84 datum, for which the
+     * appropriate conversions need to be added. See the examples below to see
+     * the difference between the two data.
+     */
+
+    System.out.println("Convert Latitude/Longitude to OS Grid Reference");
+    // Using OSGB36 (convert a latitude and longitude using the OSGB36 datum to
+    // an OSGB grid reference):
+    System.out.println("Using OSGB36");
+    LatLng ll2 = new LatLng(52.657570301933, 1.7179215806451);
+    System.out.println("Latitude/Longitude: " + ll2.toString());
+    OSRef os2 = ll2.toOSRef();
+    System.out.println("Converted to OS Grid Ref: " + os2.toString() + " - "
+        + os2.toSixFigureString());
+    System.out.println();
+
+    // Using WGS84 (convert a latitude and longitude using the WGS84 datum to an
+    // OSGB grid reference):
+    System.out.println("Using WGS84");
+    LatLng ll2w = new LatLng(52.657570301933, 1.7179215806451);
+    System.out.println("Latitude/Longitude: " + ll2.toString());
+    ll2w.toOSGB36();
+    OSRef os2w = ll2w.toOSRef();
+    System.out.println("Converted to OS Grid Ref: " + os2w.toString() + " - "
+        + os2w.toSixFigureString());
+    System.out.println();
+
+    /*
+     * Convert Six-Figure OS Grid Reference String to an OSRef Object
+     * 
+     * To convert a string representing a six-figure OSGB grid reference:
+     */
+
+    System.out
+        .println("Convert Six-Figure OS Grid Reference String to an OSRef Object");
+    String os6 = "TG514131";
+    System.out.println("Six figure string: " + os6);
+    OSRef os6x = new OSRef(os6);
+    System.out.println("Converted to OS Grid Ref: " + os6x.toString() + " - "
+        + os6x.toSixFigureString());
+    System.out.println();
+
+    /*
+     * Convert UTM Reference to Latitude/Longitude
+     */
+
+    System.out.println("Convert UTM Reference to Latitude/Longitude");
+    UTMRef utm1 = new UTMRef(456463.99, 3335334.05, 'E', 12);
+    System.out.println("UTM Reference: " + utm1.toString());
+    LatLng ll3 = utm1.toLatLng();
+    System.out.println("Converted to Lat/Long: " + ll3.toString());
+    System.out.println();
+
+    /*
+     * Convert Latitude/Longitude to UTM Reference
+     */
+
+    System.out.println("Convert Latitude/Longitude to UTM Reference");
+    LatLng ll4 = new LatLng(-60.1167, -111.7833);
+    System.out.println("Latitude/Longitude: " + ll4.toString());
+    UTMRef utm2 = ll4.toUTMRef();
+    System.out.println("Converted to UTM Ref: " + utm2.toString());
+    System.out.println();
+  }
+
+}
Index: utils/josm/plugins/wmsplugin/src/uk/me/jstott/jcoord/UTMRef.java
===================================================================
--- utils/josm/plugins/wmsplugin/src/uk/me/jstott/jcoord/UTMRef.java	(revision 2121)
+++ utils/josm/plugins/wmsplugin/src/uk/me/jstott/jcoord/UTMRef.java	(revision 2121)
@@ -0,0 +1,238 @@
+package uk.me.jstott.jcoord;
+
+/**
+ * Class to represent a UTM reference
+ * 
+ * (c) 2006 Jonathan Stott
+ * 
+ * Created on 11-Feb-2006
+ * 
+ * @author Jonathan Stott
+ * @version 1.0
+ * @since 1.0
+ */
+public class UTMRef {
+
+  /**
+   * Easting
+   */
+  private double easting;
+
+  /**
+   * Northing
+   */
+  private double northing;
+
+  /**
+   * Latitude zone character
+   */
+  private char   latZone;
+
+  /**
+   * Longitude zone number
+   */
+  private int    lngZone;
+
+
+  /**
+   * Create a new UTM reference object.
+   * 
+   * @param easting
+   *          the easting
+   * @param northing
+   *          the northing
+   * @param latZone
+   *          the latitude zone character
+   * @param lngZone
+   *          the longitude zone number
+   * @since 1.0
+   */
+  public UTMRef(double easting, double northing, char latZone, int lngZone) {
+    this.easting = easting;
+    this.northing = northing;
+    this.latZone = latZone;
+    this.lngZone = lngZone;
+  }
+
+
+  /**
+   * Convert this UTM reference to a latitude and longitude.
+   * 
+   * @return the converted latitude and longitude
+   * @since 1.0
+   */
+  public LatLng toLatLng() {
+    double UTM_F0 = 0.9996;
+    double a = RefEll.WGS84.getMaj();
+    double eSquared = RefEll.WGS84.getEcc();
+    double ePrimeSquared = eSquared / (1.0 - eSquared);
+    double e1 = (1 - Math.sqrt(1 - eSquared)) / (1 + Math.sqrt(1 - eSquared));
+    double x = easting - 500000.0;
+    ;
+    double y = northing;
+    int zoneNumber = lngZone;
+    char zoneLetter = latZone;
+
+    double longitudeOrigin = (zoneNumber - 1.0) * 6.0 - 180.0 + 3.0;
+
+    // Correct y for southern hemisphere
+    if ((zoneLetter - 'N') < 0) {
+      y -= 10000000.0;
+    }
+
+    double m = y / UTM_F0;
+    double mu =
+        m
+            / (a * (1.0 - eSquared / 4.0 - 3.0 * eSquared * eSquared / 64.0 - 5.0 * Math
+                .pow(eSquared, 3.0) / 256.0));
+
+    double phi1Rad =
+        mu + (3.0 * e1 / 2.0 - 27.0 * Math.pow(e1, 3.0) / 32.0)
+            * Math.sin(2.0 * mu)
+            + (21.0 * e1 * e1 / 16.0 - 55.0 * Math.pow(e1, 4.0) / 32.0)
+            * Math.sin(4.0 * mu) + (151.0 * Math.pow(e1, 3.0) / 96.0)
+            * Math.sin(6.0 * mu);
+
+    double n =
+        a / Math.sqrt(1.0 - eSquared * Math.sin(phi1Rad) * Math.sin(phi1Rad));
+    double t = Math.tan(phi1Rad) * Math.tan(phi1Rad);
+    double c = ePrimeSquared * Math.cos(phi1Rad) * Math.cos(phi1Rad);
+    double r =
+        a
+            * (1.0 - eSquared)
+            / Math.pow(1.0 - eSquared * Math.sin(phi1Rad) * Math.sin(phi1Rad),
+                1.5);
+    double d = x / (n * UTM_F0);
+
+    double latitude =
+        (phi1Rad - (n * Math.tan(phi1Rad) / r)
+            * (d
+                * d
+                / 2.0
+                - (5.0 + (3.0 * t) + (10.0 * c) - (4.0 * c * c) - (9.0 * ePrimeSquared))
+                * Math.pow(d, 4.0) / 24.0 + (61.0 + (90.0 * t) + (298.0 * c)
+                + (45.0 * t * t) - (252.0 * ePrimeSquared) - (3.0 * c * c))
+                * Math.pow(d, 6.0) / 720.0))
+            * (180.0 / Math.PI);
+
+    double longitude =
+        longitudeOrigin
+            + ((d - (1.0 + 2.0 * t + c) * Math.pow(d, 3.0) / 6.0 + (5.0
+                - (2.0 * c) + (28.0 * t) - (3.0 * c * c)
+                + (8.0 * ePrimeSquared) + (24.0 * t * t))
+                * Math.pow(d, 5.0) / 120.0) / Math.cos(phi1Rad))
+            * (180.0 / Math.PI);
+
+    return new LatLng(latitude, longitude);
+  }
+
+
+  /**
+   * Work out the UTM latitude zone from the latitude.
+   * 
+   * @param latitude
+   *          the latitude to find the UTM latitude zone for
+   * @return the UTM latitude zone for the given latitude
+   * @since 1.0
+   */
+  public static char getUTMLatitudeZoneLetter(double latitude) {
+    if ((84 >= latitude) && (latitude >= 72))
+      return 'X';
+    else if ((72 > latitude) && (latitude >= 64))
+      return 'W';
+    else if ((64 > latitude) && (latitude >= 56))
+      return 'V';
+    else if ((56 > latitude) && (latitude >= 48))
+      return 'U';
+    else if ((48 > latitude) && (latitude >= 40))
+      return 'T';
+    else if ((40 > latitude) && (latitude >= 32))
+      return 'S';
+    else if ((32 > latitude) && (latitude >= 24))
+      return 'R';
+    else if ((24 > latitude) && (latitude >= 16))
+      return 'Q';
+    else if ((16 > latitude) && (latitude >= 8))
+      return 'P';
+    else if ((8 > latitude) && (latitude >= 0))
+      return 'N';
+    else if ((0 > latitude) && (latitude >= -8))
+      return 'M';
+    else if ((-8 > latitude) && (latitude >= -16))
+      return 'L';
+    else if ((-16 > latitude) && (latitude >= -24))
+      return 'K';
+    else if ((-24 > latitude) && (latitude >= -32))
+      return 'J';
+    else if ((-32 > latitude) && (latitude >= -40))
+      return 'H';
+    else if ((-40 > latitude) && (latitude >= -48))
+      return 'G';
+    else if ((-48 > latitude) && (latitude >= -56))
+      return 'F';
+    else if ((-56 > latitude) && (latitude >= -64))
+      return 'E';
+    else if ((-64 > latitude) && (latitude >= -72))
+      return 'D';
+    else if ((-72 > latitude) && (latitude >= -80))
+      return 'C';
+    else
+      return 'Z';
+  }
+
+
+  /**
+   * Convert this UTM reference to a String representation for printing out.
+   * 
+   * @return a String representation of this UTM reference
+   * @since 1.0
+   */
+  public String toString() {
+    return lngZone + Character.toString(latZone) + " " + easting + " "
+        + northing;
+  }
+
+
+  /**
+   * Get the easting.
+   * 
+   * @return the easting
+   * @since 1.0
+   */
+  public double getEasting() {
+    return easting;
+  }
+
+
+  /**
+   * Get the northing.
+   * 
+   * @return the northing
+   * @since 1.0
+   */
+  public double getNorthing() {
+    return northing;
+  }
+
+
+  /**
+   * Get the latitude zone character.
+   * 
+   * @return the latitude zone character
+   * @since 1.0
+   */
+  public char getLatZone() {
+    return latZone;
+  }
+
+
+  /**
+   * Get the longitude zone number.
+   * 
+   * @return the longitude zone number
+   * @since 1.0
+   */
+  public int getLngZone() {
+    return lngZone;
+  }
+}
Index: utils/josm/plugins/wmsplugin/src/uk/me/jstott/jcoord/Util.java
===================================================================
--- utils/josm/plugins/wmsplugin/src/uk/me/jstott/jcoord/Util.java	(revision 2121)
+++ utils/josm/plugins/wmsplugin/src/uk/me/jstott/jcoord/Util.java	(revision 2121)
@@ -0,0 +1,66 @@
+package uk.me.jstott.jcoord;
+
+/**
+ * Some utility functions used by classes in the uk.me.jstott.jcoord package.
+ * 
+ * (c) 2006 Jonathan Stott
+ * 
+ * Created on 11-Feb-2006
+ * 
+ * @author Jonathan Stott
+ * @version 1.0
+ * @since 1.0
+ */
+class Util {
+
+  /**
+   * Calculate sin^2(x).
+   * 
+   * @param x
+   *          x
+   * @return sin^2(x)
+   * @since 1.0
+   */
+  protected static double sinSquared(double x) {
+    return Math.sin(x) * Math.sin(x);
+  }
+
+
+  /**
+   * Calculate cos^2(x).
+   * 
+   * @param x
+   *          x
+   * @return cos^2(x)
+   * @since 1.0
+   */
+  protected static double cosSquared(double x) {
+    return Math.cos(x) * Math.cos(x);
+  }
+
+
+  /**
+   * Calculate tan^2(x).
+   * 
+   * @param x
+   *          x
+   * @return tan^2(x)
+   * @since 1.0
+   */
+  protected static double tanSquared(double x) {
+    return Math.tan(x) * Math.tan(x);
+  }
+
+
+  /**
+   * Calculate sec(x).
+   * 
+   * @param x
+   *          x
+   * @return sec(x)
+   * @since 1.0
+   */
+  protected static double sec(double x) {
+    return 1.0 / Math.cos(x);
+  }
+}
Index: utils/josm/plugins/wmsplugin/src/wmsplugin/DownloadWMSTask.java
===================================================================
--- utils/josm/plugins/wmsplugin/src/wmsplugin/DownloadWMSTask.java	(revision 2121)
+++ utils/josm/plugins/wmsplugin/src/wmsplugin/DownloadWMSTask.java	(revision 2121)
@@ -0,0 +1,75 @@
+package wmsplugin;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.IOException;
+import java.util.*;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.DownloadAction;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+
+ 
+
+public class DownloadWMSTask extends PleaseWaitRunnable {
+
+	private WMSLayer wmsLayer;
+	private double minlat, minlon, maxlat, maxlon;
+	
+	/* whether our layer was already added. */
+	private boolean layerAdded = false;
+	
+	
+	public DownloadWMSTask(String name, String wmsurl) {
+		
+		super(tr("Downloading " + name));
+		
+		// simply check if we already have a layer created. if not, create; if yes, reuse.
+		
+		if (wmsLayer == null) {
+			
+			if (wmsurl.matches("(?i).*layers=npeoocmap.*") || wmsurl.matches("(?i).*layers=npe.*") ){
+				//then we use the OSGBLayer
+				this.wmsLayer= new OSGBLayer(name, wmsurl);
+			} else {			
+				this.wmsLayer = new WMSLayer(name, wmsurl); 
+				
+			} 
+		} 
+	}
+	
+	@Override public void realRun() throws IOException {
+		wmsLayer.grab(minlat,minlon,maxlat,maxlon);
+	}
+
+	@Override protected void finish() {
+
+		// BUG if layer is deleted, wmsLayer is not null and layerAdded remains true!
+		// FIXED, see below
+		
+		layerAdded = false;
+		for (Iterator it = Main.map.mapView.getAllLayers().iterator(); it.hasNext(); ) {
+        Object  element = it.next();
+       
+        if (element.equals(wmsLayer)) layerAdded = true;
+        
+		}
+		 
+				
+		if ((wmsLayer != null) && (!layerAdded))
+		{
+			Main.main.addLayer(wmsLayer);
+			layerAdded = true;
+		}
+	}
+
+	@Override protected void cancel() {
+	}
+
+	public void download(DownloadAction action, double minlat, double minlon, double maxlat, double maxlon) {
+		this.minlat=minlat;
+		this.minlon=minlon;
+		this.maxlat=maxlat;
+		this.maxlon=maxlon;
+		Main.worker.execute(this);
+	}
+}
Index: utils/josm/plugins/wmsplugin/src/wmsplugin/Help_WMSmenuAction.java
===================================================================
--- utils/josm/plugins/wmsplugin/src/wmsplugin/Help_WMSmenuAction.java	(revision 2121)
+++ utils/josm/plugins/wmsplugin/src/wmsplugin/Help_WMSmenuAction.java	(revision 2121)
@@ -0,0 +1,62 @@
+package wmsplugin;
+
+import java.awt.event.ActionEvent;
+import javax.swing.*;
+import org.openstreetmap.josm.actions.JosmAction;
+
+
+
+public class Help_WMSmenuAction extends JosmAction {
+
+	/**
+	 * 
+	 */
+	
+
+	public Help_WMSmenuAction() {
+		//super("Help / About");
+		super("help", "help", "Help  / About", 0, 0, false);
+		
+	}
+	
+	public void actionPerformed(ActionEvent e) {
+		//todo - put this into a txt file? 
+		  String helptext = 
+			"You can add, edit and delete WMS entries in the WMSplugin Preference Tab - "  +
+			"these will then show up in the WMS menu.\n\n"+
+			
+			"You can also do this manually in the Advanced Preferences, using the following schema:\n"+
+			"wmsplugin.url.1.name=Landsat\n"+
+			"wmsplugin.url.1.url=http://onearth.jpl.nasa.gov....\n"+
+			"wmsplugin.url.2.name=NPE Maps... etc\n\n"+
+					
+			"Full WMS URL input format example (landsat)\n"+
+			"http://onearth.jpl.nasa.gov/wms.cgi?request=GetMap&\n"+
+			"layers=global_mosaic&styles=&srs=EPSG:4326&\n"+
+			"format=image/jpeg \n\n"+
+			
+			"For Metacartas Map Rectifier http://labs.metacarta.com/rectifier/ , you only need to input the relevant 'id'.\n" +
+			"To add a Metacarta Map Rectifer menu item, manually create the url like in this example," +
+			"replacing 73 with your image id: \n" +
+			"http://labs.metacarta.com/rectifier/wms.cgi?id=73\n" +
+			"&srs=EPSG:4326&Service=WMS&Version=1.1.0&Request=GetMap&format=image/png\n" +
+			" \n" +
+			"Note: Make sure the image is suitable, copyright-wise, if in doubt, don't use.\n"+
+			 "WMS Plugin version: "+ WMSPlugin.VERSION ;		
+		
+		JTextPane tp = new JTextPane();
+		  JScrollPane js = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+				  JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+		 
+		  
+		  js.getViewport().add(tp);
+		  JFrame jf = new JFrame("WMS Plugin Help");
+		  jf.getContentPane().add(js);
+		  jf.pack();
+		  jf.setSize(400,500);
+		  jf.setVisible(true); 
+		  tp.setText(helptext);
+	
+}
+	
+}
Index: utils/josm/plugins/wmsplugin/src/wmsplugin/Map_Rectifier_WMSmenuAction.java
===================================================================
--- utils/josm/plugins/wmsplugin/src/wmsplugin/Map_Rectifier_WMSmenuAction.java	(revision 2121)
+++ utils/josm/plugins/wmsplugin/src/wmsplugin/Map_Rectifier_WMSmenuAction.java	(revision 2121)
@@ -0,0 +1,66 @@
+package wmsplugin;
+
+import java.awt.event.ActionEvent;
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.gui.MapView;
+
+
+public class Map_Rectifier_WMSmenuAction extends JosmAction {
+
+	/**
+	 * tim waters "chippy"
+	 */
+	private static final long serialVersionUID = 1L;
+
+	public Map_Rectifier_WMSmenuAction() {
+		super("Rectified Image", "OLmarker", "Download Rectified Image from Metacarta's Map Rectifer WMS", 0, 0, false);
+
+
+	}
+	DownloadWMSTask downloadTask;
+
+	public void actionPerformed(ActionEvent e) {
+
+		//String newid;
+		String newURL = "";
+		String newid = "";
+
+		newid = JOptionPane.showInputDialog(Main.parent, "Metacarta Map Rectifier image id ");
+	//	System.out.println("newid= " +newid);
+		if (newid != null){
+
+
+			if (newid.compareTo("") != 0) 
+			{
+				newURL = "http://labs.metacarta.com/rectifier/wms.cgi?id="+newid+
+				"&srs=EPSG:4326&Service=WMS&Version=1.1.0&Request=GetMap&format=image/png";
+
+
+				//System.out.println(newURL);
+
+			//	if (downloadTask == null){
+					//System.out.println("new download task!");
+					downloadTask = new DownloadWMSTask("rectifier id="+newid, newURL);
+			//	}
+				MapView mv = Main.map.mapView;
+
+				downloadTask.download(null,
+						mv.getLatLon(0, mv.getHeight()).lat(),
+						mv.getLatLon(0, mv.getHeight()).lon(),
+						mv.getLatLon(mv.getWidth(), 0).lat(),
+						mv.getLatLon(mv.getWidth(), 0).lon());			
+
+
+			}
+		}
+		//else do nuffink
+
+	}
+
+
+
+}
+
Index: utils/josm/plugins/wmsplugin/src/wmsplugin/NPE_WMSmenuAction.java
===================================================================
--- utils/josm/plugins/wmsplugin/src/wmsplugin/NPE_WMSmenuAction.java	(revision 2121)
+++ utils/josm/plugins/wmsplugin/src/wmsplugin/NPE_WMSmenuAction.java	(revision 2121)
@@ -0,0 +1,52 @@
+package wmsplugin;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+
+
+public class NPE_WMSmenuAction extends AbstractAction {
+
+	/**
+	 * 
+	 */
+	
+
+	public NPE_WMSmenuAction() {
+		super("Use NPE maps");
+		
+	}
+	
+	public void actionPerformed(ActionEvent e) {
+	
+		/*String npeURL = ("	http://www.getmapping.com/iedirectimage/getmappingwms.aspx?"+
+			"srs=EPSG:27700&Service=WMS&Version=1.1.0&"+
+			"Request=GetMap&format=image/jpeg&layers=npeoocmap");*/
+		
+		String npeURL = ("http://nick.dev.openstreetmap.org/openpaths/freemap.php?layers=npe&");
+			
+		
+
+			Main.pref.put("wmsplugin.url", npeURL);	
+	
+			JOptionPane.showMessageDialog(null, "WMS set to New Public Edition.");
+		
+		//need to reset the particular download task
+		//DownloadWMSTask
+		;
+		for (int i = 0; i < Main.main.menu.download.downloadTasks.size(); ++i) {
+			
+			if (Main.main.menu.download.downloadTasks.get(i).getPreferencesSuffix().compareTo("wmsplugin") == 0){
+				
+				Main.main.menu.download.downloadTasks.remove(i);
+			}
+		}
+
+
+
+	}
+}
Index: utils/josm/plugins/wmsplugin/src/wmsplugin/OSGBImage.java
===================================================================
--- utils/josm/plugins/wmsplugin/src/wmsplugin/OSGBImage.java	(revision 2121)
+++ utils/josm/plugins/wmsplugin/src/wmsplugin/OSGBImage.java	(revision 2121)
@@ -0,0 +1,153 @@
+package wmsplugin;
+
+import uk.me.jstott.jcoord.OSRef;
+import uk.me.jstott.jcoord.LatLng;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.gui.NavigatableComponent;
+
+import java.awt.Graphics2D;
+import java.awt.BasicStroke;
+import java.awt.Point;
+import java.awt.Graphics;
+import java.awt.Color;
+
+public class OSGBImage extends WMSImage
+{
+	public OSGBImage(String constURL)
+	{
+		super(constURL);
+	}
+
+	public void grab(NavigatableComponent nc,double minlat,double minlon,
+			double maxlat,double maxlon) throws IOException
+	{
+		// To deal with the fact that grid refs and lat/lon don't align
+		LatLng ll1 = new LatLng(minlat,minlon),
+				ll2 = new LatLng(maxlat,maxlon),
+				ll3 = new LatLng(maxlat,minlon),
+				ll4 = new LatLng(minlat,maxlon);
+
+		ll1.toOSGB36();
+		ll2.toOSGB36();
+		ll3.toOSGB36();
+		ll4.toOSGB36();
+
+		OSRef bottomLeftGR = ll1.toOSRef(),
+			  topRightGR = ll2.toOSRef(),
+		 	topLeftGR =  ll3.toOSRef(),
+			  bottomRightGR =  ll4.toOSRef();
+
+		double w = Math.min(bottomLeftGR.getEasting(),
+								topLeftGR.getEasting()),
+			   s = Math.min(bottomLeftGR.getNorthing(),
+							   bottomRightGR.getNorthing()),
+			   e = Math.max(bottomRightGR.getEasting(),
+							   topRightGR.getEasting()),
+			   n = Math.max(topLeftGR.getNorthing(),
+							   topRightGR.getNorthing());
+
+		// Adjust topLeft and bottomRight due to messing around with
+		// projections
+		LatLng tl2 = new OSRef(w,n).toLatLng();
+		LatLng br2 = new OSRef(e,s).toLatLng();
+		tl2.toWGS84();
+		br2.toWGS84();
+
+		topLeft = Main.proj.latlon2eastNorth
+					(new LatLon(tl2.getLat(),tl2.getLng() ));
+		bottomRight = Main.proj.latlon2eastNorth
+					(new LatLon(br2.getLat(),br2.getLng() ));
+
+		grabbedScale = nc.getScale(); // enPerPixel
+
+		int widthPx = (int)((bottomRight.east()-topLeft.east())/grabbedScale),
+			heightPx = (int)
+				((topLeft.north()-bottomRight.north()) / grabbedScale);
+
+		try
+		{
+			URL url =  doGetURL(w,s,e,n,widthPx,heightPx);
+			doGrab(url);
+		}
+		catch(MalformedURLException ex)
+		{
+			System.out.println("Illegal url. Error="+ex);
+		}
+	}
+
+	public void paint(Graphics g,NavigatableComponent nc) 
+	{
+		if(theImage!=null)
+		{
+			super.paint(g,nc);
+			Graphics2D g2d = (Graphics2D)g;
+			g2d.setStroke(new BasicStroke(2));
+
+			// Display markers at the OSGB intersections.
+			// The code is very convoluted - projections really are fun
+			// things to deal with :-)
+			// Oh well, at least I can let someone else do the maths :-)
+
+			double zoomInFactor = grabbedScale / nc.getScale();
+
+			EastNorth topLeftDisplaced  = 
+				new EastNorth(topLeft.east()+dEast, topLeft.north()+dNorth);
+			EastNorth bottomRightDisplaced  = 
+				new EastNorth(bottomRight.east()+dEast,
+								bottomRight.north()+dNorth);
+
+			LatLon ll5 = Main.proj.eastNorth2latlon(topLeftDisplaced),
+				ll6 = Main.proj.eastNorth2latlon(bottomRightDisplaced);
+
+			LatLng ll7 = new LatLng(ll5.lat(),ll5.lon());
+			LatLng ll8 = new LatLng(ll6.lat(),ll6.lon());
+			ll7.toOSGB36();
+			ll8.toOSGB36();
+
+			LatLng curLatLng; 
+			EastNorth curEN;
+
+			
+			OSRef osgb1 = ll7.toOSRef(),
+				 osgb2 = ll8.toOSRef();
+
+			for(int easting=(int)(osgb1.getEasting()/1000) + 1; 
+					easting<=(int)(osgb2.getEasting()/1000);
+					easting++)
+			{
+				for (int northing=(int)(osgb1.getNorthing()/1000) ;
+					 northing>(int)(osgb2.getNorthing()/1000);
+					 northing--)
+				{
+					// Now we have to convert the OSGB eastings and northings
+					// *back* to EastNorth units so we can draw the 
+					// intersections....
+					// Not to mention converting between JOSM LatLon and
+					// JCoord LatLng....
+				
+
+					curLatLng = new OSRef(easting*1000,northing*1000).
+											toLatLng();
+					curLatLng.toWGS84();
+					curEN = Main.proj.latlon2eastNorth
+								(new LatLon(curLatLng.getLat(),
+											curLatLng.getLng() ) );
+
+					// draw a cross at the intersection 
+					Point p = Main.map.mapView.getPoint(curEN);
+					g.setColor(Color.BLUE);
+					g.drawLine(p.x-5,p.y,p.x+5,p.y);
+					g.drawLine(p.x,p.y-5,p.x,p.y+5);
+				}
+			}
+			g2d.setStroke(new BasicStroke(1));
+		}
+	}
+}
Index: utils/josm/plugins/wmsplugin/src/wmsplugin/OSGBLayer.java
===================================================================
--- utils/josm/plugins/wmsplugin/src/wmsplugin/OSGBLayer.java	(revision 2121)
+++ utils/josm/plugins/wmsplugin/src/wmsplugin/OSGBLayer.java	(revision 2121)
@@ -0,0 +1,21 @@
+package wmsplugin;
+
+import java.io.IOException;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.MapView;
+
+public class OSGBLayer extends WMSLayer {
+
+	public OSGBLayer(String name, String constURL) {
+		super(name, constURL);
+	}
+
+	public void grab(double minlat,double minlon,double maxlat,double maxlon)
+	throws IOException {
+		MapView mv = Main.map.mapView;
+		OSGBImage npeImage = new OSGBImage(url);
+		npeImage.grab(mv,minlat,minlon,maxlat,maxlon);
+		wmsImages.add(npeImage);
+	}
+}
Index: utils/josm/plugins/wmsplugin/src/wmsplugin/WMSAdjustAction.java
===================================================================
--- utils/josm/plugins/wmsplugin/src/wmsplugin/WMSAdjustAction.java	(revision 2121)
+++ utils/josm/plugins/wmsplugin/src/wmsplugin/WMSAdjustAction.java	(revision 2121)
@@ -0,0 +1,92 @@
+package wmsplugin; 
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Cursor;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.actions.mapmode.MapMode;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.gui.layer.Layer;
+
+
+public class WMSAdjustAction extends MapMode implements
+		MouseListener, MouseMotionListener{
+
+	WMSImage selectedImage; 
+	boolean mouseDown;
+	EastNorth prevEastNorth;
+
+	public WMSAdjustAction(MapFrame mapFrame) {
+		super("landsatAdjust", "movelandsat", 
+						"Adjust the position of the WMS layer", mapFrame, 
+						ImageProvider.getCursor("normal", "move"));
+	}
+
+	@Override public void enterMode() {
+		super.enterMode();
+		Main.map.mapView.addMouseListener(this);
+		Main.map.mapView.addMouseMotionListener(this);
+	}
+
+	@Override public void exitMode() {
+		super.exitMode();
+		Main.map.mapView.removeMouseListener(this);
+		Main.map.mapView.removeMouseMotionListener(this);
+	}
+
+	@Override public void mousePressed(MouseEvent e) {
+		if (e.getButton() != MouseEvent.BUTTON1)
+			return;
+
+		 for(Layer layer:Main.map.mapView.getAllLayers()) {
+			if (layer instanceof WMSLayer) {
+				prevEastNorth=Main.map.mapView.getEastNorth(e.getX(),e.getY());
+				selectedImage = ((WMSLayer)layer).findImage(prevEastNorth);
+				if(selectedImage!=null){
+					Main.map.mapView.setCursor
+						(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
+				}
+			}
+		}
+	}
+
+	@Override public void mouseDragged(MouseEvent e) {
+			/*
+		if (e.getButton() != MouseEvent.BUTTON1)
+			return;
+			*/
+
+		if(selectedImage!=null) {
+			EastNorth eastNorth=
+					Main.map.mapView.getEastNorth(e.getX(),e.getY());
+			if(selectedImage.contains(eastNorth)) {
+				selectedImage.displace(eastNorth.east()-prevEastNorth.east(), 
+									eastNorth.north()-prevEastNorth.north());
+				prevEastNorth = eastNorth;
+			}
+			Main.map.mapView.repaint();
+		}
+	}
+
+	@Override public void mouseReleased(MouseEvent e) {
+		Main.map.mapView.repaint();
+		Main.map.mapView.setCursor(Cursor.getDefaultCursor());
+		selectedImage = null;	
+	}
+
+	public void mouseEntered(MouseEvent e) {
+	}
+	public void mouseExited(MouseEvent e) {
+	}
+	public void mouseMoved(MouseEvent e) {
+	}
+
+	@Override public void mouseClicked(MouseEvent e) {
+	}
+}
Index: utils/josm/plugins/wmsplugin/src/wmsplugin/WMSDownloadAction.java
===================================================================
--- utils/josm/plugins/wmsplugin/src/wmsplugin/WMSDownloadAction.java	(revision 2121)
+++ utils/josm/plugins/wmsplugin/src/wmsplugin/WMSDownloadAction.java	(revision 2121)
@@ -0,0 +1,36 @@
+package wmsplugin;
+
+import java.awt.event.ActionEvent;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.gui.MapView;
+
+public class WMSDownloadAction extends JosmAction {
+
+	private WMSInfo info;
+	
+	public WMSDownloadAction(WMSInfo info) {
+		super(info.name, "wmsmenu", "Download WMS tile from "+info.name, 0, 0, false);
+		this.info = info;
+	}
+	
+	public void actionPerformed(ActionEvent e) {
+		
+		// store the download task with the "info" object. if we create a new
+		// download task here every time, then different layers are displayed even
+		// for images from the same server, and we don't want that.
+		
+		if (info.downloadTask == null)
+			info.downloadTask = new DownloadWMSTask(info.name, info.url);
+		
+		MapView mv = Main.map.mapView;
+		
+		info.downloadTask.download(null,
+				mv.getLatLon(0, mv.getHeight()).lat(),
+				mv.getLatLon(0, mv.getHeight()).lon(),
+				mv.getLatLon(mv.getWidth(), 0).lat(),
+				mv.getLatLon(mv.getWidth(), 0).lon());			
+	}
+};
+
Index: utils/josm/plugins/wmsplugin/src/wmsplugin/WMSImage.java
===================================================================
--- utils/josm/plugins/wmsplugin/src/wmsplugin/WMSImage.java	(revision 2121)
+++ utils/josm/plugins/wmsplugin/src/wmsplugin/WMSImage.java	(revision 2121)
@@ -0,0 +1,145 @@
+package wmsplugin;
+
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.Point;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.imageio.ImageIO;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.gui.NavigatableComponent;
+
+public class WMSImage
+{
+	String constURL;
+	protected Image theImage;
+	protected double grabbedScale;
+	protected EastNorth topLeft, bottomRight;
+	double dEast, dNorth;	
+
+	public WMSImage(String constURL)
+	{
+		this.constURL = constURL;
+	}
+
+	public void grab(NavigatableComponent nc) throws IOException
+	{
+
+		EastNorth topLeft  = nc.getEastNorth(0,0);
+		grabbedScale =  nc.getScale();  // scale is enPerPixel
+
+		this.topLeft = topLeft;
+
+		try
+		{
+			URL url = getURL(nc);
+			doGrab(url);
+		}
+		catch(MalformedURLException e)
+		{
+			System.out.println("Illegal url. Error="+e);
+		}
+	}
+
+	public void grab(NavigatableComponent nc,double minlat,double minlon,
+			double maxlat,double maxlon) throws IOException
+	{
+		LatLon p = new LatLon(minlat,minlon),
+				p2 = new LatLon(maxlat,maxlon);
+
+		grabbedScale = nc.getScale(); // enPerPixel
+
+		topLeft = Main.proj.latlon2eastNorth(new LatLon(maxlat,minlon));
+		bottomRight = Main.proj.latlon2eastNorth(new LatLon(minlat,maxlon));
+
+		int widthPx = (int)((bottomRight.east()-topLeft.east())/grabbedScale),
+		heightPx = (int)
+		((topLeft.north()-bottomRight.north()) / grabbedScale);
+
+		try
+		{
+			URL url =  doGetURL(p.lon(),p.lat(),
+									p2.lon(),p2.lat(),widthPx,heightPx);
+			doGrab(url);
+		}
+		catch(MalformedURLException e)
+		{
+			System.out.println("Illegal url. Error="+e);
+		}
+	}
+
+	private URL getURL(NavigatableComponent nc) throws MalformedURLException
+	{
+		double widthEN = nc.getWidth()*grabbedScale,
+		heightEN = nc.getHeight()*grabbedScale;
+		LatLon p = Main.proj.eastNorth2latlon(new EastNorth
+				(topLeft.east(), topLeft.north()-heightEN));
+		LatLon p2 = Main.proj.eastNorth2latlon(new EastNorth
+				(topLeft.east()+widthEN, topLeft.north()));
+		return doGetURL(p.lon(),p.lat(),p2.lon(),p2.lat(),
+						(int)(widthEN/grabbedScale),
+						(int)(heightEN/grabbedScale) );
+	}
+
+	protected URL doGetURL(double w,double s,double e,double n, int wi, 
+					int ht) throws MalformedURLException
+	{
+		String str = constURL + "&bbox=" + w +"," + s + ","+
+				e+","+n + "&width=" + wi + "&height=" + ht;
+		return new URL(str);
+	}
+
+	protected void doGrab (URL url) throws IOException
+	{
+		InputStream is = url.openStream();
+		theImage = ImageIO.read(is) ;
+		is.close();
+		Main.map.repaint();
+	}
+
+	public void displace (double dEast, double dNorth)
+	{
+	 	this.dEast += dEast;	
+	 	this.dNorth += dNorth;	
+	}
+
+	public boolean contains(EastNorth eastNorth)
+	{
+		double e1 = topLeft.east()+dEast, 
+			   e2 = bottomRight.east()+dEast,
+			   n1 = bottomRight.north()+dNorth,
+			   n2 = topLeft.north()+dNorth;
+
+		boolean b =  eastNorth.east()>=e1 && eastNorth.east()<=e2 &&
+				eastNorth.north()>=n1 && eastNorth.north()<=n2;
+		return b;
+	}
+
+	public void paint(Graphics g,NavigatableComponent nc) 
+	{
+		if(theImage!=null)
+		{
+			double zoomInFactor = grabbedScale / nc.getScale();
+
+			// Find the image x and y of the supplied bottom left
+			// This will be the difference in EastNorth units, divided by the
+			// grabbed scale in EastNorth/pixel.
+
+			int w = theImage.getWidth(null), h=theImage.getHeight(null);
+			EastNorth topLeftDisplaced  = 
+				new EastNorth(topLeft.east()+dEast, topLeft.north()+dNorth);
+			Point displacement = Main.map.mapView.getPoint(topLeftDisplaced);
+			g.drawImage(theImage,displacement.x,displacement.y,
+					(int)(displacement.x+w*zoomInFactor),
+					(int)(displacement.y+h*zoomInFactor),
+					0,0,w,h,null);
+		}
+	}
+
+}
Index: utils/josm/plugins/wmsplugin/src/wmsplugin/WMSInfo.java
===================================================================
--- utils/josm/plugins/wmsplugin/src/wmsplugin/WMSInfo.java	(revision 2121)
+++ utils/josm/plugins/wmsplugin/src/wmsplugin/WMSInfo.java	(revision 2121)
@@ -0,0 +1,27 @@
+package wmsplugin;
+
+import org.openstreetmap.josm.Main;
+
+/**
+ * Class that stores info about a WMS server.
+ * 
+ * @author Frederik Ramm <frederik@remote.org>
+ */
+public class WMSInfo {
+	
+	String name;
+	String url;
+	int prefid;
+	DownloadWMSTask downloadTask;
+	
+	public WMSInfo(String name, String url, int prefid) {
+		this.name=name; this.url=url; this.prefid=prefid;
+		downloadTask = null;
+	}
+	
+	public void save() {
+		Main.pref.put("wmsplugin.url." + prefid + ".name", name);
+		Main.pref.put("wmsplugin.url." + prefid + ".url", url);
+	}
+	
+}
Index: utils/josm/plugins/wmsplugin/src/wmsplugin/WMSLayer.java
===================================================================
--- utils/josm/plugins/wmsplugin/src/wmsplugin/WMSLayer.java	(revision 2121)
+++ utils/josm/plugins/wmsplugin/src/wmsplugin/WMSLayer.java	(revision 2121)
@@ -0,0 +1,124 @@
+package wmsplugin;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Toolkit;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+import javax.swing.JMenuItem;
+import javax.swing.JSeparator;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+import org.openstreetmap.josm.data.projection.Projection;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
+import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.data.coor.EastNorth;
+
+/**
+ * This is a layer that grabs the current screen from an WMS server. The data
+ * fetched this way is tiled and managerd to the disc to reduce server load.
+ */
+public class WMSLayer extends Layer {
+
+	protected static Icon icon = new ImageIcon(Toolkit.getDefaultToolkit().createImage(WMSPlugin.class.getResource("/images/wms.png")));
+
+	protected final ArrayList<WMSImage> wmsImages;
+
+	protected final String url;
+
+	public WMSLayer(String name, String url) {
+		super(name);
+		// to calculate the world dimension, we assume that the projection does
+		// not have problems with translating longitude to a correct scale.
+		// Next to that, the projection must be linear dependend on the lat/lon
+		// unprojected scale.
+		if (Projection.MAX_LON != 180)
+			throw new IllegalArgumentException(tr
+					("Wrong longitude transformation for tile manager. "+
+							"Can't operate on {0}",Main.proj));
+
+		this.url = url;
+		//wmsImage = new WMSImage(url);
+		wmsImages = new ArrayList<WMSImage>();
+	}
+
+	public void grab() throws IOException
+	{
+		MapView mv = Main.map.mapView;
+		WMSImage wmsImage = new WMSImage(url);
+		wmsImage.grab(mv);
+		wmsImages.add(wmsImage);
+	}
+
+	public void grab(double minlat,double minlon,double maxlat,double maxlon)
+	throws IOException
+	{
+		MapView mv = Main.map.mapView;
+		WMSImage wmsImage = new WMSImage(url);
+		wmsImage.grab(mv,minlat,minlon,maxlat,maxlon);
+		wmsImages.add(wmsImage);
+	}
+
+	@Override public Icon getIcon() {
+		return icon;
+	}
+
+	@Override public String getToolTipText() {
+		return tr("WMS layer ({0}), {1} tile(s) loaded", name, wmsImages.size());
+	}
+
+	@Override public boolean isMergable(Layer other) {
+		return false;
+	}
+
+	@Override public void mergeFrom(Layer from) {
+	}
+
+	@Override public void paint(Graphics g, final MapView mv) {
+		for(WMSImage wmsImage : wmsImages) {
+			wmsImage.paint(g,mv);
+		}
+	}
+
+	@Override public void visitBoundingBox(BoundingXYVisitor v) {
+		// doesn't have a bounding box
+	}
+
+	@Override public Object getInfoComponent() {
+		return getToolTipText();
+	}
+
+	@Override public Component[] getMenuEntries() {
+		return new Component[]{
+				new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
+				new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
+				new JSeparator(),
+				new JMenuItem(new LayerListPopup.InfoAction(this))};
+	}
+
+	public WMSImage findImage(EastNorth eastNorth)
+	{
+		for(WMSImage wmsImage : wmsImages) {
+			if (wmsImage.contains(eastNorth))  {
+				return wmsImage;
+			}
+		}
+		return null;
+	}
+
+	//to enable the removal of the images when the layer is removed.
+	public void destroy() {
+	
+		wmsImages.clear();
+		
+		
+	}
+}
Index: utils/josm/plugins/wmsplugin/src/wmsplugin/WMSPlugin.java
===================================================================
--- utils/josm/plugins/wmsplugin/src/wmsplugin/WMSPlugin.java	(revision 2121)
+++ utils/josm/plugins/wmsplugin/src/wmsplugin/WMSPlugin.java	(revision 2121)
@@ -0,0 +1,132 @@
+package wmsplugin;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.TreeSet;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.plugins.Plugin;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.IconToggleButton;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+
+
+// NW 151006 only add the landsat task when the map frame is initialised with
+// data.
+
+public class WMSPlugin extends Plugin {
+
+	static String VERSION = "2.4";
+	
+	DownloadWMSTask task;
+	WMSLayer wmsLayer;
+	static JMenu wmsJMenu;
+
+	static ArrayList<WMSInfo> wmsList = new ArrayList<WMSInfo>();
+	
+
+	
+	public WMSPlugin() {
+		
+		refreshMenu();
+		
+	}
+
+	// this parses the preferences settings. preferences for the wms plugin have to
+	// look like this:
+	// wmsplugin.1.name=Landsat
+	// wmsplugin.1.url=http://and.so.on/
+	
+	public static void refreshMenu() {
+		wmsList.clear();
+		Map<String,String> prefs = Main.pref.getAllPrefix("wmsplugin.url.");
+		
+		TreeSet<String> keys = new TreeSet<String>(prefs.keySet());
+		int prefid = 0;
+		String name = null;
+		String url = null;
+		int lastid = -1;
+		for (String key : keys) {
+			String[] elements = key.split("\\.");
+			if (elements.length != 4) continue;
+			try {
+				prefid = Integer.parseInt(elements[2]);
+			} catch(NumberFormatException e) {
+				continue;
+			}
+			if (prefid != lastid) {
+				if ((name != null) && (url != null)) {
+					wmsList.add(new WMSInfo(name, url, prefid));
+				}
+				name = null; url = null; lastid = prefid; 
+			}
+			if (elements[3].equals("name")) {
+				name=prefs.get(key);
+			} else if (elements[3].equals("url")) {
+				url = prefs.get(key);
+			}		
+		}
+		if ((name != null) && (url != null)) {
+			wmsList.add(new WMSInfo(name, url, prefid));
+		}
+		
+		// if no (valid) prefs are set, initialize to a sensible default.
+		if (wmsList.isEmpty()) {
+			WMSInfo landsatInfo = new WMSInfo("Landsat", 
+					"http://onearth.jpl.nasa.gov/wms.cgi?request=GetMap&"+
+					"layers=global_mosaic&styles=&srs=EPSG:4326&"+
+					"format=image/jpeg", 1);
+			landsatInfo.save();
+			wmsList.add(landsatInfo);
+			
+			WMSInfo npeInfo = new WMSInfo("NPE Maps", 
+					"http://nick.dev.openstreetmap.org/openpaths/freemap.php?layers=npe&", 2);
+			npeInfo.save();
+			wmsList.add(npeInfo);
+		}
+		
+		JMenuBar menu = Main.main.menu;
+
+		if (wmsJMenu == null) {
+			wmsJMenu = new JMenu(tr("WMS"));
+			menu.add(wmsJMenu, 3);
+			wmsJMenu.setEnabled(false);
+		} else {
+			wmsJMenu.removeAll();
+		}
+		
+		// for each configured WMSInfo, add a menu entry.
+		for (final WMSInfo u : wmsList) {
+			wmsJMenu.add(new JMenuItem(new WMSDownloadAction(u)));
+		}	
+		wmsJMenu.addSeparator();
+		wmsJMenu.add(new JMenuItem(new Map_Rectifier_WMSmenuAction()));
+		
+		wmsJMenu.addSeparator();
+		wmsJMenu.add(new JMenuItem(new Help_WMSmenuAction()));
+		
+		
+	
+	}
+	
+	public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
+		if(oldFrame==null && newFrame!=null) { 
+			wmsJMenu.setEnabled(true);
+			Main.map.toolBarActions.addSeparator();
+			Main.map.toolBarActions.add(new IconToggleButton
+						(new WMSAdjustAction(Main.map)));
+		} else if (oldFrame!=null && newFrame==null ) {
+			wmsJMenu.setEnabled(false);
+		}
+	}
+	
+	public PreferenceSetting getPreferenceSetting() {
+		return new WMSPreferenceEditor();
+	}
+}
Index: utils/josm/plugins/wmsplugin/src/wmsplugin/WMSPreferenceEditor.java
===================================================================
--- utils/josm/plugins/wmsplugin/src/wmsplugin/WMSPreferenceEditor.java	(revision 2121)
+++ utils/josm/plugins/wmsplugin/src/wmsplugin/WMSPreferenceEditor.java	(revision 2121)
@@ -0,0 +1,126 @@
+package wmsplugin;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Dimension;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.HashMap;
+import java.util.Map;
+
+
+import javax.swing.Box;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.table.DefaultTableModel;
+
+
+import org.openstreetmap.josm.gui.preferences.PreferenceDialog;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.tools.GBC;
+
+public class WMSPreferenceEditor implements PreferenceSetting {
+	
+	private Map<String,String> orig;
+	private DefaultTableModel model;
+	private int highestIdUsed = 0;
+	private HashMap<Integer, WMSInfo> oldValues = new HashMap<Integer, WMSInfo>();
+	
+	public void addGui(final PreferenceDialog gui) {
+		JPanel p = gui.createPreferenceTab("wms", tr("WMS Plugin Preferences"), tr("Modify list of WMS servers displayed in the WMS plugin menu"));
+		
+		model = new DefaultTableModel(new String[]{"#", "Menu Name", "WMS URL"}, 0) {
+			@Override public boolean isCellEditable(int row, int column) {
+				return column != 0;
+			}
+		};
+		final JTable list = new JTable(model);
+		list.getColumnModel().removeColumn(list.getColumnModel().getColumn(0));
+		JScrollPane scroll = new JScrollPane(list);
+		p.add(scroll, GBC.eol().fill(GBC.BOTH));
+		scroll.setPreferredSize(new Dimension(400,200));
+		
+		for (WMSInfo i : WMSPlugin.wmsList) {
+			oldValues.put(i.prefid, i);
+			model.addRow(new String[]{Integer.toString(i.prefid), i.name, i.url});
+			if (i.prefid > highestIdUsed) highestIdUsed = i.prefid;
+		}
+		
+		JButton add = new JButton(tr("Add"));
+		p.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL));
+		p.add(add, GBC.std().insets(0,5,0,0));
+		add.addActionListener(new ActionListener(){
+			public void actionPerformed(ActionEvent e) {
+				JPanel p = new JPanel(new GridBagLayout());
+				p.add(new JLabel("Menu Name"), GBC.std().insets(0,0,5,0));
+				JTextField key = new JTextField(10);
+				JTextField value = new JTextField(10);
+				p.add(key, GBC.eop().insets(5,0,0,0).fill(GBC.HORIZONTAL));
+				p.add(new JLabel("WMS URL"), GBC.std().insets(0,0,5,0));
+				p.add(value, GBC.eol().insets(5,0,0,0).fill(GBC.HORIZONTAL));
+				int answer = JOptionPane.showConfirmDialog(gui, p, tr("Enter a menu name and WMS URL"), JOptionPane.OK_CANCEL_OPTION);
+				if (answer == JOptionPane.OK_OPTION) {
+					highestIdUsed++;
+					model.addRow(new String[]{Integer.toString(highestIdUsed), key.getText(), value.getText()});
+				}
+			}
+		});
+				
+		JButton delete = new JButton(tr("Delete"));
+		p.add(delete, GBC.std().insets(0,5,0,0));
+		delete.addActionListener(new ActionListener(){
+			public void actionPerformed(ActionEvent e) {
+				if (list.getSelectedRowCount() == 0) {
+					JOptionPane.showMessageDialog(gui, tr("Please select the row to delete."));
+					return;
+				}
+				while (list.getSelectedRow() != -1)
+					model.removeRow(list.getSelectedRow());
+			}
+		});
+	}
+	
+	public void ok() {
+		boolean change = false;
+		for (int i = 0; i < model.getRowCount(); ++i) {
+			int id = Integer.parseInt(model.getValueAt(i, 0).toString());
+			String name = model.getValueAt(i,1).toString();
+			String url = model.getValueAt(i,2).toString();
+			
+			WMSInfo origValue = oldValues.get(id);
+			if (origValue == null) {
+				new WMSInfo(name, url, id).save();
+				change = true;
+			} else {
+				if (origValue.name.equals(name) && origValue.url.equals(url)) {
+					// no change
+				} else {
+					origValue.name = name; 
+					origValue.url = url;
+					origValue.save();
+					change = true;
+				}
+				oldValues.remove(id);
+			}
+		}
+		
+		// josm doesn't seem to give us an option to delete preferences, 
+		// we can only overwrite them with empty strings...
+		for (WMSInfo i : oldValues.values()) {
+			i.url = ""; 
+			i.name = "";
+			i.save();
+			change = true;
+		}
+		
+		if (change) WMSPlugin.refreshMenu();
+	}
+	
+}
+
