[josm-plugins] 136/369: Imported Upstream version 0.0.svn21666

Bas Couwenberg sebastic at xs4all.nl
Sat Oct 18 12:03:35 UTC 2014


This is an automated email from the git hooks/post-receive script.

sebastic-guest pushed a commit to branch master
in repository josm-plugins.

commit 375f232adc72d7d1c46429aa04c934d1896e6cdb
Author: David Paleino <dapal at debian.org>
Date:   Sat Jun 12 13:13:11 2010 +0200

    Imported Upstream version 0.0.svn21666
---
 cadastre-fr/src/cadastre_fr/CacheControl.java      |   32 +-
 cadastre-fr/src/cadastre_fr/CadastreInterface.java |   17 +-
 cadastre-fr/src/cadastre_fr/CadastrePlugin.java    |    2 +
 .../src/cadastre_fr/CadastrePreferenceSetting.java |    4 +-
 .../src/cadastre_fr/DownloadWMSPlanImage.java      |    3 +-
 .../src/cadastre_fr/MenuActionGrabPlanImage.java   |  124 +-
 .../src/cadastre_fr/MenuActionNewLocation.java     |   45 +-
 cadastre-fr/src/cadastre_fr/WMSLayer.java          |   10 +
 .../plugin/colorscheme/ColorSchemePreference.java  |   14 +-
 editgpx/.settings/org.eclipse.jdt.ui.prefs         |   54 +
 .../josm/plugins/editgpx/EditGpxLayer.java         |  115 +-
 .../josm/plugins/editgpx/EditGpxMode.java          |  279 ++--
 .../josm/plugins/editgpx/EditGpxPlugin.java        |  116 +-
 .../josm/plugins/editgpx/GPXLayerImportAction.java |  191 ++-
 .../josm/plugins/editgpx/data/EditGpxData.java     |   62 +
 .../josm/plugins/editgpx/data/EditGpxTrack.java    |   57 +
 .../plugins/editgpx/data/EditGpxTrackSegment.java  |   44 +
 .../josm/plugins/editgpx/data/EditGpxWayPoint.java |   39 +
 livegps/src/livegps/LiveGpsAcquirer.java           |  293 ++--
 livegps/src/org/json/JSONArray.java                |  918 ++++++++++++
 livegps/src/org/json/JSONException.java            |   31 +
 livegps/src/org/json/JSONObject.java               | 1584 ++++++++++++++++++++
 livegps/src/org/json/JSONString.java               |   18 +
 livegps/src/org/json/JSONTokener.java              |  435 ++++++
 .../plugins/measurement/MeasurementDialog.java     |   67 +-
 .../josm/plugins/slippymap/SlippyMapLayer.java     |   18 +-
 svn-info.xml                                       |    8 +-
 wmsplugin/sources.cfg                              |    2 +-
 wmsplugin/src/wmsplugin/WMSLayer.java              |    2 +-
 wmsplugin/webkit-image-gtk.c                       |   45 +-
 30 files changed, 3971 insertions(+), 658 deletions(-)

diff --git a/cadastre-fr/src/cadastre_fr/CacheControl.java b/cadastre-fr/src/cadastre_fr/CacheControl.java
index 24223fe..84ca7f9 100644
--- a/cadastre-fr/src/cadastre_fr/CacheControl.java
+++ b/cadastre-fr/src/cadastre_fr/CacheControl.java
@@ -72,22 +72,24 @@ public class CacheControl implements Runnable {
     }
 
     private void checkDirSize(File path) {
-        long size = 0;
-        long oldestFileDate = Long.MAX_VALUE;
-        int oldestFile = 0;
-        File[] files = path.listFiles();
-        for (int i = 0; i < files.length; i++) {
-            size += files[i].length();
-            if (files[i].lastModified() <  oldestFileDate) {
-                oldestFile = i;
-                oldestFileDate = files[i].lastModified();
+        if (cacheSize != 0) {
+            long size = 0;
+            long oldestFileDate = Long.MAX_VALUE;
+            int oldestFile = 0;
+            File[] files = path.listFiles();
+            for (int i = 0; i < files.length; i++) {
+                size += files[i].length();
+                if (files[i].lastModified() <  oldestFileDate) {
+                    oldestFile = i;
+                    oldestFileDate = files[i].lastModified();
+                }
+            }
+            if (size > (long)cacheSize*1024*1024) {
+                System.out.println("Delete oldest file  \""+ files[oldestFile].getName()
+                        + "\" in cache dir to stay under the limit of " + cacheSize + " MB.");
+                files[oldestFile].delete();
+                checkDirSize(path);
             }
-        }
-        if (size > cacheSize*1024*1024) {
-            System.out.println("Delete oldest file  \""+ files[oldestFile].getName()
-                    + "\" in cache dir to stay under the limit of " + cacheSize + " MB.");
-            files[oldestFile].delete();
-            checkDirSize(path);
         }
     }
 
diff --git a/cadastre-fr/src/cadastre_fr/CadastreInterface.java b/cadastre-fr/src/cadastre_fr/CadastreInterface.java
index d48c1db..58484fd 100644
--- a/cadastre-fr/src/cadastre_fr/CadastreInterface.java
+++ b/cadastre-fr/src/cadastre_fr/CadastreInterface.java
@@ -151,6 +151,7 @@ public class CadastreInterface {
     public void resetInterfaceRefIfNewLayer(String newWMSLayerName) {
         if (!newWMSLayerName.equals(lastWMSLayerName)) {
             interfaceRef = null;
+            cookie = null; // new since WMS server requires that we come back to the main form
         }
     }
 
@@ -174,7 +175,15 @@ public class CadastreInterface {
                 if (listOfCommunes.size() > 1) {
                     // commune unknown, prompt the list of communes from
                     // server and try with codeCommune
-                    wmsLayer.setCodeCommune(selectCommuneDialog());
+                    String selected = selectMunicipalityDialog(wmsLayer);
+                    if (selected != null) {
+                        String newCodeCommune = selected.substring(1, selected.indexOf(">")-2);
+                        String newLocation = selected.substring(selected.indexOf(">")+1, selected.lastIndexOf(" - "));
+                        wmsLayer.setCodeCommune(newCodeCommune);
+                        wmsLayer.setLocation(newLocation);
+                        Main.pref.put("cadastrewms.codeCommune", newCodeCommune);
+                        Main.pref.put("cadastrewms.location", newLocation);
+                    }
                     checkLayerDuplicates(wmsLayer);
                     interfaceRef = postForm(wmsLayer, wmsLayer.getCodeCommune());
                 }
@@ -257,6 +266,7 @@ public class CadastreInterface {
                 content += "&codeCommune=" + codeCommune;
             }
             content += "&codeDepartement=";
+            content += wmsLayer.getDepartement();
             content += "&nbResultatParPage=10";
             content += "&x=0&y=0";
             searchFormURL = new URL(baseURL + "/scpc/rechercherPlan.do");
@@ -379,7 +389,7 @@ public class CadastreInterface {
         }
     }
     
-    private String selectCommuneDialog() {
+    private String selectMunicipalityDialog(WMSLayer wmsLayer) {
         JPanel p = new JPanel(new GridBagLayout());
         String[] communeList = new String[listOfCommunes.size() + 1];
         communeList[0] = tr("Choose from...");
@@ -399,8 +409,7 @@ public class CadastreInterface {
         // till here
         if (!Integer.valueOf(JOptionPane.OK_OPTION).equals(pane.getValue()))
             return null;
-        String result = listOfCommunes.elementAt(inputCommuneList.getSelectedIndex()-1);
-        return result.substring(1, result.indexOf(">")-2);
+        return listOfCommunes.elementAt(inputCommuneList.getSelectedIndex()-1);
     }
 
     private int selectFeuilleDialog() {
diff --git a/cadastre-fr/src/cadastre_fr/CadastrePlugin.java b/cadastre-fr/src/cadastre_fr/CadastrePlugin.java
index 6d338fb..68bf39a 100644
--- a/cadastre-fr/src/cadastre_fr/CadastrePlugin.java
+++ b/cadastre-fr/src/cadastre_fr/CadastrePlugin.java
@@ -112,6 +112,8 @@ import org.openstreetmap.josm.data.projection.*;
  *                 - download cancellation improved
  *                 - last deployment for Java1.5 compatibility
  * 2.0 xx-xxx-xxxx - update projection for "La Reunion" departement to RGR92, UTM40S. 
+ *                 - add 'departement' as option in the municipality selection
+ *                 - fixed bug in cache directory size control (and disabled by default)
  */
 public class CadastrePlugin extends Plugin {
     static String VERSION = "2.0";
diff --git a/cadastre-fr/src/cadastre_fr/CadastrePreferenceSetting.java b/cadastre-fr/src/cadastre_fr/CadastrePreferenceSetting.java
index 01a60d6..ca4c30b 100644
--- a/cadastre-fr/src/cadastre_fr/CadastrePreferenceSetting.java
+++ b/cadastre-fr/src/cadastre_fr/CadastrePreferenceSetting.java
@@ -56,7 +56,7 @@ public class CadastrePreferenceSetting implements PreferenceSetting {
 
     private JRadioButton crosspiece4 = new JRadioButton("100m");
 
-    private JCheckBox autoFirstLayer = new JCheckBox(tr("Automaticly select first WMS layer when grabing if multiple layers exist."));
+    private JCheckBox autoFirstLayer = new JCheckBox(tr("Select first WMS layer in list."));
     
     private JRadioButton grabRes1 = new JRadioButton("high");
 
@@ -79,7 +79,7 @@ public class CadastrePreferenceSetting implements PreferenceSetting {
 
     private JCheckBox enableCache = new JCheckBox(tr("Enable automatic caching."));
 
-    static final int DEFAULT_CACHE_SIZE = 500;
+    static final int DEFAULT_CACHE_SIZE = 0; // disabled by default
     JLabel jLabelCacheSize = new JLabel(tr("Max. cache size (in MB)"));
     private JTextField cacheSize = new JTextField(20);
     
diff --git a/cadastre-fr/src/cadastre_fr/DownloadWMSPlanImage.java b/cadastre-fr/src/cadastre_fr/DownloadWMSPlanImage.java
index 648eb15..c31c982 100644
--- a/cadastre-fr/src/cadastre_fr/DownloadWMSPlanImage.java
+++ b/cadastre-fr/src/cadastre_fr/DownloadWMSPlanImage.java
@@ -19,7 +19,7 @@ public class DownloadWMSPlanImage {
     private Future<Task> task = null;
     private WMSLayer wmsLayer;
     private Bounds bounds;
-    private boolean dontGeoreference = false;
+    private static boolean dontGeoreference = false;
     private static String errorMessage;
     
     private class Task extends PleaseWaitRunnable {
@@ -106,6 +106,7 @@ public class DownloadWMSPlanImage {
     public void download(WMSLayer wmsLayer) {
         MapView mv = Main.map.mapView;
         Bounds bounds = new Bounds(mv.getLatLon(0, mv.getHeight()), mv.getLatLon(mv.getWidth(), 0));
+        dontGeoreference = false;
 
         //Main.worker.execute(new DownloadWMSPlanImage(wmsLayer, bounds));
         Task t = new Task(wmsLayer, bounds);
diff --git a/cadastre-fr/src/cadastre_fr/MenuActionGrabPlanImage.java b/cadastre-fr/src/cadastre_fr/MenuActionGrabPlanImage.java
index 447f4fb..96b79a1 100644
--- a/cadastre-fr/src/cadastre_fr/MenuActionGrabPlanImage.java
+++ b/cadastre-fr/src/cadastre_fr/MenuActionGrabPlanImage.java
@@ -44,6 +44,7 @@ public class MenuActionGrabPlanImage extends JosmAction implements Runnable, Mou
     private EastNorth georefpoint1;
     private EastNorth georefpoint2;
     private boolean ignoreMouseClick = false;
+    private boolean clickOnTheMap = false;
     
     /**
      * The time which needs to pass between two clicks during georeferencing, in milliseconds
@@ -62,7 +63,10 @@ public class MenuActionGrabPlanImage extends JosmAction implements Runnable, Mou
 
     public void actionInterrupted() {
         actionCompleted();
-        wmsLayer = null;
+        if (wmsLayer != null) {
+            Main.map.mapView.removeLayer(wmsLayer);
+            wmsLayer = null;
+        }
     }
 
     @Override
@@ -99,7 +103,12 @@ public class MenuActionGrabPlanImage extends JosmAction implements Runnable, Mou
     public void run() {
         // wait until plan image is fully loaded and joined into one single image
         boolean loadedFromCache = downloadWMSPlanImage.waitFinished();
-        if (wmsLayer.images.size() == 1 && !loadedFromCache) {
+        if (loadedFromCache) {
+            Main.map.repaint();
+        } else if (wmsLayer.images.size() == 0) {
+            // action cancelled or image loaded from cache (and already georeferenced)
+            actionInterrupted();
+        } else {
             int reply = JOptionPane.CANCEL_OPTION;
             if (wmsLayer.isAlreadyGeoreferenced()) {
                 reply = JOptionPane.showConfirmDialog(null,
@@ -118,8 +127,7 @@ public class MenuActionGrabPlanImage extends JosmAction implements Runnable, Mou
                 else
                     startGeoreferencing();
             }
-        } else // action cancelled or image loaded from cache (and already georeferenced)
-            Main.map.repaint();
+        }
     }
 
     public void mouseClicked(MouseEvent e) {
@@ -132,31 +140,36 @@ public class MenuActionGrabPlanImage extends JosmAction implements Runnable, Mou
         if (e.getButton() != MouseEvent.BUTTON1)
             return;
         if (ignoreMouseClick) return; // In case we are currently just allowing zooming to read lambert coordinates
-        countMouseClicked++;
         EastNorth ea = Main.proj.latlon2eastNorth(Main.map.mapView.getLatLon(e.getX(), e.getY()));
         System.out.println("clic:"+countMouseClicked+" ,"+ea+", mode:"+mode);
-        // ignore clicks outside the image
-        if (ea.east() < wmsLayer.images.get(0).min.east() || ea.east() > wmsLayer.images.get(0).max.east()
-                || ea.north() < wmsLayer.images.get(0).min.north() || ea.north() > wmsLayer.images.get(0).max.north())
-            return;
-        if (mode == cGetCorners) {
-            if (countMouseClicked == 1) {
-                ea1 = ea;
-                continueCropping();
-            }
-            if (countMouseClicked == 2) {
-                wmsLayer.cropImage(ea1, ea);
-                Main.map.mapView.repaint();
-                startGeoreferencing();
-            }
-        } else if (mode == cGetLambertCrosspieces) {
-            if (countMouseClicked == 1) {
-                ea1 = ea;
-                inputLambertPosition(); // This will automatically asks for second point and continue the georeferencing
-            }
-            if (countMouseClicked == 2) {
-                ea2 = ea;
-                inputLambertPosition(); // This will automatically ends the georeferencing
+        if (clickOnTheMap) {
+            clickOnTheMap = false;
+            handleNewCoordinates(ea.east(), ea.north());
+        } else {
+            countMouseClicked++;
+            // ignore clicks outside the image
+            if (ea.east() < wmsLayer.images.get(0).min.east() || ea.east() > wmsLayer.images.get(0).max.east()
+                    || ea.north() < wmsLayer.images.get(0).min.north() || ea.north() > wmsLayer.images.get(0).max.north())
+                return;
+            if (mode == cGetCorners) {
+                if (countMouseClicked == 1) {
+                    ea1 = ea;
+                    continueCropping();
+                }
+                if (countMouseClicked == 2) {
+                    wmsLayer.cropImage(ea1, ea);
+                    Main.map.mapView.repaint();
+                    startGeoreferencing();
+                }
+            } else if (mode == cGetLambertCrosspieces) {
+                if (countMouseClicked == 1) {
+                    ea1 = ea;
+                    inputLambertPosition(); // This will automatically asks for second point and continue the georeferencing
+                }
+                if (countMouseClicked == 2) {
+                    ea2 = ea;
+                    inputLambertPosition(); // This will automatically ends the georeferencing
+                }
             }
         }
     }
@@ -248,6 +261,8 @@ public class MenuActionGrabPlanImage extends JosmAction implements Runnable, Mou
         wmsLayer.saveNewCache();
         Main.map.mapView.repaint();
         actionCompleted();
+        clickOnTheMap = false;
+        ignoreMouseClick = false;
     }
 
     /**
@@ -261,14 +276,14 @@ public class MenuActionGrabPlanImage extends JosmAction implements Runnable, Mou
         				"or just retry "+action+" ?"), "",
         		JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE,
         		null, options, options[0]);
+        countMouseClicked = 0;
         if (selectedValue == 0) { // "Cancel"
         	// remove layer
         	Main.map.mapView.removeLayer(wmsLayer);
             wmsLayer = null;
             Main.map.mapView.removeMouseListener(this);
-        	return false;
-        } else
-            countMouseClicked = 0;
+            return false;
+        }
         return true;
     }
 
@@ -288,9 +303,12 @@ public class MenuActionGrabPlanImage extends JosmAction implements Runnable, Mou
         p.add(inputEast, GBC.eol().fill(GBC.HORIZONTAL).insets(10, 5, 0, 5));
         p.add(labelNorth, GBC.std().insets(0, 0, 10, 0));
         p.add(inputNorth, GBC.eol().fill(GBC.HORIZONTAL).insets(10, 5, 0, 5));
+        final Object[] options = {tr("OK"),
+                tr("Cancel"),
+                tr("I use the mouse")};
         final JOptionPane pane = new JOptionPane(p,
-                JOptionPane.INFORMATION_MESSAGE, JOptionPane.OK_CANCEL_OPTION,
-                null);
+                JOptionPane.INFORMATION_MESSAGE, JOptionPane.YES_NO_CANCEL_OPTION,
+                null, options, options[0]);
         String number;
         if (countMouseClicked == 1)
             number = "first";
@@ -307,25 +325,27 @@ public class MenuActionGrabPlanImage extends JosmAction implements Runnable, Mou
             public void propertyChange(PropertyChangeEvent evt) {
                 if (JOptionPane.VALUE_PROPERTY.equals(evt.getPropertyName())) {
                     ignoreMouseClick = false;
-                    if (!Integer.valueOf(JOptionPane.OK_OPTION).equals(
-                            pane.getValue())) {
+                    // Cancel
+                    if (pane.getValue().equals(options[1])) {
                         if (canceledOrRestartCurrAction("georeferencing"))
                             startGeoreferencing();
                     }
-                    if (inputEast.getText().length() != 0
-                            && inputNorth.getText().length() != 0) {
-                        try {
-                            double e = Double.parseDouble(inputEast.getText());
-                            double n = Double.parseDouble(inputNorth.getText());
-                            if (countMouseClicked == 1) {
-                                georefpoint1 = new EastNorth(e, n);
-                                continueGeoreferencing();
-                            } else {
-                                georefpoint2 = new EastNorth(e, n);
-                                endGeoreferencing();
+                    // Click on the map
+                    if (pane.getValue().equals(options[2])) {
+                        clickOnTheMap = true;
+                    } else {
+                    // OK (coordinates manually entered)
+                        clickOnTheMap = false;
+                        if (inputEast.getText().length() != 0
+                                && inputNorth.getText().length() != 0) {
+                            double e, n;
+                            try {
+                                e = Double.parseDouble(inputEast.getText());
+                                n = Double.parseDouble(inputNorth.getText());
+                            } catch (NumberFormatException ex) {
+                                return;
                             }
-                        } catch (NumberFormatException e) {
-                            return;
+                            handleNewCoordinates(e, n);
                         }
                     }
                 }
@@ -333,6 +353,16 @@ public class MenuActionGrabPlanImage extends JosmAction implements Runnable, Mou
         });
     }
 
+    private void handleNewCoordinates(double e, double n) {
+        if (countMouseClicked == 1) {
+            georefpoint1 = new EastNorth(e, n);
+            continueGeoreferencing();
+        } else {
+            georefpoint2 = new EastNorth(e, n);
+            endGeoreferencing();
+        }
+    }
+
     /**
      * Use point org1 as anchor for scale, then move org1 to dst1, then rotate org2 on dst2
      * around org1/dst1 anchor
diff --git a/cadastre-fr/src/cadastre_fr/MenuActionNewLocation.java b/cadastre-fr/src/cadastre_fr/MenuActionNewLocation.java
index 9ddc3f1..f740da3 100644
--- a/cadastre-fr/src/cadastre_fr/MenuActionNewLocation.java
+++ b/cadastre-fr/src/cadastre_fr/MenuActionNewLocation.java
@@ -7,6 +7,7 @@ import java.awt.GridBagLayout;
 import java.awt.event.ActionEvent;
 import java.util.ArrayList;
 
+import javax.swing.JComboBox;
 import javax.swing.JLabel;
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
@@ -23,6 +24,31 @@ import org.openstreetmap.josm.tools.GBC;
 public class MenuActionNewLocation extends JosmAction {
 
     private static final long serialVersionUID = 1L;
+    
+    private static final String departements[] = {
+        "", tr("(optional)"),
+        "001", "01 - Ain",                 "002", "02 - Aisne",              "003", "03 - Allier",                "004", "04 - Alpes de Haute-Provence", "005", "05 - Hautes-Alpes",
+        "006", "06 - Alpes-Maritimes",     "007", "07 - Ard\u00eache",       "008", "08 - Ardennes",              "009", "09 - Ari\u00e8ge",             "010", "10 - Aube",
+        "011", "11 - Aude",                "012", "12 - Aveyron",            "013", "13 - Bouches-du-Rh\u00f4ne", "014", "14 - Calvados",                "015", "15 - Cantal", 
+        "016", "16 - Charente",            "017", "17 - Charente-Maritime",  "018", "18 - Cher",                  "019", "19 - Corr\u00e8ze", 
+        "02A", "2A - Corse-du-Sud",        "02B", "2B - Haute-Corse", 
+        "021", "21 - C\u00f4te-d'Or",      "022", "22 - C\u00f4tes d'Armor", "023", "23 - Creuse",                "024", "24 - Dordogne",                "025", "25 - Doubs", 
+        "026", "26 - Dr\u00f4me",          "027", "27 - Eure",               "028", "28 - Eure-et-Loir",          "029", "29 - Finist\u00e8re",          "030", "30 - Gard",
+        "031", "31 - Haute-Garonne",       "032", "32 - Gers",               "033", "33 - Gironde",               "034", "34 - H\u00e9rault",            "035", "35 - Ille-et-Vilaine", 
+        "036", "36 - Indre",               "037", "37 - Indre-et-Loire",     "038", "38 - Is\u00e8re",            "039", "39 - Jura",                    "040", "40 - Landes",
+        "041", "41 - Loir-et-Cher",        "042", "42 - Loire",              "043", "43 - Haute-Loire",           "044", "44 - Loire-Atlantique",        "045", "45 - Loiret", 
+        "046", "46 - Lot",                 "047", "47 - Lot-et-Garonne",     "048", "48 - Loz\u00e8re",           "049", "49 - Maine-et-Loire",          "050", "50 - Manche",
+        "051", "51 - Marne",               "052", "52 - Haute-Marne",        "053", "53 - Mayenne",               "054", "54 - Meurthe-et-Moselle",      "055", "55 - Meuse", 
+        "056", "56 - Morbihan",            "057", "57 - Moselle",            "058", "58 - Ni\u00e8vre",           "059", "59 - Nord",                    "060", "60 - Oise",
+        "061", "61 - Orne",                "062", "62 - Pas-de-Calais",      "063", "63 - Puy-de-D\u00f4me",      "064", "64 - Pyr\u00e9n\u00e9es-Atlantiques", "065", "65 - Hautes-Pyr\u00e9n\u00e9es", 
+        "066", "66 - Pyr\u00e9n\u00e9es-Orientales", "067", "67 - Bas-Rhin", "068", "68 - Haut-Rhin",             "069", "69 - Rh\u00f4ne",              "070", "70 - Haute-Sa\u00f4ne",
+        "071", "71 - Sa\u00f4ne-et-Loire", "072", "72 - Sarthe",             "073", "73 - Savoie",                "074", "74 - Haute-Savoie",            "075", "75 - Paris", 
+        "076", "76 - Seine-Maritime",      "077", "77 - Seine-et-Marne",     "078", "78 - Yvelines",              "079", "79 - Deux-S\u00e8vres",        "080", "80 - Somme",
+        "081", "81 - Tarn",                "082", "82 - Tarn-et-Garonne",    "083", "83 - Var",                   "084", "84 - Vaucluse",                "085", "85 - Vend\u00e9e", 
+        "086", "86 - Vienne",              "087", "87 - Haute-Vienne",       "088", "88 - Vosges",                "089", "89 - Yonne",                   "090", "90 - Territoire de Belfort",
+        "091", "91 - Essonne",             "092", "92 - Hauts-de-Seine",     "093", "93 - Seine-Saint-Denis",     "094", "94 - Val-de-Marne",            "095", "95 - Val-d'Oise", 
+        "971", "971 - Guadeloupe",         "972", "972 - Martinique",        "973", "973 - Guyane",               "974", "974 - R\u00e9union"
+    };
 
     public MenuActionNewLocation() {
         super(tr("Change location"), "cadastre_small", tr("Set a new location for the next request"), null, false);
@@ -45,10 +71,22 @@ public class MenuActionNewLocation extends JosmAction {
         final JTextField inputTown = new JTextField( Main.pref.get("cadastrewms.location") );
         inputTown.setToolTipText(tr("<html>Enter the town,village or city name.<br>"
                 + "Use the syntax and punctuation known by www.cadastre.gouv.fr .</html>"));
-
+        JLabel labelDepartement =  new JLabel(tr("Departement"));
+        final JComboBox inputDepartement = new JComboBox();
+        for (int i=1; i<departements.length; i+=2) {
+            inputDepartement.addItem(departements[i]);
+        }
+        inputDepartement.setToolTipText(tr("<html>Departement number (optional)</html>"));
+        if (!Main.pref.get("cadastrewms.codeDepartement").equals("")) {
+            for (int i=0; i < departements.length; i=i+2)
+                if (departements[i].equals(Main.pref.get("cadastrewms.codeDepartement")))
+                    inputDepartement.setSelectedIndex(i/2);        
+        }
         p.add(labelSectionNewLocation, GBC.eol());
         p.add(labelLocation, GBC.std().insets(10, 0, 0, 0));
         p.add(inputTown, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5));
+        p.add(labelDepartement, GBC.std().insets(10, 0, 0, 0));
+        p.add(inputDepartement, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5));
         JOptionPane pane = new JOptionPane(p, JOptionPane.INFORMATION_MESSAGE, JOptionPane.OK_CANCEL_OPTION, null) {
             private static final long serialVersionUID = 1L;
 
@@ -65,12 +103,14 @@ public class MenuActionNewLocation extends JosmAction {
         WMSLayer wmsLayer = null;
         if (!inputTown.getText().equals("")) {
             location = inputTown.getText().toUpperCase();
+            codeDepartement = departements[inputDepartement.getSelectedIndex()*2];
             changeInterface = true;
             Main.pref.put("cadastrewms.location", location);
             Main.pref.put("cadastrewms.codeCommune", codeCommune);
+            Main.pref.put("cadastrewms.codeDepartement", codeDepartement);
             if (Main.map != null) {
                 for (Layer l : Main.map.mapView.getAllLayers()) {
-                    if (l instanceof WMSLayer && l.getName().equalsIgnoreCase(location + codeDepartement)) {
+                    if (l instanceof WMSLayer && l.getName().equalsIgnoreCase(location)) {
                         return null;
                     }
                 }
@@ -84,6 +124,7 @@ public class MenuActionNewLocation extends JosmAction {
             else if (Main.proj instanceof UTM_France_DOM)
                 zone = ((UTM_France_DOM)Main.proj).getCurrentGeodesic();
             wmsLayer = new WMSLayer(location, codeCommune, zone);
+            wmsLayer.setDepartement(codeDepartement);
             CadastrePlugin.addWMSLayer(wmsLayer);
             System.out.println("Add new layer with Location:" + inputTown.getText());
         } else if (existingLayers != null && existingLayers.size() > 0 && Main.map.mapView.getActiveLayer() instanceof WMSLayer) {
diff --git a/cadastre-fr/src/cadastre_fr/WMSLayer.java b/cadastre-fr/src/cadastre_fr/WMSLayer.java
index d9127e9..b6bebaf 100644
--- a/cadastre-fr/src/cadastre_fr/WMSLayer.java
+++ b/cadastre-fr/src/cadastre_fr/WMSLayer.java
@@ -68,6 +68,8 @@ public class WMSLayer extends Layer implements ImageObserver {
     private CacheControl cacheControl = null;
 
     private String location = "";
+    
+    private String departement = "";
 
     private String codeCommune = "";
 
@@ -378,6 +380,14 @@ public class WMSLayer extends Layer implements ImageObserver {
         setName(rebuildName());
     }
 
+    public String getDepartement() {
+        return departement;
+    }
+
+    public void setDepartement(String departement) {
+        this.departement = departement;
+    }
+
     public String getCodeCommune() {
         return codeCommune;
     }
diff --git a/colorscheme/src/at/dallermassl/josm/plugin/colorscheme/ColorSchemePreference.java b/colorscheme/src/at/dallermassl/josm/plugin/colorscheme/ColorSchemePreference.java
index bcb784d..0a2c493 100644
--- a/colorscheme/src/at/dallermassl/josm/plugin/colorscheme/ColorSchemePreference.java
+++ b/colorscheme/src/at/dallermassl/josm/plugin/colorscheme/ColorSchemePreference.java
@@ -16,6 +16,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.StringTokenizer;
 
+import javax.swing.BorderFactory;
 import javax.swing.Box;
 import javax.swing.DefaultListModel;
 import javax.swing.JButton;
@@ -52,6 +53,9 @@ public class ColorSchemePreference implements PreferenceSetting {
      * @see org.openstreetmap.josm.gui.preferences.PreferenceSetting#addGui(org.openstreetmap.josm.gui.preferences.PreferenceDialog)
      */
     public void addGui(final PreferenceTabbedPane gui) {
+        JPanel panel = new JPanel(new GridBagLayout());
+        panel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+
         Map<String, String> colorMap = Main.pref.getAllPrefix(PREF_KEY_COLOR_PREFIX);
         colorKeys = new ArrayList<String>(colorMap.keySet());
         Collections.sort(colorKeys);
@@ -109,14 +113,18 @@ public class ColorSchemePreference implements PreferenceSetting {
         addScheme.setToolTipText(tr("Use the current colors as a new color scheme."));
         deleteScheme.setToolTipText(tr("Delete the selected scheme from the list."));
 
-        gui.map.add(new JLabel(tr("Color Schemes")), GBC.eol().insets(0,5,0,0));
-        gui.map.add(new JScrollPane(schemesList), GBC.eol().fill(GBC.BOTH));
+        panel.add(new JLabel(tr("Color Schemes")), GBC.eol().insets(0,5,0,0));
+        panel.add(new JScrollPane(schemesList), GBC.eol().fill(GBC.BOTH));
         JPanel buttonPanel = new JPanel(new GridBagLayout());
-        gui.map.add(buttonPanel, GBC.eol().fill(GBC.HORIZONTAL));
+        panel.add(buttonPanel, GBC.eol().fill(GBC.HORIZONTAL));
         buttonPanel.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL));
         buttonPanel.add(useScheme, GBC.std().insets(0,5,5,0));
         buttonPanel.add(addScheme, GBC.std().insets(0,5,5,0));
         buttonPanel.add(deleteScheme, GBC.std().insets(0,5,5,0));
+
+        JScrollPane scrollpane = new JScrollPane(panel);
+        scrollpane.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 ));
+        gui.displaycontent.addTab(tr("Color Schemes"), scrollpane);
     }
 
     /**
diff --git a/editgpx/.settings/org.eclipse.jdt.ui.prefs b/editgpx/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..4a3398d
--- /dev/null
+++ b/editgpx/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,54 @@
+#Tue May 25 21:33:38 CEST 2010
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=true
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=false
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=true
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxLayer.java b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxLayer.java
index 4d4f56f..6f49c7c 100644
--- a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxLayer.java
+++ b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxLayer.java
@@ -11,14 +11,6 @@ import java.awt.Graphics2D;
 import java.awt.Point;
 import java.awt.Toolkit;
 import java.awt.event.ActionEvent;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
 
 import javax.swing.AbstractAction;
 import javax.swing.Icon;
@@ -29,31 +21,29 @@ import javax.swing.JSeparator;
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.gpx.GpxData;
-import org.openstreetmap.josm.data.gpx.ImmutableGpxTrack;
-import org.openstreetmap.josm.data.gpx.WayPoint;
-import org.openstreetmap.josm.data.osm.DataSet;
-import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
 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.GpxLayer;
 import org.openstreetmap.josm.gui.layer.Layer;
-import org.openstreetmap.josm.tools.DateUtils;
+import org.openstreetmap.josm.plugins.editgpx.data.EditGpxData;
+import org.openstreetmap.josm.plugins.editgpx.data.EditGpxTrack;
+import org.openstreetmap.josm.plugins.editgpx.data.EditGpxTrackSegment;
+import org.openstreetmap.josm.plugins.editgpx.data.EditGpxWayPoint;
 import org.openstreetmap.josm.tools.ImageProvider;
 
 
 public class EditGpxLayer extends Layer {
 
 	private static Icon icon = new ImageIcon(Toolkit.getDefaultToolkit().createImage(EditGpxPlugin.class.getResource("/images/editgpx_layer.png")));
-	private DataSet dataSet;
+	public final EditGpxData data;
 	private GPXLayerImportAction layerImport;
 
-	public EditGpxLayer(String str, DataSet ds) {
+	public EditGpxLayer(String str, EditGpxData gpxData) {
 		super(str);
-		dataSet = ds;
-		layerImport = new GPXLayerImportAction(dataSet);
+		data = gpxData;
+		layerImport = new GPXLayerImportAction(data);
 	}
 
 	/**
@@ -62,7 +52,7 @@ public class EditGpxLayer extends Layer {
 	 */
 	public void initializeImport() {
 		try {
-			if(dataSet.getNodes().isEmpty() ) {
+			if(data.isEmpty()) {
 				layerImport.activateImport();
 			}
 		} catch (Exception e) {
@@ -116,10 +106,14 @@ public class EditGpxLayer extends Layer {
 
 		//don't iterate through dataSet whiling making changes
 		synchronized(layerImport.importing) {
-			for(Node n: dataSet.getNodes()) {
-				if (!n.isDeleted()) {
-					Point pnt = Main.map.mapView.getPoint(n.getEastNorth());
-					g.drawOval(pnt.x - 2, pnt.y - 2, 4, 4);
+			for (EditGpxTrack track: data.getTracks()) {
+				for (EditGpxTrackSegment segment: track.getSegments()) {
+					for (EditGpxWayPoint wayPoint: segment.getWayPoints()) {
+						if (!wayPoint.isDeleted()) {
+							Point pnt = Main.map.mapView.getPoint(wayPoint.getCoor().getEastNorth());
+							g.drawOval(pnt.x - 2, pnt.y - 2, 4, 4);
+						}
+					}
 				}
 			}
 		}
@@ -144,76 +138,7 @@ public class EditGpxLayer extends Layer {
 	 * @return GPXData
 	 */
 	private GpxData toGpxData(boolean anonTime) {
-		GpxData gpxData = new GpxData();
-		HashSet<Node> doneNodes = new HashSet<Node>();
-		//add all ways
-		for (Way w : dataSet.getWays()) {
-			if (w.isIncomplete() || w.isDeleted()) continue;
-			List<Collection<WayPoint>> segments = new ArrayList<Collection<WayPoint>>();
-
-			List<WayPoint> trkseg = null;
-			for (Node n : w.getNodes()) {
-				if (n.isIncomplete() || n.isDeleted()) {
-					trkseg = null;
-					continue;
-				}
-
-				Date tstamp = n.getTimestamp();
-
-				if (trkseg == null) {
-					trkseg = new ArrayList<WayPoint>();
-					segments.add(trkseg);
-				}
-				doneNodes.add(n);
-
-				WayPoint wpt = new WayPoint(n.getCoor());
-				if (anonTime) {
-					wpt.attr.put("time", "1970-01-01T00:00:00Z");
-				} else {
-					wpt.attr.put("time", DateUtils.fromDate(tstamp));
-				}
-				wpt.setTime();
-
-				trkseg.add(wpt);
-			}
-
-			// Do not create empty segments
-			for (Iterator<Collection<WayPoint>>  segIt = segments.iterator(); segIt.hasNext(); ) {
-				if (segIt.next().isEmpty()) {
-					segIt.remove();
-				}
-			}
-
-			Map<String, Object> trkAttributes = new HashMap<String, Object>();
-			if (w.get("name") != null) {
-				trkAttributes.put("name", w.get("name"));
-			}
-			if (!segments.isEmpty()) {
-				gpxData.tracks.add(new ImmutableGpxTrack(segments, trkAttributes));
-			}
-
-		}
-
-		// add nodes as waypoints
-		for (Node n : dataSet.getNodes()) {
-			if (n.isIncomplete() || n.isDeleted() || doneNodes.contains(n)) continue;
-
-			Date tstamp = n.getTimestamp();
-
-			WayPoint wpt = new WayPoint(n.getCoor());
-			if (anonTime) {
-				wpt.attr.put("time", "1970-01-01T00:00:00Z");
-			} else {
-				wpt.attr.put("time", DateUtils.fromDate(tstamp));
-			}
-			wpt.setTime();
-
-			if (n.getKeys() != null && n.keySet().contains("name")) {
-				wpt.attr.put("name", n.get("name"));
-			}
-			gpxData.waypoints.add(wpt);
-		}
-		return gpxData;
+		return data.createGpxData();
 	}
 
 	//context item "Convert to GPX layer"
@@ -224,6 +149,8 @@ public class EditGpxLayer extends Layer {
 		public void actionPerformed(ActionEvent e) {
 			Main.main.addLayer(new GpxLayer(toGpxData(false), tr("Converted from: {0}", getName())));
 			Main.main.removeLayer(EditGpxLayer.this);
+			if(Main.map.mapMode instanceof EditGpxMode)
+				Main.map.selectSelectTool(false);
 		}
 	}
 
@@ -235,6 +162,8 @@ public class EditGpxLayer extends Layer {
 		public void actionPerformed(ActionEvent e) {
 			Main.main.addLayer(new GpxLayer(toGpxData(true), tr("Converted from: {0}", getName())));
 			Main.main.removeLayer(EditGpxLayer.this);
+			if(Main.map.mapMode instanceof EditGpxMode)
+				Main.map.selectSelectTool(false);
 		}
 	}
 }
diff --git a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxMode.java b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxMode.java
index ae17cb9..42a8058 100644
--- a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxMode.java
+++ b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxMode.java
@@ -3,6 +3,8 @@
  */
 package org.openstreetmap.josm.plugins.editgpx;
 
+import static org.openstreetmap.josm.tools.I18n.tr;
+
 import java.awt.Color;
 import java.awt.Cursor;
 import java.awt.Graphics;
@@ -10,126 +12,169 @@ import java.awt.Point;
 import java.awt.Rectangle;
 import java.awt.event.InputEvent;
 import java.awt.event.MouseEvent;
+import java.util.List;
 
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.mapmode.MapMode;
-import org.openstreetmap.josm.data.osm.DataSet;
-import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.plugins.editgpx.data.EditGpxData;
+import org.openstreetmap.josm.plugins.editgpx.data.EditGpxTrack;
+import org.openstreetmap.josm.plugins.editgpx.data.EditGpxTrackSegment;
+import org.openstreetmap.josm.plugins.editgpx.data.EditGpxWayPoint;
+
+
+public class EditGpxMode extends MapMode implements LayerChangeListener {
+
+	private static final long serialVersionUID = 7940589057093872411L;
+	Point pointPressed;
+	MapFrame mapFrame;
+	Rectangle oldRect;
+	MapFrame frame;
+	EditGpxLayer currentEditLayer;
+
+	public EditGpxMode(MapFrame mapFrame, String name, String desc) {
+		super(name, "editgpx_mode.png", desc, mapFrame, Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
+	}
+
+	@Override public void enterMode() {
+		super.enterMode();
+		Main.map.mapView.addMouseListener(this);
+		Main.map.mapView.addMouseMotionListener(this);
+		MapView.addLayerChangeListener(this);
+		updateLayer();
+	}
+
+	@Override public void exitMode() {
+		super.exitMode();
+		Main.map.mapView.removeMouseListener(this);
+		Main.map.mapView.removeMouseMotionListener(this);
+	}
+
+
+	@Override public void mousePressed(MouseEvent e) {
+		pointPressed = new Point(e.getPoint());
+	}
+
+
+	@Override public void mouseDragged(MouseEvent e) {
+		if ( (e.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) ==  InputEvent.BUTTON1_DOWN_MASK) {
+			//if button1 is hold, draw the rectangle.
+			paintRect(pointPressed, e.getPoint());
+		}
+	}
+
+	@Override public void mouseReleased(MouseEvent e) {
+		if (e.getButton() != MouseEvent.BUTTON1) {
+			return;
+		}
+
+		Point pointReleased = e.getPoint();
+
+		Rectangle r = createRect(pointReleased, pointPressed);
+
+		//go through nodes and mark the ones in the selection rect as deleted
+		if (currentEditLayer != null) {
+			for (EditGpxTrack track: currentEditLayer.data.getTracks()) {
+				for (EditGpxTrackSegment segment: track.getSegments()) {
+					for (EditGpxWayPoint wayPoint: segment.getWayPoints()) {
+						Point p = Main.map.mapView.getPoint(wayPoint.getCoor().getEastNorth());
+						if (r.contains(p)) {
+							wayPoint.setDeleted(true);
+						}
+					}
+				}
+			}
+		}
+		oldRect = null;
+		Main.map.mapView.repaint();
+
+	}
+
+	/**
+	 * create rectangle out of two given corners
+	 */
+	public Rectangle createRect(Point p1, Point p2) {
+		int x,y,w,h;
+		if (p1.x == p2.x && p1.y == p2.y) {
+			//if p1 and p2 same points draw a small rectangle around them
+			x = p1.x -1;
+			y = p1.y -1;
+			w = 3;
+			h = 3;
+		} else {
+			if (p1.x < p2.x){
+				x = p1.x;
+				w = p2.x-p1.x;
+			} else {
+				x = p2.x;
+				w = p1.x-p2.x;
+			}
+			if (p1.y < p2.y) {
+				y = p1.y;
+				h = p2.y-p1.y;
+			} else {
+				y = p2.y;
+				h = p1.y-p2.y;
+			}
+		}
+		return new Rectangle(x,y,w,h);
+	}
+
+	/**
+	 * Draw a selection rectangle on screen.
+	 */
+	private void paintRect(Point p1, Point p2) {
+		Graphics g = frame.getGraphics();//Main.map.mapView.getGraphics();
+
+		Rectangle r = oldRect;
+		if (r != null) {
+			//overwrite old rct
+			g.setXORMode(Color.BLACK);
+			g.setColor(Color.WHITE);
+			g.drawRect(r.x,r.y,r.width,r.height);
+		}
+
+		g.setXORMode(Color.BLACK);
+		g.setColor(Color.WHITE);
+		r = createRect(p1,p2);
+		g.drawRect(r.x,r.y,r.width,r.height);
+		oldRect = r;
+	}
+
+
+	public void setFrame(MapFrame mapFrame) {
+		frame = mapFrame;
+	}
+
+	/**
+	 * create new layer, add listeners and try importing gpx data.
+	 */
+	private void updateLayer() {
+
+		List<EditGpxLayer> layers = Main.map.mapView.getLayersOfType(EditGpxLayer.class);
+		currentEditLayer = layers.isEmpty()?null:layers.get(0);
+
+		if(currentEditLayer == null) {
+			currentEditLayer = new EditGpxLayer(tr("EditGpx"), new EditGpxData());
+			Main.main.addLayer(currentEditLayer);
+			currentEditLayer.initializeImport();
+		}
+		Main.map.mapView.repaint();
+	}
+
+	public void activeLayerChange(Layer oldLayer, Layer newLayer) { }
+
+	public void layerAdded(Layer newLayer) { }
+
+	public void layerRemoved(Layer oldLayer) {
+		if (oldLayer instanceof EditGpxLayer) {
+			currentEditLayer = null;
+			if(Main.map.mapMode instanceof EditGpxMode)
+				Main.map.selectSelectTool(false);
+		}
+	}
 
-
-public class EditGpxMode extends MapMode {
-
-    private static final long serialVersionUID = 7940589057093872411L;
-    Point pointPressed;
-    DataSet dataSet;
-    MapFrame mapFrame;
-    Rectangle oldRect;
-    MapFrame frame;
-
-    public EditGpxMode(MapFrame mapFrame, String name, String desc, DataSet ds) {
-        super(name, "editgpx_mode.png", desc, mapFrame, Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
-        dataSet = ds;
-    }
-
-    @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) {
-        pointPressed = new Point(e.getPoint());
-    }
-
-
-    @Override public void mouseDragged(MouseEvent e) {
-        if ( (e.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) ==  InputEvent.BUTTON1_DOWN_MASK) {
-            //if button1 is hold, draw the rectangle.
-            paintRect(pointPressed, e.getPoint());
-        }
-    }
-
-    @Override public void mouseReleased(MouseEvent e) {
-        if (e.getButton() != MouseEvent.BUTTON1) {
-            return;
-        } else {
-            Point pointReleased = e.getPoint();
-
-            Rectangle r = createRect(pointReleased, pointPressed);
-
-            //go through nodes and mark the ones in the selection rect as deleted
-            for (Node n: dataSet.getNodes()) {
-                Point p = Main.map.mapView.getPoint(n);
-                if (r.contains(p)) {
-                	n.setDeleted(true);
-                }
-            }
-            oldRect = null;
-            Main.map.mapView.repaint();
-        }
-    }
-
-    /**
-     * create rectangle out of two given corners
-     */
-    public Rectangle createRect(Point p1, Point p2) {
-        int x,y,w,h;
-        if (p1.x == p2.x && p1.y == p2.y) {
-            //if p1 and p2 same points draw a small rectangle around them
-            x = p1.x -1;
-            y = p1.y -1;
-            w = 3;
-            h = 3;
-        } else {
-            if (p1.x < p2.x){
-                x = p1.x;
-                w = p2.x-p1.x;
-            } else {
-                x = p2.x;
-                w = p1.x-p2.x;
-            }
-            if (p1.y < p2.y) {
-                y = p1.y;
-                h = p2.y-p1.y;
-            } else {
-                y = p2.y;
-                h = p1.y-p2.y;
-            }
-        }
-        return new Rectangle(x,y,w,h);
-    }
-
-    /**
-     * Draw a selection rectangle on screen.
-     */
-    private void paintRect(Point p1, Point p2) {
-        Graphics g = frame.getGraphics();//Main.map.mapView.getGraphics();
-
-        Rectangle r = oldRect;
-        if (r != null) {
-            //overwrite old rct
-            g.setXORMode(Color.BLACK);
-            g.setColor(Color.WHITE);
-            g.drawRect(r.x,r.y,r.width,r.height);
-        }
-
-        g.setXORMode(Color.BLACK);
-        g.setColor(Color.WHITE);
-        r = createRect(p1,p2);
-        g.drawRect(r.x,r.y,r.width,r.height);
-        oldRect = r;
-    }
-
-
-    public void setFrame(MapFrame mapFrame) {
-        frame = mapFrame;
-    }
 }
diff --git a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxPlugin.java b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxPlugin.java
index 5345966..6699953 100644
--- a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxPlugin.java
+++ b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/EditGpxPlugin.java
@@ -3,24 +3,18 @@
  */
 package org.openstreetmap.josm.plugins.editgpx;
 
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
 import java.net.URL;
 
 import javax.swing.ImageIcon;
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.gui.IconToggleButton;
 import org.openstreetmap.josm.gui.MapFrame;
-import org.openstreetmap.josm.gui.MapView;
-import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
-import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.plugins.Plugin;
 import org.openstreetmap.josm.plugins.PluginInformation;
 
-import static org.openstreetmap.josm.tools.I18n.tr;
-
 /**
  * Provides an editable GPX layer. Editable layer here means the deletion of points is supported.
  * This plugin can be used to prepare tracks for upload to OSM eg. delete uninteresting parts
@@ -37,82 +31,32 @@ import static org.openstreetmap.josm.tools.I18n.tr;
  */
 public class EditGpxPlugin extends Plugin {
 
-    private IconToggleButton btn;
-    private EditGpxMode mode;
-    protected static EditGpxLayer eGpxLayer;
-    protected static DataSet dataSet;
-    public static boolean active = false;
-
-    public EditGpxPlugin(PluginInformation info) {
-    	super(info);
-        dataSet = new DataSet();
-        mode = new EditGpxMode(Main.map, "editgpx", tr("edit gpx tracks"), dataSet);
-
-        btn = new IconToggleButton(mode);
-        btn.setVisible(true);
-    }
-
-    /**
-     * initialize button. if button is pressed create new layer.
-     */
-    @Override
-    public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
-        if(oldFrame == null && newFrame != null) {
-            mode.setFrame(newFrame);
-
-            if(Main.map != null)
-                Main.map.addMapMode(btn);
-
-            active = btn.isSelected();
-
-            btn.addActionListener(new ActionListener() {
-                public void actionPerformed(ActionEvent e) {
-                    active = btn.isSelected();
-                    if (active) {
-                        Main.worker.execute(new Runnable() {
-                            public void run() {
-                                updateLayer();
-                            }
-                        });
-                    }
-                }
-            });
-        }
-    }
-
-    /**
-     * create new layer, add listeners and try importing gpx data.
-     */
-    private void updateLayer() {
-        if(eGpxLayer == null) {
-            eGpxLayer = new EditGpxLayer(tr("EditGpx"), dataSet);
-            Main.main.addLayer(eGpxLayer);
-            MapView.addLayerChangeListener(new LayerChangeListener(){
-
-                public void activeLayerChange(final Layer oldLayer, final Layer newLayer) {
-                    if(newLayer instanceof EditGpxLayer)
-                        EditGpxPlugin.eGpxLayer = (EditGpxLayer)newLayer;
-                }
-
-                public void layerAdded(final Layer newLayer) {
-                }
-
-                public void layerRemoved(final Layer oldLayer) {
-                    if(oldLayer == eGpxLayer) {
-                        eGpxLayer = null;
-                        //dataSet = new DataSet();
-                        MapView.removeLayerChangeListener(this);
-                    }
-                }
-            });
-
-            eGpxLayer.initializeImport();
-        }
-        Main.map.mapView.repaint();
-    }
-
-    public static ImageIcon loadIcon(String name) {
-        URL url = EditGpxPlugin.class.getResource("/images/editgpx.png");
-        return new ImageIcon(url);
-    }
+	private IconToggleButton btn;
+	private EditGpxMode mode;
+
+	public EditGpxPlugin(PluginInformation info) {
+		super(info);
+		mode = new EditGpxMode(Main.map, "editgpx", tr("edit gpx tracks"));
+
+		btn = new IconToggleButton(mode);
+		btn.setVisible(true);
+	}
+
+	/**
+	 * initialize button. if button is pressed create new layer.
+	 */
+	@Override
+	public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
+		if(oldFrame == null && newFrame != null) {
+			mode.setFrame(newFrame);
+
+			if(Main.map != null)
+				Main.map.addMapMode(btn);
+		}
+	}
+
+	public static ImageIcon loadIcon(String name) {
+		URL url = EditGpxPlugin.class.getResource("/images/editgpx.png");
+		return new ImageIcon(url);
+	}
 }
diff --git a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/GPXLayerImportAction.java b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/GPXLayerImportAction.java
index 654ebda..65d23d5 100644
--- a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/GPXLayerImportAction.java
+++ b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/GPXLayerImportAction.java
@@ -24,15 +24,9 @@ import javax.swing.JList;
 import javax.swing.JOptionPane;
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.data.gpx.GpxTrack;
-import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
-import org.openstreetmap.josm.data.gpx.WayPoint;
-import org.openstreetmap.josm.data.osm.DataSet;
-import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.gui.layer.GpxLayer;
 import org.openstreetmap.josm.gui.layer.Layer;
-import org.openstreetmap.josm.tools.DateUtils;
+import org.openstreetmap.josm.plugins.editgpx.data.EditGpxData;
 import org.openstreetmap.josm.tools.ImageProvider;
 
 /**
@@ -43,102 +37,89 @@ import org.openstreetmap.josm.tools.ImageProvider;
 class GPXLayerImportAction extends AbstractAction {
 
 
-    private static final long serialVersionUID = 5794897888911798168L;
-    private DataSet dataSet;
-    public Object importing = new Object(); //used for synchronization
-
-    public GPXLayerImportAction(DataSet ds) {
-        //TODO what is icon at the end?
-        super(tr("Import path from GPX layer"), ImageProvider.get("dialogs", "edit"));
-        this.dataSet = ds;
-    }
-
-    /**
-     * shows a list of GPX layers. if user selects one the data from this layer is
-     * imported.
-     */
-    public void activateImport() {
-        Box panel = Box.createVerticalBox();
-        DefaultListModel dModel= new DefaultListModel();
-
-        final JList layerList = new JList(dModel);
-        Collection<Layer> data = Main.map.mapView.getAllLayers();
-        Layer lastLayer = null;
-        int layerCnt = 0;
-
-        for (Layer l : data){
-                if(l instanceof GpxLayer){
-                    dModel.addElement(l);
-                    lastLayer = l;
-                    layerCnt++;
-                }
-        }
-        if(layerCnt == 1){
-                layerList.setSelectedValue(lastLayer, true);
-        }
-        if(layerCnt > 0){
-            layerList.setCellRenderer(new DefaultListCellRenderer(){
-                    @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
-                        Layer layer = (Layer)value;
-                        JLabel label = (JLabel)super.getListCellRendererComponent(list,
-                                                                                  layer.getName(), index, isSelected, cellHasFocus);
-                        Icon icon = layer.getIcon();
-                        label.setIcon(icon);
-                        label.setToolTipText(layer.getToolTipText());
-                        return label;
-                    }
-                });
-
-            JCheckBox dropFirst = new JCheckBox(tr("Drop existing path"));
-
-            panel.add(layerList);
-            panel.add(dropFirst);
-
-            final JOptionPane optionPane = new JOptionPane(panel, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION){
-                    @Override public void selectInitialValue() {
-                        layerList.requestFocusInWindow();
-                    }
-                };
-            final JDialog dlg = optionPane.createDialog(Main.parent, tr("Import path from GPX layer"));
-            dlg.setVisible(true);
-
-            Object answer = optionPane.getValue();
-            if (answer == null || answer == JOptionPane.UNINITIALIZED_VALUE ||
-                (answer instanceof Integer && (Integer)answer != JOptionPane.OK_OPTION)) {
-                return;
-            }
-
-            GpxLayer gpx = (GpxLayer)layerList.getSelectedValue();
-
-            synchronized(importing) {
-                for (GpxTrack trk : gpx.data.tracks) {
-                    for (GpxTrackSegment segment : trk.getSegments()) {
-                        Way w = new Way();
-                        for (WayPoint p : segment.getWayPoints()) {
-                            Node n = new Node(p.getCoor());
-                            String timestr = p.getString("time");
-                            if(timestr != null)
-                                n.setTimestamp(DateUtils.fromString(timestr));
-                            dataSet.addPrimitive(n);
-                            w.addNode(n); //TODO what to do with these while deletion
-                        }
-                        dataSet.addPrimitive(w);
-                    }
-                }
-            }
-            Main.map.mapView.repaint();
-
-        } else {
-            // no gps layer
-            JOptionPane.showMessageDialog(Main.parent,tr("No GPX data layer found."));
-        }
-    }
-
-    /**
-     * called when pressing "Import.." from context menu of EditGpx layer
-     *
-     */
-    public void actionPerformed(ActionEvent arg0) {
-        activateImport();
-    }
+	private static final long serialVersionUID = 5794897888911798168L;
+	private EditGpxData data;
+	public Object importing = new Object(); //used for synchronization
+
+	public GPXLayerImportAction(EditGpxData data) {
+		//TODO what is icon at the end?
+		super(tr("Import path from GPX layer"), ImageProvider.get("dialogs", "edit"));
+		this.data = data;
+	}
+
+	/**
+	 * shows a list of GPX layers. if user selects one the data from this layer is
+	 * imported.
+	 */
+	public void activateImport() {
+		Box panel = Box.createVerticalBox();
+		DefaultListModel dModel= new DefaultListModel();
+
+		final JList layerList = new JList(dModel);
+		Collection<Layer> data = Main.map.mapView.getAllLayers();
+		Layer lastLayer = null;
+		int layerCnt = 0;
+
+		for (Layer l : data){
+			if(l instanceof GpxLayer){
+				dModel.addElement(l);
+				lastLayer = l;
+				layerCnt++;
+			}
+		}
+		if(layerCnt == 1){
+			layerList.setSelectedValue(lastLayer, true);
+		}
+		if(layerCnt > 0){
+			layerList.setCellRenderer(new DefaultListCellRenderer(){
+				@Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+					Layer layer = (Layer)value;
+					JLabel label = (JLabel)super.getListCellRendererComponent(list,
+							layer.getName(), index, isSelected, cellHasFocus);
+					Icon icon = layer.getIcon();
+					label.setIcon(icon);
+					label.setToolTipText(layer.getToolTipText());
+					return label;
+				}
+			});
+
+			JCheckBox dropFirst = new JCheckBox(tr("Drop existing path"));
+
+			panel.add(layerList);
+			panel.add(dropFirst);
+
+			final JOptionPane optionPane = new JOptionPane(panel, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION){
+				@Override public void selectInitialValue() {
+					layerList.requestFocusInWindow();
+				}
+			};
+			final JDialog dlg = optionPane.createDialog(Main.parent, tr("Import path from GPX layer"));
+			dlg.setVisible(true);
+
+			Object answer = optionPane.getValue();
+			if (answer == null || answer == JOptionPane.UNINITIALIZED_VALUE ||
+					(answer instanceof Integer && (Integer)answer != JOptionPane.OK_OPTION)) {
+				return;
+			}
+
+			GpxLayer gpx = (GpxLayer)layerList.getSelectedValue();
+
+			synchronized(importing) {
+				this.data.load(gpx.data, dropFirst.isSelected());
+			}
+			Main.map.mapView.repaint();
+
+		} else {
+			// no gps layer
+			JOptionPane.showMessageDialog(Main.parent,tr("No GPX data layer found."));
+		}
+	}
+
+	/**
+	 * called when pressing "Import.." from context menu of EditGpx layer
+	 *
+	 */
+	public void actionPerformed(ActionEvent arg0) {
+		activateImport();
+	}
 }
diff --git a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/data/EditGpxData.java b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/data/EditGpxData.java
new file mode 100644
index 0000000..2e4a394
--- /dev/null
+++ b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/data/EditGpxData.java
@@ -0,0 +1,62 @@
+package org.openstreetmap.josm.plugins.editgpx.data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.data.gpx.GpxRoute;
+import org.openstreetmap.josm.data.gpx.GpxTrack;
+import org.openstreetmap.josm.data.gpx.WayPoint;
+
+public class EditGpxData {
+
+	private final List<EditGpxTrack> tracks = new ArrayList<EditGpxTrack>();
+	// Only copy of routes and waypoints to preserve all info when converting back to gpx track
+	private final List<GpxRoute> routes = new ArrayList<GpxRoute>();
+	private final List<WayPoint> waypoints = new ArrayList<WayPoint>();
+
+	public void load(GpxData data, boolean drop) {
+		if(drop)
+			tracks.clear();
+		for (GpxTrack track: data.tracks) {
+			tracks.add(new EditGpxTrack(track));
+		}
+		routes.clear();
+		routes.addAll(data.routes);
+		waypoints.clear();
+		waypoints.addAll(data.waypoints);
+	}
+
+	public boolean isEmpty() {
+		for (EditGpxTrack track: tracks) {
+			for (EditGpxTrackSegment segment: track.getSegments()) {
+				if (!segment.getWayPoints().isEmpty()) {
+					return false;
+				}
+			}
+		}
+		return true;
+	}
+
+	public List<EditGpxTrack> getTracks() {
+		return tracks;
+	}
+
+	public GpxData createGpxData() {
+		GpxData result = new GpxData();
+
+		for (EditGpxTrack track: tracks) {
+			if (!track.isDeleted()) {
+				GpxTrack newTrack = track.createGpxTrack();
+				if (!newTrack.getSegments().isEmpty()) {
+					result.tracks.add(newTrack);
+				}
+			}
+		}
+
+		result.routes.addAll(routes);
+		result.waypoints.addAll(waypoints);
+		return result;
+	}
+
+}
diff --git a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/data/EditGpxTrack.java b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/data/EditGpxTrack.java
new file mode 100644
index 0000000..9cf5a91
--- /dev/null
+++ b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/data/EditGpxTrack.java
@@ -0,0 +1,57 @@
+package org.openstreetmap.josm.plugins.editgpx.data;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.josm.data.gpx.GpxTrack;
+import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
+import org.openstreetmap.josm.data.gpx.ImmutableGpxTrack;
+import org.openstreetmap.josm.data.gpx.WayPoint;
+
+public class EditGpxTrack {
+
+	private final List<EditGpxTrackSegment> segments = new ArrayList<EditGpxTrackSegment>();
+	private final Map<String, Object> attributes = new HashMap<String, Object>();
+	private boolean isDeleted;
+
+	public EditGpxTrack(GpxTrack track) {
+		attributes.putAll(track.getAttributes());
+		for (GpxTrackSegment segment: track.getSegments()) {
+			segments.add(new EditGpxTrackSegment(segment));
+		}
+	}
+
+	public List<EditGpxTrackSegment> getSegments() {
+		return segments;
+	}
+	public Map<String, Object> getAttributes() {
+		return attributes;
+	}
+
+	public GpxTrack createGpxTrack() {
+
+		Collection<Collection<WayPoint>> wayPoints = new ArrayList<Collection<WayPoint>>();
+
+		for (EditGpxTrackSegment segment: segments) {
+			if (!segment.isDeleted()) {
+				List<WayPoint> points = segment.getNonDeletedWaypoints();
+				if (!points.isEmpty()) {
+					wayPoints.add(points);
+				}
+			}
+		}
+
+		return new ImmutableGpxTrack(wayPoints, attributes);
+	}
+
+	public void setDeleted(boolean isDeleted) {
+		this.isDeleted = isDeleted;
+	}
+
+	public boolean isDeleted() {
+		return isDeleted;
+	}
+}
diff --git a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/data/EditGpxTrackSegment.java b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/data/EditGpxTrackSegment.java
new file mode 100644
index 0000000..f11f8b4
--- /dev/null
+++ b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/data/EditGpxTrackSegment.java
@@ -0,0 +1,44 @@
+package org.openstreetmap.josm.plugins.editgpx.data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
+import org.openstreetmap.josm.data.gpx.WayPoint;
+
+public class EditGpxTrackSegment {
+
+	private final List<EditGpxWayPoint> wayPoints = new ArrayList<EditGpxWayPoint>();
+	private boolean deleted;
+
+	public EditGpxTrackSegment(GpxTrackSegment segment) {
+		for (WayPoint wayPoint: segment.getWayPoints()) {
+			wayPoints.add(new EditGpxWayPoint(wayPoint));
+		}
+	}
+
+	public List<EditGpxWayPoint> getWayPoints() {
+		return wayPoints;
+	}
+
+	public List<WayPoint> getNonDeletedWaypoints() {
+		List<WayPoint> result = new ArrayList<WayPoint>();
+
+		for (EditGpxWayPoint wp: wayPoints) {
+			if (!wp.isDeleted()) {
+				result.add(wp.createWayPoint());
+			}
+		}
+
+		return result;
+	}
+
+	public void setDeleted(boolean deleted) {
+		this.deleted = deleted;
+	}
+
+	public boolean isDeleted() {
+		return deleted;
+	}
+
+}
diff --git a/editgpx/src/org/openstreetmap/josm/plugins/editgpx/data/EditGpxWayPoint.java b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/data/EditGpxWayPoint.java
new file mode 100644
index 0000000..f0a5a42
--- /dev/null
+++ b/editgpx/src/org/openstreetmap/josm/plugins/editgpx/data/EditGpxWayPoint.java
@@ -0,0 +1,39 @@
+package org.openstreetmap.josm.plugins.editgpx.data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.josm.data.coor.CachedLatLon;
+import org.openstreetmap.josm.data.gpx.WayPoint;
+
+public class EditGpxWayPoint {
+	private final double time;
+	private final CachedLatLon coor;
+	private boolean deleted;
+	private Map<String, Object> attributes;
+
+	public EditGpxWayPoint(WayPoint wayPoint) {
+		this.time = wayPoint.time;
+		this.coor = new CachedLatLon(wayPoint.getCoor());
+		this.attributes = new HashMap<String, Object>(wayPoint.attr);
+	}
+
+	public WayPoint createWayPoint() {
+		WayPoint result = new WayPoint(getCoor());
+		result.time = time;
+		result.attr = attributes;
+		return result;
+	}
+
+	public void setDeleted(boolean deleted) {
+		this.deleted = deleted;
+	}
+
+	public boolean isDeleted() {
+		return deleted;
+	}
+
+	public CachedLatLon getCoor() {
+		return coor;
+	}
+}
diff --git a/livegps/src/livegps/LiveGpsAcquirer.java b/livegps/src/livegps/LiveGpsAcquirer.java
index fc8c048..c4542aa 100644
--- a/livegps/src/livegps/LiveGpsAcquirer.java
+++ b/livegps/src/livegps/LiveGpsAcquirer.java
@@ -2,6 +2,8 @@ package livegps;
 
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.lang.Float;
+
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.io.BufferedReader;
@@ -15,13 +17,19 @@ import java.util.List;
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.coor.LatLon;
 
+import org.json.JSONObject;
+import org.json.JSONException;
+
 public class LiveGpsAcquirer implements Runnable {
-	Socket gpsdSocket;
-	BufferedReader gpsdReader;
-	boolean connected = false;
-	String gpsdHost;
-	int gpsdPort;
-	boolean shutdownFlag = false;
+	private String gpsdHost;
+	private int gpsdPort;
+
+	private Socket gpsdSocket;
+	private BufferedReader gpsdReader;
+	private boolean connected = false;
+	private boolean shutdownFlag = false;
+	private boolean JSONProtocol = true;
+
 	private final List<PropertyChangeListener> propertyChangeListener = new ArrayList<PropertyChangeListener>();
 	private PropertyChangeEvent lastStatusEvent;
 	private PropertyChangeEvent lastDataEvent;
@@ -111,124 +119,37 @@ public class LiveGpsAcquirer implements Runnable {
 
 		shutdownFlag = false;
 		while (!shutdownFlag) {
-			double lat = 0;
-			double lon = 0;
-			float speed = 0;
-			float course = 0;
-			boolean haveFix = false;
 
 			try {
-				if (!connected) {
-					System.out.println("LiveGps tries to connect to gpsd");
-					fireGpsStatusChangeEvent(
-							LiveGpsStatus.GpsStatus.CONNECTING,
-							tr("Connecting"));
-					InetAddress[] addrs = InetAddress.getAllByName(gpsdHost);
-					for (int i = 0; i < addrs.length && gpsdSocket == null; i++) {
-						try {
-							gpsdSocket = new Socket(addrs[i], gpsdPort);
-							break;
-						} catch (Exception e) {
-							System.out
-									.println("LiveGps: Could not open connection to gpsd: "
-											+ e);
-							gpsdSocket = null;
-						}
-					}
-
-					if (gpsdSocket != null) {
-						gpsdReader = new BufferedReader(new InputStreamReader(
-								gpsdSocket.getInputStream()));
-						gpsdSocket.getOutputStream().write(
-								new byte[] { 'w', 13, 10 });
-						fireGpsStatusChangeEvent(
-								LiveGpsStatus.GpsStatus.CONNECTING,
-								tr("Connecting"));
-						connected = true;
-						System.out.println("LiveGps: Connected to gpsd");
-					}
-				}
+				if (!connected)
+					connect();
 
 				if (connected) {
+					String line;
+
 					// <FIXXME date="23.06.2007" author="cdaller">
 					// TODO this read is blocking if gps is connected but has no
 					// fix, so gpsd does not send positions
-					String line = gpsdReader.readLine();
+					line = gpsdReader.readLine();
 					// </FIXXME>
 					if (line == null)
 						break;
-					String words[] = line.split(",");
 
-					if ((words.length == 0) || (!words[0].equals("GPSD"))) {
-						// unexpected response.
+					if (JSONProtocol == true)
+						gpsData = ParseJSON(line);
+					else
+						gpsData = ParseOld(line);
+
+					if (gpsData == null)
 						continue;
-					}
-
-					for (int i = 1; i < words.length; i++) {
-
-						if ((words[i].length() < 2)
-								|| (words[i].charAt(1) != '=')) {
-							// unexpected response.
-							continue;
-						}
-
-						char what = words[i].charAt(0);
-						String value = words[i].substring(2);
-						oldGpsData = gpsData;
-						gpsData = new LiveGpsData();
-						switch (what) {
-						case 'O':
-							// full report, tab delimited.
-							String[] status = value.split("\\s+");
-							if (status.length >= 5) {
-								lat = Double.parseDouble(status[3]);
-								lon = Double.parseDouble(status[4]);
-								try {
-									speed = Float.parseFloat(status[9]);
-									course = Float.parseFloat(status[8]);
-									// view.setSpeed(speed);
-									// view.setCourse(course);
-								} catch (NumberFormatException nex) {
-								}
-								haveFix = true;
-							}
-							break;
-						case 'P':
-							// position report, tab delimited.
-							String[] pos = value.split("\\s+");
-							if (pos.length >= 2) {
-								lat = Double.parseDouble(pos[0]);
-								lon = Double.parseDouble(pos[1]);
-								speed = Float.NaN;
-								course = Float.NaN;
-								haveFix = true;
-							}
-							break;
-						default:
-							// not interested
-						}
-						fireGpsStatusChangeEvent(
-								LiveGpsStatus.GpsStatus.CONNECTED,
-								tr("Connected"));
-						gpsData.setFix(haveFix);
-						if (haveFix) {
-							// view.setCurrentPosition(lat, lon);
-							gpsData.setLatLon(new LatLon(lat, lon));
-							gpsData.setSpeed(speed);
-							gpsData.setCourse(course);
-							fireGpsDataChangeEvent(oldGpsData, gpsData);
-						}
-					}
+
+					fireGpsDataChangeEvent(oldGpsData, gpsData);
+					oldGpsData = gpsData;
 				} else {
-					// not connected:
-					fireGpsStatusChangeEvent(
-							LiveGpsStatus.GpsStatus.DISCONNECTED,
-							tr("Not connected"));
+					fireGpsStatusChangeEvent(LiveGpsStatus.GpsStatus.DISCONNECTED, tr("Not connected"));
 					try {
 						Thread.sleep(1000);
-					} catch (InterruptedException ignore) {
-					}
-					;
+					} catch (InterruptedException ignore) {}
 				}
 			} catch (IOException iox) {
 				connected = false;
@@ -241,11 +162,8 @@ public class LiveGpsAcquirer implements Runnable {
 						tr("Connection Failed"));
 				try {
 					Thread.sleep(1000);
-				} catch (InterruptedException ignore) {
-				}
-				;
+				} catch (InterruptedException ignore) {} ;
 				// send warning to layer
-
 			}
 		}
 
@@ -257,8 +175,7 @@ public class LiveGpsAcquirer implements Runnable {
 				gpsdSocket = null;
 				System.out.println("LiveGps: Disconnected from gpsd");
 			} catch (Exception e) {
-				System.out
-						.println("LiveGps: Unable to close socket; reconnection may not be possible");
+				System.out.println("LiveGps: Unable to close socket; reconnection may not be possible");
 			}
 		}
 	}
@@ -267,4 +184,150 @@ public class LiveGpsAcquirer implements Runnable {
 		shutdownFlag = true;
 	}
 
+	private void connect() throws IOException {
+		JSONObject greeting;
+		String line, type, release;
+
+		System.out.println("LiveGps: trying to connect to gpsd at " + gpsdHost + ":" + gpsdPort);
+		fireGpsStatusChangeEvent( LiveGpsStatus.GpsStatus.CONNECTING, tr("Connecting"));
+
+		InetAddress[] addrs = InetAddress.getAllByName(gpsdHost);
+		for (int i = 0; i < addrs.length && gpsdSocket == null; i++) {
+			try {
+				gpsdSocket = new Socket(addrs[i], gpsdPort);
+				break;
+			} catch (Exception e) {
+				System.out.println("LiveGps: Could not open connection to gpsd: " + e);
+				gpsdSocket = null;
+			}
+		}
+
+		if (gpsdSocket == null)
+			return;
+
+		fireGpsStatusChangeEvent(LiveGpsStatus.GpsStatus.CONNECTING, tr("Connecting"));
+
+		/*
+		 * First emit the "w" symbol. The older version will activate, the newer one will ignore it.
+		 */
+		gpsdSocket.getOutputStream().write(new byte[] { 'w', 13, 10 });
+
+		gpsdReader = new BufferedReader(new InputStreamReader(gpsdSocket.getInputStream()));
+		line = gpsdReader.readLine();
+		if (line == null)
+			return;
+
+		try {
+			greeting = new JSONObject(line);
+			type = greeting.getString("class");
+			if (type.equals("VERSION")) {
+				release = greeting.getString("release");
+				System.out.println("LiveGps: Connected to gpsd " + release);
+			} else
+				System.out.println("LiveGps: Unexpected JSON in gpsd greeting: " + line);
+		} catch (JSONException jex) {
+			if (line.startsWith("GPSD,")) {
+				connected = true;
+				JSONProtocol = false;
+				System.out.println("LiveGps: Connected to old gpsd protocol version.");
+				fireGpsStatusChangeEvent(LiveGpsStatus.GpsStatus.CONNECTED, tr("Connected"));
+			}
+		}
+
+		if (JSONProtocol == true) {
+			JSONObject Watch = new JSONObject();
+			try { 
+				Watch.put("enable", true);
+				Watch.put("json", true);
+			} catch (JSONException je) {};
+
+			String Request = "?WATCH=" + Watch.toString() + ";\n";
+			gpsdSocket.getOutputStream().write(Request.getBytes());
+
+			connected = true;
+			fireGpsStatusChangeEvent(LiveGpsStatus.GpsStatus.CONNECTED, tr("Connected"));
+		}
+	}
+
+	private LiveGpsData ParseJSON(String line) {
+		JSONObject report;
+		String type;
+		double lat = 0;
+		double lon = 0;
+		float speed = 0;
+		float course = 0;
+
+		try {
+			report = new JSONObject(line);
+			type = report.getString("class");
+		} catch (JSONException jex) {
+			System.out.println("LiveGps: line read from gpsd is not a JSON object:" + line);
+			return null;
+		}
+		if (!type.equals("TPV"))
+			return null;
+
+		try {
+			lat = report.getDouble("lat");
+			lon = report.getDouble("lon");
+			speed = (new Float(report.getDouble("speed"))).floatValue();
+			course = (new Float(report.getDouble("track"))).floatValue();
+
+			return new LiveGpsData(lat, lon, course, speed, true);
+		} catch (JSONException je) {}
+
+		return null;
+	}
+
+	private LiveGpsData ParseOld(String line) {
+		String words[];
+		double lat = 0;
+		double lon = 0;
+		float speed = 0;
+		float course = 0;
+
+		words = line.split(",");
+		if ((words.length == 0) || (!words[0].equals("GPSD")))
+			return null;
+
+		for (int i = 1; i < words.length; i++) {
+			if ((words[i].length() < 2) || (words[i].charAt(1) != '=')) {
+				// unexpected response.
+				continue;
+			}
+
+			char what = words[i].charAt(0);
+			String value = words[i].substring(2);
+			switch (what) {
+			case 'O':
+				// full report, tab delimited.
+				String[] status = value.split("\\s+");
+				if (status.length >= 5) {
+					lat = Double.parseDouble(status[3]);
+					lon = Double.parseDouble(status[4]);
+					try {
+						speed = Float.parseFloat(status[9]);
+						course = Float.parseFloat(status[8]);
+					} catch (NumberFormatException nex) {}
+					return new LiveGpsData(lat, lon, course, speed, true);
+				}
+				break;
+			case 'P':
+				// position report, tab delimited.
+				String[] pos = value.split("\\s+");
+				if (pos.length >= 2) {
+					lat = Double.parseDouble(pos[0]);
+					lon = Double.parseDouble(pos[1]);
+					speed = Float.NaN;
+					course = Float.NaN;
+					return new LiveGpsData(lat, lon, course, speed, true);
+				}
+				break;
+			default:
+				// not interested
+			}
+		}
+
+		return null;
+	}
 }
diff --git a/livegps/src/org/json/JSONArray.java b/livegps/src/org/json/JSONArray.java
new file mode 100644
index 0000000..5cc6f71
--- /dev/null
+++ b/livegps/src/org/json/JSONArray.java
@@ -0,0 +1,918 @@
+package org.json;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+import java.io.IOException;
+import java.io.Writer;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * A JSONArray is an ordered sequence of values. Its external text form is a
+ * string wrapped in square brackets with commas separating the values. The
+ * internal form is an object having <code>get</code> and <code>opt</code>
+ * methods for accessing the values by index, and <code>put</code> methods for
+ * adding or replacing values. The values can be any of these types:
+ * <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
+ * <code>Number</code>, <code>String</code>, or the
+ * <code>JSONObject.NULL object</code>.
+ * <p>
+ * The constructor can convert a JSON text into a Java object. The
+ * <code>toString</code> method converts to JSON text.
+ * <p>
+ * A <code>get</code> method returns a value if one can be found, and throws an
+ * exception if one cannot be found. An <code>opt</code> method returns a
+ * default value instead of throwing an exception, and so is useful for
+ * obtaining optional values.
+ * <p>
+ * The generic <code>get()</code> and <code>opt()</code> methods return an
+ * object which you can cast or query for type. There are also typed
+ * <code>get</code> and <code>opt</code> methods that do type checking and type
+ * coercion for you.
+ * <p>
+ * The texts produced by the <code>toString</code> methods strictly conform to
+ * JSON syntax rules. The constructors are more forgiving in the texts they will
+ * accept:
+ * <ul>
+ * <li>An extra <code>,</code> <small>(comma)</small> may appear just
+ *     before the closing bracket.</li>
+ * <li>The <code>null</code> value will be inserted when there
+ *     is <code>,</code> <small>(comma)</small> elision.</li>
+ * <li>Strings may be quoted with <code>'</code> <small>(single
+ *     quote)</small>.</li>
+ * <li>Strings do not need to be quoted at all if they do not begin with a quote
+ *     or single quote, and if they do not contain leading or trailing spaces,
+ *     and if they do not contain any of these characters:
+ *     <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers
+ *     and if they are not the reserved words <code>true</code>,
+ *     <code>false</code>, or <code>null</code>.</li>
+ * <li>Values can be separated by <code>;</code> <small>(semicolon)</small> as
+ *     well as by <code>,</code> <small>(comma)</small>.</li>
+ * <li>Numbers may have the 
+ *     <code>0x-</code> <small>(hex)</small> prefix.</li>
+ * </ul>
+
+ * @author JSON.org
+ * @version 2009-04-14
+ */
+public class JSONArray {
+
+
+    /**
+     * The arrayList where the JSONArray's properties are kept.
+     */
+    private ArrayList myArrayList;
+
+
+    /**
+     * Construct an empty JSONArray.
+     */
+    public JSONArray() {
+        this.myArrayList = new ArrayList();
+    }
+
+    /**
+     * Construct a JSONArray from a JSONTokener.
+     * @param x A JSONTokener
+     * @throws JSONException If there is a syntax error.
+     */
+    public JSONArray(JSONTokener x) throws JSONException {
+        this();
+        char c = x.nextClean();
+        char q;
+        if (c == '[') {
+            q = ']';
+        } else if (c == '(') {
+            q = ')';
+        } else {
+            throw x.syntaxError("A JSONArray text must start with '['");
+        }
+        if (x.nextClean() == ']') {
+            return;
+        }
+        x.back();
+        for (;;) {
+            if (x.nextClean() == ',') {
+                x.back();
+                this.myArrayList.add(null);
+            } else {
+                x.back();
+                this.myArrayList.add(x.nextValue());
+            }
+            c = x.nextClean();
+            switch (c) {
+            case ';':
+            case ',':
+                if (x.nextClean() == ']') {
+                    return;
+                }
+                x.back();
+                break;
+            case ']':
+            case ')':
+                if (q != c) {
+                    throw x.syntaxError("Expected a '" + new Character(q) + "'");
+                }
+                return;
+            default:
+                throw x.syntaxError("Expected a ',' or ']'");
+            }
+        }
+    }
+
+
+    /**
+     * Construct a JSONArray from a source JSON text.
+     * @param source     A string that begins with
+     * <code>[</code> <small>(left bracket)</small>
+     *  and ends with <code>]</code> <small>(right bracket)</small>.
+     *  @throws JSONException If there is a syntax error.
+     */
+    public JSONArray(String source) throws JSONException {
+        this(new JSONTokener(source));
+    }
+
+
+    /**
+     * Construct a JSONArray from a Collection.
+     * @param collection     A Collection.
+     */
+    public JSONArray(Collection collection) {
+		this.myArrayList = new ArrayList();
+		if (collection != null) {
+			Iterator iter = collection.iterator();
+			while (iter.hasNext()) {
+			    Object o = iter.next();
+                this.myArrayList.add(JSONObject.wrap(o));  
+			}
+		}
+    }
+
+    
+    /**
+     * Construct a JSONArray from an array
+     * @throws JSONException If not an array.
+     */
+    public JSONArray(Object array) throws JSONException {
+        this();
+        if (array.getClass().isArray()) {
+            int length = Array.getLength(array);
+            for (int i = 0; i < length; i += 1) {
+                this.put(JSONObject.wrap(Array.get(array, i)));
+            }
+        } else {
+            throw new JSONException(
+"JSONArray initial value should be a string or collection or array.");
+        }
+    }
+
+     
+    /**
+     * Get the object value associated with an index.
+     * @param index
+     *  The index must be between 0 and length() - 1.
+     * @return An object value.
+     * @throws JSONException If there is no value for the index.
+     */
+    public Object get(int index) throws JSONException {
+        Object o = opt(index);
+        if (o == null) {
+            throw new JSONException("JSONArray[" + index + "] not found.");
+        }
+        return o;
+    }
+
+
+    /**
+     * Get the boolean value associated with an index.
+     * The string values "true" and "false" are converted to boolean.
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @return      The truth.
+     * @throws JSONException If there is no value for the index or if the
+     *  value is not convertable to boolean.
+     */
+    public boolean getBoolean(int index) throws JSONException {
+        Object o = get(index);
+        if (o.equals(Boolean.FALSE) ||
+                (o instanceof String &&
+                ((String)o).equalsIgnoreCase("false"))) {
+            return false;
+        } else if (o.equals(Boolean.TRUE) ||
+                (o instanceof String &&
+                ((String)o).equalsIgnoreCase("true"))) {
+            return true;
+        }
+        throw new JSONException("JSONArray[" + index + "] is not a Boolean.");
+    }
+
+
+    /**
+     * Get the double value associated with an index.
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @return      The value.
+     * @throws   JSONException If the key is not found or if the value cannot
+     *  be converted to a number.
+     */
+    public double getDouble(int index) throws JSONException {
+        Object o = get(index);
+        try {
+            return o instanceof Number ?
+                ((Number)o).doubleValue() :
+                Double.valueOf((String)o).doubleValue();
+        } catch (Exception e) {
+            throw new JSONException("JSONArray[" + index +
+                "] is not a number.");
+        }
+    }
+
+
+    /**
+     * Get the int value associated with an index.
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @return      The value.
+     * @throws   JSONException If the key is not found or if the value cannot
+     *  be converted to a number.
+     *  if the value cannot be converted to a number.
+     */
+    public int getInt(int index) throws JSONException {
+        Object o = get(index);
+        return o instanceof Number ?
+                ((Number)o).intValue() : (int)getDouble(index);
+    }
+
+
+    /**
+     * Get the JSONArray associated with an index.
+     * @param index The index must be between 0 and length() - 1.
+     * @return      A JSONArray value.
+     * @throws JSONException If there is no value for the index. or if the
+     * value is not a JSONArray
+     */
+    public JSONArray getJSONArray(int index) throws JSONException {
+        Object o = get(index);
+        if (o instanceof JSONArray) {
+            return (JSONArray)o;
+        }
+        throw new JSONException("JSONArray[" + index +
+                "] is not a JSONArray.");
+    }
+
+
+    /**
+     * Get the JSONObject associated with an index.
+     * @param index subscript
+     * @return      A JSONObject value.
+     * @throws JSONException If there is no value for the index or if the
+     * value is not a JSONObject
+     */
+    public JSONObject getJSONObject(int index) throws JSONException {
+        Object o = get(index);
+        if (o instanceof JSONObject) {
+            return (JSONObject)o;
+        }
+        throw new JSONException("JSONArray[" + index +
+            "] is not a JSONObject.");
+    }
+
+
+    /**
+     * Get the long value associated with an index.
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @return      The value.
+     * @throws   JSONException If the key is not found or if the value cannot
+     *  be converted to a number.
+     */
+    public long getLong(int index) throws JSONException {
+        Object o = get(index);
+        return o instanceof Number ?
+                ((Number)o).longValue() : (long)getDouble(index);
+    }
+
+
+    /**
+     * Get the string associated with an index.
+     * @param index The index must be between 0 and length() - 1.
+     * @return      A string value.
+     * @throws JSONException If there is no value for the index.
+     */
+    public String getString(int index) throws JSONException {
+        return get(index).toString();
+    }
+
+
+    /**
+     * Determine if the value is null.
+     * @param index The index must be between 0 and length() - 1.
+     * @return true if the value at the index is null, or if there is no value.
+     */
+    public boolean isNull(int index) {
+        return JSONObject.NULL.equals(opt(index));
+    }
+
+
+    /**
+     * Make a string from the contents of this JSONArray. The
+     * <code>separator</code> string is inserted between each element.
+     * Warning: This method assumes that the data structure is acyclical.
+     * @param separator A string that will be inserted between the elements.
+     * @return a string.
+     * @throws JSONException If the array contains an invalid number.
+     */
+    public String join(String separator) throws JSONException {
+        int len = length();
+        StringBuffer sb = new StringBuffer();
+
+        for (int i = 0; i < len; i += 1) {
+            if (i > 0) {
+                sb.append(separator);
+            }
+            sb.append(JSONObject.valueToString(this.myArrayList.get(i)));
+        }
+        return sb.toString();
+    }
+
+
+    /**
+     * Get the number of elements in the JSONArray, included nulls.
+     *
+     * @return The length (or size).
+     */
+    public int length() {
+        return this.myArrayList.size();
+    }
+
+
+    /**
+     * Get the optional object value associated with an index.
+     * @param index The index must be between 0 and length() - 1.
+     * @return      An object value, or null if there is no
+     *              object at that index.
+     */
+    public Object opt(int index) {
+        return (index < 0 || index >= length()) ?
+            null : this.myArrayList.get(index);
+    }
+
+
+    /**
+     * Get the optional boolean value associated with an index.
+     * It returns false if there is no value at that index,
+     * or if the value is not Boolean.TRUE or the String "true".
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @return      The truth.
+     */
+    public boolean optBoolean(int index)  {
+        return optBoolean(index, false);
+    }
+
+
+    /**
+     * Get the optional boolean value associated with an index.
+     * It returns the defaultValue if there is no value at that index or if
+     * it is not a Boolean or the String "true" or "false" (case insensitive).
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @param defaultValue     A boolean default.
+     * @return      The truth.
+     */
+    public boolean optBoolean(int index, boolean defaultValue)  {
+        try {
+            return getBoolean(index);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+
+    /**
+     * Get the optional double value associated with an index.
+     * NaN is returned if there is no value for the index,
+     * or if the value is not a number and cannot be converted to a number.
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @return      The value.
+     */
+    public double optDouble(int index) {
+        return optDouble(index, Double.NaN);
+    }
+
+
+    /**
+     * Get the optional double value associated with an index.
+     * The defaultValue is returned if there is no value for the index,
+     * or if the value is not a number and cannot be converted to a number.
+     *
+     * @param index subscript
+     * @param defaultValue     The default value.
+     * @return      The value.
+     */
+    public double optDouble(int index, double defaultValue) {
+        try {
+            return getDouble(index);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+
+    /**
+     * Get the optional int value associated with an index.
+     * Zero is returned if there is no value for the index,
+     * or if the value is not a number and cannot be converted to a number.
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @return      The value.
+     */
+    public int optInt(int index) {
+        return optInt(index, 0);
+    }
+
+
+    /**
+     * Get the optional int value associated with an index.
+     * The defaultValue is returned if there is no value for the index,
+     * or if the value is not a number and cannot be converted to a number.
+     * @param index The index must be between 0 and length() - 1.
+     * @param defaultValue     The default value.
+     * @return      The value.
+     */
+    public int optInt(int index, int defaultValue) {
+        try {
+            return getInt(index);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+
+    /**
+     * Get the optional JSONArray associated with an index.
+     * @param index subscript
+     * @return      A JSONArray value, or null if the index has no value,
+     * or if the value is not a JSONArray.
+     */
+    public JSONArray optJSONArray(int index) {
+        Object o = opt(index);
+        return o instanceof JSONArray ? (JSONArray)o : null;
+    }
+
+
+    /**
+     * Get the optional JSONObject associated with an index.
+     * Null is returned if the key is not found, or null if the index has
+     * no value, or if the value is not a JSONObject.
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @return      A JSONObject value.
+     */
+    public JSONObject optJSONObject(int index) {
+        Object o = opt(index);
+        return o instanceof JSONObject ? (JSONObject)o : null;
+    }
+
+
+    /**
+     * Get the optional long value associated with an index.
+     * Zero is returned if there is no value for the index,
+     * or if the value is not a number and cannot be converted to a number.
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @return      The value.
+     */
+    public long optLong(int index) {
+        return optLong(index, 0);
+    }
+
+
+    /**
+     * Get the optional long value associated with an index.
+     * The defaultValue is returned if there is no value for the index,
+     * or if the value is not a number and cannot be converted to a number.
+     * @param index The index must be between 0 and length() - 1.
+     * @param defaultValue     The default value.
+     * @return      The value.
+     */
+    public long optLong(int index, long defaultValue) {
+        try {
+            return getLong(index);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+
+    /**
+     * Get the optional string value associated with an index. It returns an
+     * empty string if there is no value at that index. If the value
+     * is not a string and is not null, then it is coverted to a string.
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @return      A String value.
+     */
+    public String optString(int index) {
+        return optString(index, "");
+    }
+
+
+    /**
+     * Get the optional string associated with an index.
+     * The defaultValue is returned if the key is not found.
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @param defaultValue     The default value.
+     * @return      A String value.
+     */
+    public String optString(int index, String defaultValue) {
+        Object o = opt(index);
+        return o != null ? o.toString() : defaultValue;
+    }
+
+
+    /**
+     * Append a boolean value. This increases the array's length by one.
+     *
+     * @param value A boolean value.
+     * @return this.
+     */
+    public JSONArray put(boolean value) {
+        put(value ? Boolean.TRUE : Boolean.FALSE);
+        return this;
+    }
+
+
+    /**
+     * Put a value in the JSONArray, where the value will be a
+     * JSONArray which is produced from a Collection.
+     * @param value A Collection value.
+     * @return      this.
+     */
+    public JSONArray put(Collection value) {
+        put(new JSONArray(value));
+        return this;
+    }
+
+
+    /**
+     * Append a double value. This increases the array's length by one.
+     *
+     * @param value A double value.
+     * @throws JSONException if the value is not finite.
+     * @return this.
+     */
+    public JSONArray put(double value) throws JSONException {
+        Double d = new Double(value);
+        JSONObject.testValidity(d);
+        put(d);
+        return this;
+    }
+
+
+    /**
+     * Append an int value. This increases the array's length by one.
+     *
+     * @param value An int value.
+     * @return this.
+     */
+    public JSONArray put(int value) {
+        put(new Integer(value));
+        return this;
+    }
+
+
+    /**
+     * Append an long value. This increases the array's length by one.
+     *
+     * @param value A long value.
+     * @return this.
+     */
+    public JSONArray put(long value) {
+        put(new Long(value));
+        return this;
+    }
+
+
+    /**
+     * Put a value in the JSONArray, where the value will be a
+     * JSONObject which is produced from a Map.
+     * @param value A Map value.
+     * @return      this.
+     */
+    public JSONArray put(Map value) {
+        put(new JSONObject(value));
+        return this;
+    }
+
+
+    /**
+     * Append an object value. This increases the array's length by one.
+     * @param value An object value.  The value should be a
+     *  Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the
+     *  JSONObject.NULL object.
+     * @return this.
+     */
+    public JSONArray put(Object value) {
+        this.myArrayList.add(value);
+        return this;
+    }
+
+
+    /**
+     * Put or replace a boolean value in the JSONArray. If the index is greater
+     * than the length of the JSONArray, then null elements will be added as
+     * necessary to pad it out.
+     * @param index The subscript.
+     * @param value A boolean value.
+     * @return this.
+     * @throws JSONException If the index is negative.
+     */
+    public JSONArray put(int index, boolean value) throws JSONException {
+        put(index, value ? Boolean.TRUE : Boolean.FALSE);
+        return this;
+    }
+
+
+    /**
+     * Put a value in the JSONArray, where the value will be a
+     * JSONArray which is produced from a Collection.
+     * @param index The subscript.
+     * @param value A Collection value.
+     * @return      this.
+     * @throws JSONException If the index is negative or if the value is
+     * not finite.
+     */
+    public JSONArray put(int index, Collection value) throws JSONException {
+        put(index, new JSONArray(value));
+        return this;
+    }
+
+
+    /**
+     * Put or replace a double value. If the index is greater than the length of
+     *  the JSONArray, then null elements will be added as necessary to pad
+     *  it out.
+     * @param index The subscript.
+     * @param value A double value.
+     * @return this.
+     * @throws JSONException If the index is negative or if the value is
+     * not finite.
+     */
+    public JSONArray put(int index, double value) throws JSONException {
+        put(index, new Double(value));
+        return this;
+    }
+
+
+    /**
+     * Put or replace an int value. If the index is greater than the length of
+     *  the JSONArray, then null elements will be added as necessary to pad
+     *  it out.
+     * @param index The subscript.
+     * @param value An int value.
+     * @return this.
+     * @throws JSONException If the index is negative.
+     */
+    public JSONArray put(int index, int value) throws JSONException {
+        put(index, new Integer(value));
+        return this;
+    }
+
+
+    /**
+     * Put or replace a long value. If the index is greater than the length of
+     *  the JSONArray, then null elements will be added as necessary to pad
+     *  it out.
+     * @param index The subscript.
+     * @param value A long value.
+     * @return this.
+     * @throws JSONException If the index is negative.
+     */
+    public JSONArray put(int index, long value) throws JSONException {
+        put(index, new Long(value));
+        return this;
+    }
+
+
+    /**
+     * Put a value in the JSONArray, where the value will be a
+     * JSONObject which is produced from a Map.
+     * @param index The subscript.
+     * @param value The Map value.
+     * @return      this.
+     * @throws JSONException If the index is negative or if the the value is
+     *  an invalid number.
+     */
+    public JSONArray put(int index, Map value) throws JSONException {
+        put(index, new JSONObject(value));
+        return this;
+    }
+
+
+    /**
+     * Put or replace an object value in the JSONArray. If the index is greater
+     *  than the length of the JSONArray, then null elements will be added as
+     *  necessary to pad it out.
+     * @param index The subscript.
+     * @param value The value to put into the array. The value should be a
+     *  Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the
+     *  JSONObject.NULL object.
+     * @return this.
+     * @throws JSONException If the index is negative or if the the value is
+     *  an invalid number.
+     */
+    public JSONArray put(int index, Object value) throws JSONException {
+        JSONObject.testValidity(value);
+        if (index < 0) {
+            throw new JSONException("JSONArray[" + index + "] not found.");
+        }
+        if (index < length()) {
+            this.myArrayList.set(index, value);
+        } else {
+            while (index != length()) {
+                put(JSONObject.NULL);
+            }
+            put(value);
+        }
+        return this;
+    }
+    
+    
+    /**
+     * Remove an index and close the hole.
+     * @param index The index of the element to be removed.
+     * @return The value that was associated with the index,
+     * or null if there was no value.
+     */
+    public Object remove(int index) {
+    	Object o = opt(index);
+        this.myArrayList.remove(index);
+        return o;
+    }
+
+
+    /**
+     * Produce a JSONObject by combining a JSONArray of names with the values
+     * of this JSONArray.
+     * @param names A JSONArray containing a list of key strings. These will be
+     * paired with the values.
+     * @return A JSONObject, or null if there are no names or if this JSONArray
+     * has no values.
+     * @throws JSONException If any of the names are null.
+     */
+    public JSONObject toJSONObject(JSONArray names) throws JSONException {
+        if (names == null || names.length() == 0 || length() == 0) {
+            return null;
+        }
+        JSONObject jo = new JSONObject();
+        for (int i = 0; i < names.length(); i += 1) {
+            jo.put(names.getString(i), this.opt(i));
+        }
+        return jo;
+    }
+
+
+    /**
+     * Make a JSON text of this JSONArray. For compactness, no
+     * unnecessary whitespace is added. If it is not possible to produce a
+     * syntactically correct JSON text then null will be returned instead. This
+     * could occur if the array contains an invalid number.
+     * <p>
+     * Warning: This method assumes that the data structure is acyclical.
+     *
+     * @return a printable, displayable, transmittable
+     *  representation of the array.
+     */
+    public String toString() {
+        try {
+            return '[' + join(",") + ']';
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+
+    /**
+     * Make a prettyprinted JSON text of this JSONArray.
+     * Warning: This method assumes that the data structure is acyclical.
+     * @param indentFactor The number of spaces to add to each level of
+     *  indentation.
+     * @return a printable, displayable, transmittable
+     *  representation of the object, beginning
+     *  with <code>[</code> <small>(left bracket)</small> and ending
+     *  with <code>]</code> <small>(right bracket)</small>.
+     * @throws JSONException
+     */
+    public String toString(int indentFactor) throws JSONException {
+        return toString(indentFactor, 0);
+    }
+
+
+    /**
+     * Make a prettyprinted JSON text of this JSONArray.
+     * Warning: This method assumes that the data structure is acyclical.
+     * @param indentFactor The number of spaces to add to each level of
+     *  indentation.
+     * @param indent The indention of the top level.
+     * @return a printable, displayable, transmittable
+     *  representation of the array.
+     * @throws JSONException
+     */
+    String toString(int indentFactor, int indent) throws JSONException {
+        int len = length();
+        if (len == 0) {
+            return "[]";
+        }
+        int i;
+        StringBuffer sb = new StringBuffer("[");
+        if (len == 1) {
+            sb.append(JSONObject.valueToString(this.myArrayList.get(0),
+                    indentFactor, indent));
+        } else {
+            int newindent = indent + indentFactor;
+            sb.append('\n');
+            for (i = 0; i < len; i += 1) {
+                if (i > 0) {
+                    sb.append(",\n");
+                }
+                for (int j = 0; j < newindent; j += 1) {
+                    sb.append(' ');
+                }
+                sb.append(JSONObject.valueToString(this.myArrayList.get(i),
+                        indentFactor, newindent));
+            }
+            sb.append('\n');
+            for (i = 0; i < indent; i += 1) {
+                sb.append(' ');
+            }
+        }
+        sb.append(']');
+        return sb.toString();
+    }
+
+
+    /**
+     * Write the contents of the JSONArray as JSON text to a writer.
+     * For compactness, no whitespace is added.
+     * <p>
+     * Warning: This method assumes that the data structure is acyclical.
+     *
+     * @return The writer.
+     * @throws JSONException
+     */
+    public Writer write(Writer writer) throws JSONException {
+        try {
+            boolean b = false;
+            int     len = length();
+
+            writer.write('[');
+
+            for (int i = 0; i < len; i += 1) {
+                if (b) {
+                    writer.write(',');
+                }
+                Object v = this.myArrayList.get(i);
+                if (v instanceof JSONObject) {
+                    ((JSONObject)v).write(writer);
+                } else if (v instanceof JSONArray) {
+                    ((JSONArray)v).write(writer);
+                } else {
+                    writer.write(JSONObject.valueToString(v));
+                }
+                b = true;
+            }
+            writer.write(']');
+            return writer;
+        } catch (IOException e) {
+           throw new JSONException(e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/livegps/src/org/json/JSONException.java b/livegps/src/org/json/JSONException.java
new file mode 100644
index 0000000..45e3b8d
--- /dev/null
+++ b/livegps/src/org/json/JSONException.java
@@ -0,0 +1,31 @@
+package org.json;
+
+/**
+ * The JSONException is thrown by the JSON.org classes when things are amiss.
+ * @author JSON.org
+ * @version 2008-09-18
+ */
+public class JSONException extends Exception {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 0;
+	private Throwable cause;
+
+    /**
+     * Constructs a JSONException with an explanatory message.
+     * @param message Detail about the reason for the exception.
+     */
+    public JSONException(String message) {
+        super(message);
+    }
+
+    public JSONException(Throwable t) {
+        super(t.getMessage());
+        this.cause = t;
+    }
+
+    public Throwable getCause() {
+        return this.cause;
+    }
+}
diff --git a/livegps/src/org/json/JSONObject.java b/livegps/src/org/json/JSONObject.java
new file mode 100644
index 0000000..e34a752
--- /dev/null
+++ b/livegps/src/org/json/JSONObject.java
@@ -0,0 +1,1584 @@
+package org.json;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+import java.io.IOException;
+import java.io.Writer;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeSet;
+
+/**
+ * A JSONObject is an unordered collection of name/value pairs. Its
+ * external form is a string wrapped in curly braces with colons between the
+ * names and values, and commas between the values and names. The internal form
+ * is an object having <code>get</code> and <code>opt</code> methods for
+ * accessing the values by name, and <code>put</code> methods for adding or
+ * replacing values by name. The values can be any of these types:
+ * <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
+ * <code>Number</code>, <code>String</code>, or the <code>JSONObject.NULL</code>
+ * object. A JSONObject constructor can be used to convert an external form
+ * JSON text into an internal form whose values can be retrieved with the
+ * <code>get</code> and <code>opt</code> methods, or to convert values into a
+ * JSON text using the <code>put</code> and <code>toString</code> methods.
+ * A <code>get</code> method returns a value if one can be found, and throws an
+ * exception if one cannot be found. An <code>opt</code> method returns a
+ * default value instead of throwing an exception, and so is useful for
+ * obtaining optional values.
+ * <p>
+ * The generic <code>get()</code> and <code>opt()</code> methods return an
+ * object, which you can cast or query for type. There are also typed
+ * <code>get</code> and <code>opt</code> methods that do type checking and type
+ * coercion for you.
+ * <p>
+ * The <code>put</code> methods adds values to an object. For example, <pre>
+ *     myString = new JSONObject().put("JSON", "Hello, World!").toString();</pre>
+ * produces the string <code>{"JSON": "Hello, World"}</code>.
+ * <p>
+ * The texts produced by the <code>toString</code> methods strictly conform to
+ * the JSON syntax rules.
+ * The constructors are more forgiving in the texts they will accept:
+ * <ul>
+ * <li>An extra <code>,</code> <small>(comma)</small> may appear just
+ *     before the closing brace.</li>
+ * <li>Strings may be quoted with <code>'</code> <small>(single
+ *     quote)</small>.</li>
+ * <li>Strings do not need to be quoted at all if they do not begin with a quote
+ *     or single quote, and if they do not contain leading or trailing spaces,
+ *     and if they do not contain any of these characters:
+ *     <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers
+ *     and if they are not the reserved words <code>true</code>,
+ *     <code>false</code>, or <code>null</code>.</li>
+ * <li>Keys can be followed by <code>=</code> or <code>=></code> as well as
+ *     by <code>:</code>.</li>
+ * <li>Values can be followed by <code>;</code> <small>(semicolon)</small> as
+ *     well as by <code>,</code> <small>(comma)</small>.</li>
+ * <li>Numbers may have the <code>0x-</code> <small>(hex)</small> prefix.</li>
+ * </ul>
+ * @author JSON.org
+ * @version 2010-05-17
+ */
+public class JSONObject {
+
+    /**
+     * JSONObject.NULL is equivalent to the value that JavaScript calls null,
+     * whilst Java's null is equivalent to the value that JavaScript calls
+     * undefined.
+     */
+     private static final class Null {
+
+        /**
+         * There is only intended to be a single instance of the NULL object,
+         * so the clone method returns itself.
+         * @return     NULL.
+         */
+        protected final Object clone() {
+            return this;
+        }
+
+
+        /**
+         * A Null object is equal to the null value and to itself.
+         * @param object    An object to test for nullness.
+         * @return true if the object parameter is the JSONObject.NULL object
+         *  or null.
+         */
+        public boolean equals(Object object) {
+            return object == null || object == this;
+        }
+
+
+        /**
+         * Get the "null" string value.
+         * @return The string "null".
+         */
+        public String toString() {
+            return "null";
+        }
+    }
+
+
+    /**
+     * The map where the JSONObject's properties are kept.
+     */
+    private Map map;
+
+
+    /**
+     * It is sometimes more convenient and less ambiguous to have a
+     * <code>NULL</code> object than to use Java's <code>null</code> value.
+     * <code>JSONObject.NULL.equals(null)</code> returns <code>true</code>.
+     * <code>JSONObject.NULL.toString()</code> returns <code>"null"</code>.
+     */
+    public static final Object NULL = new Null();
+
+
+    /**
+     * Construct an empty JSONObject.
+     */
+    public JSONObject() {
+        this.map = new HashMap();
+    }
+
+
+    /**
+     * Construct a JSONObject from a subset of another JSONObject.
+     * An array of strings is used to identify the keys that should be copied.
+     * Missing keys are ignored.
+     * @param jo A JSONObject.
+     * @param names An array of strings.
+     * @throws JSONException 
+     * @exception JSONException If a value is a non-finite number or if a name is duplicated.
+     */
+    public JSONObject(JSONObject jo, String[] names) {
+        this();
+        for (int i = 0; i < names.length; i += 1) {
+        	try {
+        		putOnce(names[i], jo.opt(names[i]));
+        	} catch (Exception ignore) {
+        	}
+        }
+    }
+
+
+    /**
+     * Construct a JSONObject from a JSONTokener.
+     * @param x A JSONTokener object containing the source string.
+     * @throws JSONException If there is a syntax error in the source string
+     *  or a duplicated key.
+     */
+    public JSONObject(JSONTokener x) throws JSONException {
+        this();
+        char c;
+        String key;
+
+        if (x.nextClean() != '{') {
+            throw x.syntaxError("A JSONObject text must begin with '{'");
+        }
+        for (;;) {
+            c = x.nextClean();
+            switch (c) {
+            case 0:
+                throw x.syntaxError("A JSONObject text must end with '}'");
+            case '}':
+                return;
+            default:
+                x.back();
+                key = x.nextValue().toString();
+            }
+
+            /*
+             * The key is followed by ':'. We will also tolerate '=' or '=>'.
+             */
+
+            c = x.nextClean();
+            if (c == '=') {
+                if (x.next() != '>') {
+                    x.back();
+                }
+            } else if (c != ':') {
+                throw x.syntaxError("Expected a ':' after a key");
+            }
+            putOnce(key, x.nextValue());
+
+            /*
+             * Pairs are separated by ','. We will also tolerate ';'.
+             */
+
+            switch (x.nextClean()) {
+            case ';':
+            case ',':
+                if (x.nextClean() == '}') {
+                    return;
+                }
+                x.back();
+                break;
+            case '}':
+                return;
+            default:
+                throw x.syntaxError("Expected a ',' or '}'");
+            }
+        }
+    }
+
+
+    /**
+     * Construct a JSONObject from a Map.
+     *
+     * @param map A map object that can be used to initialize the contents of
+     *  the JSONObject.
+     * @throws JSONException 
+     */
+    public JSONObject(Map map) {
+        this.map = new HashMap();
+        if (map != null) {
+            Iterator i = map.entrySet().iterator();
+            while (i.hasNext()) {
+                Map.Entry e = (Map.Entry)i.next();
+                this.map.put(e.getKey(), wrap(e.getValue()));
+            }
+        }
+    }
+
+
+    /**
+     * Construct a JSONObject from an Object using bean getters.
+     * It reflects on all of the public methods of the object.
+     * For each of the methods with no parameters and a name starting
+     * with <code>"get"</code> or <code>"is"</code> followed by an uppercase letter,
+     * the method is invoked, and a key and the value returned from the getter method
+     * are put into the new JSONObject.
+     *
+     * The key is formed by removing the <code>"get"</code> or <code>"is"</code> prefix.
+     * If the second remaining character is not upper case, then the first
+     * character is converted to lower case.
+     *
+     * For example, if an object has a method named <code>"getName"</code>, and
+     * if the result of calling <code>object.getName()</code> is <code>"Larry Fine"</code>,
+     * then the JSONObject will contain <code>"name": "Larry Fine"</code>.
+     *
+     * @param bean An object that has getter methods that should be used
+     * to make a JSONObject.
+     */
+    public JSONObject(Object bean) {
+        this();
+        populateMap(bean);
+    }
+
+
+    /**
+     * Construct a JSONObject from an Object, using reflection to find the
+     * public members. The resulting JSONObject's keys will be the strings
+     * from the names array, and the values will be the field values associated
+     * with those keys in the object. If a key is not found or not visible,
+     * then it will not be copied into the new JSONObject.
+     * @param object An object that has fields that should be used to make a
+     * JSONObject.
+     * @param names An array of strings, the names of the fields to be obtained
+     * from the object.
+     */
+    public JSONObject(Object object, String names[]) {
+        this();
+        Class c = object.getClass();
+        for (int i = 0; i < names.length; i += 1) {
+            String name = names[i];
+            try {
+                putOpt(name, c.getField(name).get(object));
+            } catch (Exception ignore) {
+            }
+        }
+    }
+
+
+    /**
+     * Construct a JSONObject from a source JSON text string.
+     * This is the most commonly used JSONObject constructor.
+     * @param source    A string beginning
+     *  with <code>{</code> <small>(left brace)</small> and ending
+     *  with <code>}</code> <small>(right brace)</small>.
+     * @exception JSONException If there is a syntax error in the source
+     *  string or a duplicated key.
+     */
+    public JSONObject(String source) throws JSONException {
+        this(new JSONTokener(source));
+    }
+
+
+    /**
+     * Accumulate values under a key. It is similar to the put method except
+     * that if there is already an object stored under the key then a
+     * JSONArray is stored under the key to hold all of the accumulated values.
+     * If there is already a JSONArray, then the new value is appended to it.
+     * In contrast, the put method replaces the previous value.
+     * @param key   A key string.
+     * @param value An object to be accumulated under the key.
+     * @return this.
+     * @throws JSONException If the value is an invalid number
+     *  or if the key is null.
+     */
+    public JSONObject accumulate(String key, Object value)
+            throws JSONException {
+        testValidity(value);
+        Object o = opt(key);
+        if (o == null) {
+            put(key, value instanceof JSONArray ?
+                    new JSONArray().put(value) :
+                    value);
+        } else if (o instanceof JSONArray) {
+            ((JSONArray)o).put(value);
+        } else {
+            put(key, new JSONArray().put(o).put(value));
+        }
+        return this;
+    }
+
+
+    /**
+     * Append values to the array under a key. If the key does not exist in the
+     * JSONObject, then the key is put in the JSONObject with its value being a
+     * JSONArray containing the value parameter. If the key was already
+     * associated with a JSONArray, then the value parameter is appended to it.
+     * @param key   A key string.
+     * @param value An object to be accumulated under the key.
+     * @return this.
+     * @throws JSONException If the key is null or if the current value
+     *  associated with the key is not a JSONArray.
+     */
+    public JSONObject append(String key, Object value) throws JSONException {
+        testValidity(value);
+        Object o = opt(key);
+        if (o == null) {
+            put(key, new JSONArray().put(value));
+        } else if (o instanceof JSONArray) {
+            put(key, ((JSONArray)o).put(value));
+        } else {
+            throw new JSONException("JSONObject[" + key +
+                    "] is not a JSONArray.");
+        }
+        return this;
+    }
+
+
+    /**
+     * Produce a string from a double. The string "null" will be returned if
+     * the number is not finite.
+     * @param  d A double.
+     * @return A String.
+     */
+    static public String doubleToString(double d) {
+        if (Double.isInfinite(d) || Double.isNaN(d)) {
+            return "null";
+        }
+
+// Shave off trailing zeros and decimal point, if possible.
+
+        String s = Double.toString(d);
+        if (s.indexOf('.') > 0 && s.indexOf('e') < 0 && s.indexOf('E') < 0) {
+            while (s.endsWith("0")) {
+                s = s.substring(0, s.length() - 1);
+            }
+            if (s.endsWith(".")) {
+                s = s.substring(0, s.length() - 1);
+            }
+        }
+        return s;
+    }
+
+
+    /**
+     * Get the value object associated with a key.
+     *
+     * @param key   A key string.
+     * @return      The object associated with the key.
+     * @throws   JSONException if the key is not found.
+     */
+    public Object get(String key) throws JSONException {
+        Object o = opt(key);
+        if (o == null) {
+            throw new JSONException("JSONObject[" + quote(key) +
+                    "] not found.");
+        }
+        return o;
+    }
+
+
+    /**
+     * Get the boolean value associated with a key.
+     *
+     * @param key   A key string.
+     * @return      The truth.
+     * @throws   JSONException
+     *  if the value is not a Boolean or the String "true" or "false".
+     */
+    public boolean getBoolean(String key) throws JSONException {
+        Object o = get(key);
+        if (o.equals(Boolean.FALSE) ||
+                (o instanceof String &&
+                ((String)o).equalsIgnoreCase("false"))) {
+            return false;
+        } else if (o.equals(Boolean.TRUE) ||
+                (o instanceof String &&
+                ((String)o).equalsIgnoreCase("true"))) {
+            return true;
+        }
+        throw new JSONException("JSONObject[" + quote(key) +
+                "] is not a Boolean.");
+    }
+
+
+    /**
+     * Get the double value associated with a key.
+     * @param key   A key string.
+     * @return      The numeric value.
+     * @throws JSONException if the key is not found or
+     *  if the value is not a Number object and cannot be converted to a number.
+     */
+    public double getDouble(String key) throws JSONException {
+        Object o = get(key);
+        try {
+            return o instanceof Number ?
+                ((Number)o).doubleValue() :
+                Double.valueOf((String)o).doubleValue();
+        } catch (Exception e) {
+            throw new JSONException("JSONObject[" + quote(key) +
+                "] is not a number.");
+        }
+    }
+
+
+    /**
+     * Get the int value associated with a key. 
+     *
+     * @param key   A key string.
+     * @return      The integer value.
+     * @throws   JSONException if the key is not found or if the value cannot
+     *  be converted to an integer.
+     */
+    public int getInt(String key) throws JSONException {
+        Object o = get(key);
+        try {
+            return o instanceof Number ?
+                ((Number)o).intValue() :
+                Integer.parseInt((String)o);
+        } catch (Exception e) {
+            throw new JSONException("JSONObject[" + quote(key) +
+                "] is not an int.");
+        }
+    }
+
+
+    /**
+     * Get the JSONArray value associated with a key.
+     *
+     * @param key   A key string.
+     * @return      A JSONArray which is the value.
+     * @throws   JSONException if the key is not found or
+     *  if the value is not a JSONArray.
+     */
+    public JSONArray getJSONArray(String key) throws JSONException {
+        Object o = get(key);
+        if (o instanceof JSONArray) {
+            return (JSONArray)o;
+        }
+        throw new JSONException("JSONObject[" + quote(key) +
+                "] is not a JSONArray.");
+    }
+
+
+    /**
+     * Get the JSONObject value associated with a key.
+     *
+     * @param key   A key string.
+     * @return      A JSONObject which is the value.
+     * @throws   JSONException if the key is not found or
+     *  if the value is not a JSONObject.
+     */
+    public JSONObject getJSONObject(String key) throws JSONException {
+        Object o = get(key);
+        if (o instanceof JSONObject) {
+            return (JSONObject)o;
+        }
+        throw new JSONException("JSONObject[" + quote(key) +
+                "] is not a JSONObject.");
+    }
+
+
+    /**
+     * Get the long value associated with a key. 
+     *
+     * @param key   A key string.
+     * @return      The long value.
+     * @throws   JSONException if the key is not found or if the value cannot
+     *  be converted to a long.
+     */
+    public long getLong(String key) throws JSONException {
+        Object o = get(key);
+        try {
+            return o instanceof Number ?
+                ((Number)o).longValue() :
+                Long.parseLong((String)o);
+        } catch (Exception e) {
+            throw new JSONException("JSONObject[" + quote(key) +
+                "] is not a long.");
+        }
+    }
+
+
+    /**
+     * Get an array of field names from a JSONObject.
+     *
+     * @return An array of field names, or null if there are no names.
+     */
+    public static String[] getNames(JSONObject jo) {
+        int length = jo.length();
+        if (length == 0) {
+            return null;
+        }
+        Iterator i = jo.keys();
+        String[] names = new String[length];
+        int j = 0;
+        while (i.hasNext()) {
+            names[j] = (String)i.next();
+            j += 1;
+        }
+        return names;
+    }
+
+
+    /**
+     * Get an array of field names from an Object.
+     *
+     * @return An array of field names, or null if there are no names.
+     */
+    public static String[] getNames(Object object) {
+        if (object == null) {
+            return null;
+        }
+        Class klass = object.getClass();
+        Field[] fields = klass.getFields();
+        int length = fields.length;
+        if (length == 0) {
+            return null;
+        }
+        String[] names = new String[length];
+        for (int i = 0; i < length; i += 1) {
+            names[i] = fields[i].getName();
+        }
+        return names;
+    }
+
+
+    /**
+     * Get the string associated with a key.
+     *
+     * @param key   A key string.
+     * @return      A string which is the value.
+     * @throws   JSONException if the key is not found.
+     */
+    public String getString(String key) throws JSONException {
+        return get(key).toString();
+    }
+
+
+    /**
+     * Determine if the JSONObject contains a specific key.
+     * @param key   A key string.
+     * @return      true if the key exists in the JSONObject.
+     */
+    public boolean has(String key) {
+        return this.map.containsKey(key);
+    }
+    
+    
+    /**
+     * Increment a property of a JSONObject. If there is no such property,
+     * create one with a value of 1. If there is such a property, and if
+     * it is an Integer, Long, Double, or Float, then add one to it.
+     * @param key  A key string.
+     * @return this.
+     * @throws JSONException If there is already a property with this name
+     * that is not an Integer, Long, Double, or Float.
+     */
+    public JSONObject increment(String key) throws JSONException {
+    	Object value = opt(key);
+    	if (value == null) {
+    		put(key, 1);
+    	} else {
+    		if (value instanceof Integer) {
+    			put(key, ((Integer)value).intValue() + 1);
+    		} else if (value instanceof Long) {
+    			put(key, ((Long)value).longValue() + 1);    			
+    		} else if (value instanceof Double) {
+	    		put(key, ((Double)value).doubleValue() + 1);    			
+    		} else if (value instanceof Float) {
+	    		put(key, ((Float)value).floatValue() + 1);    			
+		    } else {
+		    	throw new JSONException("Unable to increment [" + key + "].");
+		    }
+	    }
+    	return this;
+    }
+
+
+    /**
+     * Determine if the value associated with the key is null or if there is
+     *  no value.
+     * @param key   A key string.
+     * @return      true if there is no value associated with the key or if
+     *  the value is the JSONObject.NULL object.
+     */
+    public boolean isNull(String key) {
+        return JSONObject.NULL.equals(opt(key));
+    }
+
+
+    /**
+     * Get an enumeration of the keys of the JSONObject.
+     *
+     * @return An iterator of the keys.
+     */
+    public Iterator keys() {
+        return this.map.keySet().iterator();
+    }
+
+
+    /**
+     * Get the number of keys stored in the JSONObject.
+     *
+     * @return The number of keys in the JSONObject.
+     */
+    public int length() {
+        return this.map.size();
+    }
+
+
+    /**
+     * Produce a JSONArray containing the names of the elements of this
+     * JSONObject.
+     * @return A JSONArray containing the key strings, or null if the JSONObject
+     * is empty.
+     */
+    public JSONArray names() {
+        JSONArray ja = new JSONArray();
+        Iterator  keys = keys();
+        while (keys.hasNext()) {
+            ja.put(keys.next());
+        }
+        return ja.length() == 0 ? null : ja;
+    }
+
+    /**
+     * Produce a string from a Number.
+     * @param  n A Number
+     * @return A String.
+     * @throws JSONException If n is a non-finite number.
+     */
+    static public String numberToString(Number n)
+            throws JSONException {
+        if (n == null) {
+            throw new JSONException("Null pointer");
+        }
+        testValidity(n);
+
+// Shave off trailing zeros and decimal point, if possible.
+
+        String s = n.toString();
+        if (s.indexOf('.') > 0 && s.indexOf('e') < 0 && s.indexOf('E') < 0) {
+            while (s.endsWith("0")) {
+                s = s.substring(0, s.length() - 1);
+            }
+            if (s.endsWith(".")) {
+                s = s.substring(0, s.length() - 1);
+            }
+        }
+        return s;
+    }
+
+
+    /**
+     * Get an optional value associated with a key.
+     * @param key   A key string.
+     * @return      An object which is the value, or null if there is no value.
+     */
+    public Object opt(String key) {
+        return key == null ? null : this.map.get(key);
+    }
+
+
+    /**
+     * Get an optional boolean associated with a key.
+     * It returns false if there is no such key, or if the value is not
+     * Boolean.TRUE or the String "true".
+     *
+     * @param key   A key string.
+     * @return      The truth.
+     */
+    public boolean optBoolean(String key) {
+        return optBoolean(key, false);
+    }
+
+
+    /**
+     * Get an optional boolean associated with a key.
+     * It returns the defaultValue if there is no such key, or if it is not
+     * a Boolean or the String "true" or "false" (case insensitive).
+     *
+     * @param key              A key string.
+     * @param defaultValue     The default.
+     * @return      The truth.
+     */
+    public boolean optBoolean(String key, boolean defaultValue) {
+        try {
+            return getBoolean(key);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+
+    /**
+     * Get an optional double associated with a key,
+     * or NaN if there is no such key or if its value is not a number.
+     * If the value is a string, an attempt will be made to evaluate it as
+     * a number.
+     *
+     * @param key   A string which is the key.
+     * @return      An object which is the value.
+     */
+    public double optDouble(String key) {
+        return optDouble(key, Double.NaN);
+    }
+
+
+    /**
+     * Get an optional double associated with a key, or the
+     * defaultValue if there is no such key or if its value is not a number.
+     * If the value is a string, an attempt will be made to evaluate it as
+     * a number.
+     *
+     * @param key   A key string.
+     * @param defaultValue     The default.
+     * @return      An object which is the value.
+     */
+    public double optDouble(String key, double defaultValue) {
+        try {
+            Object o = opt(key);
+            return o instanceof Number ? ((Number)o).doubleValue() :
+                new Double((String)o).doubleValue();
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+
+    /**
+     * Get an optional int value associated with a key,
+     * or zero if there is no such key or if the value is not a number.
+     * If the value is a string, an attempt will be made to evaluate it as
+     * a number.
+     *
+     * @param key   A key string.
+     * @return      An object which is the value.
+     */
+    public int optInt(String key) {
+        return optInt(key, 0);
+    }
+
+
+    /**
+     * Get an optional int value associated with a key,
+     * or the default if there is no such key or if the value is not a number.
+     * If the value is a string, an attempt will be made to evaluate it as
+     * a number.
+     *
+     * @param key   A key string.
+     * @param defaultValue     The default.
+     * @return      An object which is the value.
+     */
+    public int optInt(String key, int defaultValue) {
+        try {
+            return getInt(key);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+
+    /**
+     * Get an optional JSONArray associated with a key.
+     * It returns null if there is no such key, or if its value is not a
+     * JSONArray.
+     *
+     * @param key   A key string.
+     * @return      A JSONArray which is the value.
+     */
+    public JSONArray optJSONArray(String key) {
+        Object o = opt(key);
+        return o instanceof JSONArray ? (JSONArray)o : null;
+    }
+
+
+    /**
+     * Get an optional JSONObject associated with a key.
+     * It returns null if there is no such key, or if its value is not a
+     * JSONObject.
+     *
+     * @param key   A key string.
+     * @return      A JSONObject which is the value.
+     */
+    public JSONObject optJSONObject(String key) {
+        Object o = opt(key);
+        return o instanceof JSONObject ? (JSONObject)o : null;
+    }
+
+
+    /**
+     * Get an optional long value associated with a key,
+     * or zero if there is no such key or if the value is not a number.
+     * If the value is a string, an attempt will be made to evaluate it as
+     * a number.
+     *
+     * @param key   A key string.
+     * @return      An object which is the value.
+     */
+    public long optLong(String key) {
+        return optLong(key, 0);
+    }
+
+
+    /**
+     * Get an optional long value associated with a key,
+     * or the default if there is no such key or if the value is not a number.
+     * If the value is a string, an attempt will be made to evaluate it as
+     * a number.
+     *
+     * @param key   A key string.
+     * @param defaultValue     The default.
+     * @return      An object which is the value.
+     */
+    public long optLong(String key, long defaultValue) {
+        try {
+            return getLong(key);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+
+    /**
+     * Get an optional string associated with a key.
+     * It returns an empty string if there is no such key. If the value is not
+     * a string and is not null, then it is coverted to a string.
+     *
+     * @param key   A key string.
+     * @return      A string which is the value.
+     */
+    public String optString(String key) {
+        return optString(key, "");
+    }
+
+
+    /**
+     * Get an optional string associated with a key.
+     * It returns the defaultValue if there is no such key.
+     *
+     * @param key   A key string.
+     * @param defaultValue     The default.
+     * @return      A string which is the value.
+     */
+    public String optString(String key, String defaultValue) {
+        Object o = opt(key);
+        return o != null ? o.toString() : defaultValue;
+    }
+
+
+    private void populateMap(Object bean) {
+        Class klass = bean.getClass();
+
+// If klass is a System class then set includeSuperClass to false. 
+
+        boolean includeSuperClass = klass.getClassLoader() != null;
+
+        Method[] methods = (includeSuperClass) ?
+                klass.getMethods() : klass.getDeclaredMethods();
+        for (int i = 0; i < methods.length; i += 1) {
+            try {
+                Method method = methods[i];
+                if (Modifier.isPublic(method.getModifiers())) {
+                    String name = method.getName();
+                    String key = "";
+                    if (name.startsWith("get")) {
+                    	if (name.equals("getClass") || 
+                    			name.equals("getDeclaringClass")) {
+                    		key = "";
+                    	} else {
+                    		key = name.substring(3);
+                    	}
+                    } else if (name.startsWith("is")) {
+                        key = name.substring(2);
+                    }
+                    if (key.length() > 0 &&
+                            Character.isUpperCase(key.charAt(0)) &&
+                            method.getParameterTypes().length == 0) {
+                        if (key.length() == 1) {
+                            key = key.toLowerCase();
+                        } else if (!Character.isUpperCase(key.charAt(1))) {
+                            key = key.substring(0, 1).toLowerCase() +
+                                key.substring(1);
+                        }
+
+                        Object result = method.invoke(bean, (Object[])null);
+
+                        map.put(key, wrap(result));
+                    }
+                }
+            } catch (Exception ignore) {
+            }
+        }
+    }
+
+
+    /**
+     * Put a key/boolean pair in the JSONObject.
+     *
+     * @param key   A key string.
+     * @param value A boolean which is the value.
+     * @return this.
+     * @throws JSONException If the key is null.
+     */
+    public JSONObject put(String key, boolean value) throws JSONException {
+        put(key, value ? Boolean.TRUE : Boolean.FALSE);
+        return this;
+    }
+
+
+    /**
+     * Put a key/value pair in the JSONObject, where the value will be a
+     * JSONArray which is produced from a Collection.
+     * @param key   A key string.
+     * @param value A Collection value.
+     * @return      this.
+     * @throws JSONException
+     */
+    public JSONObject put(String key, Collection value) throws JSONException {
+        put(key, new JSONArray(value));
+        return this;
+    }
+
+
+    /**
+     * Put a key/double pair in the JSONObject.
+     *
+     * @param key   A key string.
+     * @param value A double which is the value.
+     * @return this.
+     * @throws JSONException If the key is null or if the number is invalid.
+     */
+    public JSONObject put(String key, double value) throws JSONException {
+        put(key, new Double(value));
+        return this;
+    }
+
+
+    /**
+     * Put a key/int pair in the JSONObject.
+     *
+     * @param key   A key string.
+     * @param value An int which is the value.
+     * @return this.
+     * @throws JSONException If the key is null.
+     */
+    public JSONObject put(String key, int value) throws JSONException {
+        put(key, new Integer(value));
+        return this;
+    }
+
+
+    /**
+     * Put a key/long pair in the JSONObject.
+     *
+     * @param key   A key string.
+     * @param value A long which is the value.
+     * @return this.
+     * @throws JSONException If the key is null.
+     */
+    public JSONObject put(String key, long value) throws JSONException {
+        put(key, new Long(value));
+        return this;
+    }
+
+
+    /**
+     * Put a key/value pair in the JSONObject, where the value will be a
+     * JSONObject which is produced from a Map.
+     * @param key   A key string.
+     * @param value A Map value.
+     * @return      this.
+     * @throws JSONException
+     */
+    public JSONObject put(String key, Map value) throws JSONException {
+        put(key, new JSONObject(value));
+        return this;
+    }
+
+
+    /**
+     * Put a key/value pair in the JSONObject. If the value is null,
+     * then the key will be removed from the JSONObject if it is present.
+     * @param key   A key string.
+     * @param value An object which is the value. It should be of one of these
+     *  types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
+     *  or the JSONObject.NULL object.
+     * @return this.
+     * @throws JSONException If the value is non-finite number
+     *  or if the key is null.
+     */
+    public JSONObject put(String key, Object value) throws JSONException {
+        if (key == null) {
+            throw new JSONException("Null key.");
+        }
+        if (value != null) {
+            testValidity(value);
+            this.map.put(key, value);
+        } else {
+            remove(key);
+        }
+        return this;
+    }
+
+
+    /**
+     * Put a key/value pair in the JSONObject, but only if the key and the
+     * value are both non-null, and only if there is not already a member
+     * with that name.
+     * @param key
+     * @param value
+     * @return his.
+     * @throws JSONException if the key is a duplicate
+     */
+    public JSONObject putOnce(String key, Object value) throws JSONException {
+        if (key != null && value != null) {
+            if (opt(key) != null) {
+                throw new JSONException("Duplicate key \"" + key + "\"");
+            }
+            put(key, value);
+        }
+        return this;
+    }
+
+
+    /**
+     * Put a key/value pair in the JSONObject, but only if the
+     * key and the value are both non-null.
+     * @param key   A key string.
+     * @param value An object which is the value. It should be of one of these
+     *  types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
+     *  or the JSONObject.NULL object.
+     * @return this.
+     * @throws JSONException If the value is a non-finite number.
+     */
+    public JSONObject putOpt(String key, Object value) throws JSONException {
+        if (key != null && value != null) {
+            put(key, value);
+        }
+        return this;
+    }
+
+
+    /**
+     * Produce a string in double quotes with backslash sequences in all the
+     * right places. A backslash will be inserted within </, allowing JSON
+     * text to be delivered in HTML. In JSON text, a string cannot contain a
+     * control character or an unescaped quote or backslash.
+     * @param string A String
+     * @return  A String correctly formatted for insertion in a JSON text.
+     */
+    public static String quote(String string) {
+        if (string == null || string.length() == 0) {
+            return "\"\"";
+        }
+
+        char         b;
+        char         c = 0;
+        int          i;
+        int          len = string.length();
+        StringBuffer sb = new StringBuffer(len + 4);
+        String       t;
+
+        sb.append('"');
+        for (i = 0; i < len; i += 1) {
+            b = c;
+            c = string.charAt(i);
+            switch (c) {
+            case '\\':
+            case '"':
+                sb.append('\\');
+                sb.append(c);
+                break;
+            case '/':
+                if (b == '<') {
+                    sb.append('\\');
+                }
+                sb.append(c);
+                break;
+            case '\b':
+                sb.append("\\b");
+                break;
+            case '\t':
+                sb.append("\\t");
+                break;
+            case '\n':
+                sb.append("\\n");
+                break;
+            case '\f':
+                sb.append("\\f");
+                break;
+            case '\r':
+                sb.append("\\r");
+                break;
+            default:
+                if (c < ' ' || (c >= '\u0080' && c < '\u00a0') ||
+                               (c >= '\u2000' && c < '\u2100')) {
+                    t = "000" + Integer.toHexString(c);
+                    sb.append("\\u" + t.substring(t.length() - 4));
+                } else {
+                    sb.append(c);
+                }
+            }
+        }
+        sb.append('"');
+        return sb.toString();
+    }
+
+    /**
+     * Remove a name and its value, if present.
+     * @param key The name to be removed.
+     * @return The value that was associated with the name,
+     * or null if there was no value.
+     */
+    public Object remove(String key) {
+        return this.map.remove(key);
+    }
+
+    /**
+     * Get an enumeration of the keys of the JSONObject.
+     * The keys will be sorted alphabetically.
+     *
+     * @return An iterator of the keys.
+     */
+    public Iterator sortedKeys() {
+      return new TreeSet(this.map.keySet()).iterator();
+    }
+
+    /**
+     * Try to convert a string into a number, boolean, or null. If the string
+     * can't be converted, return the string.
+     * @param s A String.
+     * @return A simple JSON value.
+     */
+    static public Object stringToValue(String s) {
+        if (s.equals("")) {
+            return s;
+        }
+        if (s.equalsIgnoreCase("true")) {
+            return Boolean.TRUE;
+        }
+        if (s.equalsIgnoreCase("false")) {
+            return Boolean.FALSE;
+        }
+        if (s.equalsIgnoreCase("null")) {
+            return JSONObject.NULL;
+        }
+
+        /*
+         * If it might be a number, try converting it. 
+         * We support the non-standard 0x- convention. 
+         * If a number cannot be produced, then the value will just
+         * be a string. Note that the 0x-, plus, and implied string
+         * conventions are non-standard. A JSON parser may accept
+         * non-JSON forms as long as it accepts all correct JSON forms.
+         */
+
+        char b = s.charAt(0);
+        if ((b >= '0' && b <= '9') || b == '.' || b == '-' || b == '+') {
+            if (b == '0' && s.length() > 2 &&
+                        (s.charAt(1) == 'x' || s.charAt(1) == 'X')) {
+                try {
+                    return new Integer(Integer.parseInt(s.substring(2), 16));
+                } catch (Exception ignore) {
+                }
+            }
+            try {
+                if (s.indexOf('.') > -1 || 
+                		s.indexOf('e') > -1 || s.indexOf('E') > -1) {
+                    return Double.valueOf(s);
+                } else {
+                    Long myLong = new Long(s);
+                    if (myLong.longValue() == myLong.intValue()) {
+                        return new Integer(myLong.intValue());
+                    } else {
+                        return myLong;
+                    }
+                }
+            }  catch (Exception ignore) {
+            }
+        }
+        return s;
+    }
+
+
+    /**
+     * Throw an exception if the object is an NaN or infinite number.
+     * @param o The object to test.
+     * @throws JSONException If o is a non-finite number.
+     */
+    static void testValidity(Object o) throws JSONException {
+        if (o != null) {
+            if (o instanceof Double) {
+                if (((Double)o).isInfinite() || ((Double)o).isNaN()) {
+                    throw new JSONException(
+                        "JSON does not allow non-finite numbers.");
+                }
+            } else if (o instanceof Float) {
+                if (((Float)o).isInfinite() || ((Float)o).isNaN()) {
+                    throw new JSONException(
+                        "JSON does not allow non-finite numbers.");
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Produce a JSONArray containing the values of the members of this
+     * JSONObject.
+     * @param names A JSONArray containing a list of key strings. This
+     * determines the sequence of the values in the result.
+     * @return A JSONArray of values.
+     * @throws JSONException If any of the values are non-finite numbers.
+     */
+    public JSONArray toJSONArray(JSONArray names) throws JSONException {
+        if (names == null || names.length() == 0) {
+            return null;
+        }
+        JSONArray ja = new JSONArray();
+        for (int i = 0; i < names.length(); i += 1) {
+            ja.put(this.opt(names.getString(i)));
+        }
+        return ja;
+    }
+
+    /**
+     * Make a JSON text of this JSONObject. For compactness, no whitespace
+     * is added. If this would not result in a syntactically correct JSON text,
+     * then null will be returned instead.
+     * <p>
+     * Warning: This method assumes that the data structure is acyclical.
+     *
+     * @return a printable, displayable, portable, transmittable
+     *  representation of the object, beginning
+     *  with <code>{</code> <small>(left brace)</small> and ending
+     *  with <code>}</code> <small>(right brace)</small>.
+     */
+    public String toString() {
+        try {
+            Iterator     keys = keys();
+            StringBuffer sb = new StringBuffer("{");
+
+            while (keys.hasNext()) {
+                if (sb.length() > 1) {
+                    sb.append(',');
+                }
+                Object o = keys.next();
+                sb.append(quote(o.toString()));
+                sb.append(':');
+                sb.append(valueToString(this.map.get(o)));
+            }
+            sb.append('}');
+            return sb.toString();
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+
+    /**
+     * Make a prettyprinted JSON text of this JSONObject.
+     * <p>
+     * Warning: This method assumes that the data structure is acyclical.
+     * @param indentFactor The number of spaces to add to each level of
+     *  indentation.
+     * @return a printable, displayable, portable, transmittable
+     *  representation of the object, beginning
+     *  with <code>{</code> <small>(left brace)</small> and ending
+     *  with <code>}</code> <small>(right brace)</small>.
+     * @throws JSONException If the object contains an invalid number.
+     */
+    public String toString(int indentFactor) throws JSONException {
+        return toString(indentFactor, 0);
+    }
+
+
+    /**
+     * Make a prettyprinted JSON text of this JSONObject.
+     * <p>
+     * Warning: This method assumes that the data structure is acyclical.
+     * @param indentFactor The number of spaces to add to each level of
+     *  indentation.
+     * @param indent The indentation of the top level.
+     * @return a printable, displayable, transmittable
+     *  representation of the object, beginning
+     *  with <code>{</code> <small>(left brace)</small> and ending
+     *  with <code>}</code> <small>(right brace)</small>.
+     * @throws JSONException If the object contains an invalid number.
+     */
+    String toString(int indentFactor, int indent) throws JSONException {
+        int j;
+        int n = length();
+        if (n == 0) {
+            return "{}";
+        }
+        Iterator     keys = sortedKeys();
+        StringBuffer sb = new StringBuffer("{");
+        int          newindent = indent + indentFactor;
+        Object       o;
+        if (n == 1) {
+            o = keys.next();
+            sb.append(quote(o.toString()));
+            sb.append(": ");
+            sb.append(valueToString(this.map.get(o), indentFactor,
+                    indent));
+        } else {
+            while (keys.hasNext()) {
+                o = keys.next();
+                if (sb.length() > 1) {
+                    sb.append(",\n");
+                } else {
+                    sb.append('\n');
+                }
+                for (j = 0; j < newindent; j += 1) {
+                    sb.append(' ');
+                }
+                sb.append(quote(o.toString()));
+                sb.append(": ");
+                sb.append(valueToString(this.map.get(o), indentFactor,
+                        newindent));
+            }
+            if (sb.length() > 1) {
+                sb.append('\n');
+                for (j = 0; j < indent; j += 1) {
+                    sb.append(' ');
+                }
+            }
+        }
+        sb.append('}');
+        return sb.toString();
+    }
+
+
+    /**
+     * Make a JSON text of an Object value. If the object has an
+     * value.toJSONString() method, then that method will be used to produce
+     * the JSON text. The method is required to produce a strictly
+     * conforming text. If the object does not contain a toJSONString
+     * method (which is the most common case), then a text will be
+     * produced by other means. If the value is an array or Collection,
+     * then a JSONArray will be made from it and its toJSONString method
+     * will be called. If the value is a MAP, then a JSONObject will be made
+     * from it and its toJSONString method will be called. Otherwise, the
+     * value's toString method will be called, and the result will be quoted.
+     *
+     * <p>
+     * Warning: This method assumes that the data structure is acyclical.
+     * @param value The value to be serialized.
+     * @return a printable, displayable, transmittable
+     *  representation of the object, beginning
+     *  with <code>{</code> <small>(left brace)</small> and ending
+     *  with <code>}</code> <small>(right brace)</small>.
+     * @throws JSONException If the value is or contains an invalid number.
+     */
+    static String valueToString(Object value) throws JSONException {
+        if (value == null || value.equals(null)) {
+            return "null";
+        }
+        if (value instanceof JSONString) {
+            Object o;
+            try {
+                o = ((JSONString)value).toJSONString();
+            } catch (Exception e) {
+                throw new JSONException(e);
+            }
+            if (o instanceof String) {
+                return (String)o;
+            }
+            throw new JSONException("Bad value from toJSONString: " + o);
+        }
+        if (value instanceof Number) {
+            return numberToString((Number) value);
+        }
+        if (value instanceof Boolean || value instanceof JSONObject ||
+                value instanceof JSONArray) {
+            return value.toString();
+        }
+        if (value instanceof Map) {
+            return new JSONObject((Map)value).toString();
+        }
+        if (value instanceof Collection) {
+            return new JSONArray((Collection)value).toString();
+        }
+        if (value.getClass().isArray()) {
+            return new JSONArray(value).toString();
+        }
+        return quote(value.toString());
+    }
+
+
+    /**
+     * Make a prettyprinted JSON text of an object value.
+     * <p>
+     * Warning: This method assumes that the data structure is acyclical.
+     * @param value The value to be serialized.
+     * @param indentFactor The number of spaces to add to each level of
+     *  indentation.
+     * @param indent The indentation of the top level.
+     * @return a printable, displayable, transmittable
+     *  representation of the object, beginning
+     *  with <code>{</code> <small>(left brace)</small> and ending
+     *  with <code>}</code> <small>(right brace)</small>.
+     * @throws JSONException If the object contains an invalid number.
+     */
+     static String valueToString(Object value, int indentFactor, int indent)
+            throws JSONException {
+        if (value == null || value.equals(null)) {
+            return "null";
+        }
+        try {
+            if (value instanceof JSONString) {
+                Object o = ((JSONString)value).toJSONString();
+                if (o instanceof String) {
+                    return (String)o;
+                }
+            }
+        } catch (Exception ignore) {
+        }
+        if (value instanceof Number) {
+            return numberToString((Number) value);
+        }
+        if (value instanceof Boolean) {
+            return value.toString();
+        }
+        if (value instanceof JSONObject) {
+            return ((JSONObject)value).toString(indentFactor, indent);
+        }
+        if (value instanceof JSONArray) {
+            return ((JSONArray)value).toString(indentFactor, indent);
+        }
+        if (value instanceof Map) {
+            return new JSONObject((Map)value).toString(indentFactor, indent);
+        }
+        if (value instanceof Collection) {
+            return new JSONArray((Collection)value).toString(indentFactor, indent);
+        }
+        if (value.getClass().isArray()) {
+            return new JSONArray(value).toString(indentFactor, indent);
+        }
+        return quote(value.toString());
+    }
+
+
+     /**
+      * Wrap an object, if necessary. If the object is null, return the NULL 
+      * object. If it is an array or collection, wrap it in a JSONArray. If 
+      * it is a map, wrap it in a JSONObject. If it is a standard property 
+      * (Double, String, et al) then it is already wrapped. Otherwise, if it 
+      * comes from one of the java packages, turn it into a string. And if 
+      * it doesn't, try to wrap it in a JSONObject. If the wrapping fails,
+      * then null is returned.
+      *
+      * @param object The object to wrap
+      * @return The wrapped value
+      */
+     static Object wrap(Object object) {
+         try {
+             if (object == null) {
+                 return NULL;
+             }
+             if (object instanceof JSONObject || object instanceof JSONArray || 
+            		 NULL.equals(object)      || object instanceof JSONString || 
+            		 object instanceof Byte   || object instanceof Character ||
+                     object instanceof Short  || object instanceof Integer   ||
+                     object instanceof Long   || object instanceof Boolean   || 
+                     object instanceof Float  || object instanceof Double    ||
+                     object instanceof String) {
+                 return object;
+             }
+             
+             if (object instanceof Collection) {
+                 return new JSONArray((Collection)object);
+             }
+             if (object.getClass().isArray()) {
+                 return new JSONArray(object);
+             }
+             if (object instanceof Map) {
+                 return new JSONObject((Map)object);
+             }
+             Package objectPackage = object.getClass().getPackage();
+             String objectPackageName = ( objectPackage != null ? objectPackage.getName() : "" );
+             if (objectPackageName.startsWith("java.") ||
+            		 objectPackageName.startsWith("javax.") ||
+            		 object.getClass().getClassLoader() == null) {
+                 return object.toString();
+             }
+             return new JSONObject(object);
+         } catch(Exception exception) {
+             return null;
+         }
+     }
+
+     
+     /**
+      * Write the contents of the JSONObject as JSON text to a writer.
+      * For compactness, no whitespace is added.
+      * <p>
+      * Warning: This method assumes that the data structure is acyclical.
+      *
+      * @return The writer.
+      * @throws JSONException
+      */
+     public Writer write(Writer writer) throws JSONException {
+        try {
+            boolean  b = false;
+            Iterator keys = keys();
+            writer.write('{');
+
+            while (keys.hasNext()) {
+                if (b) {
+                    writer.write(',');
+                }
+                Object k = keys.next();
+                writer.write(quote(k.toString()));
+                writer.write(':');
+                Object v = this.map.get(k);
+                if (v instanceof JSONObject) {
+                    ((JSONObject)v).write(writer);
+                } else if (v instanceof JSONArray) {
+                    ((JSONArray)v).write(writer);
+                } else {
+                    writer.write(valueToString(v));
+                }
+                b = true;
+            }
+            writer.write('}');
+            return writer;
+        } catch (IOException exception) {
+            throw new JSONException(exception);
+        }
+     }
+}
\ No newline at end of file
diff --git a/livegps/src/org/json/JSONString.java b/livegps/src/org/json/JSONString.java
new file mode 100644
index 0000000..17f4384
--- /dev/null
+++ b/livegps/src/org/json/JSONString.java
@@ -0,0 +1,18 @@
+package org.json;
+/**
+ * The <code>JSONString</code> interface allows a <code>toJSONString()</code> 
+ * method so that a class can change the behavior of 
+ * <code>JSONObject.toString()</code>, <code>JSONArray.toString()</code>,
+ * and <code>JSONWriter.value(</code>Object<code>)</code>. The 
+ * <code>toJSONString</code> method will be used instead of the default behavior 
+ * of using the Object's <code>toString()</code> method and quoting the result.
+ */
+public interface JSONString {
+	/**
+	 * The <code>toJSONString</code> method allows a class to produce its own JSON 
+	 * serialization. 
+	 * 
+	 * @return A strictly syntactically correct JSON text.
+	 */
+	public String toJSONString();
+}
diff --git a/livegps/src/org/json/JSONTokener.java b/livegps/src/org/json/JSONTokener.java
new file mode 100644
index 0000000..fe52f31
--- /dev/null
+++ b/livegps/src/org/json/JSONTokener.java
@@ -0,0 +1,435 @@
+package org.json;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+/**
+ * A JSONTokener takes a source string and extracts characters and tokens from
+ * it. It is used by the JSONObject and JSONArray constructors to parse
+ * JSON source strings.
+ * @author JSON.org
+ * @version 2010-02-02
+ */
+public class JSONTokener {
+
+    private int 	character;
+	private boolean eof;
+    private int 	index;
+    private int 	line;
+    private char 	previous;
+    private Reader 	reader;
+    private boolean usePrevious;
+
+
+    /**
+     * Construct a JSONTokener from a reader.
+     *
+     * @param reader     A reader.
+     */
+    public JSONTokener(Reader reader) {
+        this.reader = reader.markSupported() ? 
+        		reader : new BufferedReader(reader);
+        this.eof = false;
+        this.usePrevious = false;
+        this.previous = 0;
+        this.index = 0;
+        this.character = 1;
+        this.line = 1;
+    }
+
+
+    /**
+     * Construct a JSONTokener from a string.
+     *
+     * @param s     A source string.
+     */
+    public JSONTokener(String s) {
+        this(new StringReader(s));
+    }
+
+
+    /**
+     * Back up one character. This provides a sort of lookahead capability,
+     * so that you can test for a digit or letter before attempting to parse
+     * the next number or identifier.
+     */
+    public void back() throws JSONException {
+        if (usePrevious || index <= 0) {
+            throw new JSONException("Stepping back two steps is not supported");
+        }
+        this.index -= 1;
+        this.character -= 1;
+        this.usePrevious = true;
+        this.eof = false;
+    }
+
+
+    /**
+     * Get the hex value of a character (base16).
+     * @param c A character between '0' and '9' or between 'A' and 'F' or
+     * between 'a' and 'f'.
+     * @return  An int between 0 and 15, or -1 if c was not a hex digit.
+     */
+    public static int dehexchar(char c) {
+        if (c >= '0' && c <= '9') {
+            return c - '0';
+        }
+        if (c >= 'A' && c <= 'F') {
+            return c - ('A' - 10);
+        }
+        if (c >= 'a' && c <= 'f') {
+            return c - ('a' - 10);
+        }
+        return -1;
+    }
+    
+    public boolean end() {
+    	return eof && !usePrevious;    	
+    }
+
+
+    /**
+     * Determine if the source string still contains characters that next()
+     * can consume.
+     * @return true if not yet at the end of the source.
+     */
+    public boolean more() throws JSONException {
+        next();
+        if (end()) {
+            return false;
+        } 
+        back();
+        return true;
+    }
+
+
+    /**
+     * Get the next character in the source string.
+     *
+     * @return The next character, or 0 if past the end of the source string.
+     */
+    public char next() throws JSONException {
+        int c;
+        if (this.usePrevious) {
+        	this.usePrevious = false;
+            c = this.previous;
+        } else {
+	        try {
+	            c = this.reader.read();
+	        } catch (IOException exception) {
+	            throw new JSONException(exception);
+	        }
+	
+	        if (c <= 0) { // End of stream
+	        	this.eof = true;
+	        	c = 0;
+	        } 
+        }
+    	this.index += 1;
+    	if (this.previous == '\r') {
+    		this.line += 1;
+    		this.character = c == '\n' ? 0 : 1;
+    	} else if (c == '\n') {
+    		this.line += 1;
+    		this.character = 0;
+    	} else {
+    		this.character += 1;
+    	}
+    	this.previous = (char) c;
+        return this.previous;
+    }
+
+
+    /**
+     * Consume the next character, and check that it matches a specified
+     * character.
+     * @param c The character to match.
+     * @return The character.
+     * @throws JSONException if the character does not match.
+     */
+    public char next(char c) throws JSONException {
+        char n = next();
+        if (n != c) {
+            throw syntaxError("Expected '" + c + "' and instead saw '" +
+                    n + "'");
+        }
+        return n;
+    }
+
+
+    /**
+     * Get the next n characters.
+     *
+     * @param n     The number of characters to take.
+     * @return      A string of n characters.
+     * @throws JSONException
+     *   Substring bounds error if there are not
+     *   n characters remaining in the source string.
+     */
+     public String next(int n) throws JSONException {
+         if (n == 0) {
+             return "";
+         }
+
+         char[] buffer = new char[n];
+         int pos = 0;
+
+         while (pos < n) {
+             buffer[pos] = next();
+             if (end()) {
+                 throw syntaxError("Substring bounds error");                 
+             }
+             pos += 1;
+         }
+         return new String(buffer);
+     }
+
+
+    /**
+     * Get the next char in the string, skipping whitespace.
+     * @throws JSONException
+     * @return  A character, or 0 if there are no more characters.
+     */
+    public char nextClean() throws JSONException {
+        for (;;) {
+            char c = next();
+            if (c == 0 || c > ' ') {
+                return c;
+            }
+        }
+    }
+
+
+    /**
+     * Return the characters up to the next close quote character.
+     * Backslash processing is done. The formal JSON format does not
+     * allow strings in single quotes, but an implementation is allowed to
+     * accept them.
+     * @param quote The quoting character, either
+     *      <code>"</code> <small>(double quote)</small> or
+     *      <code>'</code> <small>(single quote)</small>.
+     * @return      A String.
+     * @throws JSONException Unterminated string.
+     */
+    public String nextString(char quote) throws JSONException {
+        char c;
+        StringBuffer sb = new StringBuffer();
+        for (;;) {
+            c = next();
+            switch (c) {
+            case 0:
+            case '\n':
+            case '\r':
+                throw syntaxError("Unterminated string");
+            case '\\':
+                c = next();
+                switch (c) {
+                case 'b':
+                    sb.append('\b');
+                    break;
+                case 't':
+                    sb.append('\t');
+                    break;
+                case 'n':
+                    sb.append('\n');
+                    break;
+                case 'f':
+                    sb.append('\f');
+                    break;
+                case 'r':
+                    sb.append('\r');
+                    break;
+                case 'u':
+                    sb.append((char)Integer.parseInt(next(4), 16));
+                    break;
+                case '"':
+                case '\'':
+                case '\\':
+                case '/':
+                	sb.append(c);
+                	break;
+                default:
+                    throw syntaxError("Illegal escape.");
+                }
+                break;
+            default:
+                if (c == quote) {
+                    return sb.toString();
+                }
+                sb.append(c);
+            }
+        }
+    }
+
+
+    /**
+     * Get the text up but not including the specified character or the
+     * end of line, whichever comes first.
+     * @param  d A delimiter character.
+     * @return   A string.
+     */
+    public String nextTo(char d) throws JSONException {
+        StringBuffer sb = new StringBuffer();
+        for (;;) {
+            char c = next();
+            if (c == d || c == 0 || c == '\n' || c == '\r') {
+                if (c != 0) {
+                    back();
+                }
+                return sb.toString().trim();
+            }
+            sb.append(c);
+        }
+    }
+
+
+    /**
+     * Get the text up but not including one of the specified delimiter
+     * characters or the end of line, whichever comes first.
+     * @param delimiters A set of delimiter characters.
+     * @return A string, trimmed.
+     */
+    public String nextTo(String delimiters) throws JSONException {
+        char c;
+        StringBuffer sb = new StringBuffer();
+        for (;;) {
+            c = next();
+            if (delimiters.indexOf(c) >= 0 || c == 0 ||
+                    c == '\n' || c == '\r') {
+                if (c != 0) {
+                    back();
+                }
+                return sb.toString().trim();
+            }
+            sb.append(c);
+        }
+    }
+
+
+    /**
+     * Get the next value. The value can be a Boolean, Double, Integer,
+     * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
+     * @throws JSONException If syntax error.
+     *
+     * @return An object.
+     */
+    public Object nextValue() throws JSONException {
+        char c = nextClean();
+        String s;
+
+        switch (c) {
+            case '"':
+            case '\'':
+                return nextString(c);
+            case '{':
+                back();
+                return new JSONObject(this);
+            case '[':
+            case '(':
+                back();
+                return new JSONArray(this);
+        }
+
+        /*
+         * Handle unquoted text. This could be the values true, false, or
+         * null, or it can be a number. An implementation (such as this one)
+         * is allowed to also accept non-standard forms.
+         *
+         * Accumulate characters until we reach the end of the text or a
+         * formatting character.
+         */
+
+        StringBuffer sb = new StringBuffer();
+        while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
+            sb.append(c);
+            c = next();
+        }
+        back();
+
+        s = sb.toString().trim();
+        if (s.equals("")) {
+            throw syntaxError("Missing value");
+        }
+        return JSONObject.stringToValue(s);
+    }
+
+
+    /**
+     * Skip characters until the next character is the requested character.
+     * If the requested character is not found, no characters are skipped.
+     * @param to A character to skip to.
+     * @return The requested character, or zero if the requested character
+     * is not found.
+     */
+    public char skipTo(char to) throws JSONException {
+        char c;
+        try {
+            int startIndex = this.index;
+            int startCharacter = this.character;
+            int startLine = this.line;
+            reader.mark(Integer.MAX_VALUE);
+            do {
+                c = next();
+                if (c == 0) {
+                    reader.reset();
+                    this.index = startIndex;
+                    this.character = startCharacter;
+                    this.line = startLine;
+                    return c;
+                }
+            } while (c != to);
+        } catch (IOException exc) {
+            throw new JSONException(exc);
+        }
+
+        back();
+        return c;
+    }
+    
+
+    /**
+     * Make a JSONException to signal a syntax error.
+     *
+     * @param message The error message.
+     * @return  A JSONException object, suitable for throwing
+     */
+    public JSONException syntaxError(String message) {
+        return new JSONException(message + toString());
+    }
+
+
+    /**
+     * Make a printable string of this JSONTokener.
+     *
+     * @return " at {index} [character {character} line {line}]"
+     */
+    public String toString() {
+        return " at " + index + " [character " + this.character + " line " + this.line + "]";
+    }
+}
\ No newline at end of file
diff --git a/measurement/src/org/openstreetmap/josm/plugins/measurement/MeasurementDialog.java b/measurement/src/org/openstreetmap/josm/plugins/measurement/MeasurementDialog.java
index 3bb7a1a..288d4ee 100644
--- a/measurement/src/org/openstreetmap/josm/plugins/measurement/MeasurementDialog.java
+++ b/measurement/src/org/openstreetmap/josm/plugins/measurement/MeasurementDialog.java
@@ -102,44 +102,42 @@ public class MeasurementDialog extends ToggleDialog implements ActionListener
 
         this.setPreferredSize(new Dimension(0, 92));
         final MeasurementDialog dlg = this;
-       //TODO: is this enough?
-
-        DataSet.selListeners.add(new SelectionChangedListener(){
 
+        DataSet.selListeners.add(new SelectionChangedListener() {
             public void selectionChanged(Collection<? extends OsmPrimitive> arg0) {
                 double length = 0.0;
                 double segAngle = 0.0;
-                                double area = 0.0;
-                                Node lastNode = null;
-                for(OsmPrimitive p:arg0){
-                                    // ignore incomplete nodes
-                                    if(p instanceof Node && !((Node)p).isIncomplete()){
-                                        Node n =(Node)p;
-                                        if(lastNode == null){
-                                            lastNode = n;
-                                        }else{
-                                            length += MeasurementLayer.calcDistance(lastNode.getCoor(), n.getCoor());
-                                            segAngle = MeasurementLayer.angleBetween(lastNode.getCoor(), n.getCoor());
-                                            lastNode = n;
-                                        }
-                                    } else if(p instanceof Way){
-                                        Way w = (Way)p;
-                                        Node lastN = null;
-                                        for(Node n: w.getNodes()){
-                                            if(lastN != null){
-                                                length += MeasurementLayer.calcDistance(lastN.getCoor(), n.getCoor());
-                                                //http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/
-                                                area += (MeasurementLayer.calcX(n.getCoor()) * MeasurementLayer.calcY(lastN.getCoor()))
-                              - (MeasurementLayer.calcY(n.getCoor()) * MeasurementLayer.calcX(lastN.getCoor()));
-                                            }
-                                            lastN = n;
-                                        }
-                                        if (lastN != null && lastN == w.getNodes().iterator().next()){
-                                            area = Math.abs(area / 2);
-                                        }else{
-                                            area = 0;
-                                        }
-                                    }
+                double area = 0.0;
+                Node lastNode = null;
+                for(OsmPrimitive p:arg0) {
+                    // ignore incomplete nodes
+                    if(p instanceof Node && !((Node)p).isIncomplete()) {
+                        Node n =(Node)p;
+                        if(lastNode == null) {
+                            lastNode = n;
+                        } else {
+                            length += MeasurementLayer.calcDistance(lastNode.getCoor(), n.getCoor());
+                            segAngle = MeasurementLayer.angleBetween(lastNode.getCoor(), n.getCoor());
+                            lastNode = n;
+                        }
+                    } else if(p instanceof Way) {
+                        Way w = (Way)p;
+                        Node lastN = null;
+                        for(Node n: w.getNodes()) {
+                            if(lastN != null) {
+                                length += MeasurementLayer.calcDistance(lastN.getCoor(), n.getCoor());
+                                //http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/
+                                area += (MeasurementLayer.calcX(n.getCoor()) * MeasurementLayer.calcY(lastN.getCoor()))
+                                - (MeasurementLayer.calcY(n.getCoor()) * MeasurementLayer.calcX(lastN.getCoor()));
+                                segAngle = MeasurementLayer.angleBetween(lastN.getCoor(), n.getCoor());
+                            }
+                            lastN = n;
+                        }
+                        if (lastN != null && lastN == w.getNodes().iterator().next())
+                            area = Math.abs(area / 2);
+                        else
+                            area = 0;
+                    }
                 }
                 dlg.selectLengthLabel.setText(new DecimalFormat("#0.00").format(length) + " m");
 
@@ -163,5 +161,4 @@ public class MeasurementDialog extends ToggleDialog implements ActionListener
     public void resetValues(){
         MeasurementPlugin.getCurrentLayer().reset();
     }
-
 }
diff --git a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java
index bbd0fd6..fc59189 100644
--- a/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java
+++ b/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java
@@ -89,7 +89,7 @@ public class SlippyMapLayer extends Layer implements PreferenceChangedListener,
         if (debug)
             out("clearing tile storage");
         tileCache = new MemoryTileCache();
-        tileCache.setCacheSize(2000);
+        tileCache.setCacheSize(200);
     }
 
     /**
@@ -801,8 +801,11 @@ public class SlippyMapLayer extends Layer implements PreferenceChangedListener,
         }
     }
 
+    boolean az_disable = false;
     boolean autoZoomEnabled()
     {
+        if (az_disable)
+            return false;
         return autoZoomPopup.isSelected();
     }
     /**
@@ -851,8 +854,21 @@ public class SlippyMapLayer extends Layer implements PreferenceChangedListener,
                 if (debug)
                     out("too zoomed in, (" + ts.tilesSpanned()
                         + "), increasing zoom from " + currentZoomLevel);
+                // This is a hack.  ts.tooSmall() is proabably a bad thing, and this works
+                // around it.  If we have a very small window, the tileSet may be well
+                // less than 1 real tile wide, but that's expected.  But, this sees the
+                // tile set as too small and zooms in.  The code below that checks for
+                // pixel stretching disagrees and tries to zoom out.  Both calls recurse,
+                // hillarity ensues, and the stack overflows.
+                //
+                // This really needs to get fixed properly.  We probably shouldn't even
+                // have the tooSmall() check on tileSets.  But, this also helps the zoom
+                // converge to the correct place much faster.
+                boolean tmp = az_disable;
+                az_disable = true;
                 if (increaseZoomLevel())
                      this.paint(oldg, mv, bounds);
+                az_disable = tmp;
                 return;
             }
         }
diff --git a/svn-info.xml b/svn-info.xml
index 5fb6705..1d5a2a8 100644
--- a/svn-info.xml
+++ b/svn-info.xml
@@ -3,16 +3,16 @@
 <entry
    kind="dir"
    path="plugins"
-   revision="21271">
+   revision="21666">
 <url>http://svn.openstreetmap.org/applications/editors/josm/plugins</url>
 <repository>
 <root>http://svn.openstreetmap.org</root>
 <uuid>b9d5c4c9-76e1-0310-9c85-f3177eceb1e4</uuid>
 </repository>
 <commit
-   revision="21258">
-<author>guardian</author>
-<date>2010-05-13T11:33:01.904594Z</date>
+   revision="21666">
+<author>bastik</author>
+<date>2010-06-12T07:13:56.296435Z</date>
 </commit>
 </entry>
 </info>
diff --git a/wmsplugin/sources.cfg b/wmsplugin/sources.cfg
index e832913..89837de 100644
--- a/wmsplugin/sources.cfg
+++ b/wmsplugin/sources.cfg
@@ -44,7 +44,7 @@ false;MLIT Japan (ORTHO03);http://orthophoto.mlit.go.jp:8888/wms/service/wmsRast
 #
 # only for Italy
 false;Lodi - Italy;http://sit.provincia.lodi.it/mapserver/mapserv.exe?map=ortofoto_wgs84.map&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&SRS=EPSG:4326&LAYERS=Terraitaly%20Ortofoto%202007&STYLES=%2C%2C&FORMAT=image/png&TRANSPARENT=TRUE&
-false;Sicily - Italy;http://88.53.214.52/arcgis/services/OrtofotoATA_20072008_f33/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS=CRS:84&LAYERS=0&STYLES=default&FORMAT=image/jpeg& 
+false;Sicily - Italy;http://88.53.214.52/arcgis/services/WGS84_F33/OrtofotoATA_20072008_f33/MapServer/WMSServer?LAYERS=0&REQUEST=GetMap&VERSION=1.1.1&FORMAT=image%2Fpng&STYLES=&
 #
 #
 # URLS must be designed to append arguments directly behind. So the URLS should either end with '?' or '&'
diff --git a/wmsplugin/src/wmsplugin/WMSLayer.java b/wmsplugin/src/wmsplugin/WMSLayer.java
index b691ad7..cd6fe54 100644
--- a/wmsplugin/src/wmsplugin/WMSLayer.java
+++ b/wmsplugin/src/wmsplugin/WMSLayer.java
@@ -471,7 +471,7 @@ public class WMSLayer extends Layer {
 				baseName = getName();
 			}
 			Main.pref.put("wmsplugin.url."+ i +".url",baseURL );
-			Main.pref.put("wmsplugin.url."+String.valueOf(i)+".name", baseName + "#PPD=" + getPPD() );
+			Main.pref.put("wmsplugin.url."+String.valueOf(i)+".name", baseName + "#PPD=" + pixelPerDegree );
 			WMSPlugin.refreshMenu();
 		}
 	}
diff --git a/wmsplugin/webkit-image-gtk.c b/wmsplugin/webkit-image-gtk.c
index af81e5d..cbefbe0 100644
--- a/wmsplugin/webkit-image-gtk.c
+++ b/wmsplugin/webkit-image-gtk.c
@@ -1,37 +1,36 @@
 /* There is no licence for this, I don't care what you do with it */
-#include <stdio.h>
 #include <stdlib.h>
+#include <unistd.h>
 
+#include <gio/gunixoutputstream.h>
 #include <webkit/webkit.h>
 
 #define WIDTH 2000
 
 /* compile with:
-gcc -o webkit-image-gtk webkit-image-gtk.c `pkg-config --cflags --libs webkit-1.0`
-*/
+ * gcc -o webkit-image-gtk webkit-image-gtk.c `pkg-config --cflags --libs webkit-1.0 gio-unix-2.0`
+ * Requires GTK+ 2.20 and WebKitGtk+ 1.1.1
+ */
 
 static void
-on_finished (WebKitWebView *view, WebKitWebFrame *frame)
+on_finished (WebKitWebView      *view,
+             WebKitWebFrame     *frame,
+             GtkOffscreenWindow *window)
 {
-	GdkPixmap *pixmap;
-	GdkColormap *cmap;
 	GdkPixbuf *pixbuf;
-	gchar *buffer;
-	gsize size;
+	GOutputStream *stream;
 
-	pixmap = gtk_widget_get_snapshot (GTK_WIDGET (view), NULL);
-	cmap = gdk_colormap_get_system ();
-	pixbuf = gdk_pixbuf_get_from_drawable (NULL, GDK_DRAWABLE (pixmap), cmap,
-					       0, 0, 0, 0, WIDTH, WIDTH);
+	pixbuf = gtk_offscreen_window_get_pixbuf (window);
 
-	gdk_pixbuf_save_to_buffer (pixbuf, &buffer, &size, "png", NULL, NULL);
-
-	fwrite (buffer, 1, size, stdout);
+	stream = g_unix_output_stream_new (STDOUT_FILENO, TRUE);
+	gdk_pixbuf_save_to_stream (pixbuf, stream, "png", NULL, NULL, NULL);
 
 	exit (1);
 }
 
-int main (int argc, char **argv)
+int
+main (int    argc,
+      char **argv)
 {
 	GtkWidget *window;
 	GtkWidget *view;
@@ -41,23 +40,17 @@ int main (int argc, char **argv)
 
 	gtk_init (&argc, &argv);
 
-	window = gtk_window_new (GTK_WINDOW_POPUP);
-
-	/* Check if compositing window manager is running, needs one for now */
-	if (gtk_widget_is_composited (window))
-		gtk_window_set_opacity (GTK_WINDOW (window), 0.0);
-	else
-		g_error ("This requires a compositing window manager for now");
+	window = gtk_offscreen_window_new ();
 
 	view = webkit_web_view_new ();
-	webkit_web_view_open (WEBKIT_WEB_VIEW (view), argv[1]);
+	webkit_web_view_load_uri (WEBKIT_WEB_VIEW (view), argv[1]);
 	gtk_widget_set_size_request (view, WIDTH, WIDTH);
 	gtk_container_add (GTK_CONTAINER (window), view);
 
 	gtk_widget_show_all (window);
 
-	g_signal_connect (G_OBJECT (view), "load-finished",
-			  G_CALLBACK (on_finished), NULL);
+	g_signal_connect (view, "load-finished",
+	                  G_CALLBACK (on_finished), window);
 
 	gtk_main ();
 	return 0;

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/josm-plugins.git



More information about the Pkg-grass-devel mailing list