This patch modifies the turn heading adjustment code so that the GPS
will tell you to turn when you are routing from a main road to a side
road in situations like this:
S
S ^
S |
MMMMMMMMMMMMMM |
M |
M |
M
M
You can see that the main road goes around a left corner and the side
road carries straight on. Without this patch, neither mapsource or a
Nuvi will tell you to turn right at this junction even if the side road
has a different road class/speed from the main road.
This patch also allows you to globally enable/disable the possible
adjustments by providing a bitmask with the --adjust-turn-headings
option. By default, all (currently, there are 2) kinds of adjustments
are enabled. See the help blurb for more info.
A value of 1 gives the old behaviour.
Mark
diff --git a/resources/help/en/options b/resources/help/en/options
index db69e1e..294218a 100644
--- a/resources/help/en/options
+++ b/resources/help/en/options
@@ -250,13 +250,20 @@ Miscellaneous options:
than that length will be removed. If a length is not
specified, only zero-length arcs will be removed.
---adjust-turn-headings
+--adjust-turn-headings[=BITMASK]
Where possible, ensure that turns off to side roads change
heading sufficiently so that the GPS believes that a turn is
required rather than a fork. This also avoids spurious
instructions to "keep right/left" when the road doesn't
actually fork.
+ Optional BITMASK (default value 3) allows you to specify which
+ adjustments are to be made (where necessary):
+
+ 1 = increase angle between side road and outgoing main road
+ 2 = increase angle between side road and incoming main road
+
+
--report-similar-arcs
Issue a warning when more than one arc connects two nodes and
the ways that the arcs are derived from contain identical
diff --git a/src/uk/me/parabola/imgfmt/app/net/RouteNode.java b/src/uk/me/parabola/imgfmt/app/net/RouteNode.java
index f57f08c..45d6010 100644
--- a/src/uk/me/parabola/imgfmt/app/net/RouteNode.java
+++ b/src/uk/me/parabola/imgfmt/app/net/RouteNode.java
@@ -348,26 +348,41 @@ public class RouteNode implements Comparable<RouteNode> {
return false;
}
- public void tweezeArcs() {
+ private static int ATH_OUTGOING = 1;
+ private static int ATH_INCOMING = 2;
+
+ public static final int ATH_DEFAULT_MASK = ATH_OUTGOING | ATH_INCOMING;
+
+ public void tweezeArcs(int mask) {
if(arcs.size() >= 3) {
- final int maxMainRoadHeadingChange = 120;
- final int minSideRoadHeadingChange = 45; // min change to be a "turn"
// detect the "shallow turn" scenario where at a junction
- // on some road, the side road leaves the original road at
- // a very shallow angle and the GPS says "keep right/left"
- // when it would be better if it said "turn right/left"
+ // on some "main" road, the side road leaves the main
+ // road at a very shallow angle and the GPS says "keep
+ // right/left" when it would be better if it said "turn
+ // right/left"
+
+ // also helps to produce a turn instruction when the main
+ // road bends sharply but the side road keeps close to the
+ // original heading
// the code tries to detect a pair of arcs (the "incoming"
- // arc and the "continuing" arc) that are the "main road"
+ // arc and the "outgoing" arc) that are the "main road"
// and the remaining arc (called the "other" arc) which is
// the "side road"
// having worked out the roles for the arcs, the heuristic
// applied is that if the main road doesn't change its
// heading by more than maxMainRoadHeadingChange, ensure
- // that the side road heading differs from the continuing
- // heading by at least minSideRoadHeadingChange
+ // that the side road heading differs from the outgoing
+ // heading by at least
+ // minDiffBetweenOutgoingAndOtherArcs and the side road
+ // heading differs from the incoming heading by at least
+ // minDiffBetweenIncomingAndOtherArcs
+
+ final int maxMainRoadHeadingChange = 120;
+ final int minDiffBetweenOutgoingAndOtherArcs = 45;
+ final int minDiffBetweenIncomingAndOtherArcs = 50;
for(RouteArc inArc : incomingArcs) {
@@ -377,33 +392,33 @@ public class RouteNode implements Comparable<RouteNode> {
}
int inHeading = inArc.getFinalHeading();
- // determine the outgoing (continuing) arc that is
- // likely to be the same road as the incoming arc
- RouteArc continuingArc = null;
- for(RouteArc ca : arcs) {
- if(ca.getDest() != inArc.getSource()) {
+ // 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((ca.isForward() || !ca.getRoadDef().isOneway()) &&
- possiblySameRoad(inArc, ca)) {
- continuingArc = ca;
+ if((oa.isForward() || !oa.getRoadDef().isOneway()) &&
+ possiblySameRoad(inArc, oa)) {
+ outArc = oa;
break;
}
}
}
- // if we did not find the continuing arc, give up with
+ // if we did not find the outgoing arc, give up with
// this incoming arc
- if(continuingArc == null) {
+ if(outArc == null) {
//log.info("Can't continue road " + inArc.getRoadDef() + " at " + coord.toOSMURL());
continue;
}
- int continuingHeading = continuingArc.getInitialHeading();
- int mainHeadingDelta = continuingHeading - inHeading;
+ int outHeading = outArc.getInitialHeading();
+ int mainHeadingDelta = outHeading - inHeading;
while(mainHeadingDelta > 180)
mainHeadingDelta -= 360;
while(mainHeadingDelta < -180)
mainHeadingDelta += 360;
- //log.info(inArc.getRoadDef() + " continues to " + continuingArc.getRoadDef() + " with a heading change of " + mainHeadingDelta + " at " + coord.toOSMURL());
+ //log.info(inArc.getRoadDef() + " continues to " + outArc.getRoadDef() + " with a heading change of " + mainHeadingDelta + " at " + coord.toOSMURL());
if(Math.abs(mainHeadingDelta) > maxMainRoadHeadingChange) {
// if the continuation road heading change is
@@ -416,12 +431,14 @@ public class RouteNode implements Comparable<RouteNode> {
// for each other arc leaving this node, tweeze
// its heading if its heading change from the
- // continuation heading is less than
- // minSideRoadHeadingChange
+ // outgoing heading is less than
+ // minDiffBetweenOutgoingAndOtherArcs or its
+ // heading change from the incoming heading is
+ // less than minDiffBetweenIncomingAndOtherArcs
if(otherArc.getDest() == inArc.getSource() ||
- otherArc == continuingArc) {
- // we're looking at the incoming or continuing
+ otherArc == outArc) {
+ // we're looking at the incoming or outgoing
// arc, ignore it
continue;
}
@@ -433,7 +450,7 @@ public class RouteNode implements Comparable<RouteNode> {
}
if(possiblySameRoad(inArc, otherArc) ||
- possiblySameRoad(continuingArc, otherArc)) {
+ possiblySameRoad(outArc, otherArc)) {
// not obviously a different road so give up
continue;
}
@@ -448,29 +465,46 @@ public class RouteNode implements Comparable<RouteNode> {
continue;
}
- int outHeading = otherArc.getInitialHeading();
- int outHeadingDelta = outHeading - continuingHeading;
- while(outHeadingDelta > 180)
- outHeadingDelta -= 360;
- while(outHeadingDelta < -180)
- outHeadingDelta += 360;
- // log.warn("Found turn ("+ outHeadingDelta + " deg) from " + inArc.getRoadDef() + " to " + otherArc.getRoadDef() + " at " + coord.toOSMURL());
- if(outHeadingDelta > 0 &&
- outHeadingDelta < minSideRoadHeadingChange) {
- int newHeading = continuingHeading + minSideRoadHeadingChange;
+ int otherHeading = otherArc.getInitialHeading();
+ int outToOtherDelta = otherHeading - outHeading;
+ while(outToOtherDelta > 180)
+ outToOtherDelta -= 360;
+ while(outToOtherDelta < -180)
+ outToOtherDelta += 360;
+ int inToOtherDelta = otherHeading - inHeading;
+ while(inToOtherDelta > 180)
+ inToOtherDelta -= 360;
+ while(inToOtherDelta < -180)
+ inToOtherDelta += 360;
+
+ int newHeading = otherHeading;
+ if(outToOtherDelta > 0) {
+ // side road to the right
+ if((mask & ATH_OUTGOING) != 0 &&
+ outToOtherDelta < minDiffBetweenOutgoingAndOtherArcs)
+ newHeading = outHeading + minDiffBetweenOutgoingAndOtherArcs;
+ else if((mask & ATH_INCOMING) != 0 &&
+ inToOtherDelta < minDiffBetweenIncomingAndOtherArcs)
+ newHeading = inHeading + minDiffBetweenIncomingAndOtherArcs;
+
if(newHeading > 180)
newHeading -= 360;
- otherArc.setInitialHeading(newHeading);
- log.info("Adjusting turn heading from " + outHeading + " to " + newHeading + " at junction of " + inArc.getRoadDef() + " and " + otherArc.getRoadDef() + " at " + coord.toOSMURL());
}
- else if(outHeadingDelta < 0 &&
- outHeadingDelta > -minSideRoadHeadingChange) {
- int newHeading = continuingHeading - minSideRoadHeadingChange;
+ else if(outToOtherDelta < 0) {
+ // side road to the left
+ if((mask & ATH_OUTGOING) != 0 &&
+ outToOtherDelta > -minDiffBetweenOutgoingAndOtherArcs)
+ newHeading = outHeading - minDiffBetweenOutgoingAndOtherArcs;
+ else if((mask & ATH_INCOMING) != 0 &&
+ inToOtherDelta > -minDiffBetweenIncomingAndOtherArcs)
+ newHeading = inHeading - minDiffBetweenIncomingAndOtherArcs;
if(newHeading < -180)
newHeading += 360;
+ }
+ if(newHeading != otherHeading) {
otherArc.setInitialHeading(newHeading);
- log.info("Adjusting turn heading from " + outHeading + " to " + newHeading + " at junction of " + inArc.getRoadDef() + " and " + otherArc.getRoadDef() + " at " + coord.toOSMURL());
+ log.info("Adjusting turn heading from " + otherHeading + " to " + newHeading + " at junction of " + inArc.getRoadDef() + " and " + otherArc.getRoadDef() + " at " + coord.toOSMURL());
}
}
}
diff --git a/src/uk/me/parabola/mkgmap/general/RoadNetwork.java b/src/uk/me/parabola/mkgmap/general/RoadNetwork.java
index efd08b6..6be2d0c 100644
--- a/src/uk/me/parabola/mkgmap/general/RoadNetwork.java
+++ b/src/uk/me/parabola/mkgmap/general/RoadNetwork.java
@@ -62,7 +62,7 @@ public class RoadNetwork {
private final List<RoadDef> roadDefs = new ArrayList<RoadDef>();
private List<RouteCenter> centers = new ArrayList<RouteCenter>();
- private boolean adjustTurnHeadings;
+ private int adjustTurnHeadings = 0;
private boolean checkRoundabouts;
private boolean checkRoundaboutFlares;
private int maxFlareLengthRatio = 0;
@@ -71,7 +71,13 @@ public class RoadNetwork {
private int reportDeadEnds = 0;
public void config(EnhancedProperties props) {
- adjustTurnHeadings = props.getProperty("adjust-turn-headings", false);
+ String ath = props.getProperty("adjust-turn-headings");
+ if(ath != null) {
+ if(ath.length() > 0)
+ adjustTurnHeadings = Integer.decode(ath);
+ else
+ adjustTurnHeadings = RouteNode.ATH_DEFAULT_MASK;
+ }
checkRoundabouts = props.getProperty("check-roundabouts", false);
checkRoundaboutFlares = props.getProperty("check-roundabout-flares", false);
maxFlareLengthRatio = props.getProperty("max-flare-length-ratio", 0);
@@ -239,8 +245,8 @@ public class RoadNetwork {
if(reportDeadEnds != 0)
node.reportDeadEnds(reportDeadEnds);
}
- if(adjustTurnHeadings)
- node.tweezeArcs();
+ if(adjustTurnHeadings != 0)
+ node.tweezeArcs(adjustTurnHeadings);
nod1.addNode(node);
}
centers = nod1.subdivide();
_______________________________________________
mkgmap-dev mailing list
[email protected]
http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev