v2 - no longer requires the node to have role "junction"

This patch also includes two other changes to the heading adjustment
code:

    When matching arcs, as a last resort, match road class and speed.
    
    If at a node, an outgoing arc cannot be matched to an incoming arc by
    possiblySameRoad(), try to find a single outgoing arc that has the
    same road class and speed as the incoming arc and use that.

and:

    When adjusting turn headings apply all relevant adjustments.
    
    Previously, if a heading needed adjusting because the delta from the
    outgoing arc was too small, it did not then consider whether the delta
    from the incoming arc was too small also. Now, it considers both cases
    and adjusts the heading as appropriate.

So please test even if you are not adding any through_route relations
and if you see any notable changes (good or bad) please say.

--------------------

This new relation (type through_route) specifies which 2 ways are the
"through route" at a junction. The relation requires 2 ways (no role
required) and a node with role = junction. It should only be used at
those junctions where the through route cannot be reliably inferred
from the ways' names and/or refs.

Here's an example from my local town:

     |   1
     |  /
     | /
     |/
     A
     |
     |
     |
     |
     B
    /|
   / |
  /  |
 2   |

The vertical road is the "through route", i.e. you can drive from one
end to the other without crossing any white lines. But, from point 1 to
point 2 is a B road so ways 1-A, A-B, and B-2 all have some ref,
say, B1234.

Without the through_route relation, mkgmap assumes (erroneously)
that the through route is 1-2 because all of the ways in that route
have the ref B1234.

By adding 2 through_route relations (one at A and the other at B),
mkgmap is informed of the real through route and will take that into
account when adjusting the turn headings. The end result is that the
quality of the routing directions is improved (we hope).

In this example, we need to have 2 relations because the B road
joins/leaves the through route at two places. 

So if you know of any junctions that would benefit from this relation,
please add it to the map data and try this patch to see if it improves
the routing instructions.

Mark

diff --git a/src/uk/me/parabola/imgfmt/app/net/RoadDef.java b/src/uk/me/parabola/imgfmt/app/net/RoadDef.java
index de87495..e6a187e 100644
--- a/src/uk/me/parabola/imgfmt/app/net/RoadDef.java
+++ b/src/uk/me/parabola/imgfmt/app/net/RoadDef.java
@@ -555,6 +555,10 @@ public class RoadDef implements Comparable {
 		nod2Flags |= (speed << 1);
 	}
 
+	public int getRoadSpeed() {
+		return tabAInfo & 7;
+	}
+
 	public void setOneway() {
 		tabAInfo |= TABA_FLAG_ONEWAY;
 		netFlags |= NET_FLAG_ONEWAY;
diff --git a/src/uk/me/parabola/imgfmt/app/net/RouteNode.java b/src/uk/me/parabola/imgfmt/app/net/RouteNode.java
index 45d6010..edfd266 100644
--- a/src/uk/me/parabola/imgfmt/app/net/RouteNode.java
+++ b/src/uk/me/parabola/imgfmt/app/net/RouteNode.java
@@ -67,6 +67,7 @@ public class RouteNode implements Comparable<RouteNode> {
 	private final CoordNode coord;
 	private char latOff;
 	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
@@ -395,17 +396,55 @@ public class RouteNode implements Comparable<RouteNode> {
 				// determine the outgoing arc that is likely to be the
 				// same road as the incoming arc
 				RouteArc outArc = null;
-				for(RouteArc oa : arcs) {
-					if(oa.getDest() != inArc.getSource()) {
-						// this arc is not going to the same node as
-						// inArc came from
-						if((oa.isForward() || !oa.getRoadDef().isOneway()) &&
-						   possiblySameRoad(inArc, oa)) {
-							outArc = oa;
+
+				if(throughRoutes != null) {
+					for(RouteArc[] pair : throughRoutes) {
+						if(pair[0] == inArc) {
+							outArc = pair[1];
+							log.info("Found through route from " + inArc.getRoadDef() + " to " + outArc.getRoadDef());
 							break;
 						}
 					}
 				}
+
+				if(outArc == null) {
+					for(RouteArc oa : arcs) {
+						if(oa.getDest() != inArc.getSource()) {
+							// this arc is not going to the same node as
+							// inArc came from
+							if((oa.isForward() || !oa.getRoadDef().isOneway()) &&
+							   possiblySameRoad(inArc, oa)) {
+								outArc = oa;
+								break;
+							}
+						}
+					}
+				}
+
+				if(outArc == null) {
+					// last ditch attempt to find the outgoing arc -
+					// try and find a single arc that has the same
+					// road class and speed as the incoming arc
+					int inArcClass = inArc.getRoadDef().getRoadClass();
+					int inArcSpeed = inArc.getRoadDef().getRoadSpeed();
+					for(RouteArc oa : arcs) {
+						if(oa.getDest() != inArc.getSource() &&
+						   oa.getRoadDef().getRoadClass() == inArcClass &&
+						   oa.getRoadDef().getRoadSpeed() == inArcSpeed) {
+							if(outArc != null) {
+								// multiple arcs have the same road
+								// class as the incoming arc so don't
+								// use any of them as the outgoing arc
+								outArc = null;
+								break;
+							}
+							outArc = oa;
+						}
+					}
+					if(outArc != null)
+						log.info("Matched outgoing arc " + outArc.getRoadDef() + " to " + inArc.getRoadDef() + " using road class (" + inArcClass + ") and speed (" + inArcSpeed + ")"); 
+				}
+
 				// if we did not find the outgoing arc, give up with
 				// this incoming arc
 				if(outArc == null) {
@@ -449,11 +488,16 @@ public class RouteNode implements Comparable<RouteNode> {
 						continue;
 					}
 
+					/* mb - I think this will have to go because it
+					 * will stop the through routes working (and I
+					 * can't remember why it's here anyway!)
+
 					if(possiblySameRoad(inArc, otherArc) ||
 					   possiblySameRoad(outArc, otherArc)) {
 						// not obviously a different road so give up
 						continue;
 					}
+					*/
 
 					if(inArc.getRoadDef().isLinkRoad() &&
 					   otherArc.getRoadDef().isLinkRoad()) {
@@ -483,9 +527,12 @@ public class RouteNode implements Comparable<RouteNode> {
 						if((mask & ATH_OUTGOING) != 0 &&
 						   outToOtherDelta < minDiffBetweenOutgoingAndOtherArcs)
 							newHeading = outHeading + minDiffBetweenOutgoingAndOtherArcs;
-						else if((mask & ATH_INCOMING) != 0 &&
-								inToOtherDelta < minDiffBetweenIncomingAndOtherArcs)
-							newHeading = inHeading + minDiffBetweenIncomingAndOtherArcs;
+						if((mask & ATH_INCOMING) != 0 &&
+						   inToOtherDelta < minDiffBetweenIncomingAndOtherArcs) {
+							int nh = inHeading + minDiffBetweenIncomingAndOtherArcs;
+							if(nh > newHeading)
+								newHeading = nh;
+						}
 
 						if(newHeading > 180)
 							newHeading -= 360;
@@ -495,9 +542,12 @@ public class RouteNode implements Comparable<RouteNode> {
 						if((mask & ATH_OUTGOING) != 0 &&
 						   outToOtherDelta > -minDiffBetweenOutgoingAndOtherArcs)
 							newHeading = outHeading - minDiffBetweenOutgoingAndOtherArcs;
-						else if((mask & ATH_INCOMING) != 0 &&
-								inToOtherDelta > -minDiffBetweenIncomingAndOtherArcs)
-							newHeading = inHeading - minDiffBetweenIncomingAndOtherArcs;
+						if((mask & ATH_INCOMING) != 0 &&
+						   inToOtherDelta > -minDiffBetweenIncomingAndOtherArcs) {
+							int nh = inHeading - minDiffBetweenIncomingAndOtherArcs;
+							if(nh < newHeading)
+								newHeading = nh;
+						}
 
 						if(newHeading < -180)
 							newHeading += 360;
@@ -732,4 +782,23 @@ public class RouteNode implements Comparable<RouteNode> {
 			}
 		}
 	}
+
+	public void addThroughRoute(long roadIdA, long roadIdB) {
+		if(throughRoutes == null)
+			throughRoutes = new ArrayList<RouteArc[]>();
+		for(RouteArc arc1 : incomingArcs) {
+			if(arc1.getRoadDef().getId() == roadIdA) {
+				for(RouteArc arc2 : arcs) {
+					if(arc2.getRoadDef().getId() == roadIdB)
+						throughRoutes.add(new RouteArc[] { arc1, arc2 });
+				}
+			}
+			else if(arc1.getRoadDef().getId() == roadIdB) {
+				for(RouteArc arc2 : arcs) {
+					if(arc2.getRoadDef().getId() == roadIdA)
+						throughRoutes.add(new RouteArc[] { arc1, arc2 });
+				}
+			}
+		}
+	}
 }
diff --git a/src/uk/me/parabola/mkgmap/general/MapCollector.java b/src/uk/me/parabola/mkgmap/general/MapCollector.java
index 07b32d9..6ca5bac 100644
--- a/src/uk/me/parabola/mkgmap/general/MapCollector.java
+++ b/src/uk/me/parabola/mkgmap/general/MapCollector.java
@@ -73,4 +73,10 @@ public interface MapCollector {
 	 * @param exceptMask For exceptions eg. no-left-turn except for buses.
 	 */
 	public void addRestriction(CoordNode fromNode, CoordNode toNode, CoordNode viaNode, byte exceptMask);
+
+	/**
+	 * Add a through route to the map. 
+	 *
+	 */
+	public void addThroughRoute(long junctionNodeId, long roadIdA, long roadIdB);
 }
diff --git a/src/uk/me/parabola/mkgmap/general/MapDetails.java b/src/uk/me/parabola/mkgmap/general/MapDetails.java
index a5707c5..30e638a 100644
--- a/src/uk/me/parabola/mkgmap/general/MapDetails.java
+++ b/src/uk/me/parabola/mkgmap/general/MapDetails.java
@@ -119,6 +119,10 @@ public class MapDetails implements MapCollector, MapDataSource {
 		roadNetwork.addRestriction(fromNode, toNode, viaNode, exceptMask);
 	}
 
+	public void addThroughRoute(long junctionNodeId, long roadIdA, long roadIdB) {
+		roadNetwork.addThroughRoute(junctionNodeId, roadIdA, roadIdB);
+	}
+
 	/**
 	 * Add the given point to the total bounds for the map.
 	 *
diff --git a/src/uk/me/parabola/mkgmap/general/RoadNetwork.java b/src/uk/me/parabola/mkgmap/general/RoadNetwork.java
index 6be2d0c..2740665 100644
--- a/src/uk/me/parabola/mkgmap/general/RoadNetwork.java
+++ b/src/uk/me/parabola/mkgmap/general/RoadNetwork.java
@@ -285,4 +285,10 @@ public class RoadNetwork {
 		vn.addRestriction(new RouteRestriction(fa, ta, exceptMask));
     }
 
+	public void addThroughRoute(long junctionNodeId, long roadIdA, long roadIdB) {
+		RouteNode node = nodes.get(junctionNodeId);
+		assert node != null :  "Can't find node with id " + junctionNodeId;
+
+		node.addThroughRoute(roadIdA, roadIdB);
+	}
 }
diff --git a/src/uk/me/parabola/mkgmap/main/StyleTester.java b/src/uk/me/parabola/mkgmap/main/StyleTester.java
index 998b195..cf994ae 100644
--- a/src/uk/me/parabola/mkgmap/main/StyleTester.java
+++ b/src/uk/me/parabola/mkgmap/main/StyleTester.java
@@ -713,6 +713,9 @@ public class StyleTester implements OsmConverter {
 
 		public void addRestriction(CoordNode fromNode, CoordNode toNode, CoordNode viaNode, byte exceptMask) {
 		}
+
+		public void addThroughRoute(long junctionNodeId, long roadIdA, long roadIdB) {
+		}
 	}
 
 	/**
@@ -753,6 +756,9 @@ public class StyleTester implements OsmConverter {
 		public void addRestriction(CoordNode fromNode, CoordNode toNode, CoordNode viaNode, byte exceptMask) {
 		}
 
+		public void addThroughRoute(long junctionNodeId, long roadIdA, long roadIdB) {
+		}
+
 		public long getStart() {
 			return start;
 		}
diff --git a/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java b/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
index 784f238..0ea50ed 100644
--- a/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
+++ b/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
@@ -22,6 +22,7 @@ import java.util.HashMap;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Properties;
 import java.util.regex.Pattern;
 
@@ -80,6 +81,8 @@ public class StyledConverter implements OsmConverter {
 	// Coord corresponding to the restrictions' 'via' node
 	private final Map<Coord, List<RestrictionRelation>> restrictions = new IdentityHashMap<Coord, List<RestrictionRelation>>();
 
+	private final List<Relation> throughRouteRelations = new ArrayList<Relation>();
+
 	// originalWay associates Ways that have been created due to
 	// splitting or clipping with the Ways that they were derived
 	// from
@@ -386,6 +389,43 @@ public class StyledConverter implements OsmConverter {
 				rr.addRestriction(collector);
 			}
 		}
+
+		for(Relation relation : throughRouteRelations) {
+			Node node = null;
+			Way w1 = null;
+			Way w2 = null;
+			for(Map.Entry<String,Element> member : relation.getElements()) {
+				if(member.getValue() instanceof Node) {
+					if(node == null)
+						node = (Node)member.getValue();
+					else
+						log.error("Through route relation " + relation.toBrowseURL() + " has more than 1 node");
+				}
+				else if(member.getValue() instanceof Way) {
+					Way w = (Way)member.getValue();
+					if(w1 == null)
+						w1 = w;
+					else if(w2 == null)
+						w2 = w;
+					else
+						log.error("Through route relation " + relation.toBrowseURL() + " has more than 2 ways");
+				}
+			}
+			Integer nodeId = null;
+			if(node == null)
+				log.error("Through route relation " + relation.toBrowseURL() + " is missing the junction node");
+			else {
+				nodeId = nodeIdMap.get(node.getLocation());
+				if(nodeId == null)
+					log.error("Through route relation " + relation.toBrowseURL() + " junction node is not a routing node");
+			}
+			if(w1 == null || w2 == null)
+				log.error("Through route relation " + relation.toBrowseURL() + " should have 2 ways that meet at the junction node");
+			if(nodeId != null && w1 != null && w2 != null) {
+				log.info("Adding through route for ways " + w1.toBrowseURL() + " and " + w2.toBrowseURL() + " at " + node.getLocation().toOSMURL()); 
+				collector.addThroughRoute(nodeId, w1.getId(), w2.getId());
+			}
+		}
 	}
 
 	/**
@@ -411,6 +451,9 @@ public class StyledConverter implements OsmConverter {
 				lrr.add(rr);
 			}
 		}
+		else if("through_route".equals(relation.getTag("type"))) {
+			throughRouteRelations.add(relation);
+		}
 	}
 
 	private void addLine(Way way, GType gt) {
diff --git a/src/uk/me/parabola/mkgmap/reader/overview/OverviewMapDataSource.java b/src/uk/me/parabola/mkgmap/reader/overview/OverviewMapDataSource.java
index dd5a54e..2d6e541 100644
--- a/src/uk/me/parabola/mkgmap/reader/overview/OverviewMapDataSource.java
+++ b/src/uk/me/parabola/mkgmap/reader/overview/OverviewMapDataSource.java
@@ -144,6 +144,10 @@ public class OverviewMapDataSource extends MapperBasedMapDataSource
 		getRoadNetwork().addRestriction(fromNode, toNode, viaNode, exceptMask);
 	}
 
+	public void addThroughRoute(long junctionNodeId, long roadIdA, long roadIdB) {
+		getRoadNetwork().addThroughRoute(junctionNodeId, roadIdA, roadIdB);
+	}
+
 	public int getShift() {
 		return 24 - (topBits - 1);
 	}
_______________________________________________
mkgmap-dev mailing list
mkgmap-dev@lists.mkgmap.org.uk
http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev

Reply via email to