Index: trunk/src/org/openstreetmap/josm/actions/GpxExportAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/GpxExportAction.java	(revision 443)
+++ trunk/src/org/openstreetmap/josm/actions/GpxExportAction.java	(revision 444)
@@ -29,8 +29,10 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.gui.layer.Layer;
-import org.openstreetmap.josm.gui.layer.RawGpsLayer;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.layer.GpxLayer;
 import org.openstreetmap.josm.io.GpxWriter;
 import org.openstreetmap.josm.io.XmlWriter;
 import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.data.gpx.GpxData;
 
 /**
@@ -118,14 +120,15 @@
 		if (copyright.getText().length() != 0)
 			Main.pref.put("lastCopyright", copyright.getText());
-		
-		XmlWriter.OsmWriterInterface w;
-		if (layer instanceof RawGpsLayer)
-			w = new GpxWriter.Trk(((RawGpsLayer)layer).data);
+
+		GpxData gpxData;
+		if (layer instanceof OsmDataLayer)
+			gpxData = ((OsmDataLayer)layer).toGpxData();
+		else if (layer instanceof GpxLayer)
+			gpxData = ((GpxLayer)layer).data;
 		else
-			w = new GpxWriter.All(Main.ds, layer.name, desc.getText(),
-					authorName.getText(), email.getText(), copyright.getText(),
-					copyrightYear.getText(), keywords.getText());
+			gpxData = OsmDataLayer.toGpxData(Main.ds);
+		// TODO: add copyright, etc.
 		try {
-			XmlWriter.output(new FileOutputStream(file), w);
+			new GpxWriter(new FileOutputStream(file)).write(gpxData);
 		} catch (IOException x) {
 			x.printStackTrace();
Index: trunk/src/org/openstreetmap/josm/actions/OpenAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/OpenAction.java	(revision 443)
+++ trunk/src/org/openstreetmap/josm/actions/OpenAction.java	(revision 444)
@@ -22,11 +22,9 @@
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.gui.layer.RawGpsLayer;
-import org.openstreetmap.josm.gui.layer.RawGpsLayer.GpsPoint;
+import org.openstreetmap.josm.gui.layer.GpxLayer;
 import org.openstreetmap.josm.gui.layer.markerlayer.Marker;
 import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
 import org.openstreetmap.josm.io.OsmReader;
-import org.openstreetmap.josm.io.RawCsvReader;
-import org.openstreetmap.josm.io.RawGpsReader;
+import org.openstreetmap.josm.io.GpxReader;
 import org.xml.sax.SAXException;
 
@@ -60,6 +58,6 @@
 	public void openFile(File file) {
 		try {
-			if (asRawData(file.getName()))
-				openFileAsRawGps(file);
+			if (asGpxData(file.getName()))
+				openFileAsGpx(file);
 			else
 				openAsData(file);
@@ -85,37 +83,27 @@
     }
 
-	private void openFileAsRawGps(File file) throws SAXException, IOException, FileNotFoundException {
-	    String fn = file.getName();
-	    Collection<Collection<GpsPoint>> gpsData = null;
-	    Collection<Marker> markerData = null;
-	    if (ExtensionFileFilter.filters[ExtensionFileFilter.GPX].acceptName(fn)) {
-	    	RawGpsReader r = null;
-	    	if (file.getName().endsWith(".gpx.gz"))
-	    		r = new RawGpsReader(new GZIPInputStream(new FileInputStream(file)), file.getAbsoluteFile().getParentFile());
-	    	else
-	    		r = new RawGpsReader(new FileInputStream(file), file.getAbsoluteFile().getParentFile());
-	    	gpsData = r.trackData;
-	    	markerData = r.markerData;
-	    } else if (ExtensionFileFilter.filters[ExtensionFileFilter.CSV].acceptName(fn)) {
-	    	gpsData = new LinkedList<Collection<GpsPoint>>();
-	    	gpsData.add(new RawCsvReader(new FileReader(file)).parse());
-	    } else
-	    	throw new IllegalStateException();
-	    if (gpsData != null && !gpsData.isEmpty())
-	    	Main.main.addLayer(new RawGpsLayer(false, gpsData, tr("Tracks from {0}", file.getName()), file));
-	    if (markerData != null && !markerData.isEmpty())
-	    	Main.main.addLayer(new MarkerLayer(markerData, tr ("Markers from {0}", file.getName()), file));
+	private void openFileAsGpx(File file) throws SAXException, IOException, FileNotFoundException {
+		String fn = file.getName();
+		if (ExtensionFileFilter.filters[ExtensionFileFilter.GPX].acceptName(fn)) {
+			GpxReader r = null;
+			if (file.getName().endsWith(".gpx.gz")) {
+				r = new GpxReader(new GZIPInputStream(new FileInputStream(file)), file.getAbsoluteFile().getParentFile());
+			} else{
+				r = new GpxReader(new FileInputStream(file), file.getAbsoluteFile().getParentFile());
+			}
+			r.data.storageFile = file;
+			Main.main.addLayer(new GpxLayer(r.data, fn));
+            Main.main.addLayer(new MarkerLayer(r.data, tr("Markers from {0}", fn), file));
+
+		} else {
+			throw new IllegalStateException();
+		}
     }
 
-	/**
-	 * @return Return whether the file should be opened as raw gps data. May ask the
-	 * user, if unsure.
-	 */
-	private boolean asRawData(String fn) {
-		if (ExtensionFileFilter.filters[ExtensionFileFilter.CSV].acceptName(fn))
-			return true;
-		if (!ExtensionFileFilter.filters[ExtensionFileFilter.GPX].acceptName(fn))
-			return false;
-		return true;
+
+	private boolean asGpxData(String fn) {
+		return ExtensionFileFilter.filters[ExtensionFileFilter.GPX].acceptName(fn);
 	}
+
+
 }
Index: trunk/src/org/openstreetmap/josm/actions/SaveAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/SaveAction.java	(revision 443)
+++ trunk/src/org/openstreetmap/josm/actions/SaveAction.java	(revision 444)
@@ -8,4 +8,6 @@
 import java.io.File;
 
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.GpxLayer;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 
@@ -21,11 +23,21 @@
 	 * @param layer Save this layer.
 	 */
-	public SaveAction(OsmDataLayer layer) {
+	public SaveAction(Layer layer) {
 		super(tr("Save"), "save", tr("Save the current data."), KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK, layer);
 	}
 	
-	@Override public File getFile(OsmDataLayer layer) {
-		if (layer.associatedFile != null)
-			return layer.associatedFile;
+	@Override public File getFile(Layer layer) {
+		if (layer instanceof OsmDataLayer) {
+			File f = ((OsmDataLayer)layer).associatedFile;
+			if (f != null) {
+				return f;
+			}
+		}
+		if (layer instanceof GpxLayer) {
+			File f = ((GpxLayer)layer).data.storageFile;
+			if (f != null) {
+				return f;
+			}
+		}
 		return openFileDialog();
 	}
Index: trunk/src/org/openstreetmap/josm/actions/SaveActionBase.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/SaveActionBase.java	(revision 443)
+++ trunk/src/org/openstreetmap/josm/actions/SaveActionBase.java	(revision 444)
@@ -18,11 +18,14 @@
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.GpxLayer;
 import org.openstreetmap.josm.io.OsmWriter;
+import org.openstreetmap.josm.io.GpxWriter;
 
 public abstract class SaveActionBase extends DiskAccessAction {
 
-	private OsmDataLayer layer;
-
-	public SaveActionBase(String name, String iconName, String tooltip, int shortCut, int modifiers, OsmDataLayer layer) {
+	private Layer layer;
+
+	public SaveActionBase(String name, String iconName, String tooltip, int shortCut, int modifiers, Layer layer) {
 		super(name, iconName, tooltip, shortCut, modifiers);
 		this.layer = layer;
@@ -30,7 +33,8 @@
 
 	public void actionPerformed(ActionEvent e) {
-		OsmDataLayer layer = this.layer;
-		if (layer == null && Main.map != null && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer)
-			layer = (OsmDataLayer)Main.map.mapView.getActiveLayer();
+		Layer layer = this.layer;
+		if (layer == null && Main.map != null && (Main.map.mapView.getActiveLayer() instanceof OsmDataLayer
+				|| Main.map.mapView.getActiveLayer() instanceof GpxLayer))
+			layer = Main.map.mapView.getActiveLayer();
 		if (layer == null)
 			layer = Main.main.editLayer();
@@ -51,5 +55,5 @@
 	}
 
-	protected abstract File getFile(OsmDataLayer layer);
+	protected abstract File getFile(Layer layer);
 
 	/**
@@ -58,11 +62,20 @@
 	 * @return <code>true</code>, if it is save to save.
 	 */
-	public boolean checkSaveConditions(OsmDataLayer layer) {
+	public boolean checkSaveConditions(Layer layer) {
+		if (layer == null) {
+			JOptionPane.showMessageDialog(Main.parent, tr("Internal Error: cannot check conditions for no layer. Please report this as a bug."));
+			return false;
+		}
 		if (Main.map == null) {
 			JOptionPane.showMessageDialog(Main.parent, tr("No document open so nothing to save."));
 			return false;
 		}
-		if (isDataSetEmpty(layer) && JOptionPane.NO_OPTION == JOptionPane.showConfirmDialog(Main.parent,tr("The document contains no data. Save anyway?"), tr("Empty document"), JOptionPane.YES_NO_OPTION))
-			return false;
+
+		if (layer instanceof OsmDataLayer && isDataSetEmpty((OsmDataLayer)layer) && JOptionPane.NO_OPTION == JOptionPane.showConfirmDialog(Main.parent,tr("The document contains no data. Save anyway?"), tr("Empty document"), JOptionPane.YES_NO_OPTION)) {
+			return false;
+		}
+		if (layer instanceof GpxLayer && ((GpxLayer)layer).data == null) {
+			return false;
+		}
 		if (!Main.map.conflictDialog.conflicts.isEmpty()) {
 			int answer = JOptionPane.showConfirmDialog(Main.parent, 
@@ -110,4 +123,13 @@
 		srcStream.close();
 		dstStream.close();
+	}
+
+	public static void save(File file, Layer layer) {
+		if (layer instanceof GpxLayer) {
+			save(file, (GpxLayer)layer);
+			((GpxLayer)layer).data.storageFile = file;
+		} else if (layer instanceof OsmDataLayer) {
+			save(file, (OsmDataLayer)layer);
+		}
 	}
 
@@ -152,4 +174,44 @@
 	}
 
+	public static void save(File file, GpxLayer layer) {
+		File tmpFile = null;
+		try {
+			if (ExtensionFileFilter.filters[ExtensionFileFilter.GPX].acceptName(file.getPath())) {
+
+				// use a tmp file because if something errors out in the
+				// process of writing the file, we might just end up with
+				// a truncated file.  That can destroy lots of work.
+				if (file.exists()) {
+					tmpFile = new File(file.getPath() + "~");
+					copy(file, tmpFile);
+				}
+
+				new GpxWriter(new FileOutputStream(file)).write(layer.data);
+				if (!Main.pref.getBoolean("save.keepbackup") && (tmpFile != null)) {
+					tmpFile.delete();
+				}
+			} else if (ExtensionFileFilter.filters[ExtensionFileFilter.CSV].acceptName(file.getPath())) {
+				JOptionPane.showMessageDialog(Main.parent, tr("CSV output not supported yet."));
+				return;
+			} else {
+				JOptionPane.showMessageDialog(Main.parent, tr("Unknown file extension."));
+				return;
+			}
+		} catch (IOException e) {
+			e.printStackTrace();
+			JOptionPane.showMessageDialog(Main.parent, tr("An error occurred while saving.")+"\n"+e.getMessage());
+		}
+		try {
+			// if the file save failed, then the tempfile will not
+			// be deleted.  So, restore the backup if we made one.
+			if (tmpFile != null && tmpFile.exists()) {
+				copy(tmpFile, file);
+			}
+		} catch (IOException e) {
+			e.printStackTrace();
+			JOptionPane.showMessageDialog(Main.parent, tr("An error occurred while restoring backup file.")+"\n"+e.getMessage());
+		}
+	}
+
 	/**
 	 * Check the data set if it would be empty on save. It is empty, if it contains
Index: trunk/src/org/openstreetmap/josm/actions/SaveAsAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/SaveAsAction.java	(revision 443)
+++ trunk/src/org/openstreetmap/josm/actions/SaveAsAction.java	(revision 444)
@@ -8,8 +8,8 @@
 import java.io.File;
 
-import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.layer.Layer;
 
 /**
- * Export the data  as OSM intern xml file.
+ * Export the data.
  * 
  * @author imi
@@ -21,9 +21,9 @@
 	 * @param layer Save this layer.
 	 */
-	public SaveAsAction(OsmDataLayer layer) {
+	public SaveAsAction(Layer layer) {
 		super(tr("Save as"), "save_as", tr("Save the current data to a new file."), KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK, layer);
 	}
 	
-	@Override protected File getFile(OsmDataLayer layer) {
+	@Override protected File getFile(Layer layer) {
 		return openFileDialog();
 	}
Index: trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTask.java	(revision 443)
+++ trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTask.java	(revision 444)
@@ -14,6 +14,7 @@
 import org.openstreetmap.josm.gui.download.DownloadDialog.DownloadTask;
 import org.openstreetmap.josm.gui.layer.Layer;
-import org.openstreetmap.josm.gui.layer.RawGpsLayer;
-import org.openstreetmap.josm.gui.layer.RawGpsLayer.GpsPoint;
+import org.openstreetmap.josm.gui.layer.GpxLayer;
+import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.io.BoundingBoxDownloader;
 import org.xml.sax.SAXException;
@@ -24,5 +25,5 @@
 		private BoundingBoxDownloader reader;
 		private DownloadAction action;
-		private Collection<Collection<GpsPoint>> rawData;
+		private GpxData rawData;
 		private final boolean newLayer;
 
@@ -41,6 +42,8 @@
 			if (rawData == null)
 				return;
-			String name = action.dialog.minlat + " " + action.dialog.minlon + " x " + action.dialog.maxlat + " " + action.dialog.maxlon;
-			RawGpsLayer layer = new RawGpsLayer(true, rawData, name, null);
+                        rawData.recalculateBounds();
+                        Bounds b = rawData.bounds;
+						String name = b.min.lat() + " " + b.min.lon() + " x " + b.max.lat() + " " + b.max.lon();
+						GpxLayer layer = new GpxLayer(rawData, name);
 			if (newLayer || findMergeLayer() == null)
 	            Main.main.addLayer(layer);
@@ -53,8 +56,8 @@
 				return null;
 	        Layer active = Main.map.mapView.getActiveLayer();
-	        if (active != null && active instanceof RawGpsLayer)
+	        if (active != null && active instanceof GpxLayer)
 	        	return active;
 	        for (Layer l : Main.map.mapView.getAllLayers())
-	        	if (l instanceof RawGpsLayer && ((RawGpsLayer)l).fromServer)
+				if (l instanceof GpxLayer)
 	        		return l;
 	        return null;
Index: trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java	(revision 444)
+++ trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java	(revision 444)
@@ -0,0 +1,103 @@
+//License: GPLv2 or later. Copyright 2007 by Raphael Mack and others
+
+package org.openstreetmap.josm.data.gpx;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.HashMap;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.coor.LatLon;
+import java.lang.Math;
+import java.io.File;
+
+/**
+ * objects of this class represent a gpx file with tracks, waypoints and routes
+ * it uses GPX1.1 see http://www.topografix.com/GPX/1/1/ for details
+ * 
+ * @author Raphael Mack <ramack@raphael-mack.de>
+ */
+public class GpxData extends WithAttributes {
+	public File storageFile;
+	public boolean fromServer;
+
+	public Collection<GpxTrack> tracks = new LinkedList<GpxTrack>();
+	public Collection<GpxRoute> routes = new LinkedList<GpxRoute>();
+	public Collection<WayPoint> waypoints = new LinkedList<WayPoint>();
+
+	public Bounds bounds;
+
+	public void mergeFrom(GpxData other) {
+		if (storageFile == null && other.storageFile != null) {
+			storageFile = other.storageFile;
+		}
+		fromServer = fromServer && other.fromServer;
+
+		for (Map.Entry<String, Object> ent : other.attr.entrySet()) {
+			// TODO: Detect conflicts.
+			String k = ent.getKey();
+			if (k.equals("link") && attr.containsKey("link")) {
+				((Collection<GpxLink>) attr.get("link")).addAll(
+					(Collection<GpxLink>) ent.getValue());
+			} else {
+				attr.put(k, ent.getValue());
+			}
+		}
+		tracks.addAll(other.tracks);
+		routes.addAll(other.routes);
+		waypoints.addAll(other.waypoints);
+	}
+
+	public boolean hasTrackPoints() {
+		for (GpxTrack trk : tracks) {
+			for (Collection<WayPoint> trkseg : trk.trackSegs) {
+				if (!trkseg.isEmpty())
+					return true;
+			}
+		}
+		return false;
+	}
+
+	public boolean hasRoutePoints() {
+		for (GpxRoute rte : routes) {
+			if (!rte.routePoints.isEmpty())
+				return true;
+		}
+		return false;
+	}
+
+	// FIXME might perhaps use visitor pattern?
+	public void recalculateBounds() {
+		bounds = null;
+		for (WayPoint wpt : waypoints) {
+			if (bounds == null) {
+				bounds = new Bounds(wpt.latlon, wpt.latlon);
+			} else {
+				bounds.extend(wpt.latlon);
+			}
+		}
+		for (GpxRoute rte : routes) {
+			for (WayPoint wpt : rte.routePoints) {
+				if (bounds == null) {
+					bounds = new Bounds(wpt.latlon, wpt.latlon);
+				} else {
+					bounds.extend(wpt.latlon);
+				}
+			}
+		}
+		for (GpxTrack trk : tracks) {
+			for (Collection<WayPoint> trkseg : trk.trackSegs) {
+				for (WayPoint wpt : trkseg) {
+					if (bounds == null) {
+						bounds = new Bounds(wpt.latlon, wpt.latlon);
+					} else {
+						bounds.extend(wpt.latlon);
+					}
+				}
+			}
+		}
+		if (bounds == null) {
+			bounds = new Bounds();
+		}
+	}
+}
Index: trunk/src/org/openstreetmap/josm/data/gpx/GpxLink.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/gpx/GpxLink.java	(revision 444)
+++ trunk/src/org/openstreetmap/josm/data/gpx/GpxLink.java	(revision 444)
@@ -0,0 +1,14 @@
+//License: GPLv2 or later.
+//Copyright 2007 by Raphael Mack and others
+
+package org.openstreetmap.josm.data.gpx;
+
+public class GpxLink {
+	public String uri;
+	public String text;
+	public String type;
+
+	public GpxLink(String uri) {
+		this.uri = uri;
+	}
+}
Index: trunk/src/org/openstreetmap/josm/data/gpx/GpxRoute.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/gpx/GpxRoute.java	(revision 444)
+++ trunk/src/org/openstreetmap/josm/data/gpx/GpxRoute.java	(revision 444)
@@ -0,0 +1,11 @@
+//License: GPLv2 or later
+//Copyright 2007 by Raphael Mack and others
+
+package org.openstreetmap.josm.data.gpx;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+public class GpxRoute extends WithAttributes {
+	public Collection<WayPoint> routePoints = new LinkedList<WayPoint>();
+}
Index: trunk/src/org/openstreetmap/josm/data/gpx/GpxTrack.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/gpx/GpxTrack.java	(revision 444)
+++ trunk/src/org/openstreetmap/josm/data/gpx/GpxTrack.java	(revision 444)
@@ -0,0 +1,12 @@
+//License: GPLv2 or later
+//Copyright 2007 by Raphael Mack and others
+
+package org.openstreetmap.josm.data.gpx;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+public class GpxTrack extends WithAttributes {
+	public Collection<Collection<WayPoint>> trackSegs
+		= new LinkedList<Collection<WayPoint>>();
+}
Index: trunk/src/org/openstreetmap/josm/data/gpx/WayPoint.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/gpx/WayPoint.java	(revision 444)
+++ trunk/src/org/openstreetmap/josm/data/gpx/WayPoint.java	(revision 444)
@@ -0,0 +1,24 @@
+//License: GPLv2 or later
+//Copyright 2007 by Raphael Mack and others
+
+package org.openstreetmap.josm.data.gpx;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.coor.LatLon;
+
+public class WayPoint extends WithAttributes {
+	
+	public final LatLon latlon;
+	public final EastNorth eastNorth;
+
+	public WayPoint(LatLon ll) {
+		latlon = ll; 
+		eastNorth = Main.proj.latlon2eastNorth(ll); 
+	}
+
+	@Override
+	public String toString() {
+		return "WayPoint (" + (attr.containsKey("name") ? attr.get("name") + ", " :"") + latlon.toString() + ", " + attr + ")";
+	}
+}
Index: trunk/src/org/openstreetmap/josm/data/gpx/WithAttributes.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/gpx/WithAttributes.java	(revision 444)
+++ trunk/src/org/openstreetmap/josm/data/gpx/WithAttributes.java	(revision 444)
@@ -0,0 +1,23 @@
+// License: GPL. 
+package org.openstreetmap.josm.data.gpx;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Base class for various classes in the GPX model.
+ * The "attr" hash is used to store the XML payload
+ * (not only XML attributes!)
+ * 
+ * @author Frederik Ramm <frederik@remote.org>
+ *
+ */
+public class WithAttributes {
+	
+	public Map<String, Object> attr = new HashMap<String, Object>();
+	
+	public String getString(String key) {
+		Object value = attr.get(key);
+		return (value instanceof String) ? (String)value : null;
+	}
+}
Index: trunk/src/org/openstreetmap/josm/gui/layer/GeoImageLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/GeoImageLayer.java	(revision 443)
+++ trunk/src/org/openstreetmap/josm/gui/layer/GeoImageLayer.java	(revision 444)
@@ -58,9 +58,11 @@
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.data.gpx.GpxTrack;
+import org.openstreetmap.josm.data.gpx.WayPoint;
 import org.openstreetmap.josm.gui.MapView;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
 import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
-import org.openstreetmap.josm.gui.layer.RawGpsLayer.GpsPoint;
 import org.openstreetmap.josm.tools.DateParser;
 import org.openstreetmap.josm.tools.ExifReader;
@@ -90,27 +92,30 @@
 		private GeoImageLayer layer;
 		private final Collection<File> files;
-		private final RawGpsLayer gpsLayer;
-		public Loader(Collection<File> files, RawGpsLayer gpsLayer) {
-			super(tr("Images for {0}", gpsLayer.name));
+		private final GpxLayer gpxLayer;
+		public Loader(Collection<File> files, GpxLayer gpxLayer) {
+			super(tr("Images for {0}", gpxLayer.name));
 			this.files = files;
-			this.gpsLayer = gpsLayer;
+			this.gpxLayer = gpxLayer;
 		}
 		@Override protected void realRun() throws IOException {
-			Main.pleaseWaitDlg.currentAction.setText(tr("Read GPS..."));
+			Main.pleaseWaitDlg.currentAction.setText(tr("Read GPX..."));
 			LinkedList<TimedPoint> gps = new LinkedList<TimedPoint>();
 
-			// Extract dates and locations from GPS input
-			for (Collection<GpsPoint> c : gpsLayer.data) {
-				for (GpsPoint p : c) {
-					if (p.time == null)
+			// Extract dates and locations from GPX input
+
+			for (GpxTrack trk : gpxLayer.data.tracks) {
+				for (Collection<WayPoint> segment : trk.trackSegs) {
+					for (WayPoint p : segment) {
+					if (!p.attr.containsKey("time"))
 						throw new IOException(tr("No time for point {0} x {1}",p.latlon.lat(),p.latlon.lon()));
 					Date d = null;
 					try {
-						d = DateParser.parse(p.time);
+						d = DateParser.parse((String) p.attr.get("time"));
 					} catch (ParseException e) {
-						throw new IOException(tr("Cannot read time \"{0}\" from point {1} x {2}",p.time,p.latlon.lat(),p.latlon.lon()));
+						throw new IOException(tr("Cannot read time \"{0}\" from point {1} x {2}",p.attr.get("time"),p.latlon.lat(),p.latlon.lon()));
 					}
 					gps.add(new TimedPoint(d, p.eastNorth));
 				}
+			}
 			}
 
@@ -188,6 +193,6 @@
 	}
 
-	public static void create(Collection<File> files, RawGpsLayer gpsLayer) {
-		Loader loader = new Loader(files, gpsLayer);
+	public static void create(Collection<File> files, GpxLayer gpxLayer) {
+		Loader loader = new Loader(files, gpxLayer);
 		Main.worker.execute(loader);
 	}
Index: trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 444)
+++ trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 444)
@@ -0,0 +1,413 @@
+//License: GPL. Copyright 2007 by Immanuel Scholz, Raphael Mack and others
+
+package org.openstreetmap.josm.gui.layer;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+import static org.openstreetmap.josm.tools.I18n.trn;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.GridBagLayout;
+import java.awt.Point;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.UnknownHostException;
+
+import javax.swing.AbstractAction;
+import javax.swing.Box;
+import javax.swing.ButtonGroup;
+import javax.swing.Icon;
+import javax.swing.JColorChooser;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JSeparator;
+import javax.swing.JRadioButton;
+import javax.swing.JTextField;
+import javax.swing.JCheckBox;
+import javax.swing.filechooser.FileFilter;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.SaveAction;
+import org.openstreetmap.josm.actions.SaveAsAction;
+import org.openstreetmap.josm.actions.RenameLayerAction;
+import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.data.gpx.GpxTrack;
+import org.openstreetmap.josm.data.gpx.GpxRoute;
+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.io.MultiPartFormOutputStream;
+import org.openstreetmap.josm.io.GpxWriter;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
+import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
+import org.openstreetmap.josm.tools.ColorHelper;
+import org.openstreetmap.josm.tools.DontShowAgainInfo;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.UrlLabel;
+
+public class GpxLayer extends Layer {
+	public GpxData data;
+
+	public GpxLayer(GpxData d) {
+		super((String) d.attr.get("name"));
+		data = d;		
+	}
+
+	public GpxLayer(GpxData d, String name) {
+		this(d);
+		this.name = name;
+	}
+
+	@Override public Icon getIcon() {
+		return ImageProvider.get("layer", "gpx_small");
+	}
+
+	@Override public Object getInfoComponent() {
+		return "<html>" + data.attr.get("name") + ":" + "<br />" + data.attr.get("desc") + "</html>";
+	}
+
+	@Override public Component[] getMenuEntries() {
+		JMenuItem line = new JMenuItem(tr("Customize line drawing"), ImageProvider.get("mapmode/addsegment"));
+		line.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				JRadioButton[] r = new JRadioButton[3];
+				r[0] = new JRadioButton(tr("Use global settings."));
+				r[1] = new JRadioButton(tr("Draw lines between points for this layer."));
+				r[2] = new JRadioButton(tr("Do not draw lines between points for this layer."));
+				ButtonGroup group = new ButtonGroup();
+				Box panel = Box.createVerticalBox();
+				for (JRadioButton b : r) {
+					group.add(b);
+					panel.add(b);
+				}
+				String propName = "draw.rawgps.lines.layer "+name;
+				if (Main.pref.hasKey(propName))
+					group.setSelected(r[Main.pref.getBoolean(propName) ? 1:2].getModel(), true);
+				else
+					group.setSelected(r[0].getModel(), true);
+				int answer = JOptionPane.showConfirmDialog(Main.parent, panel, tr("Select line drawing options"), JOptionPane.OK_CANCEL_OPTION);
+				if (answer == JOptionPane.CANCEL_OPTION)
+					return;
+				if (group.getSelection() == r[0].getModel())
+					Main.pref.put(propName, null);
+				else
+					Main.pref.put(propName, group.getSelection() == r[1].getModel());
+				Main.map.repaint();
+			}
+		});
+
+		JMenuItem color = new JMenuItem(tr("Customize Color"), ImageProvider.get("colorchooser"));
+		color.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				String col = Main.pref.get("color.layer "+name, Main.pref.get("color.gps point", ColorHelper.color2html(Color.gray)));
+				JColorChooser c = new JColorChooser(ColorHelper.html2color(col));
+				Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")};
+				int answer = JOptionPane.showOptionDialog(Main.parent, c, tr("Choose a color"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
+				switch (answer) {
+				case 0:
+					Main.pref.put("color.layer "+name, ColorHelper.color2html(c.getColor()));
+					break;
+				case 1:
+					return;
+				case 2:
+					Main.pref.put("color.layer "+name, null);
+					break;
+				}
+				Main.map.repaint();
+			}
+		});
+
+		JMenuItem tagimage = new JMenuItem(tr("Import images"), ImageProvider.get("tagimages"));
+		tagimage.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				JFileChooser fc = new JFileChooser(Main.pref.get("tagimages.lastdirectory"));
+				fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
+				fc.setMultiSelectionEnabled(true);
+				fc.setAcceptAllFileFilterUsed(false);
+				fc.setFileFilter(new FileFilter() {
+					@Override public boolean accept(File f) {
+						return f.isDirectory() || f.getName().toLowerCase().endsWith(".jpg");
+					}
+					@Override public String getDescription() {
+						return tr("JPEG images (*.jpg)");
+					}
+				});
+				fc.showOpenDialog(Main.parent);
+				File[] sel = fc.getSelectedFiles();
+				if (sel == null || sel.length == 0)
+					return;
+				LinkedList<File> files = new LinkedList<File>();
+				addRecursiveFiles(files, sel);
+				Main.pref.put("tagimages.lastdirectory", fc.getCurrentDirectory().getPath());
+				GeoImageLayer.create(files, GpxLayer.this);
+			}
+
+			private void addRecursiveFiles(LinkedList<File> files, File[] sel) {
+				for (File f : sel) {
+					if (f.isDirectory())
+						addRecursiveFiles(files, f.listFiles());
+					else if (f.getName().toLowerCase().endsWith(".jpg"))
+						files.add(f);
+				}
+			}
+		});
+
+		if (Main.applet)
+			return new Component[] {
+				new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
+				new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
+				new JSeparator(),
+				color,
+				line,
+				new JMenuItem(new ConvertToDataLayerAction()),
+				new JSeparator(),
+				new JMenuItem(new RenameLayerAction(associatedFile, this)),
+				new JSeparator(),
+				new JMenuItem(new LayerListPopup.InfoAction(this))};
+		return new Component[] {
+				new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
+				new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
+				new JSeparator(),
+				new JMenuItem(new SaveAction(this)),
+				new JMenuItem(new SaveAsAction(this)),
+				// new JMenuItem(new UploadTraceAction()),
+				color,
+				line,
+				tagimage,
+				new JMenuItem(new ConvertToDataLayerAction()),
+				new JSeparator(),
+				new JMenuItem(new RenameLayerAction(associatedFile, this)),
+				new JSeparator(),
+				new JMenuItem(new LayerListPopup.InfoAction(this))};
+	}
+
+	@Override public String getToolTipText() {
+		return "<html>" + data.attr.get("name") + ":" + "<br />" + data.attr.get("desc") + "</html>";
+	}
+
+	@Override public boolean isMergable(Layer other) {
+		return other instanceof GpxLayer;
+	}
+
+	@Override public void mergeFrom(Layer from) {
+		data.mergeFrom(((GpxLayer)from).data);
+	}
+
+	@Override public void paint(Graphics g, MapView mv) {
+		String gpsCol = Main.pref.get("color.gps point");
+		String gpsColSpecial = Main.pref.get("color.layer "+name);
+		if (!gpsColSpecial.equals("")) {
+			g.setColor(ColorHelper.html2color(gpsColSpecial));
+		} else if (!gpsCol.equals("")) {
+			g.setColor(ColorHelper.html2color(gpsCol));
+		} else{
+			g.setColor(Color.GRAY);
+		}
+		
+		boolean forceLines = Main.pref.getBoolean("draw.rawgps.lines.force");
+		boolean lines = Main.pref.getBoolean("draw.rawgps.lines");
+		String linesKey = "draw.rawgps.lines.layer "+name;
+		if (Main.pref.hasKey(linesKey))
+			lines = Main.pref.getBoolean(linesKey);
+		boolean large = Main.pref.getBoolean("draw.rawgps.large");
+
+		Point old = null;
+		for (GpxTrack trk : data.tracks) {
+			if (!forceLines) {
+				old = null;
+			}
+			for (Collection<WayPoint> segment : trk.trackSegs) {
+				for (WayPoint trkPnt : segment) {
+					Point screen = mv.getPoint(trkPnt.eastNorth);
+					if (lines && old != null) {
+						g.drawLine(old.x, old.y, screen.x, screen.y);
+					} else if (!large) {
+						g.drawRect(screen.x, screen.y, 0, 0);
+					}
+					if (large)
+						g.fillRect(screen.x-1, screen.y-1, 3, 3);
+					old = screen;
+				}
+			}
+		}
+	}
+
+	@Override public void visitBoundingBox(BoundingXYVisitor v) {
+		for (WayPoint p : data.waypoints)
+			v.visit(p.eastNorth);
+
+		for (GpxRoute rte : data.routes) {
+			Collection<WayPoint> r = rte.routePoints;
+			for (WayPoint p : r) {
+				v.visit(p.eastNorth);
+			}
+		}
+
+		for (GpxTrack trk : data.tracks) {
+			for (Collection<WayPoint> seg : trk.trackSegs) {
+				for (WayPoint p : seg) {
+					v.visit(p.eastNorth);
+				}
+			}
+		}
+	}
+
+	public class UploadTraceAction extends AbstractAction {
+		public UploadTraceAction() {
+			super(tr("Upload this trace..."), ImageProvider.get("uploadtrace"));
+		}
+		public void actionPerformed(ActionEvent e) {
+			JPanel msg = new JPanel(new GridBagLayout());
+			msg.add(new JLabel(tr("<html>This functionality has been added only recently. Please<br>"+
+			"use with care and check if it works as expected.</html>")), GBC.eop());
+			ButtonGroup bg = new ButtonGroup();
+			JRadioButton c1 = null;
+			JRadioButton c2 = null;
+
+			//TODO
+			//check whether data comes from server
+			//check whether data changed sind last save/open
+
+			c1 = new JRadioButton(tr("Upload track filtered by JOSM"), true);
+			c2 = new JRadioButton(tr("Upload raw file: "), false);
+			c2.setEnabled(false);
+			c1.setEnabled(false);
+			bg.add(c1);
+			bg.add(c2);
+
+			msg.add(c1, GBC.eol());
+			msg.add(c2, GBC.eop());
+
+
+			JLabel description = new JLabel((String) data.attr.get("desc"));
+			JTextField tags = new JTextField();
+			tags.setText((String) data.attr.get("keywords"));
+			msg.add(new JLabel(tr("Description:")), GBC.std());
+			msg.add(description, GBC.eol().fill(GBC.HORIZONTAL));
+			msg.add(new JLabel(tr("Tags (keywords in GPX):")), GBC.std());
+			msg.add(tags, GBC.eol().fill(GBC.HORIZONTAL));
+			JCheckBox c3 = new JCheckBox("public");
+			msg.add(c3, GBC.eop());
+			msg.add(new JLabel("Please ensure that you don't upload your traces twice."), GBC.eop());
+
+			int answer = JOptionPane.showConfirmDialog(Main.parent, msg, tr("GPX-Upload"), JOptionPane.OK_CANCEL_OPTION);
+			if (answer == JOptionPane.OK_OPTION)
+			{
+				try {
+					String version = Main.pref.get("osm-server.version", "0.5");
+					URL url = new URL(Main.pref.get("osm-server.url") +
+							"/" + version + "/gpx/create");
+
+					// create a boundary string
+					String boundary = MultiPartFormOutputStream.createBoundary();
+					URLConnection urlConn = MultiPartFormOutputStream.createConnection(url);
+					urlConn.setRequestProperty("Accept", "*/*");
+					urlConn.setRequestProperty("Content-Type", 
+							MultiPartFormOutputStream.getContentType(boundary));
+					// set some other request headers...
+					urlConn.setRequestProperty("Connection", "Keep-Alive");
+					urlConn.setRequestProperty("Cache-Control", "no-cache");
+					// no need to connect cuz getOutputStream() does it
+					MultiPartFormOutputStream out = 
+						new MultiPartFormOutputStream(urlConn.getOutputStream(), boundary);
+					out.writeField("description", description.getText());
+					out.writeField("tags", tags.getText());
+					out.writeField("public", (c3.getSelectedObjects() != null) ? "1" : "0");
+					// upload a file
+					// out.writeFile("gpx_file", "text/xml", associatedFile);
+					// can also write bytes directly
+					// out.writeFile("myFile", "text/plain", "C:\\test.txt", 
+					// "This is some file text.".getBytes("ASCII"));
+					File tmp = File.createTempFile("josm", "tmp.gpx");
+					FileOutputStream outs = new FileOutputStream(tmp);
+					new GpxWriter(outs).write(data);
+					outs.close();
+					FileInputStream ins = new FileInputStream(tmp);
+					new GpxWriter(System.out).write(data);
+					out.writeFile("gpx_file", "text/xml", data.storageFile.getName(), ins);
+					out.close();
+					tmp.delete();
+					// read response from server
+					BufferedReader in = new BufferedReader(
+							new InputStreamReader(urlConn.getInputStream()));
+					String line = "";
+					while((line = in.readLine()) != null) {
+						System.out.println(line);
+					}
+					in.close();
+
+					//TODO check response
+					/*					int retCode = urlConn.getResponseCode();
+					System.out.println("got return: " + retCode);
+					String retMsg = urlConn.getResponseMessage();
+					urlConn.disconnect();
+					if (retCode != 200) {
+						// Look for a detailed error message from the server
+						if (urlConn.getHeaderField("Error") != null)
+							retMsg += "\n" + urlConn.getHeaderField("Error");
+
+						// Report our error
+						ByteArrayOutputStream o = new ByteArrayOutputStream();
+						System.out.println(new String(o.toByteArray(), "UTF-8").toString());
+						throw new RuntimeException(retCode+" "+retMsg);
+					}
+					 */
+				} catch (UnknownHostException ex) {
+					throw new RuntimeException(tr("Unknown host")+": "+ex.getMessage(), ex);
+				} catch (Exception ex) {
+					//if (cancel)
+					//	return; // assume cancel
+					if (ex instanceof RuntimeException)
+						throw (RuntimeException)ex;
+					throw new RuntimeException(ex.getMessage(), ex);
+				}	
+			}
+		}
+	}
+
+
+	public class ConvertToDataLayerAction extends AbstractAction {
+		public ConvertToDataLayerAction() {
+			super(tr("Convert to data layer"), ImageProvider.get("converttoosm"));
+		}
+		public void actionPerformed(ActionEvent e) {
+			JPanel msg = new JPanel(new GridBagLayout());
+			msg.add(new JLabel(tr("<html>Upload of unprocessed GPS data as map data is considered harmful.<br>If you want to upload traces, look here:")), GBC.eol());
+			msg.add(new UrlLabel(tr("http://www.openstreetmap.org/traces")), GBC.eop());
+			if (!DontShowAgainInfo.show("convert_to_data", msg))
+				return;
+			DataSet ds = new DataSet();
+			for (GpxTrack trk : data.tracks) {
+				for (Collection<WayPoint> segment : trk.trackSegs) {
+					Way w = new Way();
+					for (WayPoint p : segment) {
+						Node n = new Node(p.latlon);
+						ds.nodes.add(n);
+						w.nodes.add(n);
+					}
+					ds.ways.add(w);
+				}
+			}
+			Main.main.addLayer(new OsmDataLayer(ds, tr("Converted from: {0}", GpxLayer.this.name), null));
+			Main.main.removeLayer(GpxLayer.this);
+		}
+	}
+
+}
Index: trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 443)
+++ trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 444)
@@ -17,5 +17,7 @@
 import java.util.LinkedList;
 import java.util.Set;
-
+import java.util.ArrayList;
+
+import javax.swing.AbstractAction;
 import javax.swing.Icon;
 import javax.swing.JLabel;
@@ -42,4 +44,7 @@
 import org.openstreetmap.josm.data.osm.visitor.SimplePaintVisitor;
 import org.openstreetmap.josm.data.osm.visitor.Visitor;
+import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.data.gpx.GpxTrack;
+import org.openstreetmap.josm.data.gpx.WayPoint;
 import org.openstreetmap.josm.gui.MapView;
 import org.openstreetmap.josm.gui.dialogs.ConflictDialog;
@@ -309,4 +314,5 @@
 				new JMenuItem(new SaveAsAction(this)),
 				new JMenuItem(new GpxExportAction(this)),
+				new JMenuItem(new ConvertToGpxLayerAction()),
 				new JSeparator(),
 				new JMenuItem(new RenameLayerAction(associatedFile, this)),
@@ -325,3 +331,50 @@
 		}
 	}
+
+	public static GpxData toGpxData(DataSet data) {
+		GpxData gpxData = new GpxData();
+		HashSet<Node> doneNodes = new HashSet<Node>();
+		for (Way w : data.ways) {
+			if (w.incomplete || w.deleted) continue;
+			GpxTrack trk = new GpxTrack();
+			gpxData.tracks.add(trk);
+			ArrayList<WayPoint> trkseg = null;
+			for (Node n : w.nodes) {
+				if (n.incomplete || n.deleted) {
+					trkseg = null;
+					continue;
+				}
+				if (trkseg == null) {
+					trkseg = new ArrayList<WayPoint>();
+					trk.trackSegs.add(trkseg);
+				}
+				if (!n.tagged) {
+					doneNodes.add(n);
+				}
+				trkseg.add(new WayPoint(n.coor));
+			}
+		}
+		for (Node n : data.nodes) {
+			if (n.incomplete || n.deleted || doneNodes.contains(n)) continue;
+			WayPoint wpt = new WayPoint(n.coor);
+			if (n.keys.containsKey("name")) {
+				wpt.attr.put("name", n.keys.get("name"));
+			}
+		}
+		return gpxData;
+	}
+
+	public GpxData toGpxData() {
+		return toGpxData(data);
+	}
+
+	public class ConvertToGpxLayerAction extends AbstractAction {
+		public ConvertToGpxLayerAction() {
+			super(tr("Convert to GPX layer"), ImageProvider.get("converttogpx"));
+		}
+		public void actionPerformed(ActionEvent e) {
+			Main.main.addLayer(new GpxLayer(toGpxData(), tr("Converted from: {0}", name)));
+			Main.main.removeLayer(OsmDataLayer.this);
+		}
+	}
 }
Index: trunk/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java	(revision 443)
+++ 	(revision )
@@ -1,430 +1,0 @@
-// License: GPL. Copyright 2007 by Immanuel Scholz and others
-package org.openstreetmap.josm.gui.layer;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-import static org.openstreetmap.josm.tools.I18n.trn;
-
-import java.awt.CheckboxGroup;
-import java.awt.Color;
-import java.awt.Component;
-import java.awt.Graphics;
-import java.awt.GridBagLayout;
-import java.awt.Point;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.io.BufferedReader;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.net.URLConnection;
-import java.net.UnknownHostException;
-import java.util.Collection;
-import java.util.LinkedList;
-
-import javax.swing.AbstractAction;
-import javax.swing.Box;
-import javax.swing.ButtonGroup;
-import javax.swing.Icon;
-import javax.swing.JCheckBox;
-import javax.swing.JColorChooser;
-import javax.swing.JFileChooser;
-import javax.swing.JLabel;
-import javax.swing.JMenuItem;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JRadioButton;
-import javax.swing.JSeparator;
-import javax.swing.JTextField;
-import javax.swing.filechooser.FileFilter;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.actions.GpxExportAction;
-import org.openstreetmap.josm.actions.RenameLayerAction;
-import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
-import org.openstreetmap.josm.data.coor.EastNorth;
-import org.openstreetmap.josm.data.coor.LatLon;
-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.io.MultiPartFormOutputStream;
-import org.openstreetmap.josm.io.OsmWriter;
-import org.openstreetmap.josm.tools.ColorHelper;
-import org.openstreetmap.josm.tools.DontShowAgainInfo;
-import org.openstreetmap.josm.tools.GBC;
-import org.openstreetmap.josm.tools.ImageProvider;
-import org.openstreetmap.josm.tools.UrlLabel;
-
-/**
- * A layer holding data from a gps source.
- * The data is read only.
- * 
- * @author imi
- */
-public class RawGpsLayer extends Layer implements PreferenceChangedListener {
-
-	public class ConvertToDataLayerAction extends AbstractAction {
-		public ConvertToDataLayerAction() {
-			super(tr("Convert to data layer"), ImageProvider.get("converttoosm"));
-		}
-		public void actionPerformed(ActionEvent e) {
-			JPanel msg = new JPanel(new GridBagLayout());
-			msg.add(new JLabel(tr("<html>Upload of unprocessed GPS data as map data is considered harmful.<br>If you want to upload traces, look here:")), GBC.eol());
-			msg.add(new UrlLabel(tr("http://www.openstreetmap.org/traces")), GBC.eop());
-			if (!DontShowAgainInfo.show("convert_to_data", msg))
-				return;
-			DataSet ds = new DataSet();
-			for (Collection<GpsPoint> c : data) {
-				Way w = new Way();
-				for (GpsPoint p : c) {
-					Node n = new Node(p.latlon);
-					ds.nodes.add(n);
-					w.nodes.add(n);
-				}
-				ds.ways.add(w);
-			}
-			Main.main.addLayer(new OsmDataLayer(ds, tr("Converted from: {0}", RawGpsLayer.this.name), null));
-			Main.main.removeLayer(RawGpsLayer.this);
-		}
-	}
-	
-	public class UploadTraceAction extends AbstractAction {
-		public UploadTraceAction() {
-			super(tr("Upload this trace..."), ImageProvider.get("uploadtrace"));
-		}
-		public void actionPerformed(ActionEvent e) {
-			JPanel msg = new JPanel(new GridBagLayout());
-			msg.add(new JLabel(tr("<html>This functionality has been added only recently. Please<br>"+
-					              "use with care and check if it works as expected.</html>")), GBC.eop());
-			ButtonGroup bg = new ButtonGroup();
-			JRadioButton c1 = null;
-			JRadioButton c2 = null;
-			
-			if (associatedFile != null) {
-				c1 = new JRadioButton(tr("Upload track filtered by JOSM"), false);
-				c2 = new JRadioButton(tr("Upload raw file: {0}", associatedFile.getName()), true);
-			}
-			else
-			{
-				c1 = new JRadioButton(tr("Upload track filtered by JOSM"), true);
-				c2 = new JRadioButton(tr("Upload raw file: "), false);
-				c2.setEnabled(false);
-			}
-			c1.setEnabled(false);
-			bg.add(c1);
-			bg.add(c2);
-
-			msg.add(c1, GBC.eol());
-			msg.add(c2, GBC.eop());
-
-			
-			JTextField description = new JTextField();
-			JTextField tags = new JTextField();
-			msg.add(new JLabel(tr("Description:")), GBC.std());
-			msg.add(description, GBC.eol().fill(GBC.HORIZONTAL));
-			msg.add(new JLabel(tr("Tags:")), GBC.std());
-			msg.add(tags, GBC.eol().fill(GBC.HORIZONTAL));
-			JCheckBox c3 = new JCheckBox("public");
-			msg.add(c3, GBC.eop());
-			msg.add(new JLabel("Please ensure that you don't upload your traces twice."), GBC.eop());
-			
-			int answer = JOptionPane.showConfirmDialog(Main.parent, msg, tr("GPX-Upload"), JOptionPane.OK_CANCEL_OPTION);
-			if (answer == JOptionPane.OK_OPTION)
-			{
-				try {
-					String version = Main.pref.get("osm-server.version", "0.5");
-					URL url = new URL(Main.pref.get("osm-server.url") +
-							"/" + version + "/gpx/create");
-
-					// create a boundary string
-					String boundary = MultiPartFormOutputStream.createBoundary();
-					URLConnection urlConn = MultiPartFormOutputStream.createConnection(url);
-					urlConn.setRequestProperty("Accept", "*/*");
-					urlConn.setRequestProperty("Content-Type", 
-						MultiPartFormOutputStream.getContentType(boundary));
-					// set some other request headers...
-					urlConn.setRequestProperty("Connection", "Keep-Alive");
-					urlConn.setRequestProperty("Cache-Control", "no-cache");
-					// no need to connect cuz getOutputStream() does it
-					MultiPartFormOutputStream out = 
-						new MultiPartFormOutputStream(urlConn.getOutputStream(), boundary);
-					out.writeField("description", description.getText());
-					out.writeField("tags", tags.getText());
-					out.writeField("public", (c3.getSelectedObjects() != null) ? "1" : "0");
-					// upload a file
-					out.writeFile("gpx_file", "text/xml", associatedFile);
-					// can also write bytes directly
-					// out.writeFile("myFile", "text/plain", "C:\\test.txt", 
-					// "This is some file text.".getBytes("ASCII"));
-					out.close();
-					// read response from server
-					BufferedReader in = new BufferedReader(
-						new InputStreamReader(urlConn.getInputStream()));
-					String line = "";
-					while((line = in.readLine()) != null) {
-						 System.out.println(line);
-					}
-					in.close();
-					
-					/*
-					int retCode = activeConnection.getResponseCode();
-					System.out.println("got return: "+retCode);
-					String retMsg = activeConnection.getResponseMessage();
-					activeConnection.disconnect();
-					if (retCode != 200) {
-						// Look for a detailed error message from the server
-						if (activeConnection.getHeaderField("Error") != null)
-							retMsg += "\n" + activeConnection.getHeaderField("Error");
-
-						// Report our error
-						ByteArrayOutputStream o = new ByteArrayOutputStream();
-						System.out.println(new String(o.toByteArray(), "UTF-8").toString());
-						throw new RuntimeException(retCode+" "+retMsg);
-					}
-					*/
-				} catch (UnknownHostException ex) {
-					throw new RuntimeException(tr("Unknown host")+": "+ex.getMessage(), ex);
-				} catch (Exception ex) {
-					//if (cancel)
-					//	return; // assume cancel
-					if (ex instanceof RuntimeException)
-						throw (RuntimeException)ex;
-					throw new RuntimeException(ex.getMessage(), ex);
-				}	
-			}
-		}
-	}
-
-	public static class GpsPoint {
-		public final LatLon latlon;
-		public final EastNorth eastNorth;
-		public final String time;
-		public GpsPoint(LatLon ll, String t) {
-			latlon = ll; 
-			eastNorth = Main.proj.latlon2eastNorth(ll); 
-			time = t;
-		}
-	}
-
-	/**
-	 * A list of ways which containing a list of points.
-	 */
-	public final Collection<Collection<GpsPoint>> data;
-	public final boolean fromServer;
-
-	public RawGpsLayer(boolean fromServer, Collection<Collection<GpsPoint>> data, String name, File associatedFile) {
-		super(name);
-		this.fromServer = fromServer;
-		this.associatedFile = associatedFile;
-		this.data = data;
-		Main.pref.listener.add(this);
-	}
-
-	/**
-	 * Return a static icon.
-	 */
-	@Override public Icon getIcon() {
-		return ImageProvider.get("layer", "rawgps_small");
-	}
-
-	@Override public void paint(Graphics g, MapView mv) {
-		String gpsCol = Main.pref.get("color.gps point");
-		String gpsColSpecial = Main.pref.get("color.layer "+name);
-		if (!gpsColSpecial.equals(""))
-			g.setColor(ColorHelper.html2color(gpsColSpecial));
-		else if (!gpsCol.equals(""))
-			g.setColor(ColorHelper.html2color(gpsCol));
-		else
-			g.setColor(Color.GRAY);
-		Point old = null;
-
-		boolean force = Main.pref.getBoolean("draw.rawgps.lines.force");
-		boolean lines = Main.pref.getBoolean("draw.rawgps.lines");
-		String linesKey = "draw.rawgps.lines.layer "+name;
-		if (Main.pref.hasKey(linesKey))
-			lines = Main.pref.getBoolean(linesKey);
-		boolean large = Main.pref.getBoolean("draw.rawgps.large");
-		for (Collection<GpsPoint> c : data) {
-			if (!force)
-				old = null;
-			for (GpsPoint p : c) {
-				Point screen = mv.getPoint(p.eastNorth);
-				if (lines && old != null)
-					g.drawLine(old.x, old.y, screen.x, screen.y);
-				else if (!large)
-					g.drawRect(screen.x, screen.y, 0, 0);
-				if (large)
-					g.fillRect(screen.x-1, screen.y-1, 3, 3);
-				old = screen;
-			}
-		}
-	}
-
-	@Override public String getToolTipText() {
-		int points = 0;
-		for (Collection<GpsPoint> c : data)
-			points += c.size();
-		String tool = data.size()+" "+trn("track", "tracks", data.size())
-		+" "+points+" "+trn("point", "points", points);
-		if (associatedFile != null)
-			tool = "<html>"+tool+"<br>"+associatedFile.getPath()+"</html>";
-		return tool;
-	}
-
-	@Override public void mergeFrom(Layer from) {
-		RawGpsLayer layer = (RawGpsLayer)from;
-		data.addAll(layer.data);
-	}
-
-	@Override public boolean isMergable(Layer other) {
-		return other instanceof RawGpsLayer;
-	}
-
-	@Override public void visitBoundingBox(BoundingXYVisitor v) {
-		for (Collection<GpsPoint> c : data)
-			for (GpsPoint p : c)
-				v.visit(p.eastNorth);
-	}
-
-	@Override public Object getInfoComponent() {
-		StringBuilder b = new StringBuilder();
-		int points = 0;
-		for (Collection<GpsPoint> c : data) {
-			b.append("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"+trn("a track with {0} point","a track with {0} points", c.size(), c.size())+"<br>");
-			points += c.size();
-		}
-		b.append("</html>");
-		return "<html>"+trn("{0} consists of {1} track", "{0} consists of {1} tracks", data.size(), name, data.size())+" ("+trn("{0} point", "{0} points", points, points)+")<br>"+b.toString();
-	}
-
-	@Override public Component[] getMenuEntries() {
-		JMenuItem line = new JMenuItem(tr("Customize line drawing"), ImageProvider.get("mapmode/addsegment"));
-		line.addActionListener(new ActionListener(){
-			public void actionPerformed(ActionEvent e) {
-				JRadioButton[] r = new JRadioButton[3];
-				r[0] = new JRadioButton(tr("Use global settings."));
-				r[1] = new JRadioButton(tr("Draw lines between points for this layer."));
-				r[2] = new JRadioButton(tr("Do not draw lines between points for this layer."));
-				ButtonGroup group = new ButtonGroup();
-				Box panel = Box.createVerticalBox();
-				for (JRadioButton b : r) {
-					group.add(b);
-					panel.add(b);
-				}
-				String propName = "draw.rawgps.lines.layer "+name;
-				if (Main.pref.hasKey(propName))
-					group.setSelected(r[Main.pref.getBoolean(propName) ? 1:2].getModel(), true);
-				else
-					group.setSelected(r[0].getModel(), true);
-				int answer = JOptionPane.showConfirmDialog(Main.parent, panel, tr("Select line drawing options"), JOptionPane.OK_CANCEL_OPTION);
-				if (answer == JOptionPane.CANCEL_OPTION)
-					return;
-				if (group.getSelection() == r[0].getModel())
-					Main.pref.put(propName, null);
-				else
-					Main.pref.put(propName, group.getSelection() == r[1].getModel());
-				Main.map.repaint();
-			}
-		});
-
-		JMenuItem color = new JMenuItem(tr("Customize Color"), ImageProvider.get("colorchooser"));
-		color.addActionListener(new ActionListener(){
-			public void actionPerformed(ActionEvent e) {
-				String col = Main.pref.get("color.layer "+name, Main.pref.get("color.gps point", ColorHelper.color2html(Color.gray)));
-				JColorChooser c = new JColorChooser(ColorHelper.html2color(col));
-				Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")};
-				int answer = JOptionPane.showOptionDialog(Main.parent, c, tr("Choose a color"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
-				switch (answer) {
-				case 0:
-					Main.pref.put("color.layer "+name, ColorHelper.color2html(c.getColor()));
-					break;
-				case 1:
-					return;
-				case 2:
-					Main.pref.put("color.layer "+name, null);
-					break;
-				}
-				Main.map.repaint();
-			}
-		});
-
-		JMenuItem tagimage = new JMenuItem(tr("Import images"), ImageProvider.get("tagimages"));
-		tagimage.addActionListener(new ActionListener(){
-			public void actionPerformed(ActionEvent e) {
-				JFileChooser fc = new JFileChooser(Main.pref.get("tagimages.lastdirectory"));
-				fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
-				fc.setMultiSelectionEnabled(true);
-				fc.setAcceptAllFileFilterUsed(false);
-				fc.setFileFilter(new FileFilter(){
-					@Override public boolean accept(File f) {
-						return f.isDirectory() || f.getName().toLowerCase().endsWith(".jpg");
-					}
-					@Override public String getDescription() {
-						return tr("JPEG images (*.jpg)");
-					}
-				});
-				fc.showOpenDialog(Main.parent);
-				File[] sel = fc.getSelectedFiles();
-				if (sel == null || sel.length == 0)
-					return;
-				LinkedList<File> files = new LinkedList<File>();
-				addRecursiveFiles(files, sel);
-				Main.pref.put("tagimages.lastdirectory", fc.getCurrentDirectory().getPath());
-				GeoImageLayer.create(files, RawGpsLayer.this);
-			}
-
-			private void addRecursiveFiles(LinkedList<File> files, File[] sel) {
-				for (File f : sel) {
-					if (f.isDirectory())
-						addRecursiveFiles(files, f.listFiles());
-					else if (f.getName().toLowerCase().endsWith(".jpg"))
-						files.add(f);
-				}
-			}
-		});
-
-		if (Main.applet)
-			return new Component[]{
-				new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
-				new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
-				new JSeparator(),
-				color,
-				line,
-				new JMenuItem(new ConvertToDataLayerAction()),
-				//new JMenuItem(new UploadTraceAction()),
-				new JSeparator(),
-				new JMenuItem(new RenameLayerAction(associatedFile, this)),
-				new JSeparator(),
-				new JMenuItem(new LayerListPopup.InfoAction(this))};
-		return new Component[]{
-				new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
-				new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
-				new JSeparator(),
-				new JMenuItem(new GpxExportAction(this)),
-				color,
-				line,
-				tagimage,
-				new JMenuItem(new ConvertToDataLayerAction()),
-				//new JMenuItem(new UploadTraceAction()),
-				new JSeparator(),
-				new JMenuItem(new RenameLayerAction(associatedFile, this)),
-				new JSeparator(),
-				new JMenuItem(new LayerListPopup.InfoAction(this))};
-	}
-
-	public void preferenceChanged(String key, String newValue) {
-		if (Main.map != null && (key.equals("draw.rawgps.lines") || key.equals("draw.rawgps.lines.force")))
-			Main.map.repaint();
-	}
-
-	@Override public void destroy() {
-		Main.pref.listener.remove(RawGpsLayer.this);
-    }
-}
Index: trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java	(revision 443)
+++ trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java	(revision 444)
@@ -9,4 +9,5 @@
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.LinkedList;
@@ -18,4 +19,6 @@
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.gpx.GpxLink;
+import org.openstreetmap.josm.data.gpx.WayPoint;
 import org.openstreetmap.josm.gui.MapView;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -72,19 +75,27 @@
 	static {
 		Marker.markerProducers.add(new MarkerProducers() {
-			public Marker createMarker(LatLon ll, Map<String,String> data, File relativePath) {
-				String link = data.get("link");
+			public Marker createMarker(WayPoint wpt, File relativePath) {
+				String uri = null;
+				// cheapest way to check whether "link" object exists and is a non-empty
+				// collection of GpxLink objects...
+				try {
+					for (GpxLink oneLink : (Collection<GpxLink>) wpt.attr.get("link")) {
+						uri = oneLink.uri;
+						break;
+					}
+				} catch (Exception ex) {};
 
 				// Try a relative file:// url, if the link is not in an URL-compatible form
-				if (relativePath != null && link != null && !isWellFormedAddress(link))
-					link = new File(relativePath, link).toURI().toString();
+				if (relativePath != null && uri != null && !isWellFormedAddress(uri))
+					uri = new File(relativePath, uri).toURI().toString();
 
-				if (link == null)
-					return new Marker(ll, data.get("name"), data.get("symbol"));
-				if (link.endsWith(".wav"))
-					return AudioMarker.create(ll, link);
-				else if (link.endsWith(".png") || link.endsWith(".jpg") || link.endsWith(".jpeg") || link.endsWith(".gif"))
-					return ImageMarker.create(ll, link);
+				if (uri == null)
+					return new Marker(wpt.latlon, wpt.getString("name"), wpt.getString("symbol"));
+				if (uri.endsWith(".wav"))
+					return AudioMarker.create(wpt.latlon, uri);
+				else if (uri.endsWith(".png") || uri.endsWith(".jpg") || uri.endsWith(".jpeg") || uri.endsWith(".gif"))
+					return ImageMarker.create(wpt.latlon, uri);
 				else
-					return WebMarker.create(ll, link);
+					return WebMarker.create(wpt.latlon, uri);
 			}
 
@@ -160,7 +171,7 @@
 	 * @return a new Marker object
 	 */
-	public static Marker createMarker(LatLon ll, HashMap<String,String> data, File relativePath) {
+	public static Marker createMarker(WayPoint wpt, File relativePath) {
 		for (MarkerProducers maker : Marker.markerProducers) {
-			Marker marker = maker.createMarker(ll, data, relativePath);
+			Marker marker = maker.createMarker(wpt, relativePath);
 			if (marker != null)
 				return marker;
Index: trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java	(revision 443)
+++ trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java	(revision 444)
@@ -14,4 +14,5 @@
 import java.awt.event.MouseEvent;
 import java.io.File;
+import java.util.ArrayList;
 import java.util.Collection;
 
@@ -25,4 +26,6 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.RenameLayerAction;
+import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.data.gpx.WayPoint;
 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
 import org.openstreetmap.josm.gui.MapView;
@@ -52,8 +55,15 @@
 	private boolean mousePressed = false;
 	
-	public MarkerLayer(Collection<Marker> indata, String name, File associatedFile) {
+	public MarkerLayer(GpxData indata, String name, File associatedFile) {
+		
 		super(name);
 		this.associatedFile = associatedFile;
-		this.data = indata;
+		this.data = new ArrayList<Marker>();
+		
+		for (WayPoint wpt : indata.waypoints) {
+            Marker m = Marker.createMarker(wpt, indata.storageFile);
+            if (m != null)
+            	data.add(m);
+		}
 		
 		SwingUtilities.invokeLater(new Runnable(){
Index: trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerProducers.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerProducers.java	(revision 443)
+++ trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerProducers.java	(revision 444)
@@ -6,4 +6,5 @@
 
 import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.gpx.WayPoint;
 
 /**
@@ -26,4 +27,4 @@
 	 * @return A Marker object, or <code>null</code>.
 	 */
-	public Marker createMarker(LatLon ll, Map<String,String> data, File relativePath);
+	public Marker createMarker(WayPoint wp, File relativePath);
 }
Index: trunk/src/org/openstreetmap/josm/io/BoundingBoxDownloader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/BoundingBoxDownloader.java	(revision 443)
+++ trunk/src/org/openstreetmap/josm/io/BoundingBoxDownloader.java	(revision 444)
@@ -6,6 +6,4 @@
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.Collection;
-import java.util.LinkedList;
 
 import org.openstreetmap.josm.Main;
@@ -14,5 +12,5 @@
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.DataSource;
-import org.openstreetmap.josm.gui.layer.RawGpsLayer.GpsPoint;
+import org.openstreetmap.josm.data.gpx.GpxData;
 import org.xml.sax.SAXException;
 
@@ -41,34 +39,29 @@
      * 		ways.
      */
-    public Collection<Collection<GpsPoint>> parseRawGps() throws IOException, SAXException {
+	public GpxData parseRawGps() throws IOException, SAXException {
 		Main.pleaseWaitDlg.currentAction.setText(tr("Contacting OSM Server..."));
     	try {
     		String url = "trackpoints?bbox="+lon1+","+lat1+","+lon2+","+lat2+"&page=";
-    		Collection<Collection<GpsPoint>> data = new LinkedList<Collection<GpsPoint>>();
-    		Collection<GpsPoint> list = new LinkedList<GpsPoint>();
 
-    		for (int i = 0;;++i) {
+			boolean done = false;
+			GpxData result = null;
+			for (int i = 0;!done;++i) {
     			Main.pleaseWaitDlg.currentAction.setText(tr("Downloading points {0} to {1}...", i * 5000, ((i + 1) * 5000)));
     			InputStream in = getInputStream(url+i, Main.pleaseWaitDlg);
     			if (in == null)
     				break;
-    			// Use only track points, since the server mix everything together 
-    			Collection<Collection<GpsPoint>> allWays = new RawGpsReader(in, null).trackData;
-
-    			boolean foundSomething = false;
-    			for (Collection<GpsPoint> t : allWays) {
-    				if (!t.isEmpty()) {
-    					foundSomething = true;
-    					list.addAll(t);
-    				}
+				GpxData currentGpx = new GpxReader(in, null).data;
+				if (result == null) {
+					result = currentGpx;
+				} else if (currentGpx.hasTrackPoints()) {
+					result.mergeFrom(currentGpx);
+				} else{
+					done = true;
     			}
-    			if (!foundSomething)
-    				break;
     			in.close();
     			activeConnection = null;
     		}
-    		if (!list.isEmpty())
-    			data.add(list);
-    		return data;
+			result.fromServer = true;
+			return result;
     	} catch (IllegalArgumentException e) {
     		// caused by HttpUrlConnection in case of illegal stuff in the response
Index: trunk/src/org/openstreetmap/josm/io/GpxReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/GpxReader.java	(revision 444)
+++ trunk/src/org/openstreetmap/josm/io/GpxReader.java	(revision 444)
@@ -0,0 +1,271 @@
+//License: GPL. Copyright 2007 by Immanuel Scholz and others
+
+//TODO: this is far from complete, but can emulate old RawGps behaviour
+package org.openstreetmap.josm.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Stack;
+import java.util.Map;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.data.gpx.GpxLink;
+import org.openstreetmap.josm.data.gpx.GpxTrack;
+import org.openstreetmap.josm.data.gpx.WayPoint;
+import org.openstreetmap.josm.data.gpx.GpxRoute;
+import org.openstreetmap.josm.gui.layer.markerlayer.MarkerProducers;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Read a gpx file. Bounds are not read, as we caluclate them. @see GpxData.recalculateBounds()
+ * @author imi, ramack
+ */
+public class GpxReader {
+	// TODO: implement GPX 1.0 parsing
+
+	/**
+	 * The resulting gpx data
+	 */
+	public GpxData data;
+	public enum state { init, metadata, wpt, rte, trk, ext, author, link, trkseg }
+
+	private class Parser extends DefaultHandler {
+
+		private GpxData currentData;
+		private GpxTrack currentTrack;
+		private Collection<WayPoint> currentTrackSeg;
+		private GpxRoute currentRoute;
+		private WayPoint currentWayPoint;
+
+		private state currentState = state.init;
+	
+		private GpxLink currentLink;
+		private Stack<state> states;
+
+		private StringBuffer accumulator = new StringBuffer();
+
+		@Override public void startDocument() {
+			accumulator = new StringBuffer();
+			states = new Stack<state>();
+			currentData = new GpxData();
+		}		
+
+		@Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
+			switch(currentState) {
+			case init:
+				if (qName.equals("metadata")) {
+					states.push(currentState);
+					currentState = state.metadata;
+				} else if (qName.equals("wpt")) {
+					LatLon ll = new LatLon(Double.parseDouble(atts.getValue("lat")), Double.parseDouble(atts.getValue("lon")));
+					states.push(currentState);
+					currentState = state.wpt;
+					currentWayPoint = new WayPoint(ll);
+				} else if (qName.equals("rte")) {
+					states.push(currentState);
+					currentState = state.rte;
+					currentRoute = new GpxRoute();
+				} else if (qName.equals("trk")) {
+					states.push(currentState);
+					currentState = state.trk;
+					currentTrack = new GpxTrack();
+				} else if (qName.equals("extensions")) {
+					states.push(currentState);
+					currentState = state.ext;
+				}
+				break;
+			case author:
+				if (qName.equals("link")) {
+					states.push(currentState);
+					currentState = state.link;
+					currentLink = new GpxLink(atts.getValue("href"));
+				}
+				break;
+			case trk:
+				if (qName.equals("trkseg")) {
+					states.push(currentState);
+					currentState = state.trkseg;
+					currentTrackSeg = new LinkedList<WayPoint>();
+				}
+				if (qName.equals("link")) {
+					states.push(currentState);
+					currentState = state.link;
+					currentLink = new GpxLink(atts.getValue("href"));
+				}
+				break;
+			case metadata:
+				if (qName.equals("author")) {
+					states.push(currentState);
+					currentState = state.author;
+				}
+				break;
+			case trkseg:
+				if (qName.equals("trkpt")) {
+					LatLon ll = new LatLon(Double.parseDouble(atts.getValue("lat")), Double.parseDouble(atts.getValue("lon")));
+					states.push(currentState);
+					currentState = state.wpt;
+					currentWayPoint = new WayPoint(ll);
+				}
+				break;
+			case wpt:
+				if (qName.equals("link")) {
+					states.push(currentState);
+					currentState = state.link;
+					currentLink = new GpxLink(atts.getValue("href"));
+				}
+				break;
+			case rte:
+				if (qName.equals("link")) {
+					states.push(currentState);
+					currentState = state.link;
+					currentLink = new GpxLink(atts.getValue("href"));
+				}
+				if (qName.equals("rtept")) {
+					LatLon ll = new LatLon(Double.parseDouble(atts.getValue("lat")), Double.parseDouble(atts.getValue("lon")));
+					states.push(currentState);
+					currentState = state.wpt;
+					currentWayPoint = new WayPoint(ll);
+				}
+				break;
+			default:
+			}
+			accumulator.setLength(0);
+		}
+
+		@Override public void characters(char[] ch, int start, int length) {
+			accumulator.append(ch, start, length);
+		}
+
+		private Map<String, Object> getAttr() {
+			switch (currentState) {
+				case rte: return currentRoute.attr;
+				case metadata: return currentData.attr;
+				case wpt: return currentWayPoint.attr;
+				case trk: return currentTrack.attr;
+				default: return null;
+			}
+		}
+
+		@Override public void endElement(String namespaceURI, String localName, String qName) {
+			switch (currentState) {
+			case metadata:
+				if (qName.equals("name") || qName.equals("desc") ||
+						qName.equals("time") || qName.equals("keywords")) {
+					currentData.attr.put(qName, accumulator.toString());
+				} else if (qName.equals("metadata")) {
+					currentState = states.pop();
+				}
+				//TODO: parse copyright, bounds, extensions
+				break;
+			case author:
+				if (qName.equals("author")) {
+					currentState = states.pop();
+				} else if (qName.equals("name") || qName.equals("email")) {
+					currentData.attr.put("author" + qName, accumulator.toString());
+				} else if (qName.equals("link")) {
+					currentData.attr.put("authorlink", currentLink);
+				}
+				break;
+			case link:
+				if (qName.equals("text")) {
+					currentLink.text = accumulator.toString();
+				} else if (qName.equals("type")) {
+					currentLink.type = accumulator.toString();
+				} else if (qName.equals("link")) {
+					currentState = states.pop();
+				}
+				if (currentState == state.author) {
+					currentData.attr.put("authorlink", currentLink);
+				} else if (currentState != state.link) {
+					Map<String, Object> attr = getAttr();
+					if (!attr.containsKey("link")) {
+						attr.put("link", new LinkedList<GpxLink>());
+					}
+					((Collection<GpxLink>) attr.get("link")).add(currentLink);
+				}
+				break;
+			case wpt:
+				if (qName.equals("ele") || qName.equals("time") || qName.equals("desc")
+						|| qName.equals("magvar") || qName.equals("geoidheight")
+						|| qName.equals("name") || qName.equals("time")
+						|| qName.equals("sym") || qName.equals("cmt") || qName.equals("type")) {
+					currentWayPoint.attr.put(qName, accumulator.toString());
+				} else if (qName.equals("rtept")) {
+					currentState = states.pop();
+					currentRoute.routePoints.add(currentWayPoint);
+				} else if (qName.equals("trkpt")) {
+					currentState = states.pop();
+					currentTrackSeg.add(currentWayPoint);
+				} else if (qName.equals("wpt")) {
+					currentState = states.pop();
+					currentData.waypoints.add(currentWayPoint);
+				}
+				break;
+			case trkseg:
+				if (qName.equals("trkseg")) {
+					currentState = states.pop();
+					currentTrack.trackSegs.add(currentTrackSeg);
+				}
+				break;
+			case trk:
+				if (qName.equals("trk")) {
+					currentState = states.pop();
+					currentData.tracks.add(currentTrack);
+				} else if (qName.equals("name") || qName.equals("cmt")
+						|| qName.equals("desc") || qName.equals("src")
+						|| qName.equals("type") || qName.equals("number")) {
+					currentTrack.attr.put(qName, accumulator.toString());
+				}
+			default:
+				if (qName.equals("wpt")) {
+					currentState = states.pop();
+				} else if (qName.equals("rte")) {
+					currentState = states.pop();
+					currentData.routes.add(currentRoute);
+				} else if (qName.equals("extensions")) {
+					currentState = states.pop();
+				}
+			}
+		}
+
+		@Override public void endDocument() throws SAXException  {
+			if (!states.empty()) {
+				throw new SAXException(tr("Parse error: invalid document structure for gpx document"));
+			}
+			data = currentData;
+		}
+	}
+
+	/**
+	 * Parse the input stream and store the result in trackData and markerData
+	 * 
+	 * @param relativeMarkerPath The directory to use as relative path for all &lt;wpt&gt; 
+	 *    marker tags. Maybe <code>null</code>, in which case no relative urls are constructed for the markers. 
+	 */
+	public GpxReader(InputStream source, File relativeMarkerPath) throws SAXException, IOException {
+		
+		Parser parser = new Parser();
+		InputSource inputSource = new InputSource(new InputStreamReader(source, "UTF-8"));
+		try {
+			SAXParserFactory.newInstance().newSAXParser().parse(inputSource, parser);
+			data.storageFile = relativeMarkerPath;
+		} catch (ParserConfigurationException e) {
+			e.printStackTrace(); // broken SAXException chaining
+			throw new SAXException(e);
+		}
+	}
+}
Index: trunk/src/org/openstreetmap/josm/io/GpxWriter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/GpxWriter.java	(revision 443)
+++ trunk/src/org/openstreetmap/josm/io/GpxWriter.java	(revision 444)
@@ -3,14 +3,15 @@
 
 import java.io.PrintWriter;
+import java.io.OutputStream;
 import java.util.Collection;
-import java.util.LinkedList;
+import java.util.Map;
 
 import org.openstreetmap.josm.data.Bounds;
-import org.openstreetmap.josm.data.coor.LatLon;
-import org.openstreetmap.josm.data.osm.DataSet;
-import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.gui.layer.RawGpsLayer.GpsPoint;
+
+import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.data.gpx.GpxTrack;
+import org.openstreetmap.josm.data.gpx.GpxRoute;
+import org.openstreetmap.josm.data.gpx.GpxLink;
+import org.openstreetmap.josm.data.gpx.WayPoint;
 
 /**
@@ -22,4 +23,6 @@
  * or less than 2 &lt;trkpt&gt; are exported.
  * 
+ * TODO: to export OSM data as gpx do a transformation into a GpxData instance first
+ *
  * @author imi
  */
@@ -30,183 +33,182 @@
 	}
 
-	/**
-	 * Export the dataset to gpx.  The ways are converted to trksegs, each in
-	 * a seperate trk.  Finally, all remaining nodes are added as wpt.
+	public GpxWriter(OutputStream out) {
+		super(new PrintWriter(out));
+	}
+
+	public GpxWriter() {
+		super(null);
+		//sorry for this one here, this will be cleaned up once the new scheme works
+	}
+
+	private GpxData data;
+	private String indent = "";
+
+	private final static int WAY_POINT = 0;
+	private final static int ROUTE_POINT = 1;
+	private final static int TRACK_POINT = 2;
+
+	public void write(GpxData data) {
+		this.data = data;
+		out.println("<?xml version='1.0' encoding='UTF-8'?>");
+		out.println("<gpx version=\"1.1\" creator=\"JOSM GPX export\" xmlns=\"http://www.topografix.com/GPX/1/1\">");
+		indent = "  ";
+		writeMetaData();
+		writeWayPoints();
+		writeRoutes();
+		writeTracks();
+		out.print("</gpx>");
+		out.flush();
+		out.close();
+	}
+
+	private void writeAttr(Map<String, Object> attr) {
+		boolean hasAuthor = false;
+		for (Map.Entry<String, Object> ent : attr.entrySet()) {
+			String k = ent.getKey();
+			if (k.indexOf("author") == 0) {
+				hasAuthor = true;
+			} else if (k.equals("link")) {
+				for (GpxLink link : (Collection<GpxLink>) ent.getValue()) {
+					gpxLink(link);
+				}
+			} else {
+				simpleTag(k, (String) ent.getValue());
+			}
+		}
+
+		if (hasAuthor) {
+			open("author");
+			simpleTag("name", (String) attr.get("authorname"));
+			simpleTag("email", (String) attr.get("authoremail"));
+			gpxLink((GpxLink) attr.get("authorlink"));
+			closeln("author");
+		}
+
+		// TODO: copyright
+	}
+
+	private void writeMetaData() {
+		openln("metadata");
+		writeAttr(data.attr);
+
+		data.recalculateBounds();
+		Bounds bounds = data.bounds;
+		String b = "minlat=\"" + bounds.min.lat() + "\" minlon=\"" + bounds.min.lon() +
+			"\" maxlat=\"" + bounds.max.lat() + "\" maxlon=\"" + bounds.max.lon() + "\"" ;
+		inline("bounds", b);
+
+		closeln("metadata");
+	}
+
+	private void writeWayPoints() {
+		for (WayPoint pnt : data.waypoints) {
+			wayPoint(pnt, WAY_POINT);
+		}        
+	}
+	
+	private void writeRoutes() {
+		for (GpxRoute rte : data.routes) {
+			openln("rte");
+			writeAttr(rte.attr);
+			for (WayPoint pnt : rte.routePoints) {
+				wayPoint(pnt, ROUTE_POINT);
+			}
+			closeln("rte");
+		}
+	}
+	
+	private void writeTracks() {
+		for (GpxTrack trk : data.tracks) {
+			open("trk");
+			writeAttr(trk.attr);
+			for (Collection<WayPoint> seg : trk.trackSegs) {
+				openln("trkseg");
+				for (WayPoint pnt : seg) {
+					wayPoint(pnt, TRACK_POINT);
+				}
+				closeln("trkseg");
+			}
+			closeln("trk");
+		}
+	}
+
+	private void openln(String tag) {
+		open(tag);
+		out.print("\n");
+	}
+
+	private void open(String tag) {
+		out.print(indent + "<" + tag + ">");
+		indent += "  ";
+	}
+
+	private void openAtt(String tag, String attributes) {
+		out.println(indent + "<" + tag + " " + attributes + ">");
+		indent += "  ";
+	}
+	
+	private void inline(String tag, String attributes) {
+		out.println(indent + "<" + tag + " " + attributes + " />");
+	}
+
+	private void close(String tag) {
+		indent = indent.substring(2);
+		out.print(indent + "</" + tag + ">");
+	}
+
+	private void closeln(String tag) {
+		close(tag);
+		out.print("\n");
+	}
+
+	/**       
+	 * if content not null, open tag, write encoded content, and close tag
+	 * else do nothing.
 	 */
-	public static final class All implements XmlWriter.OsmWriterInterface {
-		private final DataSet data;
-		private final String name;
-		private final String desc;
-		private final String author;
-		private final String email;
-		private final String copyright;
-		private final String year;
-		private final String keywords;
-		private boolean metadataClosed = false;
-
-		public All(DataSet data, String name, String desc, String author, String email, String copyright, String year, String keywords) {
-			this.data = data;
-			this.name = name;
-			this.desc = desc;
-			this.author = author;
-			this.email = email;
-			this.copyright = copyright;
-			this.year = year;
-			this.keywords = keywords;
-		}
-
-		public void header(PrintWriter out) {
-			out.println("<gpx version='1.1' creator='JOSM' xmlns='http://www.topografix.com/GPX/1/1'>");
-			out.println("  <metadata>");
-			if (!name.equals(""))
-				out.println("    <name>"+XmlWriter.encode(name)+"</name>");
-			if (!desc.equals(""))
-				out.println("    <desc>"+XmlWriter.encode(desc)+"</desc>");
-			if (!author.equals("")) {
-				out.println("    <author>");
-				out.println("      <name>"+XmlWriter.encode(author)+"</name>");
-				if (!email.equals(""))
-					out.println("      <email>"+XmlWriter.encode(email)+"</email>");
-				out.println("    </author>");
-				if (!copyright.equals("")) {
-					out.println("    <copyright author='"+XmlWriter.encode(author)+"'>");
-					if (!year.equals(""))
-						out.println("      <year>"+XmlWriter.encode(year)+"</year>");
-					out.println("      <license>"+XmlWriter.encode(copyright)+"</license>");
-					out.println("    </copyright>");
-				}
-			}
-			if (!keywords.equals("")) {
-				out.println("    <keywords>"+XmlWriter.encode(keywords)+"</keywords>");
-			}
-			// don't finish here, to give output functions the chance to add <bounds>
-		}
-
-		public void write(PrintWriter out) {
-			Collection<OsmPrimitive> all = data.allNonDeletedPrimitives();
-			if (all.isEmpty())
-				return;
-			GpxWriter writer = new GpxWriter(out);
-			// calculate bounds
-			Bounds b = new Bounds(new LatLon(Double.MAX_VALUE, Double.MAX_VALUE), new LatLon(-Double.MAX_VALUE, -Double.MAX_VALUE));
-			for (Node n : data.nodes)
-				if (!n.deleted)
-					b.extend(n.coor);
-			out.println("    <bounds minlat='"+b.min.lat()+"' minlon='"+b.min.lon()+"' maxlat='"+b.max.lat()+"' maxlon='"+b.max.lon()+"' />");
-			out.println("  </metadata>");
-			metadataClosed = true;
-
-			// add ways
-			for (Way w : data.ways) {
-				if (w.deleted)
-					continue;
-				out.println("  <trk>");
-						out.println("    <trkseg>");
-				for (Node n : w.nodes) {
-					writer.outputNode(n, false);
-					all.remove(n);
-			}
-					out.println("    </trkseg>");
-				out.println("  </trk>");
-				all.remove(w);
-			}
-
-			// finally add the remaining nodes
-			for (OsmPrimitive osm : all)
-				if (osm instanceof Node)
-					writer.outputNode((Node)osm, true);
-		}
-
-		public void footer(PrintWriter out) {
-			if (!metadataClosed)
-				out.println("  </metadata>");
-			out.println("</gpx>");
-		}
-	}
-
-
-	/**
-	 * Export the collection structure to gpx. The gpx will consists of only one
-	 * trk with as many trkseg as there are collections in the outer collection.
+	private void simpleTag(String tag, String content) {
+		if (content != null && content.length() > 0) {
+			open(tag);
+			out.print(encode(content));
+			out.println("</" + tag + ">");
+			indent = indent.substring(2);
+		}
+	}
+
+	/**       
+	 * output link
 	 */
-	public static final class Trk implements XmlWriter.OsmWriterInterface {
-		private final Collection<Collection<GpsPoint>> data;
-		public Trk(Collection<Collection<GpsPoint>> data) {
-			this.data = data;
-		}
-
-		public void header(PrintWriter out) {
-			out.println("<gpx version='1.1' creator='JOSM' xmlns='http://www.topografix.com/GPX/1/1'>");
-		}
-
-		public void write(PrintWriter out) {
-			if (data.size() == 0)
-				return;
-			// calculate bounds
-			Bounds b = new Bounds(new LatLon(Double.MAX_VALUE, Double.MAX_VALUE), new LatLon(Double.MIN_VALUE, Double.MIN_VALUE));
-			for (Collection<GpsPoint> c : data)
-				for (GpsPoint p : c)
-					b.extend(p.latlon);
-			out.println("  <metadata>");
-			out.println("    <bounds minlat='"+b.min.lat()+"' minlon='"+b.min.lon()+"' maxlat='"+b.max.lat()+"' maxlon='"+b.max.lon()+"' />");
-			out.println("  </metadata>");
-
-			out.println("  <trk>");
-			for (Collection<GpsPoint> c : data) {
-				out.println("    <trkseg>");
-				LatLon last = null;
-				for (GpsPoint p : c) {
-					// skip double entries
-					if (p.latlon.equals(last))
-						continue;
-					last =  p.latlon;
-					LatLon ll = p.latlon;
-					out.print("      <trkpt lat='"+ll.lat()+"' lon='"+ll.lon()+"'");
-					if (p.time != null && p.time.length()!=0) {
-						out.println(">");
-						out.println("        <time>"+p.time+"</time>");
-						out.println("      </trkpt>");
-					} else
-						out.println(" />");
-				}
-				out.println("    </trkseg>");
-			}
-			out.println("  </trk>");
-		}
-
-		public void footer(PrintWriter out) {
-			out.println("</gpx>");
-        }
-	}
-
-	private void outputNode(Node n, boolean wpt) {
-		out.print((wpt?"  <wpt":"      <trkpt")+" lat='"+n.coor.lat()+"' lon='"+n.coor.lon()+"'");
-		if (n.keys == null) {
-			out.println(" />");
-			return;
-		}
-		boolean found = false;
-		String[] possibleKeys = {"ele", "time", "magvar", "geoidheight", "name",
-				"cmt", "desc", "src", "link", "sym", "type", "fix", "sat",
-				"hdop", "vdop", "pdop", "ageofgpsdata", "dgpsid"};
-		Collection<String> keys = n.keySet();
-		for (String k : possibleKeys) {
-			if (keys.contains(k)) {
-				if (!found) {
-					found = true;
-					out.println(">");
-				}
-				if (k.equals("link")) {
-					out.println("        <link>");
-					out.println("          <text>"+XmlWriter.encode(n.get(k))+"</text>");
-					out.println("        </link>");
-				} else
-					out.println("        <"+k+">"+XmlWriter.encode(n.get(k))+"</"+k+">");
-			}
-		}
-		if (found)
-			out.println(wpt?"  </wpt>":"      </trkpt>");
-		else
-			out.println(" />");
+	private void gpxLink(GpxLink link) {
+		if (link != null) {
+			openAtt("link", "href=\"" + link.uri + "\"");
+			simpleTag("text", link.text);
+			simpleTag("type", link.type);
+			closeln("link");
+		}
+	}
+
+	/**       
+	 * output a point
+	 */
+	private void wayPoint(WayPoint pnt, int mode) {
+		String type;
+		switch(mode) {
+		case WAY_POINT:
+			type = "wpt";
+			break;
+		case ROUTE_POINT:
+			type = "rtept";
+			break;
+		case TRACK_POINT:
+			type = "trkpt";
+			break;
+		default:
+			throw new RuntimeException("Bug detected. Please report this!");
+		}
+		if (pnt != null) {
+			openAtt(type, "lat=\"" + pnt.latlon.lat() + "\" lon=\"" + pnt.latlon.lon() + "\"");
+			writeAttr(pnt.attr);
+			closeln(type);
+		}
 	}
 }
Index: trunk/src/org/openstreetmap/josm/io/RawCsvReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/RawCsvReader.java	(revision 443)
+++ 	(revision )
@@ -1,93 +1,0 @@
-// License: GPL. Copyright 2007 by Immanuel Scholz and others
-package org.openstreetmap.josm.io;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.Reader;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.StringTokenizer;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.data.coor.LatLon;
-import org.openstreetmap.josm.gui.layer.RawGpsLayer.GpsPoint;
-import org.xml.sax.SAXException;
-
-/**
- * Read raw information from a csv style file (as defined in the preferences).
- * @author imi
- */
-public class RawCsvReader {
-
-	/**
-	 * Reader to read the input from.
-	 */
-	private BufferedReader in;
-
-	public RawCsvReader(Reader in) {
-		this.in = new BufferedReader(in);
-	}
-
-	public Collection<GpsPoint> parse() throws SAXException, IOException {
-		Collection<GpsPoint> data = new LinkedList<GpsPoint>();
-		String formatStr = Main.pref.get("csv.importstring");
-		if (formatStr == null || formatStr.equals(""))
-			formatStr = in.readLine();
-		if (formatStr == null || formatStr.equals(""))
-			throw new SAXException(tr("Could not detect data format string."));
-
-		// get delimiter
-		String delim = ",";
-		for (int i = 0; i < formatStr.length(); ++i) {
-			if (!Character.isLetterOrDigit(formatStr.charAt(i))) {
-				delim = ""+formatStr.charAt(i);
-				break;
-			}
-		}
-
-		// convert format string
-		ArrayList<String> format = new ArrayList<String>();
-		for (StringTokenizer st = new StringTokenizer(formatStr, delim); st.hasMoreTokens();) {
-			String token = st.nextToken();
-			if (!token.equals("lat") && !token.equals("lon") && !token.equals("time"))
-				token = "ignore";
-			format.add(token);
-		}
-
-		// test for completness
-		if (!format.contains("lat") || !format.contains("lon")) {
-			if (Main.pref.get("csv.importstring").equals(""))
-				throw new SAXException(tr("Format string in data is incomplete or not found. Try setting an manual format string in preferences."));
-			throw new SAXException(tr("Format string is incomplete. Need at least 'lat' and 'lon' specification"));
-		}
-
-		int lineNo = 0;
-		try {
-			for (String line = in.readLine(); line != null; line = in.readLine()) {
-				lineNo++;
-				StringTokenizer st = new StringTokenizer(line, delim);
-				double lat = 0, lon = 0;
-				String time = null;
-				for (String token : format) {
-					if (token.equals("lat"))
-						lat = Double.parseDouble(st.nextToken());
-					else if (token.equals("lon"))
-						lon = Double.parseDouble(st.nextToken());
-					else if (token.equals("time"))
-						time = (time == null?"":(time+" ")) + st.nextToken();
-					else if (token.equals("ignore"))
-						st.nextToken();
-					else
-						throw new SAXException(tr("Unknown data type: \"{0}\".",token)+(Main.pref.get("csv.importstring").equals("") ? (" "+tr("Maybe add a format string in preferences.")) : ""));
-				}
-				data.add(new GpsPoint(new LatLon(lat, lon), time));
-			}
-		} catch (RuntimeException e) {
-			throw new SAXException(tr("Parsing error in line {0}",lineNo), e);
-		}
-		return data;
-	}
-}
Index: trunk/src/org/openstreetmap/josm/io/RawGpsReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/RawGpsReader.java	(revision 443)
+++ 	(revision )
@@ -1,165 +1,0 @@
-// License: GPL. Copyright 2007 by Immanuel Scholz and others
-package org.openstreetmap.josm.io;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.Stack;
-
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParserFactory;
-
-import org.openstreetmap.josm.data.coor.LatLon;
-import org.openstreetmap.josm.gui.layer.RawGpsLayer.GpsPoint;
-import org.openstreetmap.josm.gui.layer.markerlayer.Marker;
-import org.openstreetmap.josm.gui.layer.markerlayer.MarkerProducers;
-import org.xml.sax.Attributes;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.DefaultHandler;
-
-/**
- * Read raw gps data from a gpx file. Only way points with their ways segments
- * and waypoints are imported.
- * @author imi
- */
-public class RawGpsReader {
-
-	/**
-	 * The relative path when constructing markers from wpt-tags. Passed to 
-	 * {@link MarkerProducers#createMarker(LatLon, java.util.Map, String)}
-	 */
-	private File relativeMarkerPath;
-
-	/**
-	 * Hold the resulting gps data (tracks and their track points)
-	 */
-	public Collection<Collection<GpsPoint>> trackData = new LinkedList<Collection<GpsPoint>>();
-
-	/**
-	 * Hold the waypoints of the gps data.
-	 */
-	public Collection<Marker> markerData = new ArrayList<Marker>();
-
-	private class Parser extends DefaultHandler {
-		/**
-		 * Current track to be read. The last entry is the current trkpt.
-		 * If in wpt-mode, it contain only one GpsPoint.
-		 */
-		private Collection<GpsPoint> current = new LinkedList<GpsPoint>();
-		private LatLon currentLatLon;
-		private HashMap<String, String> currentTagValues = new HashMap<String, String>();
-		private Stack<String> tags = new Stack<String>();
-
-		@Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
-			if (qName.equals("wpt") || qName.equals("trkpt")) {
-				try {
-					double lat = Double.parseDouble(atts.getValue("lat"));
-					double lon = Double.parseDouble(atts.getValue("lon"));
-					if (Math.abs(lat) > 90)
-						throw new SAXException(tr("Data error: lat value \"{0}\" is out of bounds.", lat));
-					if (Math.abs(lon) > 180)
-						throw new SAXException(tr("Data error: lon value \"{0}\" is out of bounds.", lon));
-					currentLatLon = new LatLon(lat, lon);
-				} catch (NumberFormatException e) {
-					e.printStackTrace();
-					throw new SAXException(e);
-				}
-				currentTagValues.clear();
-			}
-			tags.push(qName);
-		}
-
-		@Override public void characters(char[] ch, int start, int length) {
-			String peek = tags.peek();
-			if (peek.equals("time") || peek.equals("name") || peek.equals("link") || peek.equals("symbol")) {
-				String tag = tags.pop();
-				if (tags.empty() || (!tags.peek().equals("wpt") && !tags.peek().equals("trkpt"))) {
-					tags.push(tag);
-					return;
-				}
-				String contents = new String(ch, start, length);
-				String oldContents = currentTagValues.get(peek);
-				if (oldContents == null) {
-					currentTagValues.put(peek, contents);
-				} else {
-					currentTagValues.put(peek, oldContents + contents);	
-				}
-				tags.push(tag);
-			} else if (peek.equals("text")) {
-                            String tag = tags.pop();
-                            if (tags.empty() || !tags.peek().equals("link")) {
-                                tags.push(tag);
-                                return;
-                            }
-                            String contents = new String(ch, start, length);
-                            // we just want the contents of <link><text></text></link> to
-                            // all be stored under link.
-                            String oldContents = currentTagValues.get("link");
-                            if (oldContents == null) {
-                                currentTagValues.put("link", contents);
-                            } else {
-                                currentTagValues.put("link", oldContents + contents);
-                            }
-                            tags.push(tag);
-                        }
-		}
-
-		@Override public void endElement(String namespaceURI, String localName, String qName) {
-			if (qName.equals("trkpt")) {
-				current.add(new GpsPoint(currentLatLon, currentTagValues.get("time")));
-				currentTagValues.clear();
-			} else if (qName.equals("wpt")) {
-				Marker m = Marker.createMarker(currentLatLon, currentTagValues, relativeMarkerPath);
-				if (m != null)
-					markerData.add(m);
-				currentTagValues.clear();
-			} else if (qName.equals("trkseg") || qName.equals("trk") || qName.equals("gpx")) {
-				newTrack();
-				currentTagValues.clear();
-			} else if (qName.equals("link")) {
-                            String contents = currentTagValues.get(qName);
-                            if (contents != null) {
-                                // strip off leading and trailing whitespace
-                                currentTagValues.put(qName,
-                                                     contents
-                                                      .replaceFirst("^\\s+", "")
-                                                      .replaceFirst("\\s+$", ""));
-                            }
-                        }
-			tags.pop();
-		}
-
-		private void newTrack() {
-			if (!current.isEmpty()) {
-				trackData.add(current);
-				current = new LinkedList<GpsPoint>();
-			}
-		}
-	}
-
-	/**
-	 * Parse the input stream and store the result in trackData and markerData
-	 * 
-	 * @param relativeMarkerPath The directory to use as relative path for all &lt;wpt&gt; 
-	 *    marker tags. Maybe <code>null</code>, in which case no relative urls are constructed for the markers. 
-	 */
-	public RawGpsReader(InputStream source, File relativeMarkerPath) throws SAXException, IOException {
-		this.relativeMarkerPath = relativeMarkerPath;
-		Parser parser = new Parser();
-		InputSource inputSource = new InputSource(new InputStreamReader(source, "UTF-8"));
-		try {
-			SAXParserFactory.newInstance().newSAXParser().parse(inputSource, parser);
-        } catch (ParserConfigurationException e) {
-        	e.printStackTrace(); // broken SAXException chaining
-        	throw new SAXException(e);
-        }
-	}
-}
