diff --git a/brouter-codec/src/main/java/btools/codec/WaypointMatcher.java b/brouter-codec/src/main/java/btools/codec/WaypointMatcher.java index 2957c03..2889c7e 100644 --- a/brouter-codec/src/main/java/btools/codec/WaypointMatcher.java +++ b/brouter-codec/src/main/java/btools/codec/WaypointMatcher.java @@ -11,4 +11,6 @@ public interface WaypointMatcher { void transferNode(int ilon, int ilat); void end(); + + boolean hasMatch(int lon, int lat); } diff --git a/brouter-core/src/main/java/btools/router/FormatJson.java b/brouter-core/src/main/java/btools/router/FormatJson.java index a2c5b7a..dc7dcd4 100644 --- a/brouter-core/src/main/java/btools/router/FormatJson.java +++ b/brouter-core/src/main/java/btools/router/FormatJson.java @@ -122,7 +122,7 @@ public class FormatJson extends Formatter { .append(sele).append("],\n"); nn = n; } - sb.deleteCharAt(sb.lastIndexOf(",")); + if (t.nodes != null && !t.nodes.isEmpty()) sb.deleteCharAt(sb.lastIndexOf(",")); sb.append(" ]\n"); sb.append(" }\n"); diff --git a/brouter-core/src/main/java/btools/router/RoutingContext.java b/brouter-core/src/main/java/btools/router/RoutingContext.java index 86f1415..bd1c236 100644 --- a/brouter-core/src/main/java/btools/router/RoutingContext.java +++ b/brouter-core/src/main/java/btools/router/RoutingContext.java @@ -76,6 +76,7 @@ public final class RoutingContext { public double waypointCatchingRange; public boolean correctMisplacedViaPoints; public double correctMisplacedViaPointsDistance; + public boolean useDynamicDistance; private void setModel(String className) { if (className == null) { @@ -168,6 +169,8 @@ public final class RoutingContext { // Constant power of the biker (in W) bikerPower = expctxGlobal.getVariableValue("bikerPower", 100.f); + useDynamicDistance = expctxGlobal.getVariableValue("use_dynamic_range", 0f) == 1f; + boolean test = expctxGlobal.getVariableValue("check_start_way", 1f) == 1f; if (!test) expctxGlobal.freeNoWays(); diff --git a/brouter-core/src/main/java/btools/router/RoutingEngine.java b/brouter-core/src/main/java/btools/router/RoutingEngine.java index 09113a8..7c2134c 100644 --- a/brouter-core/src/main/java/btools/router/RoutingEngine.java +++ b/brouter-core/src/main/java/btools/router/RoutingEngine.java @@ -47,6 +47,8 @@ public class RoutingEngine extends Thread { private int MAX_STEPS_CHECK = 10; + private int MAX_DYNAMIC_RANGE = 60000; + protected OsmTrack foundTrack = new OsmTrack(); private OsmTrack foundRawTrack = null; private int alternativeIndex = 0; @@ -587,7 +589,13 @@ public class RoutingEngine extends Thread { mwp.direct = waypoints.get(i).direct; matchedWaypoints.add(mwp); } + int startSize = matchedWaypoints.size(); matchWaypointsToNodes(matchedWaypoints); + if (startSize < matchedWaypoints.size()) { + refTracks = new OsmTrack[matchedWaypoints.size()]; // used ways for alternatives + lastTracks = new OsmTrack[matchedWaypoints.size()]; + hasDirectRouting = true; + } routingContext.checkMatchedWaypointAgainstNogos(matchedWaypoints); @@ -615,6 +623,12 @@ public class RoutingEngine extends Thread { if (nearbyTrack != null) { matchedWaypoints.add(nearbyTrack.endPoint); } + } else { + if (lastTracks.length < matchedWaypoints.size()) { + refTracks = new OsmTrack[matchedWaypoints.size()]; // used ways for alternatives + lastTracks = new OsmTrack[matchedWaypoints.size()]; + hasDirectRouting = true; + } } OsmPath.seg = 1; // set segment counter @@ -968,7 +982,60 @@ public class RoutingEngine extends Thread { // geometric position matching finding the nearest routable way-section private void matchWaypointsToNodes(List unmatchedWaypoints) { resetCache(false); - nodesCache.matchWaypointsToNodes(unmatchedWaypoints, routingContext.waypointCatchingRange, islandNodePairs); + boolean useDynamicDistance = routingContext.useDynamicDistance; + double range = routingContext.waypointCatchingRange; + boolean ok = nodesCache.matchWaypointsToNodes(unmatchedWaypoints, range, islandNodePairs); + if (!ok && useDynamicDistance) { + logInfo("second check for way points"); + resetCache(false); + range = -MAX_DYNAMIC_RANGE; + List tmp = new ArrayList<>(); + for (MatchedWaypoint mwp : unmatchedWaypoints) { + if (mwp.crosspoint == null) tmp.add(mwp); + } + ok = nodesCache.matchWaypointsToNodes(tmp, range, islandNodePairs); + } + if (!ok) { + for (MatchedWaypoint mwp : unmatchedWaypoints) { + if (mwp.crosspoint == null) + throw new IllegalArgumentException(mwp.name + "-position not mapped in existing datafile"); + } + } + if (useDynamicDistance) { + List waypoints = new ArrayList<>(); + for (int i = 0; i < unmatchedWaypoints.size(); i++) { + MatchedWaypoint wp = unmatchedWaypoints.get(i); + if (wp.waypoint.calcDistance(wp.crosspoint) > routingContext.waypointCatchingRange) { + MatchedWaypoint nmw = new MatchedWaypoint(); + if (i == 0) { + OsmNodeNamed onn = new OsmNodeNamed(wp.waypoint); + onn.name = "from"; + nmw.waypoint = onn; + nmw.name = onn.name; + nmw.crosspoint = new OsmNode(wp.waypoint.ilon, wp.waypoint.ilat); + nmw.direct = true; + onn = new OsmNodeNamed(wp.crosspoint); + onn.name = wp.name + "_add"; + wp.waypoint = onn; + } else { + OsmNodeNamed onn = new OsmNodeNamed(wp.crosspoint); + onn.name = wp.name + "_add"; + nmw.waypoint = onn; + nmw.crosspoint = new OsmNode(wp.crosspoint.ilon, wp.crosspoint.ilat); + nmw.node1 = new OsmNode(wp.node1.ilon, wp.node1.ilat); + nmw.node2 = new OsmNode(wp.node2.ilon, wp.node2.ilat); + nmw.direct = true; + wp.crosspoint = new OsmNode(wp.waypoint.ilon, wp.waypoint.ilat); + } + if (wp.name != null) nmw.name = wp.name; + waypoints.add(nmw); + wp.name = wp.name + "_add"; + } + waypoints.add(wp); + } + unmatchedWaypoints.clear(); + unmatchedWaypoints.addAll(waypoints); + } } private OsmTrack searchTrack(MatchedWaypoint startWp, MatchedWaypoint endWp, OsmTrack nearbyTrack, OsmTrack refTrack) { diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/NodesCache.java b/brouter-mapaccess/src/main/java/btools/mapaccess/NodesCache.java index e16b8d2..4230e8b 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/NodesCache.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/NodesCache.java @@ -17,6 +17,9 @@ import btools.codec.WaypointMatcher; import btools.expressions.BExpressionContextWay; public final class NodesCache { + + private int MAX_DYNAMIC_CATCHES = 20; // used with RoutingEngiine MAX_DYNAMIC_RANGE = 60000m + private File segmentDir; private File secondarySegmentsDir = null; @@ -177,7 +180,8 @@ public final class NodesCache { } MicroCache segment = osmf.getMicroCache(ilon, ilat); - if (segment == null) { + // needed for a second chance + if (segment == null || (waypointMatcher != null && ((WaypointMatcherImpl) waypointMatcher).useDynamicRange)) { checkEnableCacheCleaning(); segment = osmf.createMicroCache(ilon, ilat, dataBuffers, expCtxWay, waypointMatcher, directWeaving ? nodesMap : null); @@ -282,15 +286,15 @@ public final class NodesCache { return existing; } - public void matchWaypointsToNodes(List unmatchedWaypoints, double maxDistance, OsmNodePairSet islandNodePairs) { + public boolean matchWaypointsToNodes(List unmatchedWaypoints, double maxDistance, OsmNodePairSet islandNodePairs) { waypointMatcher = new WaypointMatcherImpl(unmatchedWaypoints, maxDistance, islandNodePairs); for (MatchedWaypoint mwp : unmatchedWaypoints) { int cellsize = 12500; - preloadPosition(mwp.waypoint, cellsize); + preloadPosition(mwp.waypoint, cellsize, 1, false); // get a second chance - if (mwp.crosspoint == null) { + if (mwp.crosspoint == null || mwp.radius > Math.abs(maxDistance)) { cellsize = 1000000 / 32; - preloadPosition(mwp.waypoint, cellsize); + preloadPosition(mwp.waypoint, cellsize, maxDistance < 0 ? MAX_DYNAMIC_CATCHES : 2, maxDistance < 0); } } @@ -305,7 +309,8 @@ public final class NodesCache { mwp.crosspoint = new OsmNode(mwp.waypoint.ilon, mwp.waypoint.ilat); mwp.direct = true; } else { - throw new IllegalArgumentException(mwp.name + "-position not mapped in existing datafile"); + // do not break here throw new IllegalArgumentException(mwp.name + "-position not mapped in existing datafile"); + return false; } } if (unmatchedWaypoints.size() > 1 && i == unmatchedWaypoints.size() - 1 && unmatchedWaypoints.get(i - 1).direct) { @@ -313,21 +318,27 @@ public final class NodesCache { mwp.direct = true; } } + return true; } - private void preloadPosition(OsmNode n, int d) { + private void preloadPosition(OsmNode n, int d, int maxscale, boolean bUseDynamicRange) { first_file_access_failed = false; first_file_access_name = null; loadSegmentFor(n.ilon, n.ilat); if (first_file_access_failed) { throw new IllegalArgumentException("datafile " + first_file_access_name + " not found"); } - for (int idxLat = -1; idxLat <= 1; idxLat++) - for (int idxLon = -1; idxLon <= 1; idxLon++) { - if (idxLon != 0 || idxLat != 0) { - loadSegmentFor(n.ilon + d * idxLon, n.ilat + d * idxLat); + int scale = 1; + while (scale < maxscale) { + for (int idxLat = -scale; idxLat <= scale; idxLat++) + for (int idxLon = -scale; idxLon <= scale; idxLon++) { + if (idxLon != 0 || idxLat != 0) { + loadSegmentFor(n.ilon + d * idxLon, n.ilat + d * idxLat); + } } - } + if (bUseDynamicRange && waypointMatcher.hasMatch(n.ilon, n.ilat)) break; + scale++; + } } private OsmFile fileForSegment(int lonDegree, int latDegree) throws Exception { diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/WaypointMatcherImpl.java b/brouter-mapaccess/src/main/java/btools/mapaccess/WaypointMatcherImpl.java index d646436..6096c79 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/WaypointMatcherImpl.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/WaypointMatcherImpl.java @@ -1,12 +1,12 @@ package btools.mapaccess; -import java.util.List; import java.util.Collections; import java.util.Comparator; +import java.util.List; import btools.codec.WaypointMatcher; -import btools.util.CheapRuler; import btools.util.CheapAngleMeter; +import btools.util.CheapRuler; /** * the WaypointMatcher is feeded by the decoder with geoemtries of ways that are @@ -29,6 +29,9 @@ public final class WaypointMatcherImpl implements WaypointMatcher { private int lonLast; private int latLast; boolean useAsStartWay = true; + private int maxWptIdx; + private double maxDistance; + public boolean useDynamicRange = false; private Comparator comparator; @@ -36,6 +39,13 @@ public final class WaypointMatcherImpl implements WaypointMatcher { this.waypoints = waypoints; this.islandPairs = islandPairs; MatchedWaypoint last = null; + this.maxDistance = maxDistance; + if (maxDistance < 0.) { + this.maxDistance *= -1; + maxDistance *= -1; + useDynamicRange = true; + } + for (MatchedWaypoint mwp : waypoints) { mwp.radius = maxDistance; if (last != null && mwp.directionToNext == -1) { @@ -50,6 +60,7 @@ public final class WaypointMatcherImpl implements WaypointMatcher { } else { last.directionToNext = CheapAngleMeter.getDirection(last.waypoint.ilon, last.waypoint.ilat, waypoints.get(lastidx).waypoint.ilon, waypoints.get(lastidx).waypoint.ilat); } + maxWptIdx = waypoints.size() - 1; // sort result list comparator = new Comparator<>() { @@ -79,7 +90,7 @@ public final class WaypointMatcherImpl implements WaypointMatcher { //for ( MatchedWaypoint mwp : waypoints ) for (int i = 0; i < waypoints.size(); i++) { - if (!useAsStartWay && i==0) continue; + if (!useAsStartWay && i == 0) continue; MatchedWaypoint mwp = waypoints.get(i); if (mwp.direct && @@ -105,7 +116,7 @@ public final class WaypointMatcherImpl implements WaypointMatcher { double r22 = x2 * x2 + y2 * y2; double radius = Math.abs(r12 < r22 ? y1 * dx - x1 * dy : y2 * dx - x2 * dy) / d; - if (radius <= mwp.radius) { + if (radius < mwp.radius) { double s1 = x1 * dx + y1 * dy; double s2 = x2 * dx + y2 * dy; @@ -115,8 +126,10 @@ public final class WaypointMatcherImpl implements WaypointMatcher { } if (s2 > 0.) { radius = Math.sqrt(s1 < s2 ? r12 : r22); - if (radius > mwp.radius) + + if (radius > mwp.radius) { continue; + } } // new match for that waypoint mwp.radius = radius; // shortest distance to way @@ -226,6 +239,17 @@ public final class WaypointMatcherImpl implements WaypointMatcher { } } + @Override + public boolean hasMatch(int lon, int lat) { + for (MatchedWaypoint mwp : waypoints) { + if (mwp.waypoint.ilon == lon && mwp.waypoint.ilat == lat && + (mwp.radius < this.maxDistance || mwp.crosspoint != null)) { + return true; + } + } + return false; + } + // check limit of list size (avoid long runs) void updateWayList(List ways, MatchedWaypoint mw) { ways.add(mw); diff --git a/misc/profiles2/car-vario.brf b/misc/profiles2/car-vario.brf index 34e5be1..87aeac5 100644 --- a/misc/profiles2/car-vario.brf +++ b/misc/profiles2/car-vario.brf @@ -17,6 +17,8 @@ assign avoid_toll = false # %avoid_toll% | Avoid paid roads | boolean assign avoid_unpaved = false # %avoid_unpaved% | Avoid unpaved roads, if possible | boolean assign avoid_motorways = false # %avoid_motorways% | Avoid motorways | boolean +assign use_dynamic_range = false # %use_dynamic_range% | Enable distant start/end points | boolean + # Kinematic model parameters assign vmax = 90 # %vmax% | Target speed (in km/h) | number assign recup_efficiency = 0.7 # %recup_efficiency% | (ratio) | number diff --git a/misc/profiles2/gravel.brf b/misc/profiles2/gravel.brf index 3468ae0..5485234 100644 --- a/misc/profiles2/gravel.brf +++ b/misc/profiles2/gravel.brf @@ -8,6 +8,8 @@ assign turnInstructionRoundabouts true # %turnInstructionRoundabouts% | Special assign considerTurnRestrictions true assign turnInstructionMode 1 # %turnInstructionMode% | Mode for the generated turn-by-turn directions | [0=none, 1=auto-choose, 2=locus-style, 3=osmand-style, 4=comment-style, 5=gpsies-style, 6=oruxmaps-style] +assign use_dynamic_range = false # %use_dynamic_range% | Enable distant start/end points | boolean + #assign processUnusedTags true assign pass1coefficient 4 assign validForBikes true @@ -48,7 +50,7 @@ assign uphillcutoff switch bad_when_steep ( min 1.5 uphillcutoff ) uphillcut assign any_cycleway or cycleway=track|lane|shared_lane|shared or and cycleway:right=track|lane|shared_lane reversedirection= and cycleway:left=track|lane|shared_lane reversedirection=yes - + assign any_cycleroute or route_bicycle_icn=yes or route_bicycle_ncn=yes or route_bicycle_rcn=yes route_bicycle_lcn=yes assign turncost switch junction=roundabout 15 65 @@ -166,7 +168,7 @@ assign is_paved assign initialcost multiply initialcost ( switch and prefer_unpaved_paths is_paved 2 1 ) - + assign smoothnesspenalty multiply smoothnesspenalty ( switch and prefer_unpaved_paths is_paved 2 1 ) @@ -231,7 +233,7 @@ assign noforestpenalty switch estimated_forest_class=6 1.0 1 1 assign townpenalty - switch avoid_towns + switch avoid_towns switch estimated_town_class= 1.0 switch estimated_town_class=1 1.2 switch estimated_town_class=2 1.4 diff --git a/misc/profiles2/hiking-mountain.brf b/misc/profiles2/hiking-mountain.brf index 688da66..d54aa2c 100644 --- a/misc/profiles2/hiking-mountain.brf +++ b/misc/profiles2/hiking-mountain.brf @@ -44,6 +44,8 @@ assign initialcost_value 0 # not used now assign allow_steps true # %allow_steps% | Set to false to disallow steps | boolean assign allow_ferries true # %allow_ferries% | set to false to disallow ferries | boolean +assign use_dynamic_range false # %use_dynamic_range% | Enable distant start/end points | boolean + assign cost_of_unknown 2 # 2 as default # diff --git a/misc/profiles2/moped.brf b/misc/profiles2/moped.brf index 50304de..e270fbf 100644 --- a/misc/profiles2/moped.brf +++ b/misc/profiles2/moped.brf @@ -16,7 +16,9 @@ assign uphillcutoff 0 assign validForBikes 1 assign validForCars 1 -assign turnInstructionMode = 1 # %turnInstructionMode% | Mode for the generated turn instructions | [0=none, 1=auto-choose, 2=locus-style, 3=osmand-style, 4=comment-style, 5=gpsies-style, 6=orux-style, 7=locus-old-style] +assign use_dynamic_range = false # %use_dynamic_range% | Enable distant start/end points | boolean + +assign turnInstructionMode = 1 # %turnInstructionMode% | Mode for the generated turn instructions | [0=none, 1=auto-choose, 2=locus-style, 3=osmand-style, 4=comment-style, 5=gpsies-style, 6=orux-style, 7=locus-old-style] ---context:way # following code refers to way-tags diff --git a/misc/profiles2/mtb.brf b/misc/profiles2/mtb.brf index 1b360cb..3e52cc5 100644 --- a/misc/profiles2/mtb.brf +++ b/misc/profiles2/mtb.brf @@ -11,6 +11,9 @@ # bstart /global ---context:global + + assign use_dynamic_range = false # %use_dynamic_range% | Enable distant start/end points | boolean + assign iswet 0 # 0 as default, *) flag for weather conditions assign turnInstructionMode = 1 # 0=none, 1=auto-choose, 2=locus-style, 3=osmand-style assign cycleroutes_pref 0.2 # also CRP *) costfactor penalty for not being cycleroute diff --git a/misc/profiles2/river.brf b/misc/profiles2/river.brf index 80b46cc..45fa29f 100644 --- a/misc/profiles2/river.brf +++ b/misc/profiles2/river.brf @@ -2,6 +2,8 @@ # the elevation parameters +assign use_dynamic_range = false # %use_dynamic_range% | Enable distant start/end points | boolean + assign downhillcost 0 assign downhillcutoff 1.5 assign uphillcost 0 diff --git a/misc/profiles2/trekking.brf b/misc/profiles2/trekking.brf index b4aa6ed..5e7e400 100644 --- a/misc/profiles2/trekking.brf +++ b/misc/profiles2/trekking.brf @@ -15,6 +15,8 @@ assign ignore_cycleroutes = false # %ignore_cycleroutes% | Set true for assign stick_to_cycleroutes = false # %stick_to_cycleroutes% | Set true to just follow cycleroutes | boolean assign avoid_unsafe = false # %avoid_unsafe% | Set true to avoid standard highways | boolean +assign use_dynamic_range = false # %use_dynamic_range% | Enable distant start/end points | boolean + assign consider_noise = false # %consider_noise% | Activate to prefer a low-noise route | boolean assign consider_river = false # %consider_river% | Activate to prefer a route along rivers, lakes, etc. | boolean assign consider_forest = false # %consider_forest% | Activate to prefer a route in forest or parks | boolean