[mkgmap] 01/06: Imported Upstream version 0.0.0+svn3649
Sebastiaan Couwenberg
sebastic at moszumanska.debian.org
Sun Nov 1 11:09:13 UTC 2015
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 <sebastic at 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
More information about the Pkg-grass-devel
mailing list