Hi
I've made some improvements to "Exits" handling.
The existing logic looked for tags:
exit:road_ref - the highway the exit/services is on
exit:to - the destination for the exit
exit:facilities - what is available at the services
These don't exist in OSM data. There is some processing in mkgmap to
set exit:road_ref in simple circumstances. Without this being set,
Exits don't work.
Changes in this patch:
- set exit:road_ref for highway=rest_area as well as motorway_junction
and services.
- do the above for motorroad, trunk and primary highways as well as
motorways.
- for services and rest_area, if option --link-pois-to-ways is set,
handle "Ways" in a similar manner to "Nodes".
- allow the point/area in above to be slightly off the road.
- improve the efficiency of the setting of exit:road_ref for
motorway_junction.
- Change the typCode of motorway junction from 0x2100 (Exit with
Services) to 0x2000 (Exit without Services) and only do if
exit:road_ref is set. Also attempt to set exit:to from standard OSM
tags exit_to or destination.
- Change the typCode of services from 0x210f (see above) to 0x230f
(Exit Service Area), do even of generated from polygon, set some
facilities and show at a lower resolution.
- correct the allowable ranges of the different POI subtypes.
My eTrex HCx supports "Find > Exits" and shows the facility details.
However the "Select Map" option for this defaults to "Use Best Maps"
and this always chooses the inbuilt Basemap, even when disabled.
Selecting the mkgmap generated map shows the junctions and services
along the major road that the cursor is nearest. Stupidly, the map
selection lists the tiles rather than the complete map, naming each
tile as the --series-name, which is the same for all of them!
There are stupidities with most aspect of Exits definitions and device
handling of it and I can see why support seems to have been dropped on
more modern devices, but maybe it works better on other devices,
specifically car navigation systems.
Ticker
Index: resources/styles/default/points
===================================================================
--- resources/styles/default/points (revision 4525)
+++ resources/styles/default/points (working copy)
@@ -191,9 +191,9 @@
healthcare=hospital | amenity=hospital | amenity=clinic [0x3002 resolution 22]
healthcare=* | amenity=dentist | amenity=doctors [0x3002 resolution 24]
-highway=motorway_junction [0x2100 resolution 24]
+highway=motorway_junction & exit:road_ref=* {add exit:to='${exit_to}' | '${destination}'} [0x2000 resolution 24]
-highway=services & mkgmap:area2poi!=true [0x210f resolution 24 default_name 'Services']
+highway=services {add exit:facility="0x02,I,0x47,Features"} [0x230f resolution 20 default_name 'Services']
historic=museum [0x2c02 resolution 24]
historic=archaeological_site | historic=ruins [0x2c02 resolution 24]
Index: src/uk/me/parabola/mkgmap/build/MapBuilder.java
===================================================================
--- src/uk/me/parabola/mkgmap/build/MapBuilder.java (revision 4525)
+++ src/uk/me/parabola/mkgmap/build/MapBuilder.java (working copy)
@@ -661,17 +661,22 @@
private void processExit(Map map, MapExitPoint mep) {
LBLFile lbl = map.getLblFile();
+ String exitName = mep.getName();
String ref = mep.getMotorwayRef();
String osmId = mep.getOSMId();
- if(ref != null) {
+ if (ref == null)
+ log.warn("Can't create exit", exitName, "(OSM id", osmId, ") doesn't have exit:road_ref tag");
+ else {
Highway hw = highways.get(ref);
- if(hw == null)
- hw = makeHighway(map, ref);
- if(hw == null) {
- log.warn("Can't create exit", mep.getName(), "(OSM id", osmId, ") on unknown highway", ref);
- return;
+ if (hw == null) {
+ String countryStr = mep.getCountry();
+ Country thisCountry = countryStr != null ? lbl.createCountry(locator.normalizeCountry(countryStr), locator.getCountryISOCode(countryStr)) : getDefaultCountry();
+ String regionStr = regionName != null ? regionName : mep.getRegion(); // use --region-name if set because highway will likely span regions
+ Region thisRegion = regionStr != null ? lbl.createRegion(thisCountry, regionStr, null) : getDefaultRegion(thisCountry);
+ hw = lbl.createHighway(thisRegion, ref);
+ log.info("creating highway", ref, "region:", regionStr, "country:", countryStr, "for exit:", exitName);
+ highways.put(ref, hw);
}
- String exitName = mep.getName();
String exitTo = mep.getTo();
Exit exit = new Exit(hw);
String facilityDescription = mep.getFacilityDescription();
@@ -1283,16 +1288,6 @@
}
}
- Highway makeHighway(Map map, String ref) {
- if (getDefaultRegion(null) == null) {
- log.warn("Highway " + ref + " has no region (define a default region to zap this warning)");
- }
- return highways.computeIfAbsent(ref, k-> {
- log.info("creating highway", ref);
- return map.getLblFile().createHighway(getDefaultRegion(null), ref);
- });
- }
-
/**
* It is not possible to represent large maps at the 24 bit resolution. This
* gets the largest resolution that can still cover the whole area of the
Index: src/uk/me/parabola/mkgmap/reader/osm/GType.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/GType.java (revision 4525)
+++ src/uk/me/parabola/mkgmap/reader/osm/GType.java (working copy)
@@ -57,21 +57,38 @@
// actions will always be executed
private boolean propogateActionsOnContinue;
+ @SuppressWarnings("incomplete-switch")
public static boolean checkType(FeatureKind featureKind, int type) {
if (type >= 0x010000) {
if ((type & 0xff) > 0x1f)
return false;
} else {
- if (featureKind == FeatureKind.POLYLINE && type > 0x3f
- || (featureKind == FeatureKind.POLYGON && (type > 0x7f || type == 0x4a))) {
- return false;
- } else if (featureKind == FeatureKind.POINT) {
+ switch (featureKind) {
+ case POLYLINE:
+ if (type > 0x3f)
+ return false;
+ break;
+ case POLYGON:
+ if (type > 0x7f || type == 0x4a)
+ return false;
+ break;
+ case POINT:
if (type < 0x0100)
return false;
int subtype = type & 0xff;
- if (subtype > 0x1f || MapPoint.isCityType(type) && subtype != 0) {
- return false;
+ if (MapPoint.isCityType(type)) {
+ if (subtype != 0)
+ return false;
+ } else if (type >= 0x1600 && type < 0x1e00 || // Andrzej Popowski says.
+ type >= 0x2a00 && type < 0x3100 || // These may be indexed and this
+ type >= 0x6400 && type < 0x6700) { // confines subtype to 5 bits
+ if (subtype > 0x1f)
+ return false;
+ } else {
+ if (subtype > 0x3f)
+ return false;
}
+ break;
}
}
return true;
@@ -243,7 +260,7 @@
* known to cause routing errors if used for non-routable lines.
*/
public static boolean isSpecialRoutableLineType(int type){
- return type >= 0x01 && type <= 0x13 || type == 0x16 || type == 0x1b;
+ return type >= 0x01 && type <= 0x13 || type == 0x16 || type == 0x1a || type == 0x1b;
}
/**
Index: src/uk/me/parabola/mkgmap/reader/osm/HighwayHooks.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/osm/HighwayHooks.java (revision 4525)
+++ src/uk/me/parabola/mkgmap/reader/osm/HighwayHooks.java (working copy)
@@ -15,13 +15,16 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import uk.me.parabola.imgfmt.app.Area;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.imgfmt.app.Exit;
import uk.me.parabola.log.Logger;
+import uk.me.parabola.util.ElementQuadTree;
import uk.me.parabola.util.EnhancedProperties;
/**
@@ -33,8 +36,8 @@
public class HighwayHooks implements OsmReadingHooks {
private static final Logger log = Logger.getLogger(HighwayHooks.class);
- private final List<Way> motorways = new ArrayList<>();
- private final List<Node> exits = new ArrayList<>();
+ private final List<Element> motorways = new ArrayList<>(); // will all be Ways
+ private final List<Element> exits = new ArrayList<>();
private boolean makeOppositeCycleways;
private ElementSaver saver;
@@ -77,8 +80,8 @@
@Override
public void onAddNode(Node node) {
- String val = node.getTag("highway");
- if (val != null && ("motorway_junction".equals(val) || "services".equals(val))) {
+ String highway = node.getTag("highway");
+ if (highway != null && ("motorway_junction".equals(highway) || "services".equals(highway) || "rest_area".equals(highway))) {
exits.add(node);
node.addTag("mkgmap:osmid", String.valueOf(node.getId()));
}
@@ -166,8 +169,12 @@
}
}
- if("motorway".equals(highway) || "trunk".equals(highway))
+ if ("motorway".equals(highway) || "trunk".equals(highway) || "primary".equals(highway) || way.tagIsLikeYes("motorroad"))
motorways.add(way);
+ else if (linkPOIsToWays && ("services".equals(highway) || "rest_area".equals(highway))) {
+ exits.add(way);
+ way.addTag("mkgmap:osmid", String.valueOf(way.getId()));
+ }
}
@Override
@@ -177,8 +184,12 @@
motorways.clear();
}
+ private static final int XTRA = 150; // very approx 300m
private void finishExits() {
- for (Node e : exits) {
+ if (exits.isEmpty() || motorways.isEmpty())
+ return;
+ ElementQuadTree majorRoads = new ElementQuadTree(saver.getBoundingBox(), motorways);
+ for (Element e : exits) {
String refTag = Exit.TAG_ROAD_REF;
if (e.getTag(refTag) == null) {
String exitName = e.getTag("name");
@@ -187,21 +198,86 @@
String ref = null;
Way motorway = null;
- for (Way w : motorways) {
- // uses an implicit call of Coord.equals()
- if (w.getPoints().contains(e.getLocation())) {
- motorway = w;
- ref = w.getTag("ref");
- if(ref != null)
- break;
+ Area bBox;
+ if (e instanceof Node)
+ bBox = Area.getBBox(Collections.singletonList(((Node)e).getLocation()));
+ else
+ bBox = Area.getBBox(((Way)e).getPoints());
+ String highway = e.getTag("highway");
+ boolean isServices = "services".equals(highway) || "rest_area".equals(highway);
+ if (isServices) // services will be just off the road, so increase size
+ bBox = new Area(bBox.getMinLat() - XTRA, bBox.getMinLong() - XTRA, bBox.getMaxLat() + XTRA, bBox.getMaxLong() + XTRA);
+ List<Way> possibleRoads = new ArrayList<>();
+ for (Element w : majorRoads.get(bBox)) {
+ motorway = (Way)w;
+ ref = motorway.getTag("ref");
+ if (ref != null) {
+ if (isServices) {
+ possibleRoads.add(motorway); // save all possibilities
+ } else { // probably on 2+ roads, save possibilities to find the more major road (doesn't have to be motorway)
+ // uses an implicit call of Coord.equals()
+ if (motorway.getPoints().contains(((Node)e).getLocation()))
+ possibleRoads.add(motorway);
+ }
}
}
+ if (possibleRoads.size() > 1) {
+ if (isServices) { // pick the closest road
+ Coord serviceCoord;
+ if (e instanceof Node)
+ serviceCoord = ((Node)e).getLocation();
+ else
+ // Simple-minded logic to see if a Way that probably defines [part of] a
+ // services area, hence might become a POI if option --link-pois-to-ways,
+ // is near the specified road.
+ // Just pick an arbitary Coord on the services boundary and find the nearest
+ // any Coord in the roads.
+ // No need to check if it is near the line between far-apart Coords because
+ // there also needs to be a junction so that can get off the road to the
+ // services.
+ serviceCoord = ((Way)e).getFirstPoint();
+ long closestRoad = Long.MAX_VALUE;
+ for (Way road : possibleRoads) {
+ long closestCoord = Long.MAX_VALUE;
+ for (Coord pointOnRoad : road.getPoints()) {
+ long dist = pointOnRoad.distanceInHighPrecSquared(serviceCoord);
+ if (dist < closestCoord)
+ closestCoord = dist;
+ }
+ if (closestCoord < closestRoad) {
+ closestRoad = closestCoord;
+ motorway = road;
+ ref = motorway.getTag("ref");
+ }
+ }
+ } else { // pick the most major road
+ int bestRoad = Integer.MAX_VALUE;
+ for (Way road : possibleRoads) {
+ String roadType = road.getTag("highway");
+ int thisRoad = 4;
+ if ("motorway".equals(roadType))
+ thisRoad = 0;
+ else if (road.tagIsLikeYes("motorroad"))
+ thisRoad = 1;
+ else if ("trunk".equals(roadType))
+ thisRoad = 2;
+ else if ("primary".equals(roadType))
+ thisRoad = 3;
+ if (thisRoad < bestRoad) {
+ bestRoad = thisRoad;
+ motorway = road;
+ ref = motorway.getTag("ref");
+ }
+ }
+ }
+ //log.info("Exit", exit, possibleRoads.size(), "options, chosen:", motorway, ref);
+ } // else 0 or 1 road; ref/motorway null or set correctly
if (ref != null) {
log.info("Adding", refTag + "=" + ref, "to exit", exitName);
e.addTag(refTag, ref);
} else if(motorway != null) {
- log.warn("Motorway exit", exitName, "is positioned on a motorway that doesn't have a 'ref' tag (" + e.getLocation().toOSMURL() + ")");
+ log.warn("Motorway exit", exitName, "is positioned on a motorway that doesn't have a 'ref' tag", e);
}
}
}
_______________________________________________
mkgmap-dev mailing list
[email protected]
http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev