Index: src/org/openstreetmap/josm/actions/mapmode/DebugAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/mapmode/DebugAction.java	(revision 4)
+++ src/org/openstreetmap/josm/actions/mapmode/DebugAction.java	(revision 6)
@@ -31,4 +31,5 @@
 	}
 	
+	@Override
 	public void registerListener(MapView mapView) {
 		mapView.addMouseMotionListener(this);
@@ -37,4 +38,5 @@
 	}
 
+	@Override
 	public void unregisterListener(MapView mapView) {
 		mapView.removeMouseMotionListener(this);
Index: src/org/openstreetmap/josm/actions/mapmode/ZoomAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/mapmode/ZoomAction.java	(revision 4)
+++ src/org/openstreetmap/josm/actions/mapmode/ZoomAction.java	(revision 6)
@@ -17,4 +17,7 @@
  * Holding down left and right let the user move the former selected rectangle.
  * Releasing the left button zoom to the selection.
+ * 
+ * Rectangle selections with either height or width smaller than 3 pixels 
+ * are ignored.
  * 
  * @author imi
@@ -47,13 +50,17 @@
 	 */
 	public void selectionEnded(Rectangle r, int modifier) {
-		double scale = mv.getScale() * r.getWidth()/mv.getWidth();
-		GeoPoint newCenter = mv.getPoint(r.x+r.width/2, r.y+r.height/2, false);
-		mv.zoomTo(newCenter, scale);
+		if (r.width >= 3 && r.height >= 3) {
+			double scale = mv.getScale() * r.getWidth()/mv.getWidth();
+			GeoPoint newCenter = mv.getPoint(r.x+r.width/2, r.y+r.height/2, false);
+			mv.zoomTo(newCenter, scale);
+		}
 	}
 
+	@Override
 	public void registerListener(MapView mapView) {
 		selectionManager.register(mapView);
 	}
 
+	@Override
 	public void unregisterListener(MapView mapView) {
 		selectionManager.unregister(mapView);
Index: src/org/openstreetmap/josm/data/GeoPoint.java
===================================================================
--- src/org/openstreetmap/josm/data/GeoPoint.java	(revision 4)
+++ src/org/openstreetmap/josm/data/GeoPoint.java	(revision 6)
@@ -1,3 +1,4 @@
 package org.openstreetmap.josm.data;
+
 
 /**
@@ -44,3 +45,25 @@
 		return null;
 	}
+
+	/**
+	 * GeoPoints are equal, if their lat/lon are equal or, if lat or lon are NaN, 
+	 * if their x/y are equal.
+	 */
+	@Override
+	public boolean equals(Object obj) {
+		if (!(obj instanceof GeoPoint))
+			return false;
+		GeoPoint gp = (GeoPoint)obj;
+		
+		if (Double.isNaN(lat) || Double.isNaN(lon))
+			return x == gp.x && y == gp.y;
+		return lat == gp.lat && lon == gp.lon;
+	}
+
+	@Override
+	public int hashCode() {
+		return super.hashCode();
+	}
+	
+	
 }
Index: src/org/openstreetmap/josm/data/Preferences.java
===================================================================
--- src/org/openstreetmap/josm/data/Preferences.java	(revision 4)
+++ src/org/openstreetmap/josm/data/Preferences.java	(revision 6)
@@ -4,4 +4,5 @@
 import java.io.FileReader;
 import java.io.FileWriter;
+import java.util.List;
 
 import javax.swing.UIManager;
@@ -34,13 +35,15 @@
 	public Projection projection = new UTM();
 
+
 	/**
-	 * The monitor geometry in meter per pixel. (How big is 1 pixel in meters)
-	 * Example: 17" Sony flatscreen in 1280x1024 mode: 0.000264 ppm
-	 * 
-	 * Remember: ppm = dpi/25400
+	 * Whether nodes on the same place should be considered identical.
 	 */
-	public double ppm = 0.000264;
+	public boolean mergeNodes = true;
+	
+	
 
-	
+	/**
+	 * List of all available Projections.
+	 */
 	public static final Projection[] allProjections = new Projection[]{
 		new UTM(),
@@ -85,4 +88,5 @@
 			
 			projection = (Projection)Class.forName(root.getChildText("projection")).newInstance();
+			mergeNodes = root.getChild("mergeNodes") != null;
 		} catch (Exception e) {
 			if (e instanceof PreferencesException)
@@ -100,6 +104,9 @@
 		Element root = new Element("josm-settings");
 		
-		root.getChildren().add(new Element("laf").setText(laf.getClassName()));
-		root.getChildren().add(new Element("projection").setText(projection.getClass().getName()));
+		List children = root.getChildren();
+		children.add(new Element("laf").setText(laf.getClassName()));
+		children.add(new Element("projection").setText(projection.getClass().getName()));
+		if (mergeNodes)
+			children.add(new Element("mergeNodes"));
 
 		try {
Index: src/org/openstreetmap/josm/data/osm/DataSet.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 4)
+++ src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 6)
@@ -119,4 +119,5 @@
 	}
 	
+	@Override
 	public DataSet clone() {
 		try {return (DataSet)super.clone();} catch (CloneNotSupportedException e) {}
Index: src/org/openstreetmap/josm/data/osm/Node.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/Node.java	(revision 4)
+++ src/org/openstreetmap/josm/data/osm/Node.java	(revision 6)
@@ -15,3 +15,26 @@
 	 */
 	public GeoPoint coor;
+
+	/**
+	 * Nodes are equal when their coordinates are equal.
+	 */
+	@Override
+	public boolean equals(Object obj) {
+		if (!(obj instanceof Node))
+			return false;
+		Node n = (Node)obj;
+		if (coor == null)
+			return n.coor == null;
+		return coor.equals(n.coor) && super.equals(obj);
+	}
+
+	/**
+	 * Compute the hashcode from the OsmPrimitive's hash and the coor's hash.
+	 */
+	@Override
+	public int hashCode() {
+		return (coor == null ? 0 : coor.hashCode()) + super.hashCode();
+	}
+	
+	
 }
Index: src/org/openstreetmap/josm/data/osm/OsmPrimitive.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 4)
+++ src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 6)
@@ -20,9 +20,34 @@
 	 * If set to true, this object has been modified in the current session.
 	 */
-	public boolean modified = false;
+	transient public boolean modified = false;
 	
 	/**
 	 * If set to true, this object is currently selected.
 	 */
-	public boolean selected = false;
+	transient public boolean selected = false;
+
+	/**
+	 * Osm primitives are equal, when their keys are equal. 
+	 */
+	@Override
+	public boolean equals(Object obj) {
+		if (obj == null)
+			return false;
+		if (!(obj instanceof OsmPrimitive))
+			return false;
+		OsmPrimitive osm = (OsmPrimitive)obj;
+		if (keys == null)
+			return osm.keys == null;
+		return keys.equals(osm.keys);
+	}
+
+	/**
+	 * Compute the hashCode from the keys.
+	 */
+	@Override
+	public int hashCode() {
+		return keys == null ? 0 : keys.hashCode();
+	}
+	
+	
 }
Index: src/org/openstreetmap/josm/data/projection/Projection.java
===================================================================
--- src/org/openstreetmap/josm/data/projection/Projection.java	(revision 4)
+++ src/org/openstreetmap/josm/data/projection/Projection.java	(revision 6)
@@ -45,4 +45,5 @@
 	 * Describe the projection converter in one or two words.
 	 */
+	@Override
 	abstract public String toString();
 	
Index: src/org/openstreetmap/josm/data/projection/UTM.java
===================================================================
--- src/org/openstreetmap/josm/data/projection/UTM.java	(revision 4)
+++ src/org/openstreetmap/josm/data/projection/UTM.java	(revision 6)
@@ -49,4 +49,5 @@
 			this.ecc_squared = ecc_squared;
 		}
+		@Override
 		public String toString() {
 			return name;
Index: src/org/openstreetmap/josm/gui/GBC.java
===================================================================
--- src/org/openstreetmap/josm/gui/GBC.java	(revision 6)
+++ src/org/openstreetmap/josm/gui/GBC.java	(revision 6)
@@ -0,0 +1,100 @@
+package org.openstreetmap.josm.gui;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.Insets;
+
+import javax.swing.Box;
+
+/**
+ * A wrapper for GridBagConstraints which has sane default static creators and
+ * member functions to chain calling.
+ * 
+ * @author imi
+ */
+public class GBC extends GridBagConstraints {
+
+	/**
+	 * Use public static creator functions to create an GBC.
+	 */
+	private GBC() {}
+	
+	/**
+	 * Create a standard constraint (which is not the last).
+	 * @return A standard constraint with no filling.
+	 */
+	public static GBC std() {
+		GBC c = new GBC();
+		c.anchor = WEST;
+		return c;
+	}
+	
+	/**
+	 * Create the constraint for the last elements on a line.
+	 * @return A constraint which indicates the last item on a line.
+	 */
+	public static GBC eol() {
+		GBC c = std();
+		c.gridwidth = REMAINDER;
+		return c;
+	}
+	
+	/**
+	 * Try to fill both, horizontal and vertical
+	 * @return This constraint for chaining.
+	 */
+	public GBC fill() {
+		return fill(BOTH);
+	}
+
+	/**
+	 * Set fill to the given value
+	 * @param value The filling value, either NONE, HORIZONTAL, VERTICAL or BOTH
+	 * @return This constraint for chaining.
+	 */
+	public GBC fill(int value) {
+		fill = value;
+		if (value == HORIZONTAL || value == BOTH)
+			weightx = 1.0;
+		if (value == VERTICAL || value == BOTH)
+			weighty = 1.0;
+		return this;
+	}
+	
+	/**
+	 * Set the anchor of this GBC to a.
+	 * @param a The new anchor, e.g. GBC.CENTER or GBC.EAST.
+	 * @return This constraint for chaining.
+	 */
+	public GBC anchor(int a) {
+		anchor = a;
+		return this;
+	}
+
+	/**
+	 * Adds insets to this GBC.
+	 * @param left		The left space of the insets
+	 * @param top		The top space of the insets
+	 * @param right		The right space of the insets
+	 * @param bottom	The bottom space of the insets
+	 * @return This constraint for chaining.
+	 */
+	public GBC insets(int left, int top, int right, int bottom) {
+		insets = new Insets(top, left, bottom, right);
+		return this;
+	}
+	
+	/**
+	 * This is a helper to easily create a glue with a minimum default value.
+	 * @param x If higher than 0, this will be a horizontal glue with x as minimum
+	 * 		horizontal strut.
+	 * @param y If higher than 0, this will be a vertical glue with y as minimum
+	 * 		vertical strut.
+	 */
+	public static Component glue(int x, int y) {
+		short maxx = x > 0 ? Short.MAX_VALUE : 0;
+		short maxy = y > 0 ? Short.MAX_VALUE : 0;
+		return new Box.Filler(new Dimension(x,y), new Dimension(x,y), new Dimension(maxx,maxy));
+	}
+}
Index: src/org/openstreetmap/josm/gui/MapMover.java
===================================================================
--- src/org/openstreetmap/josm/gui/MapMover.java	(revision 4)
+++ src/org/openstreetmap/josm/gui/MapMover.java	(revision 6)
@@ -64,4 +64,5 @@
 	 * Start the movement, if it was the 3rd button (right button).
 	 */
+	@Override
 	public void mousePressed(MouseEvent e) {
 		int offMask = MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON2_DOWN_MASK;
@@ -73,4 +74,5 @@
 	 * Change the cursor back to it's pre-move cursor.
 	 */
+	@Override
 	public void mouseReleased(MouseEvent e) {
 		if (e.getButton() == MouseEvent.BUTTON3)
Index: src/org/openstreetmap/josm/gui/MapView.java
===================================================================
--- src/org/openstreetmap/josm/gui/MapView.java	(revision 4)
+++ src/org/openstreetmap/josm/gui/MapView.java	(revision 6)
@@ -8,6 +8,8 @@
 import java.awt.event.ComponentListener;
 import java.awt.event.KeyEvent;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
 
 import javax.swing.AbstractAction;
@@ -246,5 +248,5 @@
 		g.setColor(Color.BLACK);
 		g.fillRect(0,0,getWidth(),getHeight());
-		
+
 		// draw tracks
 		if (dataSet.tracks != null)
@@ -257,7 +259,18 @@
 
 		// draw nodes
+		Set<Integer> alreadyDrawn = new HashSet<Integer>();
+		int width = getWidth();
 		for (Node w : dataSet.allNodes) {
 			g.setColor(w.selected ? Color.WHITE : Color.RED);
-			g.drawArc(toScreenX(w.coor.x), toScreenY(w.coor.y), 3, 3, 0, 360);
+			int x = toScreenX(w.coor.x);
+			int y = toScreenY(w.coor.y);
+			int size = 3;
+			if (alreadyDrawn.contains(y*width+x)) {
+				size = 7;
+				x -= 2;
+				y -= 2;
+			} else
+				alreadyDrawn.add(y*width+x);
+			g.drawArc(x, y, size, size, 0, 360);
 		}
 	}
Index: src/org/openstreetmap/josm/gui/PreferenceDialog.java
===================================================================
--- src/org/openstreetmap/josm/gui/PreferenceDialog.java	(revision 4)
+++ src/org/openstreetmap/josm/gui/PreferenceDialog.java	(revision 6)
@@ -1,8 +1,8 @@
 package org.openstreetmap.josm.gui;
 
-import java.awt.BorderLayout;
 import java.awt.Component;
-import java.awt.Container;
 import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.GridBagLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
@@ -11,9 +11,9 @@
 
 import javax.swing.AbstractAction;
-import javax.swing.BorderFactory;
 import javax.swing.Box;
 import javax.swing.DefaultListCellRenderer;
 import javax.swing.ImageIcon;
 import javax.swing.JButton;
+import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
 import javax.swing.JDialog;
@@ -50,4 +50,5 @@
 			pref.laf = (LookAndFeelInfo)lafCombo.getSelectedItem();
 			pref.projection = (Projection)projectionCombo.getSelectedItem();
+			pref.mergeNodes = mergeNodes.isSelected();
 			Main.pref.projection = pref.projection;
 			try {
@@ -85,11 +86,15 @@
 	private JComboBox lafCombo = new JComboBox(UIManager.getInstalledLookAndFeels());
 	/**
-	 * The tabbed pane to add tabulars to.
-	 */
-	private JTabbedPane tabPanel = new JTabbedPane();
-	/**
 	 * Combobox with all projections available
 	 */
-	private JComboBox projectionCombo = new JComboBox(Preferences.allProjections);
+	private JComboBox projectionCombo = new JComboBox(Preferences.allProjections.clone());
+	/**
+	 * The main tab panel.
+	 */
+	private JTabbedPane tabPane = new JTabbedPane(JTabbedPane.LEFT);
+	/**
+	 * The checkbox stating whether nodes should be merged together.
+	 */
+	private JCheckBox mergeNodes = new JCheckBox("Merge nodes with equal latitude/longitude.");
 
 	
@@ -113,13 +118,8 @@
 		}
 
-		getContentPane().setLayout(new BorderLayout());
-		getContentPane().add(tabPanel, BorderLayout.CENTER);
-
-		newTab("Display");
-		// laf
-		JPanel p = newPanelLine();
-		p.add(new JLabel("Look and Feel"));
+		// look and feel combo box
 		final ListCellRenderer oldRenderer = lafCombo.getRenderer();
 		lafCombo.setRenderer(new DefaultListCellRenderer(){
+			@Override
 			public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
 				return oldRenderer.getListCellRendererComponent(list, ((LookAndFeelInfo)value).getName(), index, isSelected, cellHasFocus);
@@ -130,21 +130,51 @@
 				setRequiresRestart();
 			}});
-		p.add(lafCombo);
-
-		newTab("Projection");
-		p = newPanelLine();
-		p.add(new JLabel("Projection System"));
-		p.add(projectionCombo);
-		for (int i = 0; i < projectionCombo.getItemCount(); ++i)
+
+		// projection method combo box
+		for (int i = 0; i < projectionCombo.getItemCount(); ++i) {
 			if (projectionCombo.getItemAt(i).getClass().equals(pref.projection.getClass())) {
 				projectionCombo.setSelectedIndex(i);
 				break;
 			}
-
-		// OK/Cancel
-		JPanel okPanel = new JPanel();
-		okPanel.add(new JButton(new OkAction()));
-		okPanel.add(new JButton(new CancelAction()));
-		getContentPane().add(okPanel, BorderLayout.SOUTH);
+		}
+		
+		// Display tab
+		JPanel display = createPreferenceTab("display", "Display Settings", "Various settings than influence the visual representation of the whole Program.");
+		display.add(new JLabel("Look and Feel"), GBC.std());
+		display.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
+		display.add(lafCombo, GBC.eol().fill(GBC.HORIZONTAL));
+		display.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.VERTICAL));
+
+		// Map tab
+		JPanel map = createPreferenceTab("map", "Map Settings", "Settings for the map projection and data interpretation.");
+		map.add(new JLabel("Projection method"), GBC.std());
+		map.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
+		map.add(projectionCombo, GBC.eol().fill(GBC.HORIZONTAL));
+		JLabel labelNoteProjection = new JLabel(
+				"<html>Note: This is the default projection method used for files, " +
+				"where the correct projection could not be determined. " +
+				"The actual used projection can be changed in the property " +
+				"settings of each map.</html>");
+		labelNoteProjection.setMinimumSize(new Dimension(550, 50));
+		labelNoteProjection.setPreferredSize(new Dimension(550, 50));
+		map.add(labelNoteProjection, GBC.eol().insets(0,5,0,20));
+		map.add(new JLabel("GPX import / export"), GBC.eol());
+		mergeNodes.setSelected(pref.mergeNodes);
+		map.add(mergeNodes, GBC.eol());
+		map.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.VERTICAL));
+
+		
+		tabPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
+	
+		// OK/Cancel panel at bottom
+		JPanel okPanel = new JPanel(new GridBagLayout());
+		okPanel.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL));
+		okPanel.add(new JButton(new OkAction()), GBC.std());
+		okPanel.add(new JButton(new CancelAction()), GBC.std());
+
+		// merging all in the content pane
+		getContentPane().setLayout(new GridBagLayout());
+		getContentPane().add(tabPane, GBC.eol().fill());
+		getContentPane().add(okPanel, GBC.eol().fill(GBC.HORIZONTAL));
 
 		setModal(true);
@@ -155,13 +185,23 @@
 
 	/**
-	 * Start a new tab with the given name
-	 * @param tabName The name of the new tab.
-	 */
-	private void newTab(String tabName) {
-		Box tab = Box.createVerticalBox();
-		tab.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
-		tabPanel.addTab(tabName, tab);
-	}
-
+	 * Construct a JPanel for the preference settings. Layout is GridBagLayout
+	 * and a centered title label and the description are added.
+	 * @param icon The name of the icon.
+	 * @param title The title of this preference tab.
+	 * @param desc A description in one sentence for this tab. Will be displayed
+	 * 		italic under the title.
+	 * @return The created panel ready to add other controls.
+	 */
+	private JPanel createPreferenceTab(String icon, String title, String desc) {
+		JPanel p = new JPanel(new GridBagLayout());
+		p.add(new JLabel(title), GBC.eol().anchor(GBC.CENTER).insets(0,5,0,10));
+		JLabel descLabel = new JLabel(desc);
+		descLabel.setFont(descLabel.getFont().deriveFont(Font.ITALIC));
+		p.add(descLabel, GBC.eol().insets(5,0,5,20));
+
+		tabPane.addTab(null, new ImageIcon("images/preferences/"+icon+".png"), p);
+		return p;
+	}
+	
 	/**
 	 * Remember, that the settings made requires a restart of the application.
@@ -171,11 +211,3 @@
 		requiresRestart = true;
 	}
-
-	private JPanel newPanelLine() {
-		JPanel p;
-		p = new JPanel();
-		p.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
-		((Container)tabPanel.getComponent(tabPanel.getTabCount()-1)).add(p);
-		return p;
-	}
 }
Index: src/org/openstreetmap/josm/io/GpxReader.java
===================================================================
--- src/org/openstreetmap/josm/io/GpxReader.java	(revision 4)
+++ src/org/openstreetmap/josm/io/GpxReader.java	(revision 6)
@@ -15,4 +15,5 @@
 import org.openstreetmap.josm.data.osm.Track;
 import org.openstreetmap.josm.data.osm.LineSegment;
+import org.openstreetmap.josm.gui.Main;
 
 /**
@@ -70,5 +71,5 @@
 		data.allNodes = new LinkedList<Node>(); 
 		for (Object o : e.getChildren("wpt", GPX))
-			data.allNodes.add(parseWaypoint((Element)o));
+			addNode(data, parseWaypoint((Element)o));
 
 		// read tracks
@@ -79,5 +80,5 @@
 				for (Object w : ((Element)trackSegmentElement).getChildren("trkpt", GPX)) {
 					Node node = parseWaypoint((Element)w);
-					data.allNodes.add(node);
+					node = addNode(data, node);
 					if (start == null)
 						start = node;
@@ -96,3 +97,21 @@
 		return data;
 	}
+	
+	/**
+	 * Adds the node to allNodes if it is not already listed. Does respect the
+	 * preference setting "mergeNodes". Return the node in the list that correspond
+	 * to the node in the list (either the new added or the old found).
+	 * 
+	 * @param data The DataSet to add the node to.
+	 * @param node The node that should be added.
+	 * @return Either the parameter node or the old node found in the dataset. 
+	 */
+	private Node addNode (DataSet data, Node node) {
+		if (Main.pref.mergeNodes)
+			for (Node n : data.allNodes)
+				if (node.equals(n))
+					return n;
+		data.allNodes.add(node);
+		return node;
+	}
 }
