diff --git a/resources/help/en/options b/resources/help/en/options
index 9bbfad7..ceb1921 100644
--- a/resources/help/en/options
+++ b/resources/help/en/options
@@ -182,11 +182,15 @@ Miscellaneous 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[=MinLength]
-	Merge nodes to remove short arcs that can cause routing
-	problems. If MinLength is specified (in metres), arcs shorter
-	than that length will be removed. If a length is not
-	specified, only zero-length arcs will be removed.
+--remove-short-arcs               (a)
+--remove-short-arcs=MinLength     (b)
+--remove-short-arcs=no            (c)
+	If routing is enabled, arcs shorter than 5m will be removed
+	to avoid routing problems. This behaviour can be modified with
+	this option. It has three variants:
+	(a) turn on short arc removal even if routing is not enabled.
+	(b) explicitly specify the minimum arc length (no 'm' suffix).
+	(c) completely disable short arc removal.
 
 --road-name-pois[=GarminCode]
 	Generate a POI for each named road. By default, the POIs'
diff --git a/src/uk/me/parabola/imgfmt/app/Coord.java b/src/uk/me/parabola/imgfmt/app/Coord.java
index 5462602..3e47df7 100644
--- a/src/uk/me/parabola/imgfmt/app/Coord.java
+++ b/src/uk/me/parabola/imgfmt/app/Coord.java
@@ -20,6 +20,7 @@ import java.util.Formatter;
 import java.util.Locale;
 
 import uk.me.parabola.imgfmt.Utils;
+import uk.me.parabola.imgfmt.app.net.RouteArc;
 
 /**
  * A point coordinate in unshifted map-units.
@@ -101,6 +102,11 @@ public class Coord implements Comparable<Coord> {
 		return latitude == other.latitude && longitude == other.longitude;
 	}
 
+	public boolean tooCloseTo(Coord other) {
+		return ((latitude == other.latitude && longitude == other.longitude) ||
+				!RouteArc.goodArcLength(quickDistance(other)));
+	}
+
 	/**
 	 * Distance to other point in meters.
 	 */
diff --git a/src/uk/me/parabola/imgfmt/app/net/RouteArc.java b/src/uk/me/parabola/imgfmt/app/net/RouteArc.java
index 1a0968d..ae2bf8b 100644
--- a/src/uk/me/parabola/imgfmt/app/net/RouteArc.java
+++ b/src/uk/me/parabola/imgfmt/app/net/RouteArc.java
@@ -193,6 +193,12 @@ public class RouteArc {
 		return (int) (l * factor);
 	}
 
+
+	public static boolean goodArcLength(double len) {
+		int l = convertMeters(len);
+		return l > 0 && l < (1 << 14);
+	}
+
 	public void write(ImgFileWriter writer) {
 		offset = writer.position();
 		if(log.isDebugEnabled())
diff --git a/src/uk/me/parabola/mkgmap/general/RoadNetwork.java b/src/uk/me/parabola/mkgmap/general/RoadNetwork.java
index 0c45d68..f76b4b1 100644
--- a/src/uk/me/parabola/mkgmap/general/RoadNetwork.java
+++ b/src/uk/me/parabola/mkgmap/general/RoadNetwork.java
@@ -105,8 +105,8 @@ public class RoadNetwork {
 					log.error("Road " + road.getRoadDef().getName() + " (OSM id " + road.getRoadDef().getId() + ") contains consecutive identical nodes - routing will be broken");
 					log.error("  " + co.toOSMURL());
 				}
-				else if(arcLength == 0) {
-					log.error("Road " + road.getRoadDef().getName() + " (OSM id " + road.getRoadDef().getId() + ") contains zero length arc");
+				else if(!RouteArc.goodArcLength(arcLength)) {
+					log.error("Road " + road.getRoadDef().getName() + " (OSM id " + road.getRoadDef().getId() + ") contains a bad arc of length " + String.format("%.2f", arcLength) + "m");
 					log.error("  " + co.toOSMURL());
 				}
 
diff --git a/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java b/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
index 2a549f8..60cbfde 100644
--- a/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
+++ b/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
@@ -33,7 +33,6 @@ import uk.me.parabola.log.Logger;
 import uk.me.parabola.mkgmap.general.AreaClipper;
 import uk.me.parabola.mkgmap.general.Clipper;
 import uk.me.parabola.mkgmap.general.LineAdder;
-import uk.me.parabola.mkgmap.general.LineClipper;
 import uk.me.parabola.mkgmap.general.MapCollector;
 import uk.me.parabola.mkgmap.general.MapElement;
 import uk.me.parabola.mkgmap.general.MapExitPoint;
@@ -468,9 +467,9 @@ public class StyledConverter implements OsmConverter {
 							// now split the way at the next point to
 							// limit the region that has restricted
 							// access
-							if(!p.equals(p1) &&
+							if(!p.tooCloseTo(p1) &&
 							   ((i + 2) == points.size() ||
-								!p1.equals(points.get(i + 2)))) {
+								!p1.tooCloseTo(points.get(i + 2)))) {
 								Way tail = splitWayAt(way, i + 1);
 								// recursively process tail of way
 								addRoad(tail, gt);
@@ -531,8 +530,8 @@ public class StyledConverter implements OsmConverter {
 							// first point in the way
 							if(p1 instanceof CoordPOI &&
 							   i > 0 &&
-							   !p.equals(points.get(i - 1)) &&
-							   !p.equals(p1)) {
+							   !p.tooCloseTo(points.get(i - 1)) &&
+							   !p.tooCloseTo(p1)) {
 								Way tail = splitWayAt(way, i);
 								// recursively process tail of road
 								addRoad(tail, gt);
@@ -543,59 +542,12 @@ public class StyledConverter implements OsmConverter {
 			}
 		}
 
-		// if there is a bounding box, clip the way with it
-
-		List<Way> clippedWays = null;
-
-		if(bbox != null) {
-			List<List<Coord>> lineSegs = LineClipper.clip(bbox, way.getPoints());
-
-			if (lineSegs != null) {
-
-				clippedWays = new ArrayList<Way>();
-
-				for (List<Coord> lco : lineSegs) {
-					Way nWay = new Way(way.getId());
-					nWay.setName(way.getName());
-					nWay.copyTags(way);
-					for(Coord co : lco) {
-						nWay.addPoint(co);
-						if(co.getOnBoundary()) {
-							// this point lies on a boundary
-							// make sure it becomes a node
-							co.incHighwayCount();
-						}
-					}
-					clippedWays.add(nWay);
-					// associate the original Way
-					// to the new Way
-					Way origWay = originalWay.get(way);
-					if(origWay == null)
-						origWay = way;
-					originalWay.put(nWay, origWay);
-				}
-			}
-		}
-
-		if(clippedWays != null) {
-			for(Way cw : clippedWays) {
-				while(cw.getPoints().size() > MAX_POINTS_IN_WAY) {
-					Way tail = splitWayAt(cw, MAX_POINTS_IN_WAY - 1);
-					addRoadAfterSplittingLoops(cw, gt);
-					cw = tail;
-				}
-				addRoadAfterSplittingLoops(cw, gt);
-			}
-		}
-		else {
-			// no bounding box or way was not clipped
-			while(way.getPoints().size() > MAX_POINTS_IN_WAY) {
-				Way tail = splitWayAt(way, MAX_POINTS_IN_WAY - 1);
-				addRoadAfterSplittingLoops(way, gt);
-				way = tail;
-			}
+		while(way.getPoints().size() > MAX_POINTS_IN_WAY) {
+			Way tail = splitWayAt(way, MAX_POINTS_IN_WAY - 1);
 			addRoadAfterSplittingLoops(way, gt);
+			way = tail;
 		}
+		addRoadAfterSplittingLoops(way, gt);
 	}
 
 	void addRoadAfterSplittingLoops(Way way, GType gt) {
@@ -622,7 +574,7 @@ public class StyledConverter implements OsmConverter {
 
 						// start at point before intersection point
 						// check that splitting there will not produce
-						// a zero length arc - if it does try the
+						// a short arc - if it does try the
 						// previous point(s)
 						int splitI = p2I - 1;
 						while(splitI > p1I &&
@@ -632,7 +584,7 @@ public class StyledConverter implements OsmConverter {
 						}
 
 						if(splitI == p1I) {
-							log.warn("Splitting looped way " + getDebugName(way) + " would make a zero length arc, so it will have to be pruned");
+							log.warn("Splitting looped way " + getDebugName(way) + " would make a short arc, so it will have to be pruned");
 							do {
 								log.warn("  Pruning point[" + p2I + "]");
 								wayPoints.remove(p2I);
@@ -647,7 +599,7 @@ public class StyledConverter implements OsmConverter {
 								// loop back and prune it
 							} while(p2I > p1I &&
 									(p2I + 1) == numPointsInWay &&
-									p1.equals(wayPoints.get(p2I)));
+									p1.tooCloseTo(wayPoints.get(p2I)));
 						}
 						else {
 							// split the way before the second point
@@ -686,7 +638,7 @@ public class StyledConverter implements OsmConverter {
 			Coord p = points.get(i);
 			if(p.getHighwayCount() > 1) {
 				// point is going to be a node
-				if(candidate.equals(p))
+				if(candidate.tooCloseTo(p))
 					return false;
 				// no need to test further
 				break;
@@ -697,7 +649,7 @@ public class StyledConverter implements OsmConverter {
 			Coord p = points.get(i);
 			if(p.getHighwayCount() > 1) {
 				// point is going to be a node
-				if(candidate.equals(p))
+				if(candidate.tooCloseTo(p))
 					return false;
 				// no need to test further
 				break;
diff --git a/src/uk/me/parabola/mkgmap/reader/osm/Element.java b/src/uk/me/parabola/mkgmap/reader/osm/Element.java
index a49e7c7..d2ebc69 100644
--- a/src/uk/me/parabola/mkgmap/reader/osm/Element.java
+++ b/src/uk/me/parabola/mkgmap/reader/osm/Element.java
@@ -86,7 +86,8 @@ public abstract class Element implements Iterable<String> {
 	 * element.
 	 */
 	public void copyTags(Element other) {
-		tags = other.tags.copy();
+		if(other.tags != null)
+			tags = other.tags.copy();
 	}
 
 	public String getName() {
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 e350483..869b62c 100644
--- a/src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java
+++ b/src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java
@@ -27,6 +27,7 @@ import uk.me.parabola.imgfmt.app.Area;
 import uk.me.parabola.imgfmt.app.Coord;
 import uk.me.parabola.imgfmt.app.Exit;
 import uk.me.parabola.log.Logger;
+import uk.me.parabola.mkgmap.general.LineClipper;
 import uk.me.parabola.mkgmap.general.MapDetails;
 import uk.me.parabola.mkgmap.general.RoadNetwork;
 import uk.me.parabola.mkgmap.reader.osm.CoordPOI;
@@ -73,6 +74,7 @@ class Osm5XmlHandler extends DefaultHandler {
 	private static final int MODE_BOUNDS = 5;
 
 	private static final long CYCLEWAY_ID_OFFSET = 0x10000000;
+	private static final long CLIPPED_WAYS_ID_OFFSET = 0x20000000;
 
 	private Node currentNode;
 	private Way currentWay;
@@ -107,8 +109,13 @@ class Osm5XmlHandler extends DefaultHandler {
 		ignoreBounds = props.getProperty("ignore-osm-bounds", false);
 		routing = props.containsKey("route");
 		String rsa = props.getProperty("remove-short-arcs", null);
-		if(rsa != null)
-			minimumArcLength = (rsa.length() > 0)? Double.parseDouble(rsa) : 0.0;
+		final double MIN_ALLOWED_ARC_LENGTH = 5.0;
+		if("no".equals(rsa))
+			minimumArcLength = null;
+		else if(rsa != null)
+			minimumArcLength = (rsa.length() > 0)? Double.parseDouble(rsa) : MIN_ALLOWED_ARC_LENGTH;
+		else if(routing)
+			minimumArcLength = MIN_ALLOWED_ARC_LENGTH;
 		else
 			minimumArcLength = null;
 		frigRoundabouts = props.getProperty("frig-roundabouts");
@@ -454,6 +461,9 @@ class Osm5XmlHandler extends DefaultHandler {
 
 		nodeMap = null;
 
+		if(bbox != null)
+			clipWays();
+
 		if(minimumArcLength != null)
 			removeShortArcsByMergingNodes(minimumArcLength);
 
@@ -488,6 +498,59 @@ class Osm5XmlHandler extends DefaultHandler {
 		endTask.run();
 	}
 
+	private void clipWays() {
+		log.info("Clipping ways");
+		List<Way> allClippedWays = new ArrayList<Way>();
+		List<Way> deletedWays = new ArrayList<Way>();
+		// clip the ways
+		for(Way way : wayMap.values()) {
+			List<Way> clippedWays = clipWay(way);
+			if(clippedWays != null) {
+				deletedWays.add(way);
+				allClippedWays.addAll(clippedWays);
+			}
+		}
+		// now for each way that was clipped, remove the original
+		// way from wayMap and add the clipped sub-ways
+		for(Way way : deletedWays)
+			wayMap.remove(way.getId());
+
+		long clippedWayId = CLIPPED_WAYS_ID_OFFSET;
+		for(Way cw : allClippedWays)
+			wayMap.put(clippedWayId++, cw);
+
+		log.info("Clipping ways - " + deletedWays.size() + " ways clipped");
+	}
+
+	private List<Way> clipWay(Way way) {
+
+		List<Way> clippedWays = null;
+
+		List<List<Coord>> lineSegs = LineClipper.clip(bbox, way.getPoints());
+
+		if (lineSegs != null) {
+
+			clippedWays = new ArrayList<Way>();
+
+			for (List<Coord> lco : lineSegs) {
+				Way nWay = new Way(way.getId());
+				nWay.setName(way.getName());
+				nWay.copyTags(way);
+				for(Coord co : lco) {
+					nWay.addPoint(co);
+					if(co.getOnBoundary()) {
+						// this point lies on a boundary
+						// make sure it becomes a node
+						co.incHighwayCount();
+					}
+				}
+				clippedWays.add(nWay);
+			}
+		}
+
+		return clippedWays;
+	}
+
 	private final void incArcCount(Map<Coord, Integer> map, Coord p, int inc) {
 		Integer i = map.get(p);
 		if(i != null)
@@ -500,6 +563,7 @@ class Osm5XmlHandler extends DefaultHandler {
 		Map<Coord, Integer> arcCounts = new IdentityHashMap<Coord, Integer>();
 		int numWaysDeleted = 0;
 		int numNodesMerged = 0;
+		log.info("Removing short arcs (min arc length = " + minArcLength + "m)");
 		log.info("Removing short arcs - counting arcs");
 		for(Way w : wayMap.values()) {
 			List<Coord> points = w.getPoints();
@@ -520,6 +584,7 @@ class Osm5XmlHandler extends DefaultHandler {
 		// replacements maps those nodes that have been replaced to
 		// the node that replaces them
 		Map<Coord, Coord> replacements = new IdentityHashMap<Coord, Coord>();
+		Map<Way, Way> complainedAbout = new IdentityHashMap<Way, Way>();
 		boolean anotherPassRequired = true;
 		int pass = 0;
 		while(anotherPassRequired && pass < 10) {
@@ -537,6 +602,7 @@ class Osm5XmlHandler extends DefaultHandler {
 				}
 				int previousNodeIndex = 0; // first point will be a node
 				Coord previousPoint = points.get(0);
+				double arcLength = 0;
 				for(int i = 0; i < points.size(); ++i) {
 					Coord p = points.get(i);
 					// check if this point is to be replaced because
@@ -547,7 +613,9 @@ class Osm5XmlHandler extends DefaultHandler {
 						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));
+						assert !p.getOnBoundary() : "Boundary node replaced";
+						String replacementId = (replacement.getOnBoundary())? "'boundary node'" : "" + nodeIdMap.get(replacement);
+						log.info("  Way " + way.getTag("name") + " (OSM id " + way.getId() + ") has node " + nodeIdMap.get(p) + " replaced with node " + replacementId);
 						p = replacement;
 						// replace point in way
 						points.set(i, p);
@@ -558,13 +626,14 @@ class Osm5XmlHandler extends DefaultHandler {
 					if(i > 0) {
 						// this is not the first point in the way
 						if(p == previousPoint) {
-							log.info("  Way " + way.getTag("name") + " (OSM id " + way.getId() + ") has consecutive identical nodes (" + nodeIdMap.get(p) + ") - deleting the second node");
+							log.info("  Way " + way.getTag("name") + " (OSM id " + way.getId() + ") has consecutive identical points (" + nodeIdMap.get(p) + ") - deleting the second point");
 							points.remove(i);
 							// hack alert! rewind the loop index
 							--i;
 							anotherPassRequired = true;
 							continue;
 						}
+						arcLength += p.distance(previousPoint);
 						previousPoint = p;
 						Coord previousNode = points.get(previousNodeIndex);
 						// this point is a node if it has an arc count
@@ -578,31 +647,71 @@ class Osm5XmlHandler extends DefaultHandler {
 							if(p != previousNode &&
 							   (p.equals(previousNode) ||
 								(minArcLength > 0 &&
-								 minArcLength > p.distance(previousNode)))) {
+								 minArcLength > arcLength))) {
+								if(previousNode.getOnBoundary() && p.getOnBoundary()) {
+									// both the previous node and this node
+									// are on the boundary
+									if(complainedAbout.get(way) == null) {
+										log.warn("  Way " + way.getTag("name") + " (OSM id " + way.getId() + ") has short arc (" + String.format("%.2f", arcLength) + "m) - but it can't be removed because both ends of the arc are boundary nodes!");
+										complainedAbout.put(way, way);
+									}
+									break; // give up with this way
+								}
+								String thisNodeId = (p.getOnBoundary())? "'boundary node'" : "" + nodeIdMap.get(p);
+								String previousNodeId = (previousNode.getOnBoundary())? "'boundary node'" : "" + nodeIdMap.get(previousNode);
+
 								if(p.equals(previousNode))
-									log.info("  Way " + way.getTag("name") + " (OSM id " + way.getId() + ") has zero length arc - removing it by merging node " + nodeIdMap.get(p) + " into " + nodeIdMap.get(previousNode));
+									log.info("  Way " + way.getTag("name") + " (OSM id " + way.getId() + ") has zero length arc - removing it by merging node " + thisNodeId + " into " + previousNodeId);
 								else
-									log.info("  Way " + way.getTag("name") + " (OSM id " + way.getId() + ") has short arc (" + ((int)(100 * p.distance(previousNode)))/100.0 + "m) - removing it by merging node " + nodeIdMap.get(p) + " into " + nodeIdMap.get(previousNode));
-								++numNodesMerged;
-								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);
+									log.info("  Way " + way.getTag("name") + " (OSM id " + way.getId() + ") has short arc (" + String.format("%.2f", arcLength) + "m) - removing it by merging node " + thisNodeId + " into " + previousNodeId);
+								if(p.getOnBoundary()) {
+									// current point is a boundary node so
+									// we need to merge the previous node into
+									// this node
+									++numNodesMerged;
+									replacements.put(previousNode, p);
+									// add the previous node's arc
+									// count to this node
+									incArcCount(arcCounts, p, arcCounts.get(previousNode) - 1);
+									// remove the preceding point(s)
+									// back to and including the
+									// previous node
+									for(int j = i - 1; j >= previousNodeIndex; --j) {
+										points.remove(j);
+									}
+									// hack alert! rewind the loop index
+									i = previousNodeIndex;
+									anotherPassRequired = true;
+								}
+								else {
+									// current point is not on a boundary so
+									// merge this node into the previous one
+									++numNodesMerged;
+									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;
 								}
-								// 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 arc length
+							arcLength = 0;
 						}
 					}
 				}
