diff --git a/resources/help/en/options b/resources/help/en/options
index 6dc6a7f..4c77030 100644
--- a/resources/help/en/options
+++ b/resources/help/en/options
@@ -240,6 +240,10 @@ 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-bogus-nodes
+	Removes routing nodes that are only used in a single way.
+	Such nodes are harmless but they inflate the routing data.
+
 --remove-short-arcs[=MinLength]
 	Merge nodes to remove short arcs that can cause routing
 	problems. If MinLength is specified (in metres), arcs shorter
diff --git a/src/uk/me/parabola/imgfmt/app/Coord.java b/src/uk/me/parabola/imgfmt/app/Coord.java
index 6a15fce..186ea86 100644
--- a/src/uk/me/parabola/imgfmt/app/Coord.java
+++ b/src/uk/me/parabola/imgfmt/app/Coord.java
@@ -77,10 +77,15 @@ public class Coord implements Comparable<Coord> {
 		return highwayCount;
 	}
 
-	public void incHighwayCount() {
+	public Coord incHighwayCount() {
 		// don't let it wrap
 		if(highwayCount < Byte.MAX_VALUE)
 			++highwayCount;
+		return this;
+	}
+
+	public void zeroHighwayCount() {
+		highwayCount = 0;
 	}
 
 	public boolean getOnBoundary() {
diff --git a/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java b/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
index 6c665e0..7398f08 100644
--- a/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
+++ b/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
@@ -95,14 +95,19 @@ public class StyledConverter implements OsmConverter {
 
 	private final int MAX_POINTS_IN_ARC = MAX_POINTS_IN_WAY;
 
-	private final int MAX_NODES_IN_WAY = 64; // possibly could be increased
+	private final int MAX_NODES_IN_WAY = MAX_POINTS_IN_WAY;
 
 	private final double MIN_DISTANCE_BETWEEN_NODES = 5.5;
 
 	// nodeIdMap maps a Coord into a nodeId
 	private final Map<Coord, Integer> nodeIdMap = new IdentityHashMap<Coord, Integer>();
 	private int nextNodeId = 1;
-	
+
+	private List<Way> routableWays = new ArrayList<Way>(10000);
+	private List<Way> allWays = new ArrayList<Way>(10000);
+	private List<GType> allTypes = new ArrayList<GType>(10000);
+	private int[] nodeCounts = new int[MAX_NODES_IN_WAY + 1];
+
 	private final Rule wayRules;
 	private final Rule nodeRules;
 	private final Rule relationRules;
@@ -111,6 +116,7 @@ public class StyledConverter implements OsmConverter {
 	private boolean driveOnLeft;
 	private boolean driveOnRight;
 	private boolean checkRoundabouts;
+	private boolean removeBogusNodes;
 
 	class AccessMapping {
 		private final String type;
@@ -156,6 +162,7 @@ public class StyledConverter implements OsmConverter {
 		NODHeader.setDriveOnLeft(driveOnLeft);
 		driveOnRight = props.getProperty("drive-on-right") != null;
 		checkRoundabouts = props.getProperty("check-roundabouts") != null;
+		removeBogusNodes = props.getProperty("remove-bogus-nodes") != null;
 
 		LineAdder overlayAdder = style.getOverlays(lineAdder);
 		if (overlayAdder != null)
@@ -253,25 +260,84 @@ public class StyledConverter implements OsmConverter {
 	 * @param way The OSM way.
 	 */
 	public void convertWay(Way way) {
+
+		// way being null signals that all ways have now been passed
+		if(way == null) {
+			if(removeBogusNodes) {
+				// to minimise the number of routing nodes created, we
+				// need to recalculate the highway counts as some will
+				// be too large due to the fact that a routable way
+				// has shared points with some non-routable feature
+				// (line or shape)
+				log.info("Zeroing highway counts in " + routableWays.size() + " routable ways");
+				int nodesThen = 0;
+				int nodesNow = 0;
+				for(Way w : routableWays) {
+					for(Coord p : w.getPoints()) {
+						if(p.getHighwayCount() > 1)
+							++nodesThen;
+						p.zeroHighwayCount();
+					}
+				}
+
+				log.info("Updating highway counts");
+				for(Way w : routableWays) {
+					if(!w.isBoolTag("mkgmap:synthesised")) {
+						for(Coord p : w.getPoints()) {
+							p.incHighwayCount();
+							if(p.getHighwayCount() == 2)
+								++nodesNow;
+						}
+					}
+				}
+				log.info("" + (nodesThen - nodesNow) + " bogus nodes removed, " + nodesNow + " nodes remain");
+			}
+			log.info("Processing ways");
+			for(int i = 0; i < allWays.size(); ++i) {
+				Way wi = allWays.get(i);
+				GType ti = allTypes.get(i);
+				if (ti.getFeatureKind() == GType.POLYLINE) {
+					if(ti.isRoad() &&
+					   !MapElement.hasExtendedType(ti.getType())) {
+						addRoad(wi, ti);
+					}
+					else
+						addLine(wi, ti);
+				}
+				else
+					addShape(wi, ti);
+			}
+
+			log.info("Node statistics - " + routableWays.size() + " routable ways");
+			for(int i = 0; i <= MAX_NODES_IN_WAY; ++i) {
+				int nc = nodeCounts[i];
+				if(nc > 0)
+					log.info("" + nc + " way(s) have " + i + " nodes");
+			}
+
+			routableWays = null;
+			allWays = null;
+			allTypes = null;
+
+			return;
+		}
+
 		if (way.getPoints().size() < 2)
 			return;
 
 		GType foundType = null;
 		if(way.getTag("mkgmap:gtype") != null) {
-
 			foundType = makeGTypeFromTags(way);
 			if(foundType == null)
 				return;
-
-			if (foundType.getFeatureKind() == GType.POLYLINE) {
-				if(foundType.isRoad() &&
-				   !MapElement.hasExtendedType(foundType.getType()))
-					addRoad(way, foundType);
-				else
-					addLine(way, foundType);
-			}
-			else
-				addShape(way, foundType);
+			Way dupWay = way.duplicate();
+			// remember way and foundType so it can be processed later
+			// after the highway counts have been recalculated
+			allWays.add(dupWay);
+			allTypes.add(foundType);
+			if (foundType.isRoad() &&
+				!MapElement.hasExtendedType(foundType.getType()))
+				routableWays.add(dupWay);
 		}
 		else {
 			preConvertRules(way);
@@ -284,14 +350,13 @@ public class StyledConverter implements OsmConverter {
 
 				postConvertRules(dupWay, foundType);
 
-				if (foundType.getFeatureKind() == GType.POLYLINE) {
-					if(foundType.isRoad())
-						addRoad(dupWay, foundType);
-					else
-						addLine(dupWay, foundType);
-				}
-				else
-					addShape(dupWay, foundType);
+				// remember way and foundType so it can be processed later
+				// after the highway counts have been recalculated
+				allWays.add(dupWay);
+				allTypes.add(foundType);
+				if (foundType.isRoad() &&
+					!MapElement.hasExtendedType(foundType.getType()))
+					routableWays.add(dupWay);
 			} while (!foundType.isFinal());
 		}
 	}
@@ -586,6 +651,8 @@ public class StyledConverter implements OsmConverter {
 
 	void addRoad(Way way, GType gt) {
 
+		log.info("Adding road " + way.toBrowseURL() + " with type 0x" + Integer.toHexString(gt.getType()));
+
 		String oneWay = way.getTag("oneway");
 		if("-1".equals(oneWay) || "reverse".equals(oneWay)) {
 			// it's a oneway street in the reverse direction
@@ -790,6 +857,7 @@ public class StyledConverter implements OsmConverter {
 								// insert a new point after the POI to
 								// make a short stub segment
 								p1 = p.makeBetweenPoint(p1, stubSegmentLength / dist);
+								p1.incHighwayCount();
 								points.add(i + 1, p1);
 							}
 
@@ -848,6 +916,7 @@ public class StyledConverter implements OsmConverter {
 								// insert a new point to make a short
 								// stub segment
 								p1 = p1.makeBetweenPoint(p, stubSegmentLength / dist);
+								p1.incHighwayCount();
 								points.add(i + 1, p1);
 								// as p1 is now no longer a CoordPOI,
 								// the split below will be deferred
@@ -888,6 +957,15 @@ public class StyledConverter implements OsmConverter {
 					nWay.copyTags(way);
 					for(Coord co : lco) {
 						nWay.addPoint(co);
+						if(co.getHighwayCount() == 0) {
+							// either the point is in a synthesised
+							// way or it was added by the clipper
+							// (which should not happen if the ways
+							// were "soft clipped" earlier).
+							if(false && !way.isBoolTag("mkgmap:synthesised"))
+								log.warn("Clipper added point at " + co.toOSMURL());
+							co.incHighwayCount();
+						}
 						if(co.getOnBoundary()) {
 							// this point lies on a boundary
 							// make sure it becomes a node
@@ -1059,7 +1137,7 @@ public class StyledConverter implements OsmConverter {
 			name = "";
 		else
 			name += " ";
-		return name + "(OSM id " + way.getId() + ")";
+		return name + "(" + way.toBrowseURL() + ")";
 	}
 
 	void addRoadWithoutLoops(Way way, GType gt) {
@@ -1223,6 +1301,8 @@ public class StyledConverter implements OsmConverter {
 				}
 			}
 
+			assert p.getHighwayCount() > 0 : "Point " + i + " in way " + debugWayName + " has zero highway count at " + p.toOSMURL() + " (way has " + points.size() + " points)";
+
 			if(p.getHighwayCount() > 1) {
 				// this point is a node connecting highways
 				Integer nodeId = nodeIdMap.get(p);
@@ -1419,6 +1499,14 @@ public class StyledConverter implements OsmConverter {
 		int numNodes = nodeIndices.size();
 		road.setNumNodes(numNodes);
 
+		++nodeCounts[numNodes];
+
+		if(numNodes > 0.9 * points.size()) {
+			String message = "Way " + debugWayName + " has " + points.size() + " points and " + (100 * numNodes / points.size()) + "% of those are nodes, perhaps it overlaps another way?";
+			if(points.size() > 10)
+				log.info(message);
+		}
+
 		if(numNodes > 0) {
 			// replace Coords that are nodes with CoordNodes
 			boolean hasInternalNodes = false;
@@ -1437,7 +1525,12 @@ public class StyledConverter implements OsmConverter {
 				}
 
 				CoordNode thisCoordNode = new CoordNode(coord.getLatitude(), coord.getLongitude(), nodeId, boundary);
+				thisCoordNode.incHighwayCount();
 				points.set(n, thisCoordNode);
+				// put an entry in nodeIdMap for the CoordNode so that
+				// it will get matched if this way is used to generate
+				// more than one routable way
+				nodeIdMap.put(thisCoordNode, nodeId);
 
 				// see if this node plays a role in any turn
 				// restrictions
diff --git a/src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java b/src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java
index e2f3c64..95882ca 100644
--- a/src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java
+++ b/src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java
@@ -136,12 +136,12 @@ public class MultiPolygonRelation extends Relation {
 			int iLon = inList.get(in).getLongitude();
 			if (Math.abs(oLat - iLat) > Math.abs(oLon - iLon)) {
 				int delta = (oLon > iLon)? -1 : 1;
-				outList.add(index++, new Coord(iLat + delta, iLon));
-				outList.add(index, new Coord(oLat + delta, oLon));
+				outList.add(index++, new Coord(iLat + delta, iLon).incHighwayCount());
+				outList.add(index, new Coord(oLat + delta, oLon).incHighwayCount());
 			} else{
 				int delta = (oLat > iLat)? 1 : -1;
-				outList.add(index++, new Coord(iLat, iLon + delta));
-				outList.add(index, new Coord(oLat, oLon + delta));
+				outList.add(index++, new Coord(iLat, iLon + delta).incHighwayCount());
+				outList.add(index, new Coord(oLat, oLon + delta).incHighwayCount());
 			}
 		}
 	}
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 57a6ae1..0f5466d 100644
--- a/src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java
+++ b/src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java
@@ -559,6 +559,9 @@ class Osm5XmlHandler extends DefaultHandler {
 		for (Way w: wayMap.values())
 			converter.convertWay(w);
 
+		// signal converter that all ways have been passed
+		converter.convertWay(null);
+
 		wayMap = null;
 
 		RoadNetwork roadNetwork = mapper.getRoadNetwork();
