diff --git a/OsmAnd-java/src/main/java/net/osmand/NativeLibrary.java b/OsmAnd-java/src/main/java/net/osmand/NativeLibrary.java index e8f1f73784..7131dd7389 100644 --- a/OsmAnd-java/src/main/java/net/osmand/NativeLibrary.java +++ b/OsmAnd-java/src/main/java/net/osmand/NativeLibrary.java @@ -25,11 +25,12 @@ import net.osmand.data.MapObject; import net.osmand.data.QuadRect; import net.osmand.render.RenderingRuleSearchRequest; import net.osmand.render.RenderingRulesStorage; +import net.osmand.router.NativeTransportRoutingResult; import net.osmand.router.PrecalculatedRouteDirection; import net.osmand.router.RouteCalculationProgress; import net.osmand.router.RouteSegmentResult; import net.osmand.router.RoutingConfiguration; -import net.osmand.router.TransportRoutePlanner; +import net.osmand.router.TransportRoutingConfiguration; import net.osmand.util.Algorithms; import org.apache.commons.logging.Log; @@ -130,7 +131,10 @@ public class NativeLibrary { } //todo -// public TransportRoutePlanner.TransportRouteResult[] runNativePTRouting() {} + public NativeTransportRoutingResult[] runNativePTRouting(int sx31, int sy31, int ex31, int ey31, + TransportRoutingConfiguration cfg, RouteCalculationProgress progress) { + return nativeTransportRouting(new int[] { sx31, sy31, ex31, ey31 }, cfg, progress); + } public RouteSegmentResult[] runNativeRouting(int sx31, int sy31, int ex31, int ey31, RoutingConfiguration config, RouteRegion[] regions, RouteCalculationProgress progress, PrecalculatedRouteDirection precalculatedRouteDirection, @@ -164,6 +168,9 @@ public class NativeLibrary { PrecalculatedRouteDirection precalculatedRouteDirection, boolean basemap, boolean publicTransport, boolean startTransportStop, boolean targetTransportStop); + protected static native NativeTransportRoutingResult[] nativeTransportRouting(int[] coordinates, TransportRoutingConfiguration cfg, + RouteCalculationProgress progress); + protected static native void deleteSearchResult(long searchResultHandle); protected static native boolean initBinaryMapFile(String filePath, boolean useLive, boolean routingOnly); diff --git a/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapTransportReaderAdapter.java b/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapTransportReaderAdapter.java index 56fe23e511..f857fac849 100644 --- a/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapTransportReaderAdapter.java +++ b/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapTransportReaderAdapter.java @@ -465,8 +465,6 @@ public class BinaryMapTransportReaderAdapter { s.setName(stringTable.get(e.getKey().charAt(0)), stringTable.get(e.getValue().charAt(0))); } } - - private TransportStop readTransportRouteStop(int[] dx, int[] dy, long did, TIntObjectHashMap stringTable, int filePointer) throws IOException { diff --git a/OsmAnd-java/src/main/java/net/osmand/data/TransportRoute.java b/OsmAnd-java/src/main/java/net/osmand/data/TransportRoute.java index a353148bce..02dbf811c3 100644 --- a/OsmAnd-java/src/main/java/net/osmand/data/TransportRoute.java +++ b/OsmAnd-java/src/main/java/net/osmand/data/TransportRoute.java @@ -41,7 +41,23 @@ public class TransportRoute extends MapObject { public List getForwardStops() { return forwardStops; } - + + public void setForwardStops(List forwardStops) { + this.forwardStops = forwardStops; + } + + public void setDist(Integer dist) { + this.dist = dist; + } + + public void setForwardWays(List forwardWays) { + this.forwardWays = forwardWays; + } + + public void setSchedule(TransportSchedule schedule) { + this.schedule = schedule; + } + public List getForwardWays() { if(forwardWays == null) { return Collections.emptyList(); diff --git a/OsmAnd-java/src/main/java/net/osmand/data/TransportSchedule.java b/OsmAnd-java/src/main/java/net/osmand/data/TransportSchedule.java index fc24674e1a..f7c812b43d 100644 --- a/OsmAnd-java/src/main/java/net/osmand/data/TransportSchedule.java +++ b/OsmAnd-java/src/main/java/net/osmand/data/TransportSchedule.java @@ -8,7 +8,16 @@ public class TransportSchedule { public TIntArrayList tripIntervals = new TIntArrayList(); public TIntArrayList avgStopIntervals = new TIntArrayList(); public TIntArrayList avgWaitIntervals = new TIntArrayList(); - + + public TransportSchedule() { + } + + public TransportSchedule(TIntArrayList tripIntervals, TIntArrayList avgStopIntervals, TIntArrayList avgWaitIntervals) { + this.tripIntervals = tripIntervals; + this.avgStopIntervals = avgStopIntervals; + this.avgWaitIntervals = avgWaitIntervals; + } + public int[] getTripIntervals() { return tripIntervals.toArray(); } diff --git a/OsmAnd-java/src/main/java/net/osmand/data/TransportStopExit.java b/OsmAnd-java/src/main/java/net/osmand/data/TransportStopExit.java index ce3ba30f68..a05c153df0 100644 --- a/OsmAnd-java/src/main/java/net/osmand/data/TransportStopExit.java +++ b/OsmAnd-java/src/main/java/net/osmand/data/TransportStopExit.java @@ -8,6 +8,14 @@ public class TransportStopExit extends MapObject { public int y31; public String ref = null; + public TransportStopExit() {} + + public TransportStopExit(int x31, int y31, String ref) { + this.x31 = x31; + this.y31 = y31; + this.ref = ref; + } + @Override public void setLocation(double latitude, double longitude) { super.setLocation(latitude, longitude); diff --git a/OsmAnd-java/src/main/java/net/osmand/router/NativeTransportRoutingResult.java b/OsmAnd-java/src/main/java/net/osmand/router/NativeTransportRoutingResult.java new file mode 100644 index 0000000000..024ee7658f --- /dev/null +++ b/OsmAnd-java/src/main/java/net/osmand/router/NativeTransportRoutingResult.java @@ -0,0 +1,78 @@ +package net.osmand.router; + +public class NativeTransportRoutingResult { + + public NativeTransportRouteResultSegment[] segments; + public double finishWalkDist; + public double routeTime; + + public static class NativeTransportRouteResultSegment { + public NativeTransportRoute route; + public double walkTime; + public double travelDistApproximate; + public double travelTime; + public int start; + public int end; + public double walkDist ; + public int depTime; + } + + public static class NativeTransportRoute { + //MapObject part: + public long id; + public double routeLat; + public double routeLon; + public String name; + public String enName; + //to HashMap names + public String[] namesLng; + public String[] namesNames; + public int fileOffset; + //----- + public NativeTransportStop[] forwardStops; + public String ref; + public String routeOperator; + public String type; + public int dist; + public String color; + + // Convert into TransportSchedule: + public int[] intervals; + public int[] avgStopIntervals; + public int[] avgWaitIntervals; + + // Convert into ways (and nodes): + public long[] waysIds; + public long[][] waysNodesIds; + public double[][] waysNodesLats; + public double[][] waysNodesLons; + } + + public static class NativeTransportStop { + //MapObject part: + public long id; + public double stopLat; + public double stopLon; + public String name; + public String enName; + public String[] namesLng; + public String[] namesNames; + public int fileOffset; + //Leave next 3 field as arrays: + public int[] referencesToRoutes; + public long[] deletedRoutesIds; + public long[] routesIds; + public int distance; + public int x31; + public int y31; + + public NativeTransportRoute[] routes; + // Convert into List exits: + public int[] pTStopExit_x31s; + public int[] pTStopExit_y31s; + public String[] pTStopExit_refs; + // Convert into LinkedHashMap + public String[] referenceToRoutesKeys; + public int[][] referenceToRoutesVals; + } +} diff --git a/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutePlanner.java b/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutePlanner.java index 002a0cec17..54e7ff8a26 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutePlanner.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutePlanner.java @@ -15,6 +15,8 @@ import gnu.trove.iterator.TIntIterator; import gnu.trove.list.array.TIntArrayList; import gnu.trove.map.hash.TIntObjectHashMap; import gnu.trove.map.hash.TLongObjectHashMap; + +import net.osmand.NativeLibrary; import net.osmand.binary.BinaryMapIndexReader; import net.osmand.binary.BinaryMapIndexReader.SearchRequest; import net.osmand.data.LatLon; @@ -447,11 +449,27 @@ public class TransportRoutePlanner { public TransportRouteResult(TransportRoutingContext ctx) { cfg = ctx.cfg; } - + + public TransportRouteResult(TransportRoutingConfiguration cfg) { + this.cfg = cfg; + } + public List getSegments() { return segments; } + public void setFinishWalkDist(double finishWalkDist) { + this.finishWalkDist = finishWalkDist; + } + + public void setRouteTime(double routeTime) { + this.routeTime = routeTime; + } + + public void addSegment(TransportRouteResultSegment seg) { + segments.add(seg); + } + public double getWalkDist() { double d = finishWalkDist; for (TransportRouteResultSegment s : segments) { @@ -678,7 +696,7 @@ public class TransportRoutePlanner { } public static class TransportRoutingContext { - + public NativeLibrary library; public RouteCalculationProgress calculationProgress; public TLongObjectHashMap visitedSegments = new TLongObjectHashMap(); public TransportRoutingConfiguration cfg; @@ -705,11 +723,12 @@ public class TransportRoutePlanner { - public TransportRoutingContext(TransportRoutingConfiguration cfg, BinaryMapIndexReader... readers) { + public TransportRoutingContext(TransportRoutingConfiguration cfg, NativeLibrary library, BinaryMapIndexReader... readers) { this.cfg = cfg; walkRadiusIn31 = (int) (cfg.walkRadius / MapUtils.getTileDistanceWidth(31)); walkChangeRadiusIn31 = (int) (cfg.walkChangeRadius / MapUtils.getTileDistanceWidth(31)); quadTree = new TLongObjectHashMap>(); + this.library = library; for (BinaryMapIndexReader r : readers) { routeMap.put(r, new TIntObjectHashMap()); } diff --git a/OsmAnd/src/net/osmand/plus/routing/TransportRoutingHelper.java b/OsmAnd/src/net/osmand/plus/routing/TransportRoutingHelper.java index 7fbf22e3aa..f00759701b 100644 --- a/OsmAnd/src/net/osmand/plus/routing/TransportRoutingHelper.java +++ b/OsmAnd/src/net/osmand/plus/routing/TransportRoutingHelper.java @@ -6,21 +6,29 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import net.osmand.Location; +import net.osmand.NativeLibrary; import net.osmand.PlatformUtil; import net.osmand.ValueHolder; import net.osmand.binary.BinaryMapIndexReader; import net.osmand.data.LatLon; import net.osmand.data.QuadRect; +import net.osmand.data.TransportRoute; +import net.osmand.data.TransportSchedule; +import net.osmand.data.TransportStop; +import net.osmand.data.TransportStopExit; import net.osmand.osm.edit.Node; +import net.osmand.osm.edit.Way; import net.osmand.plus.ApplicationMode; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; import net.osmand.plus.OsmandSettings; import net.osmand.plus.R; +import net.osmand.plus.render.NativeOsmandLibrary; import net.osmand.plus.routing.RouteCalculationParams.RouteCalculationResultListener; import net.osmand.plus.routing.RouteProvider.RouteService; import net.osmand.plus.routing.RoutingHelper.RouteCalculationProgressCallback; import net.osmand.router.GeneralRouter; +import net.osmand.router.NativeTransportRoutingResult; import net.osmand.router.RouteCalculationProgress; import net.osmand.router.RoutingConfiguration; import net.osmand.router.TransportRoutePlanner; @@ -32,6 +40,7 @@ import net.osmand.util.MapUtils; import java.io.IOException; import java.lang.ref.WeakReference; +import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; @@ -41,6 +50,9 @@ import java.util.Queue; import java.util.TreeMap; import java.util.concurrent.ConcurrentLinkedQueue; +import gnu.trove.list.array.TIntArrayList; +import gnu.trove.map.hash.TLongObjectHashMap; + import static net.osmand.plus.notifications.OsmandNotification.NotificationType.NAVIGATION; public class TransportRoutingHelper { @@ -211,7 +223,8 @@ public class TransportRoutingHelper { final Thread prevRunningJob = currentRunningJob; app.getSettings().LAST_ROUTE_APPLICATION_MODE.set(routingHelper.getAppMode()); RouteRecalculationThread newThread = - new RouteRecalculationThread("Calculating public transport route", params); + new RouteRecalculationThread("Calculating public transport route", params, + app.getSettings().SAFE_MODE.get() ? null : NativeOsmandLibrary.getLoadedLibrary()); currentRunningJob = newThread; startProgress(params); updateProgress(params); @@ -439,10 +452,12 @@ public class TransportRoutingHelper { private final Queue walkingSegmentsToCalculate = new ConcurrentLinkedQueue<>(); private Map, RouteCalculationResult> walkingRouteSegments = new HashMap<>(); private boolean walkingSegmentsCalculated; + private NativeLibrary lib; - public RouteRecalculationThread(String name, TransportRouteCalculationParams params) { + public RouteRecalculationThread(String name, TransportRouteCalculationParams params, NativeLibrary library) { super(name); this.params = params; + this.lib = library; if (params.calculationProgress == null) { params.calculationProgress = new RouteCalculationProgress(); } @@ -460,7 +475,7 @@ public class TransportRoutingHelper { * @throws IOException * @throws InterruptedException */ - private List calculateRouteImpl(TransportRouteCalculationParams params) throws IOException, InterruptedException { + private List calculateRouteImpl(TransportRouteCalculationParams params, NativeLibrary library) throws IOException, InterruptedException { RoutingConfiguration.Builder config = params.ctx.getRoutingConfigForMode(params.mode); BinaryMapIndexReader[] files = params.ctx.getResourceManager().getTransportRoutingMapFiles(); params.params.clear(); @@ -480,14 +495,23 @@ public class TransportRoutingHelper { params.params.put(key, vl); } } - - GeneralRouter prouter = config.getRouter(params.mode.getRoutingProfile()); TransportRoutingConfiguration cfg = new TransportRoutingConfiguration(prouter, params.params); TransportRoutePlanner planner = new TransportRoutePlanner(); - TransportRoutingContext ctx = new TransportRoutingContext(cfg, files); + TransportRoutingContext ctx = new TransportRoutingContext(cfg, library, files); ctx.calculationProgress = params.calculationProgress; - return planner.buildRoute(ctx, params.start, params.end); + if (ctx.library != null) { + NativeTransportRoutingResult[] nativeRes = library.runNativePTRouting( + MapUtils.get31TileNumberX(params.start.getLongitude()), + MapUtils.get31TileNumberY(params.start.getLatitude()), + MapUtils.get31TileNumberX(params.end.getLongitude()), + MapUtils.get31TileNumberY(params.end.getLatitude()), + cfg, ctx.calculationProgress); + + return convertToTransportRoutingResult(nativeRes, cfg); + } else { + return planner.buildRoute(ctx, params.start, params.end); + } } @Nullable @@ -625,7 +649,7 @@ public class TransportRoutingHelper { List res = null; String error = null; try { - res = calculateRouteImpl(params); + res = calculateRouteImpl(params, lib); if (res != null && !params.calculationProgress.isCancelled) { calculateWalkingRoutes(res); } @@ -668,4 +692,134 @@ public class TransportRoutingHelper { this.prevRunningJob = prevRunningJob; } } + + //cache for converted TransportRoutes: + private TLongObjectHashMap convertedRoutesCache; + private TLongObjectHashMap convertedStopsCache; + + private List convertToTransportRoutingResult(NativeTransportRoutingResult[] res, + TransportRoutingConfiguration cfg) { + List convertedRes = new ArrayList(); + for (NativeTransportRoutingResult ntrr : res) { + TransportRouteResult trr = new TransportRouteResult(cfg); + trr.setFinishWalkDist(ntrr.finishWalkDist); + trr.setRouteTime(ntrr.routeTime); + + for (NativeTransportRoutingResult.NativeTransportRouteResultSegment ntrs : ntrr.segments) { + TransportRouteResultSegment trs = new TransportRouteResultSegment(); + trs.route = convertTransportRoute(ntrs.route); + trs.walkTime = ntrs.walkTime; + trs.travelDistApproximate = ntrs.travelDistApproximate; + trs.travelTime = ntrs.travelTime; + trs.start = ntrs.start; + trs.end = ntrs.end; + trs.walkDist = ntrs.walkDist; + trs.depTime = ntrs.depTime; + + trr.addSegment(trs); + } + convertedRes.add(trr); + } + convertedStopsCache.clear(); + convertedRoutesCache.clear(); + return convertedRes; + } + + private TransportRoute convertTransportRoute(NativeTransportRoutingResult.NativeTransportRoute nr) { + TransportRoute r = new TransportRoute(); + r.setId(nr.id); + r.setLocation(nr.routeLat, nr.routeLon); + r.setName(nr.name); + r.setEnName(nr.enName); + if (nr.namesLng.length > 0 && nr.namesLng.length == nr.namesNames.length) { + for (int i = 0; i < nr.namesLng.length; i++) { + r.setName(nr.namesLng[i], nr.namesNames[i]); + } + } + r.setFileOffset(nr.fileOffset); + r.setForwardStops(convertTransportStops(nr.forwardStops)); + r.setRef(nr.ref); + r.setOperator(nr.routeOperator); + r.setType(nr.type); + r.setDist(nr.dist); + r.setColor(nr.color); + + if (nr.intervals.length > 0 || nr.avgStopIntervals.length > 0 || nr.avgWaitIntervals.length > 0) { + r.setSchedule(new TransportSchedule(new TIntArrayList(nr.intervals), new TIntArrayList(nr.avgStopIntervals), new TIntArrayList(nr.avgWaitIntervals))); + } + + for (int i = 0; i < nr.waysIds.length; i++) { + List wnodes = new ArrayList<>(); + for (int j = 0; j < nr.waysNodesIds[i].length; j++) { + wnodes.add(new Node(nr.waysNodesLats[i][j], nr.waysNodesLons[i][j], nr.waysNodesIds[i][j])); + } + r.addWay(new Way(nr.waysIds[i], wnodes)); + } + + if (convertedRoutesCache == null) { + convertedRoutesCache = new TLongObjectHashMap<>(); + } + if (convertedRoutesCache.get(r.getId()) == null) { + convertedRoutesCache.put(r.getId(), r); + } + return r; + } + + private List convertTransportStops(NativeTransportRoutingResult.NativeTransportStop[] nstops) { + List stops = new ArrayList<>(); + for (NativeTransportRoutingResult.NativeTransportStop ns : nstops) { + if (convertedStopsCache != null && convertedStopsCache.get(ns.id) != null) { + stops.add(convertedStopsCache.get(ns.id)); + continue; + } + TransportStop s = new TransportStop(); + s.setId(ns.id); + s.setLocation(ns.stopLat, ns.stopLon); + s.setName(ns.name); + s.setEnName(ns.enName); + if (ns.namesLng.length > 0 && ns.namesLng.length == ns.namesNames.length) { + for (int i = 0; i < ns.namesLng.length; i++) { + s.setName(ns.namesLng[i], ns.namesNames[i]); + } + } + s.setFileOffset(ns.fileOffset); + s.setReferencesToRoutes(ns.referencesToRoutes); + s.setDeletedRoutesIds(ns.deletedRoutesIds); + s.setRoutesIds(ns.routesIds); + s.distance = ns.distance; + s.x31 = ns.x31; + s.y31 = ns.y31; + List routes1 = new ArrayList<>(); + //cache routes to avoid circular conversion and just search them by id + for (int i = 0; i < ns.routes.length; i++) { + if (s.getRoutesIds().length == ns.routes.length && convertedRoutesCache != null + && convertedRoutesCache.get(ns.routesIds[i]) != null) { + s.addRoute(convertedRoutesCache.get(ns.routesIds[i])); + } else { + s.addRoute(convertTransportRoute(ns.routes[i])); + } + } + + if (ns.pTStopExit_refs.length > 0) { + for (int i = 0; i < ns.pTStopExit_refs.length; i++) { + s.addExit(new TransportStopExit(ns.pTStopExit_x31s[i], + ns.pTStopExit_y31s[i], ns.pTStopExit_refs[i])); + } + } + + if (ns.referenceToRoutesKeys.length > 0) { + for (int i = 0; i < ns.referenceToRoutesKeys.length; i++) { + s.putReferencesToRoutes(ns.referenceToRoutesKeys[i], ns.referenceToRoutesVals[i]); + } + } + if (convertedStopsCache == null) { + convertedStopsCache = new TLongObjectHashMap<>(); + } + if (convertedStopsCache.get(s.getId()) == null) { + convertedStopsCache.put(s.getId(), s); + } + stops.add(s); + } + return stops; + } }