v3 - turns out they handle ferry routes in a similar fashion as to
unpaved roads so I've added support for avoiding them (when route=ferry
tag is present) - untested here due to paucity of ferries in
Cheshire but I have verified that the unpaved stuff still works.
---------
v2 - sans syntax error
---------
Bloody typical, you wait around for ages hoping for a new routing
capability to be added to mkgmap and then two come along on
the same day.
I've been trying to discover how unpavedness is encoded for at least 6
months. Every now and again, I return to think about it some more.
Check and re-check the same old data structures. Very
frustrating, no progress. Damn those cunning bastards at Garmin....
However, a month or two ago, I discovered that Table C contains more
than just turn restrictions. I still don't know many of it's little
secrets but, today, having exhausted all other possibilities, I finally
twigged that Table C contains the key to understanding "unpavedness".
Gotcha!
The attached patch allows you to add either unpaved=yes/true/1 or
paved=no/false/0 to a way and then it will be ignored for routing
purposes when the GPS has been told to avoid unpaved roads.
Not sure if those are the best tags to use - any thoughts?
BTW - the unpaved road line type 0x0a has nothing to do with
unpavedness, it's just a routable way that gets drawn as a dashed line
(default rendering).
Feedback, etc.
Mark
diff --git a/src/uk/me/parabola/imgfmt/app/net/RoadDef.java b/src/uk/me/parabola/imgfmt/app/net/RoadDef.java
index 7323017..1238a5f 100644
--- a/src/uk/me/parabola/imgfmt/app/net/RoadDef.java
+++ b/src/uk/me/parabola/imgfmt/app/net/RoadDef.java
@@ -122,6 +122,8 @@ public class RoadDef implements Comparable {
private City city;
private Zip zip;
+ private boolean paved = true;
+ private boolean ferry = false;
private boolean roundabout;
private boolean linkRoad;
private boolean synthesised;
@@ -586,6 +588,22 @@ public class RoadDef implements Comparable {
return city;
}
+ public boolean paved() {
+ return paved;
+ }
+
+ public void paved(boolean p) {
+ paved = p;
+ }
+
+ public void ferry(boolean f) {
+ ferry = f;
+ }
+
+ public boolean ferry() {
+ return ferry;
+ }
+
public void setRoundabout(boolean r) {
roundabout = r;
}
diff --git a/src/uk/me/parabola/imgfmt/app/net/RouteCenter.java b/src/uk/me/parabola/imgfmt/app/net/RouteCenter.java
index a4134c4..3fc632f 100644
--- a/src/uk/me/parabola/imgfmt/app/net/RouteCenter.java
+++ b/src/uk/me/parabola/imgfmt/app/net/RouteCenter.java
@@ -47,7 +47,7 @@ public class RouteCenter {
this.nodes = nodes;
this.tabA = tabA;
this.tabB = tabB;
- this.tabC = new TableC();
+ this.tabC = new TableC(tabA);
log.info("new RouteCenter at " + centralPoint.toDegreeString() +
", nodes: " + nodes.size() + " tabA: " + tabA.size() +
@@ -106,7 +106,7 @@ public class RouteCenter {
writer.position(tablesOffset);
// Write the tables header
- writer.put(tabC.getSizeBytes());
+ writer.put(tabC.getFormat());
writer.put3(centralPoint.getLongitude());
writer.put3(centralPoint.getLatitude());
writer.put(tabA.getNumberOfItems());
diff --git a/src/uk/me/parabola/imgfmt/app/net/TableA.java b/src/uk/me/parabola/imgfmt/app/net/TableA.java
index a0c87ee..9ae5a9a 100644
--- a/src/uk/me/parabola/imgfmt/app/net/TableA.java
+++ b/src/uk/me/parabola/imgfmt/app/net/TableA.java
@@ -16,6 +16,7 @@
*/
package uk.me.parabola.imgfmt.app.net;
+import java.util.HashMap;
import java.util.LinkedHashMap;
import uk.me.parabola.imgfmt.app.ImgFileWriter;
@@ -37,10 +38,17 @@ public class TableA {
// This table's start position relative to the start of NOD 1
private int offset;
- private final LinkedHashMap<Arc,Integer> arcs = new LinkedHashMap<Arc,Integer>();
+ // arcs for paved ways
+ private final HashMap<Arc,Integer> pavedArcs = new LinkedHashMap<Arc,Integer>();
+ // arcs for unpaved ways
+ private final HashMap<Arc,Integer> unpavedArcs = new LinkedHashMap<Arc,Integer>();
+ // arcs for ferry ways
+ private final HashMap<Arc,Integer> ferryArcs = new LinkedHashMap<Arc,Integer>();
private static int count;
+ private boolean frozen = false; // true when no more arcs should be added
+
public TableA() {
log.debug("creating TableA", count);
count++;
@@ -88,24 +96,54 @@ public class TableA {
* the table fulfills the size constraint.
*/
public void addArc(RouteArc arc) {
+ assert !frozen : "trying to add arc to Table A after it has been frozen";
Arc narc = new Arc(arc);
int i;
- if (!arcs.containsKey(narc)) {
- i = arcs.size();
- arcs.put(narc, i);
- log.debug("added arc", count, narc, i);
+ if(arc.getRoadDef().ferry()) {
+ if (!ferryArcs.containsKey(narc)) {
+ i = ferryArcs.size();
+ ferryArcs.put(narc, i);
+ log.debug("added ferry arc", count, narc, i);
+ }
+ }
+ else if(arc.getRoadDef().paved()) {
+ if (!pavedArcs.containsKey(narc)) {
+ i = pavedArcs.size();
+ pavedArcs.put(narc, i);
+ log.debug("added paved arc", count, narc, i);
+ }
+ }
+ else {
+ if (!unpavedArcs.containsKey(narc)) {
+ i = unpavedArcs.size();
+ unpavedArcs.put(narc, i);
+ log.debug("added unpaved arc", count, narc, i);
+ }
}
}
/**
- * Retrieve an arc's index.
+ * Retrieve an arc's index (unpaved arcs are all before the paved arcs)
*/
public byte getIndex(RouteArc arc) {
- log.debug("getting index", arc);
+ frozen = true;
Arc narc = new Arc(arc);
- assert arcs.containsKey(narc):
+ int i;
+ if(arc.getRoadDef().ferry()) {
+ assert ferryArcs.containsKey(narc):
+ "Trying to read Table A index for non-registered arc: " + count + " " + narc;
+ i = unpavedArcs.size() + ferryArcs.get(narc);
+ }
+ else if(arc.getRoadDef().paved()) {
+ assert pavedArcs.containsKey(narc):
+ "Trying to read Table A index for non-registered arc: " + count + " " + narc;
+ i = unpavedArcs.size() + ferryArcs.size() + pavedArcs.get(narc);
+ }
+ else {
+ assert unpavedArcs.containsKey(narc):
"Trying to read Table A index for non-registered arc: " + count + " " + narc;
- int i = arcs.get(narc);
+ i = unpavedArcs.get(narc);
+ }
assert i < 0x100 : "Table A index too large: " + narc;
return (byte) i;
}
@@ -118,7 +156,15 @@ public class TableA {
* the network.
*/
public int size() {
- return arcs.size();
+ return ferryArcs.size() + unpavedArcs.size() + pavedArcs.size();
+ }
+
+ public int numUnpavedArcs() {
+ return unpavedArcs.size();
+ }
+
+ public int numFerryArcs() {
+ return ferryArcs.size();
}
/**
@@ -130,8 +176,8 @@ public class TableA {
* it isn't too large.
*/
public byte getNumberOfItems() {
- assert arcs.size() < 0x100 : "Table A too large";
- return (byte) arcs.size();
+ assert size() < 0x100 : "Table A too large";
+ return (byte)size();
}
/**
@@ -140,7 +186,7 @@ public class TableA {
*/
public void write(ImgFileWriter writer) {
offset = writer.position();
- int size = arcs.size() * ITEM_SIZE;
+ int size = size() * ITEM_SIZE;
log.debug("tab a offset", offset, "tab a size", size);
for (int i = 0; i < size; i++)
@@ -152,19 +198,31 @@ public class TableA {
*/
public void writePost(ImgFileWriter writer) {
writer.position(offset);
- for (Arc arc : arcs.keySet()) {
- // write the table A entries. Consists of a pointer to net
- // followed by 2 bytes of class and speed flags and road restrictions.
- log.debug("writing Table A entry", arcs.get(arc));
- int pos = arc.roadDef.getOffsetNet1();
- int access = arc.roadDef.getTabAAccess();
- // top bits of access go into net1 offset
- final int ACCESS_TOP_BITS = 0xc000;
- pos |= (access & ACCESS_TOP_BITS) << 8;
- access &= ~ACCESS_TOP_BITS;
- writer.put3(pos);
- writer.put((byte) arc.roadDef.getTabAInfo());
- writer.put((byte) access);
+ // unpaved arcs first
+ for (Arc arc : unpavedArcs.keySet()) {
+ writePost(writer, arc);
+ }
+ // followed by the ferry arcs
+ for (Arc arc : ferryArcs.keySet()) {
+ writePost(writer, arc);
+ }
+ // followed by the paved arcs
+ for (Arc arc : pavedArcs.keySet()) {
+ writePost(writer, arc);
}
}
+
+ public void writePost(ImgFileWriter writer, Arc arc) {
+ // write the table A entries. Consists of a pointer to net
+ // followed by 2 bytes of class and speed flags and road restrictions.
+ int pos = arc.roadDef.getOffsetNet1();
+ int access = arc.roadDef.getTabAAccess();
+ // top bits of access go into net1 offset
+ final int ACCESS_TOP_BITS = 0xc000;
+ pos |= (access & ACCESS_TOP_BITS) << 8;
+ access &= ~ACCESS_TOP_BITS;
+ writer.put3(pos);
+ writer.put((byte) arc.roadDef.getTabAInfo());
+ writer.put((byte) access);
+ }
}
diff --git a/src/uk/me/parabola/imgfmt/app/net/TableC.java b/src/uk/me/parabola/imgfmt/app/net/TableC.java
index 5cd9992..62f96d9 100644
--- a/src/uk/me/parabola/imgfmt/app/net/TableC.java
+++ b/src/uk/me/parabola/imgfmt/app/net/TableC.java
@@ -29,25 +29,39 @@ public class TableC {
// size of the table, excluding the size field
private int size;
+ private final TableA tabA;
+
private final List<RouteRestriction> restrictions = new ArrayList<RouteRestriction>();
+ public TableC(TableA tabA) {
+ this.tabA = tabA;
+ }
+
/**
* Write the table including size field.
*/
public void write(ImgFileWriter writer, int tablesOffset) {
if (restrictions.isEmpty()) {
+ if(tabA.numUnpavedArcs() > 0)
+ writer.put((byte)tabA.numUnpavedArcs());
+ if(tabA.numFerryArcs() > 0)
+ writer.put((byte)tabA.numFerryArcs());
writer.put((byte) 0);
- return;
- } else {
+ }
+ else {
byte b = getSizeBytes();
assert size < (1 << 8*b);
if (b == 1)
writer.put((byte) size);
else
writer.putChar((char) size);
+ for (RouteRestriction restr : restrictions)
+ restr.write(writer, tablesOffset);
+ if(tabA.numUnpavedArcs() > 0)
+ writer.put((byte)tabA.numUnpavedArcs());
+ if(tabA.numFerryArcs() > 0)
+ writer.put((byte)tabA.numFerryArcs());
}
- for (RouteRestriction restr : restrictions)
- restr.write(writer, tablesOffset);
}
/**
@@ -85,4 +99,13 @@ public class TableC {
for (RouteRestriction restr : restrictions)
restr.setOffsetSize(b);
}
+
+ public byte getFormat() {
+ byte format = getSizeBytes();
+ if(tabA.numUnpavedArcs() > 0)
+ format |= 0x08;
+ if(tabA.numFerryArcs() > 0)
+ format |= 0x10;
+ return format;
+ }
}
diff --git a/src/uk/me/parabola/mkgmap/general/MapRoad.java b/src/uk/me/parabola/mkgmap/general/MapRoad.java
index c540a69..3703ad2 100644
--- a/src/uk/me/parabola/mkgmap/general/MapRoad.java
+++ b/src/uk/me/parabola/mkgmap/general/MapRoad.java
@@ -72,6 +72,14 @@ public class MapRoad extends MapLine {
this.roadDef.setToll();
}
+ public void paved(boolean p) {
+ this.roadDef.paved(p);
+ }
+
+ public void ferry(boolean f) {
+ this.roadDef.ferry(f);
+ }
+
public void showOSMBrowseURL() {
this.roadDef.showOSMBrowseURL();
}
diff --git a/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java b/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
index fbff7e0..0e9d37f 100644
--- a/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
+++ b/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
@@ -1426,6 +1426,14 @@ public class StyledConverter implements OsmConverter {
if(way.isBoolTag("toll"))
road.setToll();
+ // assume road is paved unless unpaved tag is true or paved
+ // tag is false, in which case, consider road as unpaved
+ if(way.isBoolTag("unpaved") || way.isNotBoolTag("paved"))
+ road.paved(false);
+
+ if("ferry".equals(way.getTag("route")))
+ road.ferry(true);
+
Way origWay = originalWay.get(way);
if(origWay == null)
origWay = way;
_______________________________________________
mkgmap-dev mailing list
[email protected]
http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev