diff --git a/resources/help/en/options b/resources/help/en/options
index 4a85ef4..a7a3b3c 100644
--- a/resources/help/en/options
+++ b/resources/help/en/options
@@ -126,6 +126,9 @@ Misc options:
 	in which they appear in the OSM input. Without this option,
 	the order in which the elements are processed is not defined.
 
+--remove-short-arcs
+	Merge nodes to remove short arcs that can cause routing problems.
+
 --road-name-pois[=GarminCode]
 	Generate a POI for each named road. By default, the POIs'
 	Garmin type code is 0x640a. If desired, a different type code
diff --git a/src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java b/src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java
index b7a0ee0..2f72f50 100644
--- a/src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java
+++ b/src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java
@@ -18,6 +18,7 @@ package uk.me.parabola.mkgmap.reader.osm.xml;
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.IdentityHashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -55,6 +56,8 @@ class Osm5XmlHandler extends DefaultHandler {
 	private int mode;
 
 	private Map<Long, Coord> coordMap = new HashMap<Long, Coord>(50000);
+	// currently, nodeIdMap is only used for the short arc removal
+	private Map<Coord, Long> nodeIdMap = new IdentityHashMap<Coord, Long>();
 	private Map<Long, Node> nodeMap;
 	private Map<Long, Way> wayMap;
 	private Map<Long, Relation> relationMap;
@@ -83,11 +86,13 @@ class Osm5XmlHandler extends DefaultHandler {
 	private final boolean ignoreBounds;
 	private final boolean ignoreTurnRestrictions;
 	private final boolean routing;
+	private final boolean removeShortArcs;
 	private final String frigRoundabouts;
 
 	public Osm5XmlHandler(EnhancedProperties props) {
 		ignoreBounds = props.getProperty("ignore-osm-bounds", false);
 		routing = props.containsKey("route");
+		removeShortArcs = props.getProperty("remove-short-arcs", false);
 		frigRoundabouts = props.getProperty("frig-roundabouts");
 		ignoreTurnRestrictions = props.getProperty("ignore-turn-restrictions", false);
 		if (props.getProperty("preserve-element-order", false)) {
@@ -365,6 +370,11 @@ class Osm5XmlHandler extends DefaultHandler {
 
 		nodeMap = null;
 
+		if(removeShortArcs)
+			removeShortArcsByMergingNodes();
+
+		nodeIdMap = null;
+
 		for (Way w: wayMap.values())
 			converter.convertWay(w);
 
@@ -394,6 +404,132 @@ class Osm5XmlHandler extends DefaultHandler {
 		endTask.run();
 	}
 
+	private final void incArcCount(Map<Coord, Integer> map, Coord p, int inc) {
+		Integer i = map.get(p);
+		if(i != null)
+			inc += i;
+		map.put(p, inc);
+	}
+
+	private void removeShortArcsByMergingNodes() {
+		final double MIN_ARC_LENGTH = 3.5;
+		// keep track of how many arcs reach a given point
+		Map<Coord, Integer> arcCounts = new IdentityHashMap<Coord, Integer>();
+		log.info("Removing short arcs - counting arcs");
+		for(Way w : wayMap.values()) {
+			List<Coord> points = w.getPoints();
+			int numPoints = points.size();
+			if(numPoints >= 2) {
+				// all end points have 1 arc
+				incArcCount(arcCounts, points.get(0), 1);
+				incArcCount(arcCounts, points.get(numPoints - 1), 1);
+				// non-end points have 2 arcs but ignore points that
+				// are only in a single way
+				for(int i = numPoints - 2; i >= 1; --i) {
+					Coord p = points.get(i);
+					if(p.getHighwayCount() > 1)
+						incArcCount(arcCounts, p, 2);
+				}
+			}
+		}
+		// replacements maps those nodes that have been replaced to
+		// the node that replaces them
+		Map<Coord, Coord> replacements = new IdentityHashMap<Coord, Coord>();
+		boolean anotherPassRequired = true;
+		int pass = 0;
+		while(anotherPassRequired && pass < 10) {
+			anotherPassRequired = false;
+			log.info("Removing short arcs - PASS " + ++pass);
+			Way[] ways = wayMap.values().toArray(new Way[wayMap.size()]);
+			for(int w = 0; w < ways.length; ++w) {
+				Way way = ways[w];
+				List<Coord> points = way.getPoints();
+				if(points.size() < 2) {
+					log.info("  Way " + way.getTag("name") + " (OSM id " + way.getId() + ") has less than 2 points - deleting it");
+					wayMap.remove(way.getId());
+					continue;
+				}
+				int arcLength = 0;
+				int previousNodeIndex = 0; // first point will be a node
+				Coord previousPoint = points.get(0);
+				for(int i = 0; i < points.size(); ++i) {
+					Coord p = points.get(i);
+					// check if this point is to be replaced because
+					// it was previously merged into another point
+					Coord replacement = null;
+					Coord r = p;
+					while((r = replacements.get(r)) != null) {
+						replacement = r;
+					}
+					if(replacement != null) {
+						log.info("  Way " + way.getTag("name") + " (OSM id " + way.getId() + ") has node " + nodeIdMap.get(p) + " replaced with node " + nodeIdMap.get(replacement));
+						p = replacement;
+						// replace point in way
+						points.set(i, p);
+						if(i == 0)
+							previousPoint = p;
+						anotherPassRequired = true;
+					}
+					if(i > 0) {
+						if(p == previousPoint) {
+							log.info("  Way " + way.getTag("name") + " (OSM id " + way.getId() + ") has consecutive identical nodes (" + nodeIdMap.get(p) + ") - deleting the second node");
+							points.remove(i);
+							anotherPassRequired = true;
+							continue;
+						}
+						// this is not the first point in the way
+						double d = previousPoint.distance(p);
+						previousPoint = p;
+						arcLength += d;
+						Coord previousNode = points.get(previousNodeIndex);
+						// this point is a node if it has an arc count
+						Integer arcCount = arcCounts.get(p);
+						if(arcCount != null) {
+							// merge this node to previous node in way
+							// if the arc length is zero or is small
+							// and it has more than 2 arcs connected
+							// to it and the previous node has more
+							// than 2 arcs connected to it
+							if(arcLength == 0 ||
+							   (arcLength < MIN_ARC_LENGTH &&
+								arcCount > 2 &&
+								arcCounts.get(previousNode) > 2)) {
+								log.info("  Way " + way.getTag("name") + " (OSM id " + way.getId() + ") has evil short arc (" + arcLength + "m) - removing it by merging node " + nodeIdMap.get(p) + " into " + nodeIdMap.get(previousNode));
+								if(p != previousNode) {
+									replacements.put(p, previousNode);
+									// add this node's arc count to the node
+									// that is replacing it
+									incArcCount(arcCounts, previousNode, arcCount - 1);
+								}
+								// reset previous point to be the previous node
+								previousPoint = previousNode;
+								// remove the point(s) back to the previous node
+								for(int j = i; j > previousNodeIndex; --j) {
+									points.remove(j);
+								}
+								// hack alert! rewind the loop index
+								i = previousNodeIndex;
+								anotherPassRequired = true;
+							}
+							else {
+								// the node did not need to be merged so
+								// it now becomes the new previous node
+								previousNodeIndex = i;
+							}
+							// reset arcLength at we pass each node
+							arcLength = 0;
+						}
+					}
+				}
+			}
+		}
+
+		if(anotherPassRequired)
+			log.error("Removing short arcs - didn't finish in " + pass + " passes, giving up (moved " + replacements.size() + " nodes)");
+		else
+			log.info("Removing short arcs - finished in " + pass + " passes (moved " + replacements.size() + " nodes)");
+	}
+
 	private void setupBBoxFromBounds(Attributes xmlattr) {
 		try {
 			setBBox(Double.parseDouble(xmlattr.getValue("minlat")),
@@ -462,8 +598,11 @@ class Osm5XmlHandler extends DefaultHandler {
 	private void addNodeToWay(long id) {
 		Coord co = coordMap.get(id);
 		//co.incCount();
-		if (co != null)
+		if (co != null) {
 			currentWay.addPoint(co);
+			if(removeShortArcs)
+				nodeIdMap.put(co, id);
+		}
 	}
 
 	public void setConverter(OsmConverter converter) {
