Update native routing
This commit is contained in:
parent
efaf4e25c4
commit
0a2edd2afc
10 changed files with 428 additions and 314 deletions
|
@ -1,13 +1,23 @@
|
||||||
package net.osmand;
|
package net.osmand;
|
||||||
|
|
||||||
|
import gnu.trove.list.array.TIntArrayList;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion;
|
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion;
|
||||||
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteSubregion;
|
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteSubregion;
|
||||||
import net.osmand.binary.RouteDataObject;
|
import net.osmand.binary.RouteDataObject;
|
||||||
import net.osmand.render.RenderingRuleSearchRequest;
|
import net.osmand.render.RenderingRuleSearchRequest;
|
||||||
import net.osmand.render.RenderingRulesStorage;
|
import net.osmand.render.RenderingRulesStorage;
|
||||||
|
import net.osmand.router.GeneralRouter;
|
||||||
import net.osmand.router.RouteSegmentResult;
|
import net.osmand.router.RouteSegmentResult;
|
||||||
|
import net.osmand.router.RoutingConfiguration;
|
||||||
|
|
||||||
public class NativeLibrary {
|
public class NativeLibrary {
|
||||||
|
|
||||||
|
@ -99,8 +109,32 @@ public class NativeLibrary {
|
||||||
return closeBinaryMapFile(filePath);
|
return closeBinaryMapFile(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RouteSegmentResult[] testRoutingInternal(int sx31, int sy31, int ex31, int ey31) {
|
public RouteSegmentResult[] runNativeRouting(int sx31, int sy31, int ex31, int ey31, RoutingConfiguration config) {
|
||||||
return testRouting(sx31, sy31, ex31, ey31);
|
TIntArrayList state = new TIntArrayList();
|
||||||
|
List<String> keys = new ArrayList<String>();
|
||||||
|
List<String> values = new ArrayList<String>();
|
||||||
|
GeneralRouter r = (GeneralRouter) config.router;
|
||||||
|
fillObjects(state, keys, values, 0, r.highwaySpeed);
|
||||||
|
fillObjects(state, keys, values, 1, r.highwayPriorities);
|
||||||
|
fillObjects(state, keys, values, 2, r.avoid);
|
||||||
|
fillObjects(state, keys, values, 3, r.obstacles);
|
||||||
|
fillObjects(state, keys, values, 4, r.routingObstacles);
|
||||||
|
LinkedHashMap<String, String> attrs = new LinkedHashMap<String, String>(config.attributes);
|
||||||
|
attrs.putAll(r.attributes);
|
||||||
|
fillObjects(state, keys, values, 5, attrs);
|
||||||
|
|
||||||
|
return nativeRouting(new int[]{sx31, sy31, ex31, ey31}, state.toArray(), keys.toArray(new String[keys.size()]),
|
||||||
|
values.toArray(new String[values.size()]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void fillObjects(TIntArrayList state, List<String> keys, List<String> values, int s, Map<String, T> map) {
|
||||||
|
Iterator<Entry<String, T>> it = map.entrySet().iterator();
|
||||||
|
while(it.hasNext()) {
|
||||||
|
Entry<String, T> n = it.next();
|
||||||
|
state.add(s);
|
||||||
|
keys.add(n.getKey());
|
||||||
|
values.add(n.getValue()+"");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -122,7 +156,7 @@ public class NativeLibrary {
|
||||||
|
|
||||||
protected static native RouteDataObject[] getRouteDataObjects(RouteRegion reg, long rs, int x31, int y31);
|
protected static native RouteDataObject[] getRouteDataObjects(RouteRegion reg, long rs, int x31, int y31);
|
||||||
|
|
||||||
protected static native RouteSegmentResult[] testRouting(int sx31, int sy31, int ex31, int ey31);
|
protected static native RouteSegmentResult[] nativeRouting(int[] coordinates, int[] state, String[] keyConfig, String[] valueConfig);
|
||||||
|
|
||||||
protected static native void deleteSearchResult(long searchResultHandle);
|
protected static native void deleteSearchResult(long searchResultHandle);
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package net.osmand.router;
|
package net.osmand.router;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import net.osmand.binary.RouteDataObject;
|
import net.osmand.binary.RouteDataObject;
|
||||||
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion;
|
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion;
|
||||||
|
@ -17,26 +19,25 @@ public class GeneralRouter extends VehicleRouter {
|
||||||
public static final String AVOID_MOTORWAY = "avoid_motorway";
|
public static final String AVOID_MOTORWAY = "avoid_motorway";
|
||||||
public static final String AVOID_UNPAVED = "avoid_unpaved";
|
public static final String AVOID_UNPAVED = "avoid_unpaved";
|
||||||
|
|
||||||
Map<String, Float> highwaySpeed ;
|
public Map<String, Float> highwaySpeed ;
|
||||||
Map<String, Float> highwayPriorities ;
|
public Map<String, Float> highwayPriorities ;
|
||||||
Map<String, Float> highwayFuturePriorities ;
|
public Map<String, Float> avoid ;
|
||||||
Map<String, Float> avoid ;
|
public Map<String, Float> obstacles;
|
||||||
Map<String, Float> obstacles;
|
public Map<String, Float> routingObstacles;
|
||||||
Map<String, Float> routingObstacles;
|
public Map<String, String> attributes;
|
||||||
Map<String, String> attributes;
|
|
||||||
|
|
||||||
|
|
||||||
private GeneralRouterProfile profile;
|
private GeneralRouterProfile profile;
|
||||||
|
|
||||||
// cached values
|
// cached values
|
||||||
private Boolean restrictionsAware;
|
private boolean restrictionsAware = true;
|
||||||
private Float leftTurn;
|
private float leftTurn;
|
||||||
private Float roundaboutTurn;
|
private float roundaboutTurn;
|
||||||
private Float rightTurn;
|
private float rightTurn;
|
||||||
private Boolean onewayAware;
|
private boolean onewayAware = true;
|
||||||
private Boolean followSpeedLimitations;
|
private boolean followSpeedLimitations = true;
|
||||||
private Float minDefaultSpeed;
|
private float minDefaultSpeed = 10;
|
||||||
private Float maxDefaultSpeed;
|
private float maxDefaultSpeed = 10;
|
||||||
|
|
||||||
public enum GeneralRouterProfile {
|
public enum GeneralRouterProfile {
|
||||||
CAR,
|
CAR,
|
||||||
|
@ -49,21 +50,51 @@ public class GeneralRouter extends VehicleRouter {
|
||||||
this.profile = profile;
|
this.profile = profile;
|
||||||
highwaySpeed = new LinkedHashMap<String, Float>();
|
highwaySpeed = new LinkedHashMap<String, Float>();
|
||||||
highwayPriorities = new LinkedHashMap<String, Float>();
|
highwayPriorities = new LinkedHashMap<String, Float>();
|
||||||
highwayFuturePriorities = new LinkedHashMap<String, Float>();
|
|
||||||
avoid = new LinkedHashMap<String, Float>();
|
avoid = new LinkedHashMap<String, Float>();
|
||||||
obstacles = new LinkedHashMap<String, Float>();
|
obstacles = new LinkedHashMap<String, Float>();
|
||||||
routingObstacles = new LinkedHashMap<String, Float>();
|
routingObstacles = new LinkedHashMap<String, Float>();
|
||||||
|
Iterator<Entry<String, String>> e = attributes.entrySet().iterator();
|
||||||
|
while(e.hasNext()){
|
||||||
|
Entry<String, String> next = e.next();
|
||||||
|
addAttribute(next.getKey(), next.getValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public GeneralRouter(GeneralRouter pr) {
|
public GeneralRouter(GeneralRouter pr) {
|
||||||
this.highwaySpeed = new LinkedHashMap<String, Float>(pr.highwaySpeed);
|
this.highwaySpeed = new LinkedHashMap<String, Float>(pr.highwaySpeed);
|
||||||
this.highwayPriorities = new LinkedHashMap<String, Float>(pr.highwayPriorities);
|
this.highwayPriorities = new LinkedHashMap<String, Float>(pr.highwayPriorities);
|
||||||
this.highwayFuturePriorities = new LinkedHashMap<String, Float>(pr.highwayFuturePriorities);
|
|
||||||
this.avoid = new LinkedHashMap<String, Float>(pr.avoid);
|
this.avoid = new LinkedHashMap<String, Float>(pr.avoid);
|
||||||
this.obstacles = new LinkedHashMap<String, Float>(pr.obstacles);
|
this.obstacles = new LinkedHashMap<String, Float>(pr.obstacles);
|
||||||
this.routingObstacles = new LinkedHashMap<String, Float>(pr.routingObstacles);
|
this.routingObstacles = new LinkedHashMap<String, Float>(pr.routingObstacles);
|
||||||
this.attributes = new LinkedHashMap<String, String>(pr.attributes);
|
this.attributes = new LinkedHashMap<String, String>(pr.attributes);
|
||||||
this.profile = pr.profile;
|
this.profile = pr.profile;
|
||||||
|
Iterator<Entry<String, String>> e = pr.attributes.entrySet().iterator();
|
||||||
|
while(e.hasNext()){
|
||||||
|
Entry<String, String> next = e.next();
|
||||||
|
addAttribute(next.getKey(), next.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAttribute(String k, String v) {
|
||||||
|
attributes.put(k, v);
|
||||||
|
if(k.equals("restrictionsAware")) {
|
||||||
|
restrictionsAware = parseSilentBoolean(v, restrictionsAware);
|
||||||
|
} else if(k.equals("onewayAware")) {
|
||||||
|
onewayAware = parseSilentBoolean(v, onewayAware);
|
||||||
|
} else if(k.equals("followSpeedLimitations")) {
|
||||||
|
followSpeedLimitations = parseSilentBoolean(v, followSpeedLimitations);
|
||||||
|
} else if(k.equals("leftTurn")) {
|
||||||
|
leftTurn = parseSilentFloat(v, leftTurn);
|
||||||
|
} else if(k.equals("rightTurn")) {
|
||||||
|
rightTurn = parseSilentFloat(v, rightTurn);
|
||||||
|
} else if(k.equals("roundaboutTurn")) {
|
||||||
|
roundaboutTurn = parseSilentFloat(v, roundaboutTurn);
|
||||||
|
} else if(k.equals("minDefaultSpeed")) {
|
||||||
|
minDefaultSpeed = parseSilentFloat(v, minDefaultSpeed * 3.6f) / 3.6f;
|
||||||
|
} else if(k.equals("maxDefaultSpeed")) {
|
||||||
|
maxDefaultSpeed = parseSilentFloat(v, maxDefaultSpeed * 3.6f) / 3.6f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -105,9 +136,6 @@ public class GeneralRouter extends VehicleRouter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean restrictionsAware() {
|
public boolean restrictionsAware() {
|
||||||
if(restrictionsAware == null) {
|
|
||||||
restrictionsAware = parseSilentBoolean(attributes.get("restrictionsAware"), true);
|
|
||||||
}
|
|
||||||
return restrictionsAware;
|
return restrictionsAware;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,9 +184,6 @@ public class GeneralRouter extends VehicleRouter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOnewayAware() {
|
public boolean isOnewayAware() {
|
||||||
if(onewayAware == null) {
|
|
||||||
onewayAware = parseSilentBoolean(attributes.get("onewayAware"), true);
|
|
||||||
}
|
|
||||||
return onewayAware;
|
return onewayAware;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,23 +195,9 @@ public class GeneralRouter extends VehicleRouter {
|
||||||
return super.isOneWay(road);
|
return super.isOneWay(road);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getFutureRoadPriority(RouteDataObject road) {
|
|
||||||
float priority = 1;
|
|
||||||
for (int i = 0; i < road.types.length; i++) {
|
|
||||||
RouteTypeRule r = road.region.quickGetEncodingRule(road.types[i]);
|
|
||||||
if(highwayFuturePriorities.containsKey(r.getTag()+"$"+r.getValue())){
|
|
||||||
priority = highwayFuturePriorities.get(r.getTag()+"$"+r.getValue());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return priority;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFollowSpeedLimitations(){
|
public boolean isFollowSpeedLimitations(){
|
||||||
if(followSpeedLimitations == null){
|
|
||||||
followSpeedLimitations = parseSilentBoolean(attributes.get("followSpeedLimitations"), true);
|
|
||||||
}
|
|
||||||
return followSpeedLimitations;
|
return followSpeedLimitations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,38 +252,23 @@ public class GeneralRouter extends VehicleRouter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getMinDefaultSpeed() {
|
public float getMinDefaultSpeed() {
|
||||||
if(minDefaultSpeed == null ){
|
return minDefaultSpeed;
|
||||||
minDefaultSpeed = parseSilentFloat(attributes.get("minDefaultSpeed"), 10);
|
|
||||||
}
|
|
||||||
return minDefaultSpeed / 3.6f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getMaxDefaultSpeed() {
|
public float getMaxDefaultSpeed() {
|
||||||
if(maxDefaultSpeed == null ){
|
return maxDefaultSpeed ;
|
||||||
maxDefaultSpeed = parseSilentFloat(attributes.get("maxDefaultSpeed"), 10);
|
|
||||||
}
|
|
||||||
return maxDefaultSpeed / 3.6f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public double getLeftTurn() {
|
public double getLeftTurn() {
|
||||||
if(leftTurn == null) {
|
|
||||||
leftTurn = parseSilentFloat(attributes.get("leftTurn"), 0);
|
|
||||||
}
|
|
||||||
return leftTurn;
|
return leftTurn;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getRightTurn() {
|
public double getRightTurn() {
|
||||||
if(rightTurn == null) {
|
|
||||||
rightTurn = parseSilentFloat(attributes.get("rightTurn"), 0);
|
|
||||||
}
|
|
||||||
return rightTurn;
|
return rightTurn;
|
||||||
}
|
}
|
||||||
public double getRoundaboutTurn() {
|
public double getRoundaboutTurn() {
|
||||||
if(roundaboutTurn == null) {
|
|
||||||
roundaboutTurn = parseSilentFloat(attributes.get("roundaboutTurn"), 0);
|
|
||||||
}
|
|
||||||
return roundaboutTurn;
|
return roundaboutTurn;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
|
@ -307,7 +303,7 @@ public class GeneralRouter extends VehicleRouter {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void specialize(String specializationTag, Map<String, Float> m){
|
private <T> void specialize(String specializationTag, Map<String, T> m){
|
||||||
ArrayList<String> ks = new ArrayList<String>(m.keySet());
|
ArrayList<String> ks = new ArrayList<String>(m.keySet());
|
||||||
for(String s : ks){
|
for(String s : ks){
|
||||||
if(s.startsWith(specializationTag +":")) {
|
if(s.startsWith(specializationTag +":")) {
|
||||||
|
@ -319,15 +315,25 @@ public class GeneralRouter extends VehicleRouter {
|
||||||
@Override
|
@Override
|
||||||
public GeneralRouter specialization(String specializationTag) {
|
public GeneralRouter specialization(String specializationTag) {
|
||||||
GeneralRouter gr = new GeneralRouter(this);
|
GeneralRouter gr = new GeneralRouter(this);
|
||||||
gr.specialize(specializationTag, gr.highwayFuturePriorities);
|
|
||||||
gr.specialize(specializationTag, gr.highwayPriorities);
|
gr.specialize(specializationTag, gr.highwayPriorities);
|
||||||
gr.specialize(specializationTag, gr.highwaySpeed);
|
gr.specialize(specializationTag, gr.highwaySpeed);
|
||||||
gr.specialize(specializationTag, gr.avoid);
|
gr.specialize(specializationTag, gr.avoid);
|
||||||
gr.specialize(specializationTag, gr.obstacles);
|
gr.specialize(specializationTag, gr.obstacles);
|
||||||
gr.specialize(specializationTag, gr.routingObstacles);
|
gr.specialize(specializationTag, gr.routingObstacles);
|
||||||
|
gr.specialize(specializationTag, gr.attributes);
|
||||||
return gr;
|
return gr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAttribute(String attribute) {
|
||||||
|
return attributes.containsKey(attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAttribute(String attribute) {
|
||||||
|
return attributes.get(attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,38 +16,31 @@ import org.xml.sax.SAXException;
|
||||||
import org.xml.sax.helpers.DefaultHandler;
|
import org.xml.sax.helpers.DefaultHandler;
|
||||||
|
|
||||||
public class RoutingConfiguration {
|
public class RoutingConfiguration {
|
||||||
|
|
||||||
|
public static final int DEFAULT_MEMORY_LIMIT = 30;
|
||||||
|
public Map<String, String> attributes = new LinkedHashMap<String, String>();
|
||||||
|
|
||||||
// 1. parameters of routing and different tweaks
|
// 1. parameters of routing and different tweaks
|
||||||
// Influence on A* : f(x) + heuristicCoefficient*g(X)
|
// Influence on A* : f(x) + heuristicCoefficient*g(X)
|
||||||
public float heuristicCoefficient = 1;
|
public float heuristicCoefficient = 1;
|
||||||
|
|
||||||
public static final int DEFAULT_MEMORY_LIMIT = 30;
|
|
||||||
|
|
||||||
|
|
||||||
// 1.1 tile load parameters (should not affect routing)
|
// 1.1 tile load parameters (should not affect routing)
|
||||||
public int ZOOM_TO_LOAD_TILES = 16;
|
public int ZOOM_TO_LOAD_TILES = 16;
|
||||||
public int memoryLimitation;
|
public int memoryLimitation;
|
||||||
|
|
||||||
// 1.2 Dynamic road prioritizing (heuristic)
|
// 1.2 Build A* graph in backward/forward direction (can affect results)
|
||||||
public boolean useDynamicRoadPrioritising = true;
|
|
||||||
public int dynamicRoadPriorityDistance = 0;
|
|
||||||
|
|
||||||
// 1.3 Relaxing strategy
|
|
||||||
public boolean useRelaxingStrategy = true;
|
|
||||||
public int ITERATIONS_TO_RELAX_NODES = 100;
|
|
||||||
public float RELAX_NODES_IF_START_DIST_COEF = 3;
|
|
||||||
|
|
||||||
// 1.4 Build A* graph in backward/forward direction (can affect results)
|
|
||||||
// 0 - 2 ways, 1 - direct way, -1 - reverse way
|
// 0 - 2 ways, 1 - direct way, -1 - reverse way
|
||||||
public int planRoadDirection = 0;
|
public int planRoadDirection = 0;
|
||||||
|
|
||||||
// 1.5 Router specific coefficients and restrictions
|
// 1.3 Router specific coefficients and restrictions
|
||||||
public VehicleRouter router = new GeneralRouter(GeneralRouterProfile.CAR, new LinkedHashMap<String, String>());
|
public VehicleRouter router = new GeneralRouter(GeneralRouterProfile.CAR, new LinkedHashMap<String, String>());
|
||||||
public String routerName = "";
|
public String routerName = "";
|
||||||
|
|
||||||
// 1.6 Used to calculate route in movement
|
// 1.4 Used to calculate route in movement
|
||||||
public Double initialDirection;
|
public Double initialDirection;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
// Design time storage
|
// Design time storage
|
||||||
private String defaultRouter = "";
|
private String defaultRouter = "";
|
||||||
|
@ -62,10 +55,21 @@ public class RoutingConfiguration {
|
||||||
router = defaultRouter;
|
router = defaultRouter;
|
||||||
}
|
}
|
||||||
RoutingConfiguration i = new RoutingConfiguration();
|
RoutingConfiguration i = new RoutingConfiguration();
|
||||||
|
if (routers.containsKey(router)) {
|
||||||
|
i.router = routers.get(router);
|
||||||
|
if (specialization != null) {
|
||||||
|
for (String s : specialization) {
|
||||||
|
i.router = i.router.specialization(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i.routerName = router;
|
||||||
|
}
|
||||||
|
attributes.put("routerName", router);
|
||||||
|
i.attributes.putAll(attributes);
|
||||||
i.initialDirection = direction;
|
i.initialDirection = direction;
|
||||||
i.heuristicCoefficient = parseSilentFloat(getAttribute(router, "heuristicCoefficient"), i.heuristicCoefficient);
|
i.heuristicCoefficient = parseSilentFloat(getAttribute(i.router, "heuristicCoefficient"), i.heuristicCoefficient);
|
||||||
i.ZOOM_TO_LOAD_TILES = parseSilentInt(getAttribute(router, "zoomToLoadTiles"), i.ZOOM_TO_LOAD_TILES);
|
i.ZOOM_TO_LOAD_TILES = parseSilentInt(getAttribute(i.router, "zoomToLoadTiles"), i.ZOOM_TO_LOAD_TILES);
|
||||||
int desirable = parseSilentInt(getAttribute(router, "memoryLimitInMB"), 0);
|
int desirable = parseSilentInt(getAttribute(i.router, "memoryLimitInMB"), 0);
|
||||||
if(desirable != 0) {
|
if(desirable != 0) {
|
||||||
i.memoryLimitation = desirable * (1 << 20);
|
i.memoryLimitation = desirable * (1 << 20);
|
||||||
} else {
|
} else {
|
||||||
|
@ -74,29 +78,15 @@ public class RoutingConfiguration {
|
||||||
}
|
}
|
||||||
i.memoryLimitation = memoryLimitMB * (1 << 20);
|
i.memoryLimitation = memoryLimitMB * (1 << 20);
|
||||||
}
|
}
|
||||||
i.useDynamicRoadPrioritising = parseSilentBoolean(getAttribute(router, "useDynamicRoadPrioritising"), i.useDynamicRoadPrioritising);
|
|
||||||
i.useRelaxingStrategy = parseSilentBoolean(getAttribute(router, "useRelaxingStrategy"), i.useRelaxingStrategy);
|
|
||||||
i.dynamicRoadPriorityDistance = parseSilentInt(getAttribute(router, "dynamicRoadPriorityDistance"), i.dynamicRoadPriorityDistance);
|
|
||||||
i.ITERATIONS_TO_RELAX_NODES = parseSilentInt(getAttribute(router, "iterationsToRelaxRoutes"), i.ITERATIONS_TO_RELAX_NODES);
|
|
||||||
i.RELAX_NODES_IF_START_DIST_COEF = parseSilentFloat(getAttribute(router, "relaxNodesIfStartDistSmallCoeff"), i.RELAX_NODES_IF_START_DIST_COEF);
|
|
||||||
i.planRoadDirection = parseSilentInt(getAttribute(router, "planRoadDirection"), i.planRoadDirection);
|
i.planRoadDirection = parseSilentInt(getAttribute(router, "planRoadDirection"), i.planRoadDirection);
|
||||||
|
|
||||||
if (!routers.containsKey(router)) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
i.router = routers.get(router);
|
|
||||||
if(specialization != null) {
|
|
||||||
for(String s : specialization) {
|
|
||||||
i.router = i.router.specialization(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i.routerName = router;
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getAttribute(String router, String propertyName) {
|
private String getAttribute(VehicleRouter router, String propertyName) {
|
||||||
if (attributes.containsKey(router + "$" + propertyName)) {
|
if (router.containsAttribute(propertyName)) {
|
||||||
return attributes.get(router + "$" + propertyName);
|
return router.getAttribute(propertyName);
|
||||||
}
|
}
|
||||||
return attributes.get(propertyName);
|
return attributes.get(propertyName);
|
||||||
}
|
}
|
||||||
|
@ -151,15 +141,15 @@ public class RoutingConfiguration {
|
||||||
@Override
|
@Override
|
||||||
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
|
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
|
||||||
String name = parser.isNamespaceAware() ? localName : qName;
|
String name = parser.isNamespaceAware() ? localName : qName;
|
||||||
|
previousTag = name;
|
||||||
if("osmand_routing_config".equals(name)) {
|
if("osmand_routing_config".equals(name)) {
|
||||||
config.defaultRouter = attributes.getValue("defaultProfile");
|
config.defaultRouter = attributes.getValue("defaultProfile");
|
||||||
} else if("attribute".equals(name)) {
|
} else if("attribute".equals(name)) {
|
||||||
previousKey = attributes.getValue("name");
|
if(currentRouter != null) {
|
||||||
previousTag = name;
|
currentRouter.addAttribute(attributes.getValue("name"), attributes.getValue("value"));
|
||||||
if (currentSelectedRouter != null) {
|
} else {
|
||||||
previousKey = currentSelectedRouter + "$" + previousKey;
|
config.attributes.put(attributes.getValue("name"), attributes.getValue("value"));
|
||||||
}
|
}
|
||||||
config.attributes.put(previousKey, attributes.getValue("value"));
|
|
||||||
} else if("routingProfile".equals(name)) {
|
} else if("routingProfile".equals(name)) {
|
||||||
currentSelectedRouter = attributes.getValue("name");
|
currentSelectedRouter = attributes.getValue("name");
|
||||||
Map<String, String> attrs = new LinkedHashMap<String, String>();
|
Map<String, String> attrs = new LinkedHashMap<String, String>();
|
||||||
|
@ -181,12 +171,12 @@ public class RoutingConfiguration {
|
||||||
if (attributes.getValue("priority") != null) {
|
if (attributes.getValue("priority") != null) {
|
||||||
currentRouter.highwayPriorities.put(k, parseSilentFloat(attributes.getValue("priority"), 0));
|
currentRouter.highwayPriorities.put(k, parseSilentFloat(attributes.getValue("priority"), 0));
|
||||||
}
|
}
|
||||||
if (attributes.getValue("dynamicPriority") != null) {
|
|
||||||
currentRouter.highwayFuturePriorities.put(k, parseSilentFloat(attributes.getValue("dynamicPriority"), 0));
|
|
||||||
}
|
|
||||||
if (attributes.getValue("speed") != null) {
|
if (attributes.getValue("speed") != null) {
|
||||||
currentRouter.highwaySpeed.put(k, parseSilentFloat(attributes.getValue("speed"), 0));
|
currentRouter.highwaySpeed.put(k, parseSilentFloat(attributes.getValue("speed"), 0));
|
||||||
}
|
}
|
||||||
|
if ("attribute".equals(previousTag)) {
|
||||||
|
currentRouter.attributes.put(k, attributes.getValue("value"));
|
||||||
|
}
|
||||||
if ("avoid".equals(previousTag)) {
|
if ("avoid".equals(previousTag)) {
|
||||||
float priority = parseSilentFloat(attributes.getValue("decreasedPriority"), 0);
|
float priority = parseSilentFloat(attributes.getValue("decreasedPriority"), 0);
|
||||||
if (priority == 0) {
|
if (priority == 0) {
|
||||||
|
@ -198,23 +188,18 @@ public class RoutingConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if("road".equals(name)) {
|
} else if("road".equals(name)) {
|
||||||
previousTag = name;
|
|
||||||
previousKey = attributes.getValue("tag") +"$" + attributes.getValue("value");
|
previousKey = attributes.getValue("tag") +"$" + attributes.getValue("value");
|
||||||
currentRouter.highwayPriorities.put(previousKey, parseSilentFloat(attributes.getValue("priority"),
|
currentRouter.highwayPriorities.put(previousKey, parseSilentFloat(attributes.getValue("priority"),
|
||||||
1));
|
1));
|
||||||
currentRouter.highwayFuturePriorities.put(previousKey, parseSilentFloat(attributes.getValue("dynamicPriority"),
|
|
||||||
1));
|
|
||||||
currentRouter.highwaySpeed.put(previousKey, parseSilentFloat(attributes.getValue("speed"),
|
currentRouter.highwaySpeed.put(previousKey, parseSilentFloat(attributes.getValue("speed"),
|
||||||
10));
|
10));
|
||||||
} else if("obstacle".equals(name)) {
|
} else if("obstacle".equals(name)) {
|
||||||
previousTag = name;
|
|
||||||
previousKey = attributes.getValue("tag") + "$" + attributes.getValue("value");
|
previousKey = attributes.getValue("tag") + "$" + attributes.getValue("value");
|
||||||
float penalty = parseSilentFloat(attributes.getValue("penalty"), 0);
|
float penalty = parseSilentFloat(attributes.getValue("penalty"), 0);
|
||||||
currentRouter.obstacles.put(previousKey, penalty);
|
currentRouter.obstacles.put(previousKey, penalty);
|
||||||
float routingPenalty = parseSilentFloat(attributes.getValue("routingPenalty"), penalty );
|
float routingPenalty = parseSilentFloat(attributes.getValue("routingPenalty"), penalty );
|
||||||
currentRouter.routingObstacles.put(previousKey, routingPenalty);
|
currentRouter.routingObstacles.put(previousKey, routingPenalty);
|
||||||
} else if("avoid".equals(name)) {
|
} else if("avoid".equals(name)) {
|
||||||
previousTag = name;
|
|
||||||
previousKey = attributes.getValue("tag") + "$" + attributes.getValue("value");
|
previousKey = attributes.getValue("tag") + "$" + attributes.getValue("value");
|
||||||
float priority = parseSilentFloat(attributes.getValue("decreasedPriority"),
|
float priority = parseSilentFloat(attributes.getValue("decreasedPriority"),
|
||||||
0);
|
0);
|
||||||
|
|
|
@ -27,11 +27,11 @@ public abstract class VehicleRouter {
|
||||||
return road.getHighway();
|
return road.getHighway();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract boolean containsAttribute(String attribute);
|
||||||
|
|
||||||
|
public abstract String getAttribute(String attribute);
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for algorithm to multiply h(x) part A* based on current road
|
|
||||||
*/
|
|
||||||
public abstract float getFutureRoadPriority(RouteDataObject road);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* return delay in seconds
|
* return delay in seconds
|
||||||
|
|
|
@ -12,18 +12,6 @@
|
||||||
(don't specify here ! it is device dependent) -->
|
(don't specify here ! it is device dependent) -->
|
||||||
<attribute name="memoryLimitInMB" value="155" />
|
<attribute name="memoryLimitInMB" value="155" />
|
||||||
|
|
||||||
<!-- 1.2 Dynamic road prioritizing (heuristic) -->
|
|
||||||
<attribute name="useDynamicRoadPrioritising" value="false" />
|
|
||||||
<!-- for first N meters speed multiplied by priority, 0 special number - all rest distance -->
|
|
||||||
<attribute name="dynamicRoadPriorityDistance" value="30000" />
|
|
||||||
|
|
||||||
<!-- 1.3 Relaxing strategy -->
|
|
||||||
<!-- If the furthest routing branch passed distance 3 times more than small branches, small will be relaxed-->
|
|
||||||
<!-- Coefficient is configurable -->
|
|
||||||
<attribute name="useRelaxingStrategy" value="false" />
|
|
||||||
<attribute name="iterationsToRelaxRoutes" value="100" />
|
|
||||||
<attribute name="relaxNodesIfStartDistSmallCoeff" value="5"/>
|
|
||||||
|
|
||||||
<!-- 1.4 Build A* graph in backward/forward direction (can affect results) -->
|
<!-- 1.4 Build A* graph in backward/forward direction (can affect results) -->
|
||||||
<!-- 0 - 2 ways, 1 - direct way, -1 - reverse way -->
|
<!-- 0 - 2 ways, 1 - direct way, -1 - reverse way -->
|
||||||
<attribute name="planRoadDirection" value="0" />
|
<attribute name="planRoadDirection" value="0" />
|
||||||
|
|
|
@ -721,7 +721,7 @@ public class MapRouterLayer implements MapPanelLayer {
|
||||||
int sx31 = st.getRoad().getPoint31XTile(st.getSegmentStart());
|
int sx31 = st.getRoad().getPoint31XTile(st.getSegmentStart());
|
||||||
int sy31 = st.getRoad().getPoint31YTile(st.getSegmentStart());
|
int sy31 = st.getRoad().getPoint31YTile(st.getSegmentStart());
|
||||||
// FIXME
|
// FIXME
|
||||||
RouteSegmentResult[] searchRoute = ctx.nativeLib.testRoutingInternal(sx31, sy31, ex31, ey31);
|
RouteSegmentResult[] searchRoute = ctx.nativeLib.runNativeRouting(sx31, sy31, ex31, ey31, ctx.config);
|
||||||
/*List<RouteSegmentResult> searchRoute = */router.searchRoute(ctx, st, e, inters, false);
|
/*List<RouteSegmentResult> searchRoute = */router.searchRoute(ctx, st, e, inters, false);
|
||||||
// this.previousRoute = searchRoute;
|
// this.previousRoute = searchRoute;
|
||||||
if (animateRoutingCalculation) {
|
if (animateRoutingCalculation) {
|
||||||
|
|
|
@ -58,13 +58,13 @@ static double squareDist(int x1, int y1, int x2, int y2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static double h(RoutingContext* ctx, float distanceToFinalPoint, SHARED_PTR<RouteSegment> next) {
|
static double h(RoutingContext* ctx, float distanceToFinalPoint, SHARED_PTR<RouteSegment> next) {
|
||||||
return distanceToFinalPoint / ctx->getMaxDefaultSpeed();
|
return distanceToFinalPoint / ctx->config.getMaxDefaultSpeed();
|
||||||
|
|
||||||
}
|
}
|
||||||
static double h(RoutingContext* ctx, int targetEndX, int targetEndY,
|
static double h(RoutingContext* ctx, int targetEndX, int targetEndY,
|
||||||
int startX, int startY) {
|
int startX, int startY) {
|
||||||
double distance = squareRootDist(startX, startY, targetEndX, targetEndY);
|
double distance = squareRootDist(startX, startY, targetEndX, targetEndY);
|
||||||
return distance / ctx->getMaxDefaultSpeed();
|
return distance / ctx->config.getMaxDefaultSpeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -220,7 +220,7 @@ bool processRouteSegment(RoutingContext* ctx, bool reverseWaySearch,
|
||||||
// avoid empty segments to connect but mark the point as visited
|
// avoid empty segments to connect but mark the point as visited
|
||||||
visitedSegments[nt] = SHARED_PTR<RouteSegment>();
|
visitedSegments[nt] = SHARED_PTR<RouteSegment>();
|
||||||
|
|
||||||
int oneway = ctx->isOneWay(road);
|
int oneway = ctx->config.isOneWay(road);
|
||||||
bool minusAllowed;
|
bool minusAllowed;
|
||||||
bool plusAllowed;
|
bool plusAllowed;
|
||||||
if(ctx->firstRoadId == nt) {
|
if(ctx->firstRoadId == nt) {
|
||||||
|
@ -238,11 +238,11 @@ bool processRouteSegment(RoutingContext* ctx, bool reverseWaySearch,
|
||||||
int d = plusAllowed ? 1 : -1;
|
int d = plusAllowed ? 1 : -1;
|
||||||
if(segment->parentRoute.get() != NULL) {
|
if(segment->parentRoute.get() != NULL) {
|
||||||
if(plusAllowed && middle < segment->road->pointsX.size() - 1) {
|
if(plusAllowed && middle < segment->road->pointsX.size() - 1) {
|
||||||
obstaclePlusTime = ctx->calculateTurnTime(segment, segment->road->pointsX.size() - 1,
|
obstaclePlusTime = ctx->config.calculateTurnTime(segment, segment->road->pointsX.size() - 1,
|
||||||
segment->parentRoute, segment->parentSegmentEnd);
|
segment->parentRoute, segment->parentSegmentEnd);
|
||||||
}
|
}
|
||||||
if(minusAllowed && middle > 0) {
|
if(minusAllowed && middle > 0) {
|
||||||
obstacleMinusTime = ctx->calculateTurnTime(segment, 0,
|
obstacleMinusTime = ctx->config.calculateTurnTime(segment, 0,
|
||||||
segment->parentRoute, segment->parentSegmentEnd);
|
segment->parentRoute, segment->parentSegmentEnd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -291,14 +291,14 @@ bool processRouteSegment(RoutingContext* ctx, bool reverseWaySearch,
|
||||||
|
|
||||||
// 2.1 calculate possible obstacle plus time
|
// 2.1 calculate possible obstacle plus time
|
||||||
if(positive) {
|
if(positive) {
|
||||||
double obstacle = ctx->defineRoutingObstacle(road, segmentEnd);
|
double obstacle = ctx->config.defineRoutingObstacle(road, segmentEnd);
|
||||||
if (obstacle < 0) {
|
if (obstacle < 0) {
|
||||||
plusAllowed = false;
|
plusAllowed = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
obstaclePlusTime += obstacle;
|
obstaclePlusTime += obstacle;
|
||||||
} else {
|
} else {
|
||||||
double obstacle = ctx->defineRoutingObstacle(road, segmentEnd);
|
double obstacle = ctx->config.defineRoutingObstacle(road, segmentEnd);
|
||||||
if (obstacle < 0) {
|
if (obstacle < 0) {
|
||||||
minusAllowed = false;
|
minusAllowed = false;
|
||||||
continue;
|
continue;
|
||||||
|
@ -324,10 +324,10 @@ bool processRouteSegment(RoutingContext* ctx, bool reverseWaySearch,
|
||||||
// Using A* routing algorithm
|
// Using A* routing algorithm
|
||||||
// g(x) - calculate distance to that point and calculate time
|
// g(x) - calculate distance to that point and calculate time
|
||||||
|
|
||||||
double priority = ctx->defineSpeedPriority(road);
|
double priority = ctx->config.defineSpeedPriority(road);
|
||||||
double speed = ctx->defineSpeed(road) * priority;
|
double speed = ctx->config.defineSpeed(road) * priority;
|
||||||
if (speed == 0) {
|
if (speed == 0) {
|
||||||
speed = ctx->getMinDefaultSpeed() * priority;
|
speed = ctx->config.getMinDefaultSpeed() * priority;
|
||||||
}
|
}
|
||||||
double distOnRoadToPass = positive? posSegmentDist : negSegmentDist;
|
double distOnRoadToPass = positive? posSegmentDist : negSegmentDist;
|
||||||
double distStartObstacles = segment->distanceFromStart + ( positive ? obstaclePlusTime : obstacleMinusTime) + distOnRoadToPass / speed;
|
double distStartObstacles = segment->distanceFromStart + ( positive ? obstaclePlusTime : obstacleMinusTime) + distOnRoadToPass / speed;
|
||||||
|
@ -353,7 +353,7 @@ bool proccessRestrictions(RoutingContext* ctx, SHARED_PTR<RouteDataObject> road,
|
||||||
if (!reverseWay && road->restrictions.size() == 0) {
|
if (!reverseWay && road->restrictions.size() == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(!ctx->restrictionsAware()) {
|
if(!ctx->config.restrictionsAware()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
while (next.get() != NULL) {
|
while (next.get() != NULL) {
|
||||||
|
@ -497,7 +497,7 @@ bool processIntersections(RoutingContext* ctx, SEGMENTS_QUEUE& graphSegments,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME
|
// FIXME replace with adequate method
|
||||||
SHARED_PTR<RouteSegment> findRouteSegment(int px, int py, RoutingContext* ctx) {
|
SHARED_PTR<RouteSegment> findRouteSegment(int px, int py, RoutingContext* ctx) {
|
||||||
return ctx->loadSegmentAround(px, py);
|
return ctx->loadSegmentAround(px, py);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "binaryRead.h"
|
#include "binaryRead.h"
|
||||||
|
|
||||||
|
typedef UNORDERED(map)<string, float> MAP_STR_FLOAT;
|
||||||
|
typedef UNORDERED(map)<string, string> MAP_STR_STR;
|
||||||
|
|
||||||
static double measuredDist(int x1, int y1, int x2, int y2) {
|
static double measuredDist(int x1, int y1, int x2, int y2) {
|
||||||
return getDistance(get31LatitudeY(y1), get31LongitudeX(x1), get31LatitudeY(y2),
|
return getDistance(get31LatitudeY(y1), get31LongitudeX(x1), get31LatitudeY(y2),
|
||||||
get31LongitudeX(x2));
|
get31LongitudeX(x2));
|
||||||
|
@ -54,7 +57,6 @@ struct FinalRouteSegment {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const static int ZOOM_TO_LOAD_TILES = 16;
|
|
||||||
struct RoutingSubregionTile {
|
struct RoutingSubregionTile {
|
||||||
RouteSubregion subregion;
|
RouteSubregion subregion;
|
||||||
// make it without get/set for fast access
|
// make it without get/set for fast access
|
||||||
|
@ -99,9 +101,245 @@ static int64_t calcRouteId(SHARED_PTR<RouteDataObject> o, int ind) {
|
||||||
return (o->id << 10) + ind;
|
return (o->id << 10) + ind;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef std::pair<int, std::pair<string, string> > ROUTE_TRIPLE;
|
||||||
|
struct RoutingConfiguration {
|
||||||
|
// 0 index in triple
|
||||||
|
MAP_STR_FLOAT highwaySpeed ;
|
||||||
|
// 1 index in triple
|
||||||
|
MAP_STR_FLOAT highwayPriorities ;
|
||||||
|
// 2 index in triple
|
||||||
|
MAP_STR_FLOAT avoid ;
|
||||||
|
// 3 index in triple
|
||||||
|
MAP_STR_FLOAT obstacles;
|
||||||
|
// 4 index in triple
|
||||||
|
MAP_STR_FLOAT routingObstacles;
|
||||||
|
// 5 index in triple
|
||||||
|
MAP_STR_STR attributes;
|
||||||
|
|
||||||
|
int zoomToLoad;
|
||||||
|
float heurCoefficient;
|
||||||
|
float maxDefaultSpeed;
|
||||||
|
float minDefaultSpeed;
|
||||||
|
bool restrictions;
|
||||||
|
bool onewayAware;
|
||||||
|
bool followLimitations;
|
||||||
|
int memoryLimitation;
|
||||||
|
int planRoadDirection;
|
||||||
|
string routerName;
|
||||||
|
float initialDirection;
|
||||||
|
string routerProfile;
|
||||||
|
float roundaboutTurn;
|
||||||
|
float leftTurn;
|
||||||
|
float rightTurn;
|
||||||
|
|
||||||
|
float parseFloat(string key, float def) {
|
||||||
|
if(attributes.find(key) != attributes.end() && attributes[key] != "") {
|
||||||
|
return atof(attributes[key].c_str());
|
||||||
|
}
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
bool parseBool(string key, bool def) {
|
||||||
|
if (attributes.find(key) != attributes.end() && attributes[key] != "") {
|
||||||
|
return attributes[key] == "true";
|
||||||
|
}
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
string parseString(string key, string def) {
|
||||||
|
if (attributes.find(key) != attributes.end() && attributes[key] != "") {
|
||||||
|
return attributes[key];
|
||||||
|
}
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
void defaultParams() {
|
||||||
|
planRoadDirection = (int) parseFloat("planRoadDirection", 0);
|
||||||
|
restrictions = parseBool("restrictionsAware", true);
|
||||||
|
followLimitations = parseBool("followSpeedLimitations", true);
|
||||||
|
onewayAware = parseBool("onewayAware", true);
|
||||||
|
roundaboutTurn = parseFloat("roundaboutTurn", 0);
|
||||||
|
leftTurn = parseFloat("leftTurn", 0);
|
||||||
|
rightTurn = parseFloat("rightTurn", 0);
|
||||||
|
minDefaultSpeed = parseFloat("minDefaultSpeed", 45) / 3.6;
|
||||||
|
maxDefaultSpeed = parseFloat("maxDefaultSpeed", 130) / 3.6;
|
||||||
|
heurCoefficient = parseFloat("heuristicCoefficient", 1);
|
||||||
|
memoryLimitation = (int)parseFloat("memoryLimitInMB", memoryLimitation);
|
||||||
|
zoomToLoad = (int)parseFloat("zoomToLoadTiles", 16);
|
||||||
|
routerName = parseString("name", "default");
|
||||||
|
routerProfile = parseString("baseProfile", "car");
|
||||||
|
}
|
||||||
|
|
||||||
|
RoutingConfiguration(vector<ROUTE_TRIPLE>& config, int memLimit = 30, float initDirection = 0) :
|
||||||
|
memoryLimitation(memLimit), initialDirection(initDirection) {
|
||||||
|
for(int j = 0; j<config.size(); j++) {
|
||||||
|
ROUTE_TRIPLE r = config[j];
|
||||||
|
if(r.first == 0) {
|
||||||
|
highwaySpeed[r.second.first] = atof(r.second.second.c_str());
|
||||||
|
} else if(r.first == 1) {
|
||||||
|
highwayPriorities[r.second.first] = atof(r.second.second.c_str());
|
||||||
|
} else if(r.first == 2) {
|
||||||
|
avoid[r.second.first] = atof(r.second.second.c_str());
|
||||||
|
} else if(r.first == 3) {
|
||||||
|
obstacles[r.second.first] = atof(r.second.second.c_str());
|
||||||
|
} else if(r.first == 4) {
|
||||||
|
routingObstacles[r.second.first] = atof(r.second.second.c_str());
|
||||||
|
} else if(r.first == 5) {
|
||||||
|
string v = r.second.second;
|
||||||
|
attributes[r.second.first] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defaultParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool acceptLine(SHARED_PTR<RouteDataObject> r) {
|
||||||
|
std::vector<uint32_t>::iterator t = r->types.begin();
|
||||||
|
bool accepted = false;
|
||||||
|
for(; t != r->types.end(); t++) {
|
||||||
|
tag_value type = r->region->decodingRules[*t];
|
||||||
|
if(type.first=="highway" && highwaySpeed[type.second] > 0) {
|
||||||
|
accepted = true;
|
||||||
|
break;
|
||||||
|
} else if(highwaySpeed[type.first + '$' + type.second] > 0) {
|
||||||
|
accepted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!accepted) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
t = r->types.begin();
|
||||||
|
for(; t != r->types.end(); t++) {
|
||||||
|
tag_value type = r->region->decodingRules[*t];
|
||||||
|
if(avoid.find(type.first + '$' + type.second) != avoid.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string getHighway(SHARED_PTR<RouteDataObject> r) {
|
||||||
|
std::vector<uint32_t>::iterator t = r->types.begin();
|
||||||
|
for(; t != r->types.end(); t++) {
|
||||||
|
tag_value type = r->region->decodingRules[*t];
|
||||||
|
if(type.first=="highway") {
|
||||||
|
return type.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
float defineSpeedPriority(SHARED_PTR<RouteDataObject> r) {
|
||||||
|
float priority = 1;
|
||||||
|
std::vector<uint32_t>::iterator t = r->types.begin();
|
||||||
|
for(; t != r->types.end(); t++) {
|
||||||
|
tag_value type = r->region->decodingRules[*t];
|
||||||
|
if(highwayPriorities.find(type.first+"$"+type.second) != highwaySpeed.end()) {
|
||||||
|
priority *= highwayPriorities[type.first+"$"+type.second];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getMinDefaultSpeed() {
|
||||||
|
return minDefaultSpeed;
|
||||||
|
}
|
||||||
|
float getMaxDefaultSpeed() {
|
||||||
|
return maxDefaultSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
int isOneWay(SHARED_PTR<RouteDataObject> r) {
|
||||||
|
if(!onewayAware){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
std::vector<uint32_t>::iterator t = r->types.begin();
|
||||||
|
for(; t != r->types.end(); t++) {
|
||||||
|
tag_value type = r->region->decodingRules[*t];
|
||||||
|
if(type.first == "oneway") {
|
||||||
|
string v = type.second;
|
||||||
|
if("-1" ==v || "reverse" == v) {
|
||||||
|
return -1;
|
||||||
|
} else if("1" == v || "yes" == v) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
float calculateTurnTime(SHARED_PTR<RouteSegment> segment, int index, SHARED_PTR<RouteSegment> next, int nextIndex) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float defineRoutingObstacle(SHARED_PTR<RouteDataObject> road, int segmentEnd) {
|
||||||
|
if(road->pointTypes.size() <= segmentEnd) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
std::vector<uint32_t> pointTypes = road->pointTypes[segmentEnd];
|
||||||
|
std::vector<uint32_t>::iterator t = pointTypes.begin();
|
||||||
|
for(; t != pointTypes.end(); t++) {
|
||||||
|
tag_value type = road->region->decodingRules[*t];
|
||||||
|
if(routingObstacles.find(type.first + "$" + type.second) != routingObstacles.end()) {
|
||||||
|
return routingObstacles[type.first + "$" + type.second];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t = pointTypes.begin();
|
||||||
|
for(; t != pointTypes.end(); t++) {
|
||||||
|
tag_value type = road->region->decodingRules[*t];
|
||||||
|
if(routingObstacles.find(type.first + "$" ) != routingObstacles.end()) {
|
||||||
|
return routingObstacles[type.first + "$" ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool restrictionsAware() {
|
||||||
|
return restrictions;
|
||||||
|
}
|
||||||
|
|
||||||
|
float maxSpeed(SHARED_PTR<RouteDataObject> r) {
|
||||||
|
std::vector<uint32_t>::iterator t = r->types.begin();
|
||||||
|
for(; t != r->types.end(); t++) {
|
||||||
|
tag_value type = r->region->decodingRules[*t];
|
||||||
|
if(type.first=="maxspeed") {
|
||||||
|
std::string v = type.second;
|
||||||
|
int i = 0;
|
||||||
|
while(i < v.length() && v[i] >= '0' && v[i] <= '9') {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if(i > 0) {
|
||||||
|
float f = atoi(v.substr(0, i).c_str());
|
||||||
|
f = f / 3.6;
|
||||||
|
if(v.find("mph") != std::string::npos ) {
|
||||||
|
f *= 1.6;
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float defineSpeed(SHARED_PTR<RouteDataObject> r) {
|
||||||
|
if (followLimitations) {
|
||||||
|
float m = maxSpeed(r);
|
||||||
|
if(m > 0) {
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::vector<uint32_t>::iterator t = r->types.begin();
|
||||||
|
for(; t != r->types.end(); t++) {
|
||||||
|
tag_value type = r->region->decodingRules[*t];
|
||||||
|
if(highwaySpeed.find(type.first+"$"+type.second) != highwaySpeed.end()) {
|
||||||
|
return highwaySpeed[type.first+"$"+type.second] / 3.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getMinDefaultSpeed();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// FIXME configuration methods
|
|
||||||
struct RoutingContext {
|
struct RoutingContext {
|
||||||
int visitedSegments;
|
int visitedSegments;
|
||||||
int loadedTiles;
|
int loadedTiles;
|
||||||
|
@ -109,6 +347,7 @@ struct RoutingContext {
|
||||||
ElapsedTimer timeToCalculate;
|
ElapsedTimer timeToCalculate;
|
||||||
int firstRoadDirection;
|
int firstRoadDirection;
|
||||||
int64_t firstRoadId;
|
int64_t firstRoadId;
|
||||||
|
RoutingConfiguration config;
|
||||||
|
|
||||||
int startX;
|
int startX;
|
||||||
int startY;
|
int startY;
|
||||||
|
@ -122,25 +361,12 @@ struct RoutingContext {
|
||||||
UNORDERED(map)<int64_t, SHARED_PTR<RoutingSubregionTile> > subregionTiles;
|
UNORDERED(map)<int64_t, SHARED_PTR<RoutingSubregionTile> > subregionTiles;
|
||||||
UNORDERED(map)<int64_t, std::vector<SHARED_PTR<RoutingSubregionTile> > > indexedSubregions;
|
UNORDERED(map)<int64_t, std::vector<SHARED_PTR<RoutingSubregionTile> > > indexedSubregions;
|
||||||
|
|
||||||
RoutingContext() : finalRouteSegment(), firstRoadDirection(0), loadedTiles(0), visitedSegments(0){
|
RoutingContext(RoutingConfiguration& config) : finalRouteSegment(), firstRoadDirection(0), loadedTiles(0), visitedSegments(0),
|
||||||
}
|
config(config){
|
||||||
bool acceptLine(SHARED_PTR<RouteDataObject> r) {
|
|
||||||
string v = getHighway(r);
|
|
||||||
if(v != "" && v != "cycleway" && v != "footway" && v != "steps"){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string getHighway(SHARED_PTR<RouteDataObject> r) {
|
bool acceptLine(SHARED_PTR<RouteDataObject> r) {
|
||||||
std::vector<uint32_t>::iterator t = r->types.begin();
|
return config.acceptLine(r);
|
||||||
for(; t != r->types.end(); t++) {
|
|
||||||
tag_value type = r->region->decodingRules[*t];
|
|
||||||
if(type.first=="highway") {
|
|
||||||
return type.second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadHeaderObjects(int64_t tileId) {
|
void loadHeaderObjects(int64_t tileId) {
|
||||||
|
@ -167,7 +393,7 @@ struct RoutingContext {
|
||||||
|
|
||||||
void loadHeaders(uint32_t xloc, uint32_t yloc) {
|
void loadHeaders(uint32_t xloc, uint32_t yloc) {
|
||||||
timeToLoad.start();
|
timeToLoad.start();
|
||||||
int z = ZOOM_TO_LOAD_TILES;
|
int z = config.zoomToLoad;
|
||||||
int tz = 31 - z;
|
int tz = 31 - z;
|
||||||
int64_t tileId = (xloc << z) + yloc;
|
int64_t tileId = (xloc << z) + yloc;
|
||||||
if (indexedSubregions.find(tileId) == indexedSubregions.end()) {
|
if (indexedSubregions.find(tileId) == indexedSubregions.end()) {
|
||||||
|
@ -192,12 +418,12 @@ struct RoutingContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// FIXME
|
// FIXME replace with adequate method
|
||||||
SHARED_PTR<RouteSegment> loadSegmentAround(int x31, int y31) {
|
SHARED_PTR<RouteSegment> loadSegmentAround(int x31, int y31) {
|
||||||
timeToLoad.start();
|
timeToLoad.start();
|
||||||
SHARED_PTR<RouteSegment> r;
|
SHARED_PTR<RouteSegment> r;
|
||||||
float dist = -1;
|
float dist = -1;
|
||||||
int z = ZOOM_TO_LOAD_TILES;
|
int z = config.zoomToLoad;
|
||||||
uint32_t xloc = x31 >> (31 - z);
|
uint32_t xloc = x31 >> (31 - z);
|
||||||
uint32_t yloc = y31 >> (31 - z);
|
uint32_t yloc = y31 >> (31 - z);
|
||||||
uint64_t l = (((uint64_t) x31) << 31) + (uint64_t) y31;
|
uint64_t l = (((uint64_t) x31) << 31) + (uint64_t) y31;
|
||||||
|
@ -227,7 +453,7 @@ struct RoutingContext {
|
||||||
|
|
||||||
// void searchRouteRegion(SearchQuery* q, std::vector<RouteDataObject*>& list, RoutingIndex* rs, RouteSubregion* sub)
|
// void searchRouteRegion(SearchQuery* q, std::vector<RouteDataObject*>& list, RoutingIndex* rs, RouteSubregion* sub)
|
||||||
SHARED_PTR<RouteSegment> loadRouteSegment(int x31, int y31) {
|
SHARED_PTR<RouteSegment> loadRouteSegment(int x31, int y31) {
|
||||||
int z = ZOOM_TO_LOAD_TILES;
|
int z = config.zoomToLoad;
|
||||||
int64_t xloc = x31 >> (31 - z);
|
int64_t xloc = x31 >> (31 - z);
|
||||||
int64_t yloc = y31 >> (31 - z);
|
int64_t yloc = y31 >> (31 - z);
|
||||||
uint64_t l = (((uint64_t) x31) << 31) + (uint64_t) y31;
|
uint64_t l = (((uint64_t) x31) << 31) + (uint64_t) y31;
|
||||||
|
@ -262,157 +488,16 @@ struct RoutingContext {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
float getHeuristicCoefficient(){
|
float getHeuristicCoefficient(){
|
||||||
return 1;
|
return config.heurCoefficient;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool planRouteIn2Directions() {
|
bool planRouteIn2Directions() {
|
||||||
return getPlanRoadDirection() == 0;
|
return getPlanRoadDirection() == 0;
|
||||||
}
|
}
|
||||||
int getPlanRoadDirection() {
|
int getPlanRoadDirection() {
|
||||||
return 1;
|
return config.planRoadDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
float defineSpeedPriority(SHARED_PTR<RouteDataObject> r) {
|
|
||||||
string v = getHighway(r);
|
|
||||||
if(v == "") {
|
|
||||||
return 1;
|
|
||||||
} else if(v == "motorway") {
|
|
||||||
return 1.2;
|
|
||||||
} else if(v == "motorway_link") {
|
|
||||||
return 1.2;
|
|
||||||
} else if(v == "trunk") {
|
|
||||||
return 1.2;
|
|
||||||
} else if(v == "trunk_link") {
|
|
||||||
return 1.2;
|
|
||||||
} else if(v == "primary") {
|
|
||||||
return 1.1;
|
|
||||||
} else if(v == "primary_link") {
|
|
||||||
return 1.1;
|
|
||||||
} else if(v == "secondary") {
|
|
||||||
return 1.05;
|
|
||||||
} else if(v == "secondary_link") {
|
|
||||||
return 1.05;
|
|
||||||
} else if(v == "tertiary") {
|
|
||||||
return 1;
|
|
||||||
} else if(v == "tertiary_link") {
|
|
||||||
return 1;
|
|
||||||
} else if(v == "unclassified") {
|
|
||||||
return 0.7;
|
|
||||||
} else if(v == "road") {
|
|
||||||
return 0.7;
|
|
||||||
} else if(v == "service") {
|
|
||||||
return 0.5;
|
|
||||||
} else if(v == "track") {
|
|
||||||
return 0.3;
|
|
||||||
} else if(v == "residential") {
|
|
||||||
return 0.5;
|
|
||||||
} else if(v == "living_street") {
|
|
||||||
return 0.5;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
float maxSpeed(SHARED_PTR<RouteDataObject> r) {
|
|
||||||
std::vector<uint32_t>::iterator t = r->types.begin();
|
|
||||||
for(; t != r->types.end(); t++) {
|
|
||||||
tag_value type = r->region->decodingRules[*t];
|
|
||||||
if(type.first=="maxspeed") {
|
|
||||||
std::string v = type.second;
|
|
||||||
int i = 0;
|
|
||||||
while(i < v.length() && v[i] >= '0' && v[i] <= '9') {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if(i > 0) {
|
|
||||||
float f = atoi(v.substr(0, i).c_str());
|
|
||||||
f /= 3.6; // km/h -> m/s
|
|
||||||
if(v.find("mph") != std::string::npos ) {
|
|
||||||
f *= 1.6;
|
|
||||||
}
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
float defineSpeed(SHARED_PTR<RouteDataObject> r) {
|
|
||||||
float speed = maxSpeed(r);
|
|
||||||
if(speed > 0) {
|
|
||||||
return speed;
|
|
||||||
}
|
|
||||||
string v = getHighway(r);
|
|
||||||
if(v == "") {
|
|
||||||
} else if(v == "motorway") {
|
|
||||||
speed = 110;
|
|
||||||
} else if(v == "motorway_link") {
|
|
||||||
speed = 80;
|
|
||||||
} else if(v == "trunk") {
|
|
||||||
speed = 100;
|
|
||||||
} else if(v == "trunk_link") {
|
|
||||||
speed = 75;
|
|
||||||
} else if(v == "primary") {
|
|
||||||
speed = 65;
|
|
||||||
} else if(v == "primary_link") {
|
|
||||||
speed = 50;
|
|
||||||
} else if(v == "secondary") {
|
|
||||||
speed = 60;
|
|
||||||
} else if(v == "secondary_link") {
|
|
||||||
speed = 50;
|
|
||||||
} else if(v == "tertiary") {
|
|
||||||
speed = 45;
|
|
||||||
} else if(v == "tertiary_link") {
|
|
||||||
speed = 40;
|
|
||||||
} else if(v == "unclassified") {
|
|
||||||
speed = 35;
|
|
||||||
} else if(v == "road") {
|
|
||||||
speed = 35;
|
|
||||||
} else if(v == "service") {
|
|
||||||
speed = 30;
|
|
||||||
} else if(v == "track") {
|
|
||||||
speed = 15;
|
|
||||||
} else if(v == "residential") {
|
|
||||||
speed = 35;
|
|
||||||
} else if(v == "living_street") {
|
|
||||||
speed = 25;
|
|
||||||
}
|
|
||||||
return speed / 3.;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
float getMinDefaultSpeed(){
|
|
||||||
return 40 / 3.6;
|
|
||||||
}
|
|
||||||
float getMaxDefaultSpeed(){
|
|
||||||
return 130 / 3.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
int isOneWay(SHARED_PTR<RouteDataObject> r) {
|
|
||||||
std::vector<uint32_t>::iterator t = r->types.begin();
|
|
||||||
for(; t != r->types.end(); t++){
|
|
||||||
tag_value type = r->region->decodingRules[*t];
|
|
||||||
if(type.first == "oneway") {
|
|
||||||
string v = type.second;
|
|
||||||
if("-1" ==v || "reverse" == v) {
|
|
||||||
return -1;
|
|
||||||
} else if("1" == v || "yes" == v) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
float calculateTurnTime(SHARED_PTR<RouteSegment> segment, int index, SHARED_PTR<RouteSegment> next, int nextIndex){
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
float defineRoutingObstacle(SHARED_PTR<RouteDataObject> road, int segmentEnd) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool restrictionsAware(){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -580,15 +580,33 @@ extern "C" JNIEXPORT void JNICALL Java_net_osmand_NativeLibrary_deleteRouteSearc
|
||||||
}
|
}
|
||||||
delete t;
|
delete t;
|
||||||
}
|
}
|
||||||
extern "C" JNIEXPORT jobjectArray JNICALL Java_net_osmand_NativeLibrary_testRouting(JNIEnv* ienv,
|
//p RouteSegmentResult[] nativeRouting(int[] coordinates, int[] state, String[] keyConfig, String[] valueConfig);
|
||||||
jobject obj, jint sx31,
|
extern "C" JNIEXPORT jobjectArray JNICALL Java_net_osmand_NativeLibrary_nativeRouting(JNIEnv* ienv,
|
||||||
jint sy31, jint ex31, jint ey31) {
|
jobject obj, jintArray coordinates,
|
||||||
RoutingContext c;
|
jintArray stateConfig, jobjectArray keyConfig, jobjectArray valueConfig) {
|
||||||
c.startX = sx31;
|
|
||||||
c.startY = sy31;
|
vector<ROUTE_TRIPLE> cfg;
|
||||||
c.endX = ex31;
|
int* data = ienv->GetIntArrayElements(stateConfig, NULL);
|
||||||
c.endY = ey31;
|
for(int k = 0; k < ienv->GetArrayLength(stateConfig); k++) {
|
||||||
|
ROUTE_TRIPLE t = ROUTE_TRIPLE (data[k], std::pair<string, string>(
|
||||||
|
getString(ienv, (jstring) ienv->GetObjectArrayElement(keyConfig, k)),
|
||||||
|
getString(ienv, (jstring) ienv->GetObjectArrayElement(valueConfig, k)))
|
||||||
|
);
|
||||||
|
cfg.push_back(t);
|
||||||
|
}
|
||||||
|
ienv->ReleaseIntArrayElements(stateConfig, data, 0);
|
||||||
|
|
||||||
|
RoutingConfiguration config(cfg);
|
||||||
|
RoutingContext c(config);
|
||||||
|
data = ienv->GetIntArrayElements(coordinates, NULL);
|
||||||
|
c.startX = data[0];
|
||||||
|
c.startY = data[1];
|
||||||
|
c.endX = data[2];
|
||||||
|
c.endY = data[3];
|
||||||
|
ienv->ReleaseIntArrayElements(coordinates, data, 0);
|
||||||
|
|
||||||
vector<RouteSegmentResult> r = searchRouteInternal(&c, false);
|
vector<RouteSegmentResult> r = searchRouteInternal(&c, false);
|
||||||
|
// convert results
|
||||||
jobjectArray res = ienv->NewObjectArray(r.size(), jclass_RouteSegmentResult, NULL);
|
jobjectArray res = ienv->NewObjectArray(r.size(), jclass_RouteSegmentResult, NULL);
|
||||||
for (int i = 0; i < r.size(); i++) {
|
for (int i = 0; i < r.size(); i++) {
|
||||||
jobject robj = convertRouteDataObjectToJava(ienv, r[i].object.get(), NULL);
|
jobject robj = convertRouteDataObjectToJava(ienv, r[i].object.get(), NULL);
|
||||||
|
|
|
@ -160,11 +160,9 @@ void printFileInformation(const char* fileName, VerboseInfo* verbose) {
|
||||||
i, j++, rt->minZoom, rt->maxZoom, rt->length, ch);
|
i, j++, rt->minZoom, rt->maxZoom, rt->length, ch);
|
||||||
}
|
}
|
||||||
if ((verbose != NULL && verbose->vmap)) {
|
if ((verbose != NULL && verbose->vmap)) {
|
||||||
// FIXME
|
|
||||||
//printMapDetailInfo(verbose, index);
|
//printMapDetailInfo(verbose, index);
|
||||||
}
|
}
|
||||||
} else if (it->type == TRANSPORT_INDEX) {
|
} else if (it->type == TRANSPORT_INDEX) {
|
||||||
// FIXME
|
|
||||||
// TransportIndex ti = ((TransportIndex) p);
|
// TransportIndex ti = ((TransportIndex) p);
|
||||||
// int sh = (31 - BinaryMapIndexReader.TRANSPORT_STOP_ZOOM);
|
// int sh = (31 - BinaryMapIndexReader.TRANSPORT_STOP_ZOOM);
|
||||||
// println(
|
// println(
|
||||||
|
|
Loading…
Reference in a new issue