This is an automated email from the git hooks/post-receive script.

sebastic pushed a commit to branch master
in repository mkgmap.

commit 503a1714505d40cd33bede2ae02dac75525ce4f6
Author: Bas Couwenberg <sebas...@xs4all.nl>
Date:   Sun Nov 1 10:57:41 2015 +0100

    Imported Upstream version 0.0.0+svn3649
---
 build.xml                                          |   2 +-
 doc/options.txt                                    |  10 +-
 resources/LocatorConfig.xml                        |   3 +-
 resources/help/en/options                          |   9 +
 resources/mkgmap-version.properties                |   4 +-
 resources/styles/default/inc/access                |   4 +
 resources/styles/default/lines                     |  11 +-
 src/uk/me/parabola/imgfmt/app/Coord.java           |  21 +-
 .../me/parabola/imgfmt/app/net/AngleChecker.java   | 424 +++++++++++++++++++++
 src/uk/me/parabola/imgfmt/app/net/RoadNetwork.java |  50 +--
 src/uk/me/parabola/imgfmt/app/net/RouteArc.java    |  18 +-
 src/uk/me/parabola/imgfmt/app/net/RouteNode.java   | 323 +---------------
 .../mkgmap/osmstyle/housenumber/ExtNumbers.java    |   1 +
 .../osmstyle/housenumber/HousenumberGenerator.java |   7 +-
 .../osmstyle/housenumber/HousenumberGroup.java     |   3 +
 .../osmstyle/housenumber/HousenumberIvl.java       |   8 +-
 src/uk/me/parabola/mkgmap/reader/osm/Element.java  |  33 +-
 .../mkgmap/reader/osm/bin/OsmBinHandler.java       |  16 +-
 .../mkgmap/reader/osm/o5m/O5mBinHandler.java       |   4 +-
 .../mkgmap/reader/osm/xml/Osm5XmlHandler.java      |  12 +-
 .../me/parabola/mkgmap/reader/osm/ElementTest.java |  23 ++
 21 files changed, 602 insertions(+), 384 deletions(-)

diff --git a/build.xml b/build.xml
index 5ad2fc9..eca68f0 100644
--- a/build.xml
+++ b/build.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0"?>
 <!--
-               File: build.xml
+               File: build.xml 
                
                Copyright (C) 2006, 2012 mkgmap contributors
                
diff --git a/doc/options.txt b/doc/options.txt
index adc280d..ae3795f 100644
--- a/doc/options.txt
+++ b/doc/options.txt
@@ -434,7 +434,8 @@ specified, only zero-length arcs will be removed.
  
 
 ;--adjust-turn-headings[=BITMASK]
-:      Where possible, ensure that turns off to side roads change
+:  now ignored, former explanation:
+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
@@ -445,6 +446,13 @@ 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
 
+;--cycle-map
+:  Tells mkgmap that the map is for cyclists. This assumes that
+different vehicles are different kinds of bicycles, e.g. a way
+with mkgmap:car=yes and mkgmap:bicycle=no may be a road that is 
+good for racing bikes, but not for other cyclists.
+This allows to optimise sharp angles at junctions of those roads. 
+Don't use with the default style as that is a general style!
 
 ;--report-similar-arcs
 :      Issue a warning when more than one arc connects two nodes and
diff --git a/resources/LocatorConfig.xml b/resources/LocatorConfig.xml
index d2e74ba..b7a4872 100644
--- a/resources/LocatorConfig.xml
+++ b/resources/LocatorConfig.xml
@@ -147,9 +147,10 @@
                <variant>BV</variant>
                <variant>BVT</variant>
        </country>
-       <country name="Brazil" abr="BRA" streetBeforeHousenumber="true">
+       <country name="Brasil" abr="BRA" streetBeforeHousenumber="true">
                <variant>BR</variant>
                <variant>BRA</variant>
+               <variant>Brazil</variant>
        </country>
        <country name="British Indian Ocean Territory" abr="IOT">
                <variant>IO</variant>
diff --git a/resources/help/en/options b/resources/help/en/options
index 349317b..9f16789 100644
--- a/resources/help/en/options
+++ b/resources/help/en/options
@@ -437,6 +437,7 @@ Miscellaneous options:
        specified, only zero-length arcs will be removed.
 
 --adjust-turn-headings[=BITMASK]
+       Now ignored, former usage:      
        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
@@ -449,6 +450,14 @@ Miscellaneous options:
        1 = increase angle between side road and outgoing main road
        2 = increase angle between side road and incoming main road
 
+--cycle-map
+       Tells mkgmap that the map is for cyclists. This assumes that
+       different vehicles are different kinds of bicycles, e.g. a way
+       with mkgmap:car=yes and mkgmap:bicycle=no may be a road that is 
+       good for racing bikes, but not for other cyclists.
+       This allows to optimise sharp angles at junctions of those roads. 
+       Don't use with the default style as that is a general style!
+       
 --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/resources/mkgmap-version.properties 
b/resources/mkgmap-version.properties
index 20db163..b3ec584 100644
--- a/resources/mkgmap-version.properties
+++ b/resources/mkgmap-version.properties
@@ -1,2 +1,2 @@
-svn.version: 3643
-build.timestamp: 2015-09-19T08:13:00+0100
+svn.version: 3649
+build.timestamp: 2015-10-27T06:51:19+0000
diff --git a/resources/styles/default/inc/access 
b/resources/styles/default/inc/access
index 7023296..d0fd1fa 100644
--- a/resources/styles/default/inc/access
+++ b/resources/styles/default/inc/access
@@ -72,3 +72,7 @@ access=* { addaccess '${access}' }
 # Check for carpool lane (they are not really supported yet so these lines are 
commented)
 # hov=* { add carpool='${hov}' }
 # (carpool=yes | carpool=designated | carpool=permissive | carpool=official)   
  { set mkgmap:carpool=yes }
+
+# Don't route through highway=construction, they are considered unusable 
+highway=construction {setaccess no}
+
diff --git a/resources/styles/default/lines b/resources/styles/default/lines
index ab89b5a..2b3dedd 100644
--- a/resources/styles/default/lines
+++ b/resources/styles/default/lines
@@ -18,10 +18,13 @@ highway=* & name=* { set mkgmap:street='${name}' }
 # Mark highways with the toll flag
 highway=* & (toll=yes|toll=true) { set mkgmap:toll=yes }
 
-# Hide proposed ways
-highway=proposed {delete highway;delete junction}
+# Hide proposed ways 
+(highway=proposed | highway=proposal | highway=planned | highway ~ 
'.*proposed.*') {delete highway;delete junction}
 # Hide removed ways
-highway=razed {deletealltags}
+(highway=razed | highway=dismantled) {deletealltags}
+# Hide other non-existent ways
+(highway=unbuilt | highway=neverbuilt | highway=rejected | highway ~ 'x-.*') 
{delete highway;delete junction}
+
 # Hide unaccessible tunnels
 highway=* & tunnel=yes & (access=private|access=no)
 & foot!=* & bicycle!=* {delete highway;delete junction}
@@ -106,7 +109,7 @@ junction=roundabout [0x0c road_class=0 road_speed=1 
resolution 22]
 
 # Ways that may or may not be useable
 
-# Treat ways under construction almost as highway=path
+# Treat ways under construction almost as highway=path, see also extra rule in 
inc/access
 highway=construction { add mkgmap:dead-end-check = false; }
 [0x16 road_class=0 road_speed=0 resolution 23]
 
diff --git a/src/uk/me/parabola/imgfmt/app/Coord.java 
b/src/uk/me/parabola/imgfmt/app/Coord.java
index 89dc0f7..b2be354 100644
--- a/src/uk/me/parabola/imgfmt/app/Coord.java
+++ b/src/uk/me/parabola/imgfmt/app/Coord.java
@@ -49,6 +49,7 @@ public class Coord implements Comparable<Coord> {
        private final static short PART_OF_SHAPE2 = 0x0100; // use only in 
ShapeMerger
        private final static short END_OF_WAY = 0x0200; // use only in 
WrongAngleFixer
        private final static short HOUSENUMBER_NODE = 0x0400; // start/end of 
house number interval
+       private final static short ADDED_HOUSENUMBER_NODE = 0x0800; // node was 
added for house numbers
        
        public final static int HIGH_PREC_BITS = 30;
        public final static int DELTA_SHIFT = 6;
@@ -88,7 +89,7 @@ public class Coord implements Comparable<Coord> {
                int lon30 = toBit30(longitude);
                this.latDelta = (byte) ((this.latitude << 6) - lat30); 
                this.lonDelta = (byte) ((this.longitude << 6) - lon30);
-               
+
                // verify math
                assert (this.latitude << 6) - latDelta == lat30;
                assert (this.longitude << 6) - lonDelta == lon30;
@@ -340,7 +341,6 @@ public class Coord implements Comparable<Coord> {
        }
        
        /**
-        * Set or unset flag for {@link WrongAngleFixer} 
         * @param b true or false
         */
        public void setNumberNode(boolean b) {
@@ -350,6 +350,23 @@ public class Coord implements Comparable<Coord> {
                        this.flags &= ~HOUSENUMBER_NODE; 
        }
        
+       /**
+        * @return if this is the beginning/end of a house number interval 
+        */
+       public boolean isAddedNumberNode(){
+               return (flags & ADDED_HOUSENUMBER_NODE) != 0;
+       }
+       
+       /**
+        * @param b true or false
+        */
+       public void setAddedNumberNode(boolean b) {
+               if (b) 
+                       this.flags |= ADDED_HOUSENUMBER_NODE;
+               else 
+                       this.flags &= ~ADDED_HOUSENUMBER_NODE; 
+       }
+       
        public int hashCode() {
                // Use a factor for latitude to span over the whole integer 
range:
                // max lat: 4194304
diff --git a/src/uk/me/parabola/imgfmt/app/net/AngleChecker.java 
b/src/uk/me/parabola/imgfmt/app/net/AngleChecker.java
new file mode 100644
index 0000000..b938058
--- /dev/null
+++ b/src/uk/me/parabola/imgfmt/app/net/AngleChecker.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2015
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 or
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+package uk.me.parabola.imgfmt.app.net;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import uk.me.parabola.log.Logger;
+import uk.me.parabola.util.EnhancedProperties;
+
+/**
+ * Find sharp angles at junctions. The Garmin routing algorithm doesn't
+ * like to route on roads building a sharp angle. It adds a time penalty
+ * from 30 to 150 seconds and often prefers small detours instead.
+ * The penalty depends on the road speed and the vehicle, for pedestrian
+ * mode it is zero, for bicycles it is rather small, for cars it is high.
+ * The sharp angles typically don't exist in the real world, they are
+ * caused by the simplifications done by mappers.
+ * 
+ * Maps created for cyclists typically "abuse" the car routing for racing 
+ * bikes, but in this scenario the time penalties are much too high,
+ * and detours are likely.
+ * 
+ * This method tries to modify the initial heading values of the arcs 
+ * which are used to calculate the angles. Where possible, the values are
+ * changed so that angles appear larger. 
+ * 
+ * @author Gerd Petermann
+ *
+ */
+public class AngleChecker {
+       private static final Logger log = Logger.getLogger(AngleChecker.class);
+
+       private boolean ignoreSharpAngles;
+       private boolean cycleMap;
+//     private final Coord test = new Coord(48.074815,16.272771);
+
+       private final int MIN_ANGLE = 0x10;
+       private final int MIN_LOW_SPEED_ANGLE = 0x20;
+
+       private int mask;
+
+       // helper class to collect multiple arcs with (nearly) the same initial 
headings
+       private class ArcGroup {
+               float initialHeading;
+               byte imgHeading;
+               int isOneWayTrueCount;
+               int isForwardTrueCount;
+               int maxRoadSpeed;
+               byte orAccessMask;
+               HashSet<RoadDef> roadDefs = new HashSet<>();
+               
+               List<RouteArc> arcs = new ArrayList<>();
+               public void addArc(RouteArc arc) {
+                       arcs.add(arc);
+                       if (arc.getRoadDef().isOneway())
+                               isOneWayTrueCount++;
+                       if (arc.isForward())
+                               isForwardTrueCount++;
+                       if (arc.getRoadDef().getRoadSpeed() > maxRoadSpeed)
+                               maxRoadSpeed = arc.getRoadDef().getRoadSpeed();
+                       orAccessMask |= arc.getRoadDef().getAccess();
+                       roadDefs.add(arc.getRoadDef());
+               }
+               public float getInitialHeading() {
+                       return initialHeading;
+               }
+               public boolean isOneway() {
+                       return isOneWayTrueCount == arcs.size();
+               }
+               public boolean isForward() {
+                       return isForwardTrueCount == arcs.size();
+               }
+               /**
+                * @return
+                */
+               public void setInitialHeading(float modIH) {
+                       while (modIH > 180)
+                               modIH -= 360; 
+                       while (modIH < -180)
+                               modIH += 360; 
+                       initialHeading = modIH;
+                       imgHeading = (byte) 
(RouteArc.directionFromDegrees(initialHeading) & mask); 
+                       
+                       for (RouteArc arc : arcs){
+                               arc.setInitialHeading(modIH);
+                       }
+               }
+               
+               public String toString(){
+                       return arcs.get(0).toString();
+               }
+       }
+       
+       public void config(EnhancedProperties props) {
+               // undocumented option - usually used for debugging only
+               ignoreSharpAngles = props.getProperty("ignore-sharp-angles", 
false);
+               cycleMap = props.getProperty("cycle-map", false);
+//             float a = 0;
+//             for (int i = 0; i <= 1440; i++){
+//                     int ar = (int) Math.round(a * 256.0 / 360);
+//                     int am = ar & 0xf0;
+//                     log.error(a,ar,"0x" + Integer.toHexString(am));
+//                     a +=0.25;
+//                     if (a >= 180)
+//                             a -= 360;
+//             }
+               return;
+       }
+
+       public void check(Map<Integer, RouteNode> nodes) {
+               if (!ignoreSharpAngles){
+                       byte sharpAnglesCheckMask = cycleMap ? (byte) (0xff & 
~AccessTagsAndBits.FOOT) : AccessTagsAndBits.BIKE;
+
+                       for (RouteNode node : nodes.values()){
+                               mask = 0xf0; // we assume compacted format
+                               fixSharpAngles(node, sharpAnglesCheckMask);     
                        
+                       }
+               }
+       }
+
+       public void fixSharpAngles(RouteNode node, byte sharpAnglesCheckMask) {
+
+               // get direct arcs leaving the node
+               List<ArcGroup> arcGroups = buildArcGroups(node);
+
+               int n = arcGroups.size();
+               if (n <= 1)
+                       return;
+               // sort the arcs by initial heading 
+               Collections.sort(arcGroups, new Comparator<ArcGroup>() {
+                               public int compare(ArcGroup ag1, ArcGroup ag2) {
+                                       if (ag1.initialHeading < 
ag2.initialHeading)
+                                               return -1;
+                                       if (ag1.initialHeading > 
ag2.initialHeading)
+                                               return 1;
+                                       return 0;
+                               }
+                       });
+               
+               class AngleAttr {
+                       int angle;
+                       int maskedAngle;
+                       int maskedMinAngle = MIN_ANGLE;
+                       boolean noAccess;
+                       
+                       int maskedDeltaToMin(){
+                               return maskedAngle - maskedMinAngle;
+                       }
+                       void setMaskedMinAngle(int maskedMinAngle){
+                               this.maskedMinAngle = maskedMinAngle;
+                       }
+                       
+                       public String toString(){
+                               return angle + "° " + maskedAngle + " " + 
maskedMinAngle + " " + noAccess;
+                       }
+               }
+               
+               // step one: calculate the existing angles
+               AngleAttr[] angles = new AngleAttr[n];
+               for (int i = 0; i < n; i++){
+                       ArcGroup ag1 = arcGroups.get(i);
+                       ArcGroup ag2 = arcGroups.get(i+1 < n ? i+1 : 0);
+                       AngleAttr angleAttr = new AngleAttr(); 
+                       angles[i] = angleAttr;
+                       angleAttr.angle = Math.round(ag2.getInitialHeading() - 
ag1.getInitialHeading());
+                       angleAttr.maskedAngle = ag2.imgHeading - ag1.imgHeading;
+                       if (i + 1 >= n){
+                               angleAttr.angle += 360;
+                       }
+                       if (angleAttr.maskedAngle < 0)
+                               angleAttr.maskedAngle += 256;
+
+                       if (ag1.isOneway() && ag1.isForward()){
+                               // the "incoming" arc is a wrong direction 
oneway
+                               angleAttr.noAccess = true;
+                       } else if (ag2.isOneway() && ag2.isForward() == false){
+                               // the "outgoing" arc is a wrong direction 
oneway
+                               angleAttr.noAccess = true;
+                       }
+                       
+//                     if (node.getCoord().distance(test) < 2){
+//                             if (angleAttr.angle == 20){
+//                                     angleAttr.maskedMinAngle = 0x30;
+//                                     continue;
+//                             }
+//                     }
+                       int sumSpeeds = ag1.maxRoadSpeed + ag2.maxRoadSpeed;
+                       if (sumSpeeds <= 1)
+                               continue;
+                       byte pathAccessMask = (byte) (ag1.orAccessMask & 
ag2.orAccessMask);
+                       if (pathAccessMask == 0){
+                               // no common vehicle allowed on both arcs
+                               angleAttr.noAccess = true;
+                       }
+                       if (angleAttr.noAccess)
+                               continue;
+                       int maskedMinAngle = MIN_LOW_SPEED_ANGLE;
+                       // the Garmin algorithm sees rounded values, so the 
thresholds are probably 
+                       // near 22.5 (0x10), 45(0x20), 67.5 (0x30), 90, 112.5 
(0x40)
+
+                       // the following code doesn't seem to improve anything, 
I leave it as comment 
+                       // for further experiments.
+//                     if (cycleMap){
+//                             if (sumSpeeds >= 14)
+//                                     maskedMinAngle = 0x80;
+//                             if (sumSpeeds >= 12)
+//                                     maskedMinAngle = 0x70;
+//                             if (sumSpeeds >= 10)
+//                                     maskedMinAngle = 0x60;
+//                             if (sumSpeeds >= 8)
+//                                     maskedMinAngle = 0x50;
+//                             else if (sumSpeeds >= 6)
+//                                     maskedMinAngle = 0x40;
+//                             else if (sumSpeeds >= 4)
+//                                     maskedMinAngle = 0x30;
+//                     }
+                       angleAttr.setMaskedMinAngle(maskedMinAngle);
+                       
+                       if (angleAttr.maskedDeltaToMin() >= 0)
+                               continue;
+
+                       String ignoredReason = null;
+                       if (pathAccessMask == AccessTagsAndBits.FOOT)
+                               ignoredReason = "because it can only be used by 
pedestrians";
+                       else if ((pathAccessMask & sharpAnglesCheckMask) == 0)
+                               ignoredReason = "because it can not be used by 
bike";
+                       else if (ag1.isOneway() && ag2.isOneway()){
+                               // both arcs are one-ways, probably the road 
splits here 
+                               // to avoid the sharp angles we are looking for
+                               ignoredReason = "because it seems to be a flare 
road";
+                       }               
+                       else if (ag1.roadDefs.size() == 1 && 
ag2.roadDefs.size() == 1 && ag1.roadDefs.containsAll(ag2.roadDefs)){
+                               ignoredReason = "because both arcs belong to 
the same road";
+                       }
+                       if (ignoredReason != null){
+                               if (log.isInfoEnabled()){
+                                       String sharpAngle = "sharp angle "  + 
angleAttr.angle + "° at " + node.getCoord().toDegreeString();
+                                       log.info(sharpAngle, 
"headings",getCompassBearing(ag1.getInitialHeading()) , 
getCompassBearing(ag2.getInitialHeading()),"speeds",ag1.maxRoadSpeed, 
ag2.maxRoadSpeed);
+                                       log.info("ignoring", sharpAngle, 
ignoredReason);
+                               }
+                               angleAttr.setMaskedMinAngle(MIN_ANGLE);
+                               angleAttr.noAccess = true;
+                       }
+               }
+               
+               for (int i = 0; i < n; i++){
+                       AngleAttr aa = angles[i];
+                       if (aa.maskedAngle >= aa.maskedMinAngle || aa.noAccess)
+                               continue;
+                       int oldAngle = aa.angle;
+                       ArcGroup ag1 = arcGroups.get(i);
+                       ArcGroup ag2 = arcGroups.get(i+1 < n ? i+1 : 0);
+                       String sharpAngle = "";
+                       if (log.isInfoEnabled()){
+                               sharpAngle = "sharp angle "  + aa.angle + "° at 
" + node.getCoord().toDegreeString();
+                               log.info(sharpAngle, 
"headings",getCompassBearing(ag1.getInitialHeading()) , 
getCompassBearing(ag2.getInitialHeading()),"speeds",ag1.maxRoadSpeed, 
ag2.maxRoadSpeed);
+                       }
+
+                       // XXX restrictions ?
+                       boolean fixed = false;
+                       int wantedIncrement = Math.abs(aa.maskedDeltaToMin()) ;
+                       AngleAttr predAA = angles[i == 0 ? n - 1 : i - 1]; 
+                       AngleAttr nextAA = angles[i >= n - 1 ? 0 : i + 1];
+
+                       // we can increase the angle by changing the heading 
values of one or both arcs
+                       // find out which one to change first
+                       byte origImgDir1 = ag1.imgHeading;
+                       byte origImgDir2 = ag2.imgHeading;
+                       int origImgAngle = getImgAngle(ag1.imgHeading, 
ag2.imgHeading);
+
+                       int deltaPred = predAA.maskedDeltaToMin();
+                       int deltaNext = nextAA.maskedDeltaToMin();
+
+                       if (deltaNext > 0 && (deltaNext > deltaPred || 
deltaPred < wantedIncrement)){
+                               int usedIncrement = Math.min(wantedIncrement, 
deltaNext);
+                               float oldIH = ag2.getInitialHeading();
+                               int modIH = ag2.imgHeading + usedIncrement;
+                               if (modIH > 128)
+                                       modIH -= 256;
+                               ag2.setInitialHeading(modIH * 360/256);
+                               int modAngle = 
Math.round(ag2.getInitialHeading() - ag1.getInitialHeading());
+                               if (modAngle < 0)
+                                       modAngle += 360;                        
        
+                               int modImgAngle = getImgAngle(ag1.imgHeading, 
ag2.imgHeading);
+                               if (modImgAngle >= aa.maskedMinAngle)
+                                       fixed = true;
+                               log.info(sharpAngle, "changing arc with 
heading", getCompassBearing(oldIH), 
"->",getCompassBearing(ag2.getInitialHeading()), 
+                                               "angle is now",modAngle+"°, in 
img format:",origImgDir2,"->",ag2.imgHeading, "img angle (0-255)",origImgAngle, 
"->", modImgAngle);
+                               aa.angle = modAngle;
+                               nextAA.angle -= usedIncrement;
+                       }
+                       if (!fixed && deltaPred > 0){
+                               wantedIncrement = 
Math.abs(aa.maskedDeltaToMin());
+                               int usedIncrement = Math.min(wantedIncrement, 
deltaPred);
+                               float oldIH = ag1.getInitialHeading();
+                               int modIH = ag1.imgHeading - usedIncrement;
+                               if (modIH < -128)
+                                       modIH += 256;
+                               ag1.setInitialHeading(modIH * 360/256);
+                               int modAngle = 
Math.round(ag2.getInitialHeading() - ag1.getInitialHeading()); 
+                               if (modAngle < 0)
+                                       modAngle += 360;                        
        
+                               int modImgAngle = getImgAngle(ag1.imgHeading, 
ag2.imgHeading);
+                               if (modImgAngle >= aa.maskedMinAngle)
+                                       fixed = true;
+                               
+                               log.info(sharpAngle, "changing arc with 
heading", getCompassBearing(oldIH), "->", 
getCompassBearing(ag1.getInitialHeading()), 
+                                               "angle is now",modAngle+"°, in 
img format:",origImgDir1,"->",ag1.imgHeading, "img angle (0-255)",origImgAngle, 
"->", modImgAngle);
+                               aa.angle = modAngle;
+                               predAA.angle -= usedIncrement;
+                       }
+                       if (!fixed){
+                               if (aa.angle == oldAngle)
+                                       log.info(sharpAngle, "don't know how to 
fix it");
+                               else 
+                                       log.info(sharpAngle, "don't know how to 
enlarge it further");
+                       }
+               }
+               return;
+       }
+
+
+       /**
+        * Combine arcs with nearly the same initial heading.
+        * @param node
+        * @return
+        */
+       private List<ArcGroup> buildArcGroups(RouteNode node) {
+               List<ArcGroup> arcGroups = new ArrayList<>();
+               List<RouteArc> directArcs = new ArrayList<>();
+               for (RouteArc arc : node.getArcs()){
+                       if (arc.isDirect()){
+                               directArcs.add(arc);
+                       }
+               }
+               if (directArcs.size() < 2)
+                       return arcGroups; // should not happen
+               
+               // sort the arcs by initial heading 
+               Collections.sort(directArcs, new Comparator<RouteArc>() {
+                               public int compare(RouteArc ra1, RouteArc ra2) {
+                                       if (ra1.getInitialHeading() < 
ra2.getInitialHeading())
+                                               return -1;
+                                       if (ra1.getInitialHeading() > 
ra2.getInitialHeading())
+                                               return 1;
+                                       int d = 
Integer.compare(ra1.getPointsHash(), ra2.getPointsHash());
+                                       if (d != 0)
+                                               return d;
+                                       d = 
Long.compare(ra1.getRoadDef().getId() , ra2.getRoadDef().getId());
+                                       if (d != 0)
+                                               return d;
+                                       return d;
+                               }
+                       });
+               
+               Iterator<RouteArc> iter = directArcs.listIterator();
+               RouteArc arc1 = iter.next();
+               boolean addArc1 = false;
+               while (iter.hasNext() || addArc1){
+                       ArcGroup ag = new ArcGroup();
+                       ag.initialHeading = arc1.getInitialHeading();
+                       ag.addArc(arc1);
+                       arcGroups.add(ag);
+                       addArc1 = false;
+                       while (iter.hasNext()){
+                               RouteArc arc2 = iter.next();
+                               if (Math.abs(arc1.getInitialHeading()- 
arc2.getInitialHeading()) < 1){
+                                       if (arc1.getDest() != arc2.getDest() && 
arc1.getRoadDef().getId() != arc2.getRoadDef().getId())
+                                               log.warn("sharp angle < 1° 
at",node.getCoord().toDegreeString(),",maybe duplicated OSM way with 
bearing",getCompassBearing(arc1.getInitialHeading()));
+                                       ag.addArc(arc2);
+                               } else{
+                                       arc1 = arc2;
+                                       if (iter.hasNext() == false)
+                                               addArc1 = true;
+                                       break;
+                               }
+                       }
+               }
+               for (ArcGroup ag : arcGroups){
+                       ag.imgHeading = (byte) 
(RouteArc.directionFromDegrees(ag.initialHeading) & mask);
+               }
+               return arcGroups;
+       }
+
+       /**
+        * for log messages
+        */
+       private String getCompassBearing (float bearing){
+               float cb = (bearing + 360) % 360;
+               return Math.round(cb) + "°";
+       }
+
+       /**
+        * Debugging aid: guess what angle the Garmin algorithm is using.  
+        * @param heading1
+        * @param heading2
+        * @return
+        */
+       private int getImgAngle(byte heading1, byte heading2){
+               int angle = heading2 - heading1;
+               if (angle < 0)
+                       angle += 256;
+               if (angle > 255)
+                       angle -= 256;
+               return angle;
+       }
+       
+}
diff --git a/src/uk/me/parabola/imgfmt/app/net/RoadNetwork.java 
b/src/uk/me/parabola/imgfmt/app/net/RoadNetwork.java
index 77fe5cc..bc52d4f 100644
--- a/src/uk/me/parabola/imgfmt/app/net/RoadNetwork.java
+++ b/src/uk/me/parabola/imgfmt/app/net/RoadNetwork.java
@@ -49,25 +49,19 @@ public class RoadNetwork {
        private final List<RouteNode> boundary = new ArrayList<>();
        private final List<RoadDef> roadDefs = new ArrayList<>();
        private List<RouteCenter> centers = new ArrayList<>();
-       private int adjustTurnHeadings ;
+       private AngleChecker angleChecker = new AngleChecker();
+
        private boolean checkRoundabouts;
        private boolean checkRoundaboutFlares;
        private int maxFlareLengthRatio ;
        private boolean reportSimilarArcs;
 
        public void config(EnhancedProperties props) {
-               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);
-
                reportSimilarArcs = props.getProperty("report-similar-arcs", 
false);
+               angleChecker.config(props);
        }
 
        public void addRoad(RoadDef roadDef, List<Coord> coordList) {
@@ -128,32 +122,33 @@ public class RoadNetwork {
 
                                RouteNode node1 = getOrAddNode(lastId, 
lastCoord);
                                RouteNode node2 = getOrAddNode(id, co);
-
                                if(node1 == node2)
                                        log.error("Road " + roadDef + " 
contains consecutive identical nodes at " + co.toOSMURL() + " - routing will be 
broken");
                                else if(arcLength == 0)
                                        log.warn("Road " + roadDef + " contains 
zero length arc at " + co.toOSMURL());
 
                                Coord forwardBearingPoint = 
coordList.get(lastIndex + 1);
-                               if(lastCoord.equals(forwardBearingPoint)) {
+                               if(lastCoord.equals(forwardBearingPoint) || 
forwardBearingPoint.isAddedNumberNode()) {
                                        // bearing point is too close to last 
node to be
                                        // useful - try some more points
                                        for(int bi = lastIndex + 2; bi <= 
index; ++bi) {
-                                               
if(!lastCoord.equals(coordList.get(bi))) {
-                                                       forwardBearingPoint = 
coordList.get(bi);
-                                                       break;
-                                               }
+                                               Coord coTest = 
coordList.get(bi);
+                                               if (coTest.isAddedNumberNode() 
|| lastCoord.equals(coTest))
+                                                       continue;
+                                               forwardBearingPoint = coTest;
+                                               break;
                                        }
                                }
                                Coord reverseBearingPoint = coordList.get(index 
- 1);
-                               if(co.equals(reverseBearingPoint)) {
+                               if(co.equals(reverseBearingPoint) || 
reverseBearingPoint.isAddedNumberNode()) {
                                        // bearing point is too close to this 
node to be
                                        // useful - try some more points
                                        for(int bi = index - 2; bi >= 
lastIndex; --bi) {
-                                               
if(!co.equals(coordList.get(bi))) {
-                                                       reverseBearingPoint = 
coordList.get(bi);
-                                                       break;
-                                               }
+                                               Coord coTest = 
coordList.get(bi);
+                                               if (coTest.isAddedNumberNode() 
|| co.equals(coTest))
+                                                       continue;
+                                               reverseBearingPoint = coTest;
+                                               break;
                                        }
                                }
                                
@@ -162,24 +157,16 @@ public class RoadNetwork {
 
                                double reverseInitialBearing = 
co.bearingTo(reverseBearingPoint);
                                double directLength = (lastIndex + 1 == index) 
? arcLength : lastCoord.distance(co);
-                               double reverseFinalBearing, 
forwardFinalBearing, reverseDirectBearing;
+                               double reverseDirectBearing = 0;
                                if (directLength > 0){
                                        // bearing on rhumb line is a constant, 
so we can simply revert
                                        reverseDirectBearing = 
(forwardDirectBearing <= 0) ? 180 + forwardDirectBearing: -(180 - 
forwardDirectBearing) % 180.0;
-                                       forwardFinalBearing = 
(reverseInitialBearing <= 0) ? 180 + reverseInitialBearing : -(180 - 
reverseInitialBearing) % 180.0;
-                                       reverseFinalBearing = 
(forwardInitialBearing <= 0) ? 180 + forwardInitialBearing : -(180 - 
forwardInitialBearing) % 180.0;
-                               }
-                               else {
-                                       reverseDirectBearing = 0;
-                                       forwardFinalBearing = 0;
-                                       reverseFinalBearing = 0;
                                }
                                // Create forward arc from node1 to node2
                                RouteArc arc = new RouteArc(roadDef,
                                                                                
        node1,
                                                                                
        node2,
                                                                                
        forwardInitialBearing,
-                                                                               
        forwardFinalBearing,
                                                                                
        forwardDirectBearing,
                                                                                
        arcLength,
                                                                                
        arcLength,
@@ -192,7 +179,6 @@ public class RoadNetwork {
                                RouteArc reverseArc = new RouteArc(roadDef,
                                                                   node2, node1,
                                                                   
reverseInitialBearing,
-                                                                  
reverseFinalBearing,
                                                                   
reverseDirectBearing,
                                                                   arcLength,
                                                                   arcLength,
@@ -269,8 +255,7 @@ public class RoadNetwork {
                                        if(reportSimilarArcs)
                                                node.reportSimilarArcs();
                                }
-                               if(adjustTurnHeadings != 0)
-                                       node.tweezeArcs(adjustTurnHeadings);
+                               
                                nod1.addNode(node);
                                n++;
                        }
@@ -281,6 +266,7 @@ public class RoadNetwork {
 
        public List<RouteCenter> getCenters() {
                if (centers.isEmpty()){
+                       angleChecker.check(nodes);
                        addArcsToMajorRoads();
                        splitCenters();
                }
diff --git a/src/uk/me/parabola/imgfmt/app/net/RouteArc.java 
b/src/uk/me/parabola/imgfmt/app/net/RouteArc.java
index 5f0f320..7fa9302 100644
--- a/src/uk/me/parabola/imgfmt/app/net/RouteArc.java
+++ b/src/uk/me/parabola/imgfmt/app/net/RouteArc.java
@@ -43,7 +43,6 @@ public class RouteArc {
 
        // heading / bearing: 
        private float initialHeading; // degrees (A-> B in an arc ABCD) 
-       private final float finalHeading; // degrees (C-> D in an arc ABCD)
        private final float directHeading; // degrees (A-> D in an arc ABCD)
 
        private final RoadDef roadDef;
@@ -81,7 +80,6 @@ public class RouteArc {
         * @param source The source node. (A)
         * @param dest The destination node (E).
         * @param initialBearing The initial heading (signed degrees) (A->B)
-        * @param finalBearing The final heading (signed degrees) (D->E)
         * @param directBearing The direct heading (signed degrees) (A->E)
         * @param arcLength the length of the arc in meter (A->B->C->D->E)
         * @param pathLength the length of the arc in meter (summed length for 
additional arcs)
@@ -90,7 +88,7 @@ public class RouteArc {
         */
        public RouteArc(RoadDef roadDef,
                                        RouteNode source, RouteNode dest,
-                                       double initialBearing, double 
finalBearing, double directBearing,
+                                       double initialBearing, double 
directBearing,
                                        double arcLength,
                                        double pathLength,
                                        double directLength,
@@ -100,7 +98,6 @@ public class RouteArc {
                this.source = source;
                this.dest = dest;
                this.initialHeading = (float) initialBearing;
-               this.finalHeading = (float) finalBearing;
                this.directHeading = (directBearing < 180) ? (float) 
directBearing : -180.0f;
                int len = NODHeader.metersToRaw(arcLength);
                if (len >= (1 << 22)) {
@@ -129,12 +126,21 @@ public class RouteArc {
                return initialHeading;
        }
 
+       public float getDirectHeading() {
+               return directHeading;
+       }
+
        public void setInitialHeading(float ih) {
                initialHeading = ih;
        }
 
        public float getFinalHeading() {
-               return finalHeading;
+               float fh = 0;
+               if (lengthInMeter != 0){
+                       fh = getReverseArc().getInitialHeading();
+                       fh = (fh <= 0) ? 180.0f + fh : -(180.0f - fh) % 180.0f;
+               }
+               return fh;
        }
 
        public RouteNode getSource() {
@@ -224,7 +230,7 @@ public class RouteArc {
                return lengthInMeter;
        }
        
-       public static byte directionFromDegrees(double dir) {
+       public static byte directionFromDegrees(float dir) {
                return (byte) Math.round(dir * 256.0 / 360) ;
        }
 
diff --git a/src/uk/me/parabola/imgfmt/app/net/RouteNode.java 
b/src/uk/me/parabola/imgfmt/app/net/RouteNode.java
index 026d7e2..13c0711 100644
--- a/src/uk/me/parabola/imgfmt/app/net/RouteNode.java
+++ b/src/uk/me/parabola/imgfmt/app/net/RouteNode.java
@@ -17,14 +17,12 @@ package uk.me.parabola.imgfmt.app.net;
 import it.unimi.dsi.fastutil.ints.IntArrayList;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import uk.me.parabola.imgfmt.app.Coord;
 import uk.me.parabola.imgfmt.app.CoordNode;
 import uk.me.parabola.imgfmt.app.ImgFileWriter;
-import uk.me.parabola.imgfmt.app.Label;
 import uk.me.parabola.log.Logger;
 
 /**
@@ -53,10 +51,6 @@ public class RouteNode implements Comparable<RouteNode> {
        // only used internally in mkgmap
        private static final int F_DISCARDED = 0x100; // node has been discarded
 
-       private static final int MAX_MAIN_ROAD_HEADING_CHANGE = 120;
-       private static final int MIN_DIFF_BETWEEN_OUTGOING_AND_OTHER_ARCS = 45;
-       private static final int MIN_DIFF_BETWEEN_INCOMING_AND_OTHER_ARCS = 50;
-
        private int offsetNod1 = -1;
 
        // arcs from this node
@@ -352,310 +346,6 @@ public class RouteNode implements Comparable<RouteNode> {
                return coord.compareTo(otherNode.getCoord());
        }
 
-       private static boolean possiblySameRoad(RouteArc raa, RouteArc rab) {
-
-               RoadDef rda = raa.getRoadDef();
-               RoadDef rdb = rab.getRoadDef();
-
-               if(rda.getId() == rdb.getId()) {
-                       // roads have the same (OSM) id
-                       return true;
-               }
-
-               boolean bothArcsNamed = false;
-               for(Label laba : rda.getLabels()) {
-                       if(laba != null && laba.getOffset() != 0) {
-                               for(Label labb : rdb.getLabels()) {
-                                       if(labb != null && labb.getOffset() != 
0) {
-                                               bothArcsNamed = true;
-                                               if(laba.equals(labb)) {
-                                                       // the roads have the 
same label
-                                                       if(rda.isLinkRoad() == 
rdb.isLinkRoad()) {
-                                                               // if both are 
a link road or both are
-                                                               // not a link 
road, consider them the
-                                                               // same road
-                                                               return true;
-                                                       }
-                                                       // One is a link road 
and the other isn't
-                                                       // so consider them 
different roads - this
-                                                       // is because people 
often give a link
-                                                       // road that's leaving 
some road the same
-                                                       // ref as that road but 
it suits us better
-                                                       // to consider them as 
different roads
-                                                       return false;
-                                               }
-                                       }
-                               }
-                       }
-               }
-
-               if(bothArcsNamed) {
-                       // both roads have names and they don't match
-                       return false;
-               }
-
-               // at least one road is unnamed
-               if(rda.isRoundabout() && rdb.isRoundabout()) {
-                       // hopefully, segments of the same (unnamed) roundabout
-                       return true;
-               }
-
-               return false;
-       }
-
-       private static boolean rightTurnRequired(float inHeading, float 
outHeading, float otherHeading) {
-               // given the headings of the incoming, outgoing and side
-               // roads, decide whether a side road is to the left or the
-               // right of the main road
-
-               outHeading -= inHeading;
-               while(outHeading < -180)
-                       outHeading += 360;
-               while(outHeading > 180)
-                       outHeading -= 360;
-
-               otherHeading -= inHeading;
-               while(otherHeading < -180)
-                       otherHeading += 360;
-               while(otherHeading > 180)
-                       otherHeading -= 360;
-
-               return otherHeading > outHeading;
-       }
-
-       private static final int ATH_OUTGOING = 1;
-       private static final int ATH_INCOMING = 2;
-
-       public static final int ATH_DEFAULT_MASK = ATH_OUTGOING | ATH_INCOMING;
-
-       public void tweezeArcs(int mask) {
-               if(arcs.size() >= 3) {
-
-                       // detect the "shallow turn" scenario where at a 
junction
-                       // 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 "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 outgoing
-                       // heading by at least
-                       // minDiffBetweenOutgoingAndOtherArcs and the side road
-                       // heading differs from the incoming heading by at least
-                       // minDiffBetweenIncomingAndOtherArcs
-
-                       // list of outgoing arcs discovered at this node
-                       List<RouteArc> outgoingArcs = new ArrayList<RouteArc>();
-
-                       // sort incoming arcs by decreasing class/speed
-                       List<RouteArc> inArcs = new ArrayList<RouteArc>();
-                       for (RouteArc arc : arcs){
-                               if (arc.isDirect())
-                                       inArcs.add(arc.getReverseArc());
-                       }
-
-                       Collections.sort(inArcs, new Comparator<RouteArc>() {
-                                       public int compare(RouteArc ra1, 
RouteArc ra2) {
-                                               int c1 = 
ra1.getRoadDef().getRoadClass();
-                                               int c2 = 
ra2.getRoadDef().getRoadClass();
-                                               if(c1 == c2)
-                                                       return 
(ra2.getRoadDef().getRoadSpeed() - 
-                                                                       
ra1.getRoadDef().getRoadSpeed());
-                                               return c2 - c1;
-                                       }
-                               });
-
-                       // look at incoming arcs in order of decreasing 
class/speed
-                       for(RouteArc inArc : inArcs) {
-
-                               RoadDef inRoadDef = inArc.getRoadDef();
-
-                               if(!inArc.isForward() && inRoadDef.isOneway()) {
-                                       // ignore reverse arc if road is oneway
-                                       continue;
-                               }
-
-                               float inHeading = inArc.getFinalHeading();
-                               // determine the outgoing arc that is likely to 
be the
-                               // same road as the incoming arc
-                               RouteArc outArc = null;
-
-                               if(throughRoutes != null) {
-                                       // through_route relations have the 
highest precedence
-                                       for(RouteArc[] pair : throughRoutes) {
-                                               if(pair[0] == inArc) {
-                                                       outArc = pair[1];
-                                                       log.info("Found through 
route from " + inRoadDef + " to " + outArc.getRoadDef());
-                                                       break;
-                                               }
-                                       }
-                               }
-
-                               if(outArc == null) {
-                                       // 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
-                                                       if(oa.getRoadDef() == 
inRoadDef) {
-                                                               outArc = oa;
-                                                               break;
-                                                       }
-                                               }
-                                       }
-                               }
-
-                               if(outArc == null) {
-                                       // next, although the RoadDefs don't 
match, use
-                                       // 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
-                                                       if((oa.isForward() || 
!oa.getRoadDef().isOneway()) &&
-                                                          
possiblySameRoad(inArc, oa)) {
-                                                               outArc = oa;
-                                                               break;
-                                                       }
-                                               }
-                                       }
-                               }
-
-                               // if we did not find the outgoing arc, give up 
with
-                               // this incoming arc
-                               if(outArc == null) {
-                                       //log.info("Can't continue road " + 
inRoadDef + " at " + coord.toOSMURL());
-                                       continue;
-                               }
-
-                               // remember that this arc is an outgoing arc
-                               outgoingArcs.add(outArc);
-
-                               float outHeading = outArc.getInitialHeading();
-                               float mainHeadingDelta = outHeading - inHeading;
-                               while(mainHeadingDelta > 180)
-                                       mainHeadingDelta -= 360;
-                               while(mainHeadingDelta < -180)
-                                       mainHeadingDelta += 360;
-                               //log.info(inRoadDef + " continues to " + 
outArc.getRoadDef() + " with a heading change of " + mainHeadingDelta + " at " 
+ coord.toOSMURL());
-
-                               if(Math.abs(mainHeadingDelta) > 
MAX_MAIN_ROAD_HEADING_CHANGE) {
-                                       // if the continuation road heading 
change is
-                                       // greater than 
maxMainRoadHeadingChange don't
-                                       // adjust anything
-                                       continue;
-                               }
-
-                               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
-                                       // outgoing heading is less than
-                                       // minDiffBetweenOutgoingAndOtherArcs 
or its
-                                       // heading change from the incoming 
heading is
-                                       // less than 
minDiffBetweenIncomingAndOtherArcs
-
-                                       if(otherArc.getDest() == 
inArc.getSource() ||
-                                          otherArc == outArc) {
-                                               // we're looking at the 
incoming or outgoing
-                                               // arc, ignore it
-                                               continue;
-                                       }
-
-                                       if(!otherArc.isForward() &&
-                                          otherArc.getRoadDef().isOneway()) {
-                                               // ignore reverse arc if road 
is oneway
-                                               continue;
-                                       }
-
-                                       if(inRoadDef.isLinkRoad() &&
-                                          otherArc.getRoadDef().isLinkRoad()) {
-                                               // it's a link road leaving a 
link road so
-                                               // leave the angle unchanged to 
avoid
-                                               // introducing a time penalty 
by increasing
-                                               // the angle (this stops the 
router using link
-                                               // roads that "cut the corner" 
at roundabouts)
-                                               continue;
-                                       }
-
-                                       if(outgoingArcs.contains(otherArc)) {
-                                               // this arc was previously 
matched as an
-                                               // outgoing arc so we don't 
want to change its
-                                               // heading now
-                                               continue;
-                                       }
-
-                                       float otherHeading = 
otherArc.getInitialHeading();
-                                       float outToOtherDelta = otherHeading - 
outHeading;
-                                       while(outToOtherDelta > 180)
-                                               outToOtherDelta -= 360;
-                                       while(outToOtherDelta < -180)
-                                               outToOtherDelta += 360;
-                                       float inToOtherDelta = otherHeading - 
inHeading;
-                                       while(inToOtherDelta > 180)
-                                               inToOtherDelta -= 360;
-                                       while(inToOtherDelta < -180)
-                                               inToOtherDelta += 360;
-
-                                       float newHeading = otherHeading;
-                                       if(rightTurnRequired(inHeading, 
outHeading, otherHeading)) {
-                                               // side road to the right
-                                               if((mask & ATH_OUTGOING) != 0 &&
-                                                  Math.abs(outToOtherDelta) < 
MIN_DIFF_BETWEEN_OUTGOING_AND_OTHER_ARCS)
-                                                       newHeading = outHeading 
+ MIN_DIFF_BETWEEN_OUTGOING_AND_OTHER_ARCS;
-                                               if((mask & ATH_INCOMING) != 0 &&
-                                                  Math.abs(inToOtherDelta) < 
MIN_DIFF_BETWEEN_INCOMING_AND_OTHER_ARCS) {
-                                                       float nh = inHeading + 
MIN_DIFF_BETWEEN_INCOMING_AND_OTHER_ARCS;
-                                                       if(nh > newHeading)
-                                                               newHeading = nh;
-                                               }
-
-                                               if(newHeading > 180)
-                                                       newHeading -= 360;
-                                       }
-                                       else {
-                                               // side road to the left
-                                               if((mask & ATH_OUTGOING) != 0 &&
-                                                  Math.abs(outToOtherDelta) < 
MIN_DIFF_BETWEEN_OUTGOING_AND_OTHER_ARCS)
-                                                       newHeading = outHeading 
- MIN_DIFF_BETWEEN_OUTGOING_AND_OTHER_ARCS;
-                                               if((mask & ATH_INCOMING) != 0 &&
-                                                  Math.abs(inToOtherDelta) < 
MIN_DIFF_BETWEEN_INCOMING_AND_OTHER_ARCS) {
-                                                       float nh = inHeading - 
MIN_DIFF_BETWEEN_INCOMING_AND_OTHER_ARCS;
-                                                       if(nh < newHeading)
-                                                               newHeading = nh;
-                                               }
-
-                                               if(newHeading < -180)
-                                                       newHeading += 360;
-                                       }
-                                       if(Math.abs(newHeading - otherHeading) 
> 0.0000001) {
-                                               
otherArc.setInitialHeading(newHeading);
-                                               log.info("Adjusting turn 
heading from " + otherHeading + " to " + newHeading + " at junction of " + 
inRoadDef + " and " + otherArc.getRoadDef() + " at " + coord.toOSMURL());
-                                       }
-                               }
-                       }
-               }
-       }
-
        public void checkRoundabouts() {
 
                List<RouteArc> roundaboutArcs = new ArrayList<RouteArc>();
@@ -1010,7 +700,6 @@ public class RouteNode implements Comparable<RouteNode> {
                                                                sourceNode, 
                                                                destNode, 
                                                                
roadArcs.get(i).getInitialHeading(), // not used
-                                                               
arcToDest.getFinalHeading(),  // not used
                                                                
c1.bearingTo(c2),
                                                                
partialArcLength, // from stepNode to destNode on road
                                                                pathLength, // 
from sourceNode to destNode on road
@@ -1111,4 +800,16 @@ public class RouteNode implements Comparable<RouteNode> {
        public int hashCode(){
                return getCoord().getId();
        }
+
+       public List<RouteArc> getDirectArcsBetween(RouteNode otherNode) {
+               List<RouteArc> result = new ArrayList<>();
+               for(RouteArc a : arcs){
+                       if(a.isDirect() && a.getDest() == otherNode){
+                               result.add(a);
+                       }
+               }
+               return result;
+       }
+
+       
 }
diff --git a/src/uk/me/parabola/mkgmap/osmstyle/housenumber/ExtNumbers.java 
b/src/uk/me/parabola/mkgmap/osmstyle/housenumber/ExtNumbers.java
index 36e7e19..733d104 100644
--- a/src/uk/me/parabola/mkgmap/osmstyle/housenumber/ExtNumbers.java
+++ b/src/uk/me/parabola/mkgmap/osmstyle/housenumber/ExtNumbers.java
@@ -1070,6 +1070,7 @@ public class ExtNumbers {
         */
        private int addAsNumberNode(int pos, Coord toAdd){
                toAdd.setNumberNode(true);
+               toAdd.setAddedNumberNode(true);
                getRoad().getPoints().add(pos, toAdd);
                
                ExtNumbers work = next;
diff --git 
a/src/uk/me/parabola/mkgmap/osmstyle/housenumber/HousenumberGenerator.java 
b/src/uk/me/parabola/mkgmap/osmstyle/housenumber/HousenumberGenerator.java
index e7e389e..753d5f2 100644
--- a/src/uk/me/parabola/mkgmap/osmstyle/housenumber/HousenumberGenerator.java
+++ b/src/uk/me/parabola/mkgmap/osmstyle/housenumber/HousenumberGenerator.java
@@ -908,10 +908,7 @@ public class HousenumberGenerator {
                                MapRoad uncheckedRoads[] = new 
MapRoad[houses.length];
                                for (int i = 0 ; i < houses.length; i++)
                                        uncheckedRoads[i] = houses[i].getRoad();
-                                       
                                isOK = info.checkRoads();
-                               if (!isOK)
-                                       continue;
                                // check if houses are assigned to different 
roads now
                                houses = info.getHouseNodes();
                                for (int i = 0 ; i < houses.length; i++){
@@ -919,6 +916,10 @@ public class HousenumberGenerator {
                                                
initialHousesForRoads.removeMapping(uncheckedRoads[i], houses[i]);
                                                if (houses[i].isIgnored() == 
false)
                                                        
initialHousesForRoads.add(houses[i].getRoad(), houses[i]);
+                                               else {
+                                                       if (!isOK)
+                                                               
log.info("housenumber is assigned to different road after checking 
addr:interpolation way which turned out to be invalid",houses[i],info );
+                                               }
                                        }
                                }
                        }
diff --git 
a/src/uk/me/parabola/mkgmap/osmstyle/housenumber/HousenumberGroup.java 
b/src/uk/me/parabola/mkgmap/osmstyle/housenumber/HousenumberGroup.java
index f09df40..1b4fa58 100644
--- a/src/uk/me/parabola/mkgmap/osmstyle/housenumber/HousenumberGroup.java
+++ b/src/uk/me/parabola/mkgmap/osmstyle/housenumber/HousenumberGroup.java
@@ -164,9 +164,11 @@ public class HousenumberGroup {
                if (timesToAdd == 2){
                        // add two new points between c1 and c2
                        points.add(seg + 1, pointToUse);
+                       pointToUse.setAddedNumberNode(true);
                        pointToUse = new Coord (pointToUse);
                        pointToUse.setNumberNode(true);
                        points.add(seg + 1, pointToUse);
+                       pointToUse.setAddedNumberNode(true);
                        linkNode = pointToUse;
                } else {
                // copy it
@@ -174,6 +176,7 @@ public class HousenumberGroup {
                        pointToUse.setNumberNode(true);
                        // add copy before c2 
                        points.add(seg + 1, pointToUse);
+                       pointToUse.setAddedNumberNode(true);
                        if (pointToUse.highPrecEquals(c1)){
                                linkNode = c1;
                        } else { 
diff --git a/src/uk/me/parabola/mkgmap/osmstyle/housenumber/HousenumberIvl.java 
b/src/uk/me/parabola/mkgmap/osmstyle/housenumber/HousenumberIvl.java
index b6c25d8..20008fa 100644
--- a/src/uk/me/parabola/mkgmap/osmstyle/housenumber/HousenumberIvl.java
+++ b/src/uk/me/parabola/mkgmap/osmstyle/housenumber/HousenumberIvl.java
@@ -171,9 +171,11 @@ public class HousenumberIvl {
                        while 
(streetName.equals(knownHouses[i].getRoad().getStreet()) == false && 
knownHouses[i].hasAlternativeRoad()){
                                HousenumberMatch testx = new 
HousenumberMatch(knownHouses[i]);
                                MapRoad r = 
knownHouses[i].getAlternativeRoads().remove(0);
-                               
HousenumberGenerator.findClosestRoadSegment(testx, r);
-                               if (testx.getDistance() < 
MAX_INTERPOLATION_DISTANCE_TO_ROAD){
-                                       copyRoadData(testx, knownHouses[i]);
+                               if (streetName.equals(r.getStreet())){
+                                       
HousenumberGenerator.findClosestRoadSegment(testx, r);
+                                       if (testx.getDistance() < 
MAX_INTERPOLATION_DISTANCE_TO_ROAD){
+                                               copyRoadData(testx, 
knownHouses[i]);
+                                       }
                                }
                        }
                }
diff --git a/src/uk/me/parabola/mkgmap/reader/osm/Element.java 
b/src/uk/me/parabola/mkgmap/reader/osm/Element.java
index 5421f3a..eb5dafd 100644
--- a/src/uk/me/parabola/mkgmap/reader/osm/Element.java
+++ b/src/uk/me/parabola/mkgmap/reader/osm/Element.java
@@ -19,10 +19,16 @@ import java.util.Collections;
 import java.util.Iterator;
 import java.util.Map;
 
+import uk.me.parabola.imgfmt.app.Label;
+import uk.me.parabola.log.Logger;
+
+
 /**
  * Superclass of the node, segment and way OSM elements.
  */
 public abstract class Element {
+       private static final Logger log = Logger.getLogger(Element.class);
+       
        private Tags tags;
        private long id;
        private long originalId;
@@ -32,7 +38,30 @@ public abstract class Element {
        }
        
        /**
-        * Add a tag to the way.  Some tags are recognised separately and saved 
in
+        * Add a tag to the element. This method should be called by OSM readers
+        * because it trims obsolete spaces from the value.
+        *
+        * @param key The tag name.
+        * @param val Its value.
+        */
+       public void addTagFromRawOSM(String key, String val) {
+               if (val != null){
+                       val = val.trim();
+                       if (val.isEmpty() == false){
+                               // remove duplicated spaces within value
+                               String squashed = Label.squashSpaces(val);
+                               if (val.equals(squashed) == false) {
+                                       if (log.isInfoEnabled())
+                                               
log.info(this.toBrowseURL(),"obsolete blanks removed from tag", key, " '" + val 
+ "' -> '" + squashed + "'");
+                                       val = squashed;
+                               }
+                       }
+               }
+               addTag(key, val.intern());
+       }
+
+       /**
+        * Add a tag to the element.  Some tags are recognised separately and 
saved in
         * separate fields.
         *
         * @param key The tag name.
@@ -45,7 +74,7 @@ public abstract class Element {
        }
 
        /**
-        * Add a tag to the way.  Some tags are recognised separately and saved 
in
+        * Add a tag to the element.  Some tags are recognised separately and 
saved in
         * separate fields.
         *
         * @param tagKey The tag id created by TagDict
diff --git a/src/uk/me/parabola/mkgmap/reader/osm/bin/OsmBinHandler.java 
b/src/uk/me/parabola/mkgmap/reader/osm/bin/OsmBinHandler.java
index efba2ac..2811e58 100644
--- a/src/uk/me/parabola/mkgmap/reader/osm/bin/OsmBinHandler.java
+++ b/src/uk/me/parabola/mkgmap/reader/osm/bin/OsmBinHandler.java
@@ -70,10 +70,10 @@ public class OsmBinHandler extends OsmHandler {
                                        Node node = new Node(id, co);
                                        for (int tid = 0; tid < tagCount; 
tid++) {
                                                String key = 
getStringById(binNode.getKeys(tid));
-                                               String val = 
getStringById(binNode.getVals(tid)).trim();
+                                               String val = 
getStringById(binNode.getVals(tid));
                                                key = keepTag(key, val);
                                                if (key != null)
-                                                       node.addTag(key, 
val.intern());
+                                                       
node.addTagFromRawOSM(key, val);
                                        }
 
                                        saver.addNode(node);
@@ -105,12 +105,12 @@ public class OsmBinHandler extends OsmHandler {
                                                int keyid = 
nodes.getKeysVals(kvid++);
                                                int valid = 
nodes.getKeysVals(kvid++);
                                                String key = 
getStringById(keyid);
-                                               String val = 
getStringById(valid).trim();
+                                               String val = 
getStringById(valid);
                                                key = keepTag(key, val);
                                                if (key != null) {
                                                        if (node == null)
                                                                node = new 
Node(id, co);
-                                                       node.addTag(key, 
val.intern());
+                                                       
node.addTagFromRawOSM(key, val);
                                                        ntags++;
                                                }
                                        }
@@ -132,10 +132,10 @@ public class OsmBinHandler extends OsmHandler {
                                for (int j = 0; j < binWay.getKeysCount(); j++) 
{
 
                                        String key = 
getStringById(binWay.getKeys(j));
-                                       String val = 
getStringById(binWay.getVals(j)).trim();
+                                       String val = 
getStringById(binWay.getVals(j));
                                        key = keepTag(key, val);
                                        if (key != null)
-                                               way.addTag(key, val.intern());
+                                               way.addTagFromRawOSM(key, val);
                                }
 
                                long nid = 0;
@@ -157,7 +157,7 @@ public class OsmBinHandler extends OsmHandler {
                                boolean tagsIncomplete = false;
                                for (int j = 0; j < binRel.getKeysCount(); j++) 
{
                                        String key = 
getStringById(binRel.getKeys(j));
-                                       String val = 
getStringById(binRel.getVals(j)).trim();
+                                       String val = 
getStringById(binRel.getVals(j));
                                        // type is required for relations - all 
other tags are filtered
                                        if ("type".equals(key))
                                                // intern the string
@@ -167,7 +167,7 @@ public class OsmBinHandler extends OsmHandler {
                                        if (key == null)
                                                tagsIncomplete = true;
                                        else
-                                               rel.addTag(key, val.intern());
+                                               rel.addTagFromRawOSM(key, val);
                                }
 
                                if (tagsIncomplete) {
diff --git a/src/uk/me/parabola/mkgmap/reader/osm/o5m/O5mBinHandler.java 
b/src/uk/me/parabola/mkgmap/reader/osm/o5m/O5mBinHandler.java
index 1992725..b1b4863 100644
--- a/src/uk/me/parabola/mkgmap/reader/osm/o5m/O5mBinHandler.java
+++ b/src/uk/me/parabola/mkgmap/reader/osm/o5m/O5mBinHandler.java
@@ -314,7 +314,7 @@ public class O5mBinHandler extends OsmHandler{
                while (bytesToRead > 0){
                        readStringPair();
                        String key = stringPair[0];
-                       String val = stringPair[1].trim();
+                       String val = stringPair[1];
                        // the type tag is required for relations - all other 
tags are filtered
                        if (elem instanceof Relation && "type".equals(key))
                                // intern the string
@@ -322,7 +322,7 @@ public class O5mBinHandler extends OsmHandler{
                        else
                                key = keepTag(key, val);
                        if (key != null)
-                               elem.addTag(key, val.intern());
+                               elem.addTagFromRawOSM(key, val);
                        else 
                                tagsIncomplete = true;
                }
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 8af21ec..54b1c69 100644
--- a/src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java
+++ b/src/uk/me/parabola/mkgmap/reader/osm/xml/Osm5XmlHandler.java
@@ -207,7 +207,7 @@ public class Osm5XmlHandler extends OsmHandler {
        private void startInNode(String qName, Attributes attributes) {
                if (qName.equals("tag")) {
                        String key = attributes.getValue("k");
-                       String val = attributes.getValue("v").trim();
+                       String val = attributes.getValue("v");
 
                        // We only want to create a full node for nodes that 
are POI's
                        // and not just one point of a way.  Only create if it 
has tags that
@@ -219,7 +219,7 @@ public class Osm5XmlHandler extends OsmHandler {
                                        currentNode = new 
Node(currentElementId, co);
                                }
 
-                               currentNode.addTag(key, val.intern());
+                               currentNode.addTagFromRawOSM(key, val);
                        }
                }
        }
@@ -235,10 +235,10 @@ public class Osm5XmlHandler extends OsmHandler {
                        addCoordToWay(currentWay, id);
                } else if (qName.equals("tag")) {
                        String key = attributes.getValue("k");
-                       String val = attributes.getValue("v").trim();
+                       String val = attributes.getValue("v");
                        key = keepTag(key, val);
                        if (key != null)
-                               currentWay.addTag(key, val.intern());
+                               currentWay.addTagFromRawOSM(key, val);
                }
        }
 
@@ -276,7 +276,7 @@ public class Osm5XmlHandler extends OsmHandler {
                                
currentRelation.addElement(attributes.getValue("role"), el);
                } else if (qName.equals("tag")) {
                        String key = attributes.getValue("k");
-                       String val = attributes.getValue("v").trim();
+                       String val = attributes.getValue("v");
                        // the type tag is required for relations - all other 
tags are filtered
                        if ("type".equals(key))
                                // intern the key
@@ -286,7 +286,7 @@ public class Osm5XmlHandler extends OsmHandler {
                        if (key == null) {
                                currentRelation.addTag(TAGS_INCOMPLETE_TAG, 
"true");
                        } else {
-                               currentRelation.addTag(key, val.intern());
+                               currentRelation.addTagFromRawOSM(key, val);
                        }
                }
        }
diff --git a/test/uk/me/parabola/mkgmap/reader/osm/ElementTest.java 
b/test/uk/me/parabola/mkgmap/reader/osm/ElementTest.java
index b5ce727..0c1bd7d 100644
--- a/test/uk/me/parabola/mkgmap/reader/osm/ElementTest.java
+++ b/test/uk/me/parabola/mkgmap/reader/osm/ElementTest.java
@@ -54,4 +54,27 @@ public class ElementTest {
                                new String[] {"1", "2", "3"},
                                values.toArray());
        }
+
+       @Test
+       public void testaddTagFromRawOSM() {
+               Element el = new Way(1);
+
+               el.addTagFromRawOSM("a", "1");
+               el.addTagFromRawOSM("b", "1 ");
+               el.addTagFromRawOSM("c", " 1");
+               el.addTagFromRawOSM("d", "1  2");
+               el.addTagFromRawOSM("e", "1  2  3");
+               el.addTagFromRawOSM("f", "   1  2  3 4  ");
+               el.addTagFromRawOSM("g", " ");
+               el.addTagFromRawOSM("h", "   ");
+
+               assertEquals("1", el.getTag("a"));
+               assertEquals("1", el.getTag("b"));
+               assertEquals("1", el.getTag("c"));
+               assertEquals("1 2", el.getTag("d"));
+               assertEquals("1 2 3", el.getTag("e"));
+               assertEquals("1 2 3 4", el.getTag("f"));
+               assertEquals("", el.getTag("g"));
+               assertEquals("", el.getTag("h"));
+       }
 }

-- 
Alioth's /usr/local/bin/git-commit-notice on 
/srv/git.debian.org/git/pkg-grass/mkgmap.git

_______________________________________________
Pkg-grass-devel mailing list
Pkg-grass-devel@lists.alioth.debian.org
http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-grass-devel

Reply via email to