Index: src/uk/me/parabola/imgfmt/app/net/RoadDef.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/net/RoadDef.java	(revision 3066)
+++ src/uk/me/parabola/imgfmt/app/net/RoadDef.java	(working copy)
@@ -481,6 +481,10 @@
 		this.node = node;
 	}
 
+	public RouteNode getNode(){
+		return node;
+	}
+	
 	private boolean hasNodInfo() {
 		return (netFlags & NET_FLAG_NODINFO) != 0;
 	}
Index: src/uk/me/parabola/imgfmt/app/net/RouteArc.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/net/RouteArc.java	(revision 3066)
+++ src/uk/me/parabola/imgfmt/app/net/RouteArc.java	(working copy)
@@ -65,7 +65,13 @@
 	private final byte lengthRatio;
 	private final int pointsHash;
 	private boolean isForward;
+	private final float lengthInMeter;
 
+	private boolean isDirect;
+
+	// this field may limit the arcs destination class
+	private byte maxDestClass = -1;
+
 	/**
 	 * Create a new arc. An arc can contain multiple points (eg. A->B->C->D->E)
 	 * @param roadDef The road that this arc segment is part of.
@@ -76,7 +82,6 @@
 	 * @param directBearing The direct heading (signed degrees) (A->E)
 	 * @param arcLength the length of the arc in meter (A->B->C->D->E)
 	 * @param directLength the length of the arc in meter (A-E) 
-	 * @param curveEnabled false means don't write curve bytes 
 	 * @param pointsHash
 	 */
 	public RouteArc(RoadDef roadDef,
@@ -84,8 +89,8 @@
 					double initialBearing, double finalBearing, double directBearing,
 					double arcLength,
 					double directLength,
-					boolean curveEnabled,
 					int pointsHash) {
+		this.isDirect = true; // unless changed
 		this.roadDef = roadDef;
 		this.source = source;
 		this.dest = dest;
@@ -98,6 +103,7 @@
 			len = (1 << 22) - 1;
 		}
 		this.length = len;
+		this.lengthInMeter = (float) arcLength;
 		this.pointsHash = pointsHash;
 		int ratio = 0;
 		if (arcLength > directLength){
@@ -108,7 +114,7 @@
 		if (ratio == 0 && length >= (1 << 14))
 			ratio = 0x1f;
 		lengthRatio = (byte)ratio;
-		haveCurve = curveEnabled && lengthRatio > 0;
+		haveCurve = lengthRatio > 0;
 	}
 
 	public float getInitialHeading() {
@@ -203,14 +209,23 @@
 	}
 	 
 
-	// units of 16 feet
-	final static double LENGTH_FACTOR = 1.0 / (2.391 * 2);
-	//old 3.2808 / 16;
+	// units of 16 feet with one feet ~ 1/3 meter
+	final static double LENGTH_FACTOR = 3.34 / 16;
 	private static int convertMeters(double l) {
 		return (int) (l * LENGTH_FACTOR + 0.5);
+	}
 
+	public int getArcDestClass(){
+		int cl = Math.min(getRoadDef().getRoadClass(), dest.getNodeClass());
+		if (maxDestClass >= 0)
+			cl = Math.min(maxDestClass, cl);
+		assert cl <= getRoadDef().getRoadClass();
+		assert cl <= dest.getNodeClass();
+		return cl;
 	}
-
+	public float getLengthInMeter(){
+		return lengthInMeter;
+	}
 	public void write(ImgFileWriter writer, RouteArc lastArc, boolean useCompactDirs, Byte compactedDir) {
 		boolean first = lastArc == null;
 		if (first){
@@ -230,7 +245,7 @@
 			log.debug("writing arc at", offset, ", flagA=", Integer.toHexString(flagA));
 
 		// fetch destination class -- will have been set correctly by now
-		setDestinationClass(dest.getNodeClass());
+		setDestinationClass(getArcDestClass());
 
 		// determine how to write length and curve bit
 		int[] lendat = encodeLength();
@@ -259,13 +274,16 @@
 		for (int aLendat : lendat)
 			writer.put((byte) aLendat);
 
-		if (useCompactDirs){
-			// determine if we have to write direction info
-			if (compactedDir != null)
-				writer.put(compactedDir);
-		} else 
-			writer.put((byte)(initialHeading * 256 / 360));
-		
+		if (first || lastArc.indexA != this.indexA || lastArc.isForward != isForward){
+			if (useCompactDirs){
+				// determine if we have to write direction info
+				if (compactedDir != null)
+					writer.put(compactedDir);
+			} else 
+				writer.put((byte)(initialHeading * 256 / 360));
+		} else {
+//			System.out.println("skipped writing of initial dir");
+		}
 		if (haveCurve) {
 			int[] curvedat = encodeCurve();
 			for (int aCurvedat : curvedat)
@@ -400,4 +418,28 @@
 			log.debug("setting destination class", destinationClass);
 		flagA |= (destinationClass & MASK_DESTCLASS);
 	}
+
+	public void setIndirect() {
+		this.isDirect = false;
+		
+	}
+
+	public boolean isDirect() {
+		return isDirect;
+	}
+
+	public void setMaxDestClass(int destClass) {
+		if (this.maxDestClass < 0)
+			this.maxDestClass = (byte) destClass;
+		else if (destClass < maxDestClass)
+			maxDestClass = (byte)destClass;
+	}
+
+	@Override
+	public String toString() {
+		return "RouteArc [" + (isForward ? "->":"<-") + (isDirect ? "direct":"indirect")
+				+ roadDef +  " " + dest + "]";
+	}
+
+	
 }
Index: src/uk/me/parabola/imgfmt/app/net/RouteNode.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/net/RouteNode.java	(revision 3066)
+++ src/uk/me/parabola/imgfmt/app/net/RouteNode.java	(working copy)
@@ -72,9 +72,9 @@
 	private char lonOff;
 	private List<RouteArc[]> throughRoutes;
 
-	// this is for setting destination class on arcs
-	// we're taking the maximum of roads this node is
-	// on for now -- unsure of precise mechanic
+	// contains the maximum of roads this node is on, written with the flags
+	// field. It is also used for the calculation of the destination class on
+	// arcs.
 	private int nodeClass;
 
 	public RouteNode(Coord coord) {
@@ -464,6 +464,8 @@
 					// next, if oa has the same RoadDef as inArc, it's
 					// definitely the same road
 					for(RouteArc oa : arcs) {
+						if (oa.isDirect() == false)
+							continue;
 						if(oa.getDest() != inArc.getSource()) {
 							// this arc is not going to the same node as
 							// inArc came from
@@ -480,6 +482,8 @@
 					// possiblySameRoad() to see if the roads' id or
 					// labels (names/refs) match
 					for(RouteArc oa : arcs) {
+						if (oa.isDirect() == false)
+							continue;
 						if(oa.getDest() != inArc.getSource()) {
 							// this arc is not going to the same node as
 							// inArc came from
@@ -553,6 +557,8 @@
 				}
 
 				for(RouteArc otherArc : arcs) {
+					if (otherArc.isDirect() == false)
+						continue;
 
 					// for each other arc leaving this node, tweeze
 					// its heading if its heading change from the
@@ -885,4 +891,158 @@
 		else
 			log.warn("Failed to add through route between ways " + roadIdA + " and " + roadIdB + " at " + coord.toOSMURL() + " - perhaps they don't meet here?");
 	}
+
+	/**
+	 * For each arc on the road, check if we can add indirect arcs to 
+	 * other nodes of the same road. This is done if the other node
+	 * lies on a different road with a higher road class than the
+	 * highest other road of the target node of the arc. We do this
+	 * for both forward and reverse arcs. Multiple indirect arcs
+	 * may be added for each Node. An indirect arc will 
+	 * always point to a higher road than the previous arc.
+	 * The length and direct bearing of the additional arc is measured 
+	 * from the target node of the preceding arc to the new target node.
+	 * The initial bearing doesn't really matter as it is not written
+	 * for indirect arcs.  
+	 * @param road
+	 * @param maxRoadClass
+	 */
+	public void addArcsToMajorRoads(RoadDef road){
+		// collect the nodes of the road
+		assert road.getNode() == this;
+		RouteNode current = this;
+		int maxRoadClass = 0;
+		// the nodes of this road
+		List<RouteNode> nodes = new ArrayList<>();
+		// the forward arcs of this road
+		List<RouteArc> forwardArcs = new ArrayList<>();
+		// will contain the highest other road of each node 
+		List<RouteArc> highestOtherRoads = new ArrayList<>(); 
+		IntArrayList forwardArcPositions = new IntArrayList();
+		List<RouteArc> reverseArcs = new ArrayList<>();
+		IntArrayList reverseArcPositions = new IntArrayList();
+		nodes.add(current);
+		while (current != null){
+			RouteArc highestRoadArc = null;
+			int highestRoadClass = -1; 
+			RouteNode next = null;
+			for (int i = 0; i < current.arcs.size(); i++){
+				RouteArc arc = current.arcs.get(i);
+				if (arc.getRoadDef() == road){
+					if (arc.isDirect()){
+						if (arc.isForward()){
+							next = arc.getDest();
+							nodes.add(next);
+							forwardArcs.add(arc);
+							forwardArcPositions.add(i);
+						} else {
+							reverseArcPositions.add(i);
+							reverseArcs.add(arc);
+						}
+					}
+				} else {
+					int cl = arc.getRoadDef().getRoadClass();
+					if (cl > highestRoadClass){
+						highestRoadArc = arc;
+						highestRoadClass = cl;
+					}
+				}
+			}
+			if (highestRoadClass > maxRoadClass)
+				maxRoadClass = highestRoadClass;
+			highestOtherRoads.add(highestRoadArc);
+			current = next;
+		}
+		if (nodes.size() < 3)
+			return;
+//		System.out.println(road + " " + nodes.size() + " " + forwardArcs.size());
+		ArrayList<RouteArc> newArcs = new ArrayList<>(); 
+		IntArrayList arcPositions = forwardArcPositions;
+		List<RouteArc> roadArcs = forwardArcs;
+		for (int dir = 0; dir < 2; dir++){
+			// forward arcs first
+			for (int i = 0; i + 2 < nodes.size(); i++){
+				newArcs.clear();
+				RouteArc arcWithHighestOtherRoad = highestOtherRoads.get(i+1);
+				if (arcWithHighestOtherRoad == null)
+					continue;
+				int seenRoadClass = arcWithHighestOtherRoad.getRoadDef().getRoadClass();
+				RouteNode sourceNode = nodes.get(i+1);
+				RouteArc arcToSourceNode = roadArcs.get(i);
+				assert arcToSourceNode.getDest() == sourceNode;
+				double arcLength = 0;
+				for (int j = i+2; j < nodes.size(); j++){
+					for (RouteRestriction rr: nodes.get(j).getRestrictions()){
+						boolean restricted = rr.isRestricted(road);
+						if (restricted){
+							// maybe too cautious, but on the safe side
+							// we don't add indirect arcs if the nodes between
+							// have restrictions
+							break; 
+						}
+					} 		
+					
+					RouteArc arcToDest = roadArcs.get(j-1);
+					arcLength += arcToDest.getLengthInMeter();
+					if (highestOtherRoads.get(j) == null){
+						// node is not connected to other roads (first,last,boundary,part of restriction,...)
+						continue;
+					}
+					int cl2 = highestOtherRoads.get(j).getRoadDef().getRoadClass();
+					if (cl2 > seenRoadClass){
+						// create indirect arc from node i+1 to node j
+						RouteNode dest = nodes.get(j);
+						Coord c1 = sourceNode.getCoord();
+						Coord c2 = dest.getCoord();
+						RouteArc newArc = new RouteArc(road, 
+								nodes.get(i), // original source node of direct arc
+								dest, 
+								roadArcs.get(i).getInitialHeading(), // not used
+								arcToDest.getFinalHeading(),  // not used
+								c1.bearingTo(c2),
+								arcLength, 
+								c1.distance(c2), 
+								c1.hashCode() + c2.hashCode());
+						if (arcToSourceNode.isDirect())
+							arcToSourceNode.setMaxDestClass(0);
+						else 
+							newArc.setMaxDestClass(cl2);
+						if (dir == 0)
+							newArc.setForward();
+						newArc.setIndirect();
+						newArcs.add(newArc);
+						arcToSourceNode = newArc;
+						sourceNode = dest;
+						arcLength = 0;
+						seenRoadClass = cl2;
+						if (seenRoadClass == maxRoadClass)
+							break;
+					}
+				}
+				if (newArcs.isEmpty() == false){
+					int directArcPos =  arcPositions.getInt(i);
+					assert nodes.get(i).arcs.get(directArcPos).isDirect();
+					assert nodes.get(i).arcs.get(directArcPos).getRoadDef() == newArcs.get(0).getRoadDef();
+					assert nodes.get(i).arcs.get(directArcPos).isForward() == newArcs.get(0).isForward();
+					nodes.get(i).arcs.addAll(directArcPos + 1, newArcs);
+					if (dir == 0 && i > 0){
+						// check if the inserted arcs change the position of the direct reverse arc
+						int reverseArcPos = reverseArcPositions.get(i-1); // i-1 because first node doesn't have reverse arc 
+						if (directArcPos < reverseArcPos)
+							reverseArcPositions.set(i - 1, reverseArcPos + newArcs.size());
+							
+					}
+				}
+			}
+			if (dir > 0)
+				break;
+			// reverse the arrays for the other direction
+			Collections.reverse(reverseArcs);
+			Collections.reverse(reverseArcPositions);
+			Collections.reverse(nodes);
+			Collections.reverse(highestOtherRoads);
+			arcPositions = reverseArcPositions;
+			roadArcs = reverseArcs;
+		}
+	}
 }
Index: src/uk/me/parabola/imgfmt/app/net/RouteRestriction.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/net/RouteRestriction.java	(revision 3066)
+++ src/uk/me/parabola/imgfmt/app/net/RouteRestriction.java	(working copy)
@@ -181,4 +181,14 @@
 	public void setLast() {
 		last = true;
 	}
+
+	/**
+	 * Check if there is a restriction when travelling along a given road via this node.
+	 * @param rd the road
+	 * @return true if there is a restriction 
+	 */
+	public boolean isRestricted(RoadDef rd) {
+		return (from.getRoadDef() == rd && to.getRoadDef() == rd);
+			
+	}
 }
Index: src/uk/me/parabola/mkgmap/general/RoadNetwork.java
===================================================================
--- src/uk/me/parabola/mkgmap/general/RoadNetwork.java	(revision 3066)
+++ src/uk/me/parabola/mkgmap/general/RoadNetwork.java	(working copy)
@@ -63,10 +63,10 @@
 	private List<RouteCenter> centers = new ArrayList<RouteCenter>();
 	private int adjustTurnHeadings ;
 	private boolean checkRoundabouts;
+	private boolean addIndirectLinks;
 	private boolean checkRoundaboutFlares;
 	private int maxFlareLengthRatio ;
 	private boolean reportSimilarArcs;
-	private boolean outputCurveData;
 
 	public void config(EnhancedProperties props) {
 		String ath = props.getProperty("adjust-turn-headings");
@@ -77,12 +77,11 @@
 				adjustTurnHeadings = RouteNode.ATH_DEFAULT_MASK;
 		}
 		checkRoundabouts = props.getProperty("check-roundabouts", false);
+		addIndirectLinks = props.getProperty("add-indirect-links", false);
 		checkRoundaboutFlares = props.getProperty("check-roundabout-flares", false);
 		maxFlareLengthRatio = props.getProperty("max-flare-length-ratio", 0);
 
 		reportSimilarArcs = props.getProperty("report-similar-arcs", false);
-
-		outputCurveData = !props.getProperty("no-arc-curves", false);
 	}
 
 	public void addRoad(MapRoad road) {
@@ -178,7 +177,6 @@
 											forwardDirectBearing,
 											arcLength,
 											directLength,
-											outputCurveData,
 											pointsHash);
 				arc.setForward();
 				node1.addArc(arc);
@@ -192,7 +190,6 @@
 								   reverseDirectBearing,
 								   arcLength,
 								   directLength,
-								   outputCurveData,
 								   pointsHash);
 				node2.addArc(arc);
 				node1.addIncomingArc(arc);
@@ -254,12 +251,28 @@
 	}
 
 	public List<RouteCenter> getCenters() {
-		if (centers.isEmpty())
+		if (centers.isEmpty()){
+			if (addIndirectLinks)
+				addArcsToMajorRoads();
 			splitCenters();
+		}
 		return centers;
 	}
 
 	/**
+	 * add indirect arcs for each road class (in descending order)
+	 */
+	private void addArcsToMajorRoads() {
+		long t1 = System.currentTimeMillis();
+		
+		for (RoadDef rd: roadDefs){
+			if (rd.getRoadClass() > 1)
+				rd.getNode().addArcsToMajorRoads(rd);
+		}
+		log.error(" added arcs in " + (System.currentTimeMillis() - t1) + " ms");
+	}
+
+	/**
 	 * Get the list of nodes on the boundary of the network.
 	 *
 	 * Currently empty.
