diff --git a/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapIndexReader.java b/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapIndexReader.java index db598b7d70..704b8cee4a 100644 --- a/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapIndexReader.java +++ b/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapIndexReader.java @@ -1731,6 +1731,7 @@ public class BinaryMapIndexReader { searchResults = new ArrayList(); cacheCoordinates.clear(); cacheTypes.clear(); + stringTable = null; land = false; ocean = false; numberOfVisitedObjects = 0; 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 8900a8c308..9bebda3dbb 100644 --- a/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapTransportReaderAdapter.java +++ b/OsmAnd-java/src/main/java/net/osmand/binary/BinaryMapTransportReaderAdapter.java @@ -436,7 +436,7 @@ public class BinaryMapTransportReaderAdapter { } } dataObject.setId(did); - dataObject.setLocation(MapUtils.getLatitudeFromTile(BinaryMapIndexReader.TRANSPORT_STOP_ZOOM, dy), MapUtils.getLongitudeFromTile(BinaryMapIndexReader.TRANSPORT_STOP_ZOOM, dx)); + dataObject.setLocation(BinaryMapIndexReader.TRANSPORT_STOP_ZOOM, dx, dy); return dataObject; } @@ -461,7 +461,7 @@ public class BinaryMapTransportReaderAdapter { req.cacheTypes.clear(); TransportStop dataObject = new TransportStop(); - dataObject.setLocation(MapUtils.getLatitudeFromTile(BinaryMapIndexReader.TRANSPORT_STOP_ZOOM, y), MapUtils.getLongitudeFromTile(BinaryMapIndexReader.TRANSPORT_STOP_ZOOM, x)); + dataObject.setLocation(BinaryMapIndexReader.TRANSPORT_STOP_ZOOM, x, y); dataObject.setFileOffset(shift); while(true){ int t = codedIS.readTag(); diff --git a/OsmAnd-java/src/main/java/net/osmand/binary/GeocodingUtilities.java b/OsmAnd-java/src/main/java/net/osmand/binary/GeocodingUtilities.java index b6b46a820f..ddcd137d92 100644 --- a/OsmAnd-java/src/main/java/net/osmand/binary/GeocodingUtilities.java +++ b/OsmAnd-java/src/main/java/net/osmand/binary/GeocodingUtilities.java @@ -142,7 +142,7 @@ public class GeocodingUtilities { public List reverseGeocodingSearch(RoutingContext ctx, double lat, double lon, boolean allowEmptyNames) throws IOException { - RoutePlannerFrontEnd rp = new RoutePlannerFrontEnd(false); + RoutePlannerFrontEnd rp = new RoutePlannerFrontEnd(); List lst = new ArrayList(); List listR = new ArrayList(); rp.findRouteSegment(lat, lon, ctx, listR); 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 88f9bdd372..f056ace22f 100644 --- a/OsmAnd-java/src/main/java/net/osmand/data/TransportRoute.java +++ b/OsmAnd-java/src/main/java/net/osmand/data/TransportRoute.java @@ -1,8 +1,16 @@ package net.osmand.data; -import java.util.ArrayList; -import java.util.List; +import gnu.trove.list.array.TIntArrayList; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import net.osmand.osm.edit.Node; import net.osmand.osm.edit.Way; import net.osmand.util.MapUtils; @@ -14,6 +22,7 @@ public class TransportRoute extends MapObject { private Integer dist = null; private String color; private List forwardWays; + public static final double SAME_STOP = 25; public TransportRoute(){ } @@ -26,6 +35,110 @@ public class TransportRoute extends MapObject { return forwardWays; } + + public void mergeForwardWays() { + boolean changed = true; + // combine as many ways as possible + while (changed) { + changed = false; + Iterator it = forwardWays.iterator(); + while (it.hasNext() && !changed) { + // scan to merge with the next segment + double d = SAME_STOP; + Way w = it.next(); + Way toCombine = null; + boolean reverseOriginal = false; + boolean reverseCombine = false; + for (int i = 0; i < forwardWays.size(); i++) { + Way combine = forwardWays.get(i); + if (combine == w) { + continue; + } + double distAttachAfter = MapUtils.getDistance(w.getFirstNode().getLatLon(), combine.getLastNode().getLatLon()); + double distReverseAttachAfter = MapUtils.getDistance(w.getLastNode().getLatLon(), combine.getLastNode() + .getLatLon()); + double distAttachAfterReverse = MapUtils.getDistance(w.getFirstNode().getLatLon(), combine.getFirstNode().getLatLon()); + if (distAttachAfter < d) { + toCombine = combine; + reverseOriginal = false; + reverseCombine = false; + d = distAttachAfter; + } else if (distReverseAttachAfter < d) { + toCombine = combine; + reverseOriginal = true; + reverseCombine = false; + d = distReverseAttachAfter; + } else if (distAttachAfterReverse < d) { + toCombine = combine; + reverseOriginal = false; + reverseCombine = true; + d = distAttachAfterReverse; + } + } + if (toCombine != null) { + if(reverseCombine) { + toCombine.reverseNodes(); + } + if(reverseOriginal) { + w.reverseNodes(); + } + for (int i = 1; i < w.getNodes().size(); i++) { + toCombine.addNode(w.getNodes().get(i)); + } + it.remove(); + changed = true; + } + } + } + if (forwardStops.size() > 0) { + // resort ways to stops order + final Map orderWays = new HashMap(); + for (Way w : forwardWays) { + int[] pair = new int[] { 0, 0 }; + Node firstNode = w.getFirstNode(); + TransportStop st = forwardStops.get(0); + double firstDistance = MapUtils.getDistance(st.getLocation(), firstNode.getLatitude(), + firstNode.getLongitude()); + Node lastNode = w.getLastNode(); + double lastDistance = MapUtils.getDistance(st.getLocation(), lastNode.getLatitude(), + lastNode.getLongitude()); + for (int i = 1; i < forwardStops.size(); i++) { + st = forwardStops.get(i); + double firstd = MapUtils.getDistance(st.getLocation(), firstNode.getLatitude(), + firstNode.getLongitude()); + double lastd = MapUtils.getDistance(st.getLocation(), lastNode.getLatitude(), + lastNode.getLongitude()); + if (firstd < firstDistance) { + pair[0] = i; + firstDistance = firstd; + } + if (lastd < lastDistance) { + pair[1] = i; + lastDistance = lastd; + } + } + orderWays.put(w, pair); + if(pair[0] > pair[1]) { + w.reverseNodes(); + } + } + if(orderWays.size() > 1) { + Collections.sort(forwardWays, new Comparator() { + @Override + public int compare(Way o1, Way o2) { + int[] is1 = orderWays.get(o1); + int[] is2 = orderWays.get(o2); + int i1 = is1 != null ? Math.min(is1[0], is1[1]) : 0; + int i2 = is2 != null ? Math.min(is2[0], is2[1]) : 0; + return Integer.compare(i1, i2); + } + }); + } + + } + } + + public String getColor() { return color; } diff --git a/OsmAnd-java/src/main/java/net/osmand/data/TransportStop.java b/OsmAnd-java/src/main/java/net/osmand/data/TransportStop.java index 3cc84afc18..981b182a63 100644 --- a/OsmAnd-java/src/main/java/net/osmand/data/TransportStop.java +++ b/OsmAnd-java/src/main/java/net/osmand/data/TransportStop.java @@ -1,9 +1,14 @@ package net.osmand.data; +import net.osmand.util.MapUtils; + public class TransportStop extends MapObject { private int[] referencesToRoutes = null; private Amenity amenity; public int distance; + public int x31; + public int y31; + public TransportStop(){ } @@ -23,4 +28,15 @@ public class TransportStop extends MapObject { public void setAmenity(Amenity amenity) { this.amenity = amenity; } + + @Override + public void setLocation(double latitude, double longitude) { + super.setLocation(latitude, longitude); + } + + public void setLocation(int zoom, int dx, int dy) { + x31 = dx << (31 - zoom); + y31 = dy << (31 - zoom); + setLocation(MapUtils.getLatitudeFromTile(zoom, dy), MapUtils.getLongitudeFromTile(zoom, dx)); + } } diff --git a/OsmAnd-java/src/main/java/net/osmand/router/BinaryRoutePlannerOld.java b/OsmAnd-java/src/main/java/net/osmand/router/BinaryRoutePlannerOld.java deleted file mode 100644 index 311ead4591..0000000000 --- a/OsmAnd-java/src/main/java/net/osmand/router/BinaryRoutePlannerOld.java +++ /dev/null @@ -1,580 +0,0 @@ -package net.osmand.router; - -import gnu.trove.map.hash.TLongObjectHashMap; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; -import java.util.PriorityQueue; - -import net.osmand.PlatformUtil; -import net.osmand.binary.RouteDataObject; -import net.osmand.osm.MapRenderingTypes; -import net.osmand.router.BinaryRoutePlanner.FinalRouteSegment; -import net.osmand.router.BinaryRoutePlanner.RouteSegment; -import net.osmand.util.MapUtils; - -import org.apache.commons.logging.Log; - -public class BinaryRoutePlannerOld { - - public static boolean PRINT_TO_CONSOLE_ROUTE_INFORMATION_TO_TEST = true; - private static final int REVERSE_WAY_RESTRICTION_ONLY = 1024; - private static final int STANDARD_ROAD_IN_QUEUE_OVERHEAD = 900; - - protected static final Log log = PlatformUtil.getLog(BinaryRoutePlannerOld.class); - - private static final int ROUTE_POINTS = 11; - - - private static double squareRootDist(int x1, int y1, int x2, int y2) { - return MapUtils.squareRootDist31(x1, y1, x2, y2); -// return measuredDist(x1, y1, x2, y2); - } - - - /** - * Calculate route between start.segmentEnd and end.segmentStart (using A* algorithm) - * return list of segments - */ - void searchRouteInternal(final RoutingContext ctx, RouteSegment start, RouteSegment end) throws IOException, InterruptedException { - // measure time - ctx.timeToLoad = 0; - ctx.visitedSegments = 0; - ctx.timeToCalculate = System.nanoTime(); - if(ctx.config.initialDirection != null) { - ctx.firstRoadId = (start.getRoad().id << ROUTE_POINTS) + start.getSegmentStart(); - double plusDir = start.getRoad().directionRoute(start.getSegmentStart(), true); - double diff = plusDir - ctx.config.initialDirection; - if(Math.abs(MapUtils.alignAngleDifference(diff)) <= Math.PI / 3) { - ctx.firstRoadDirection = 1; - } else if(Math.abs(MapUtils.alignAngleDifference(diff - Math.PI)) <= Math.PI / 3) { - ctx.firstRoadDirection = -1; - } - - } - - // Initializing priority queue to visit way segments - Comparator segmentsComparator = new Comparator(){ - @Override - public int compare(RouteSegment o1, RouteSegment o2) { - return ctx.roadPriorityComparator(o1.distanceFromStart, o1.distanceToEnd, o2.distanceFromStart, o2.distanceToEnd); - } - }; - - Comparator nonHeuristicSegmentsComparator = new Comparator(){ - @Override - public int compare(RouteSegment o1, RouteSegment o2) { - return roadPriorityComparator(o1.distanceFromStart, o1.distanceToEnd, o2.distanceFromStart, o2.distanceToEnd, 0.5); - } - }; - - PriorityQueue graphDirectSegments = new PriorityQueue(50, segmentsComparator); - PriorityQueue graphReverseSegments = new PriorityQueue(50, segmentsComparator); - - // Set to not visit one segment twice (stores road.id << X + segmentStart) - TLongObjectHashMap visitedDirectSegments = new TLongObjectHashMap(); - TLongObjectHashMap visitedOppositeSegments = new TLongObjectHashMap(); - - boolean runRecalculation = ctx.previouslyCalculatedRoute != null && ctx.previouslyCalculatedRoute.size() > 0 - && ctx.config.recalculateDistance != 0; - if (runRecalculation) { - RouteSegment previous = null; - List rlist = new ArrayList(); - float distanceThreshold = ctx.config.recalculateDistance; - float threshold = 0; - for(RouteSegmentResult rr : ctx.previouslyCalculatedRoute) { - threshold += rr.getDistance(); - if(threshold > distanceThreshold) { - rlist.add(rr); - } - } - runRecalculation = rlist.size() > 0; - if (rlist.size() > 0) { - for (RouteSegmentResult rr : rlist) { - RouteSegment segment = new RouteSegment(rr.getObject(), rr.getEndPointIndex()); - if (previous != null) { - previous.setParentRoute(segment); - previous.setParentSegmentEnd(rr.getStartPointIndex()); - long t = (rr.getObject().getId() << ROUTE_POINTS) + segment.getSegmentStart(); - visitedOppositeSegments.put(t, segment); - } - previous = segment; - } - end = previous; - } - } - - // for start : f(start) = g(start) + h(start) = 0 + h(start) = h(start) - int targetEndX = end.road.getPoint31XTile(end.getSegmentStart()); - int targetEndY = end.road.getPoint31YTile(end.getSegmentStart()); - int startX = start.road.getPoint31XTile(start.getSegmentStart()); - int startY = start.road.getPoint31YTile(start.getSegmentStart()); - float estimatedDistance = (float) h(ctx, targetEndX, targetEndY, startX, startY); - end.distanceToEnd = start.distanceToEnd = estimatedDistance; - - graphDirectSegments.add(start); - graphReverseSegments.add(end); - - // Extract & analyze segment with min(f(x)) from queue while final segment is not found - boolean inverse = false; - boolean init = false; - - PriorityQueue graphSegments; - if(inverse) { - graphSegments = graphReverseSegments; - } else { - graphSegments = graphDirectSegments; - } - while (!graphSegments.isEmpty()) { - RouteSegment segment = graphSegments.poll(); - ctx.visitedSegments++; - // for debug purposes - if (ctx.visitor != null) { -// ctx.visitor.visitSegment(segment, true); - } - updateCalculationProgress(ctx, graphDirectSegments, graphReverseSegments); - boolean routeFound = false; - if (!inverse) { - routeFound = processRouteSegment(ctx, false, graphDirectSegments, visitedDirectSegments, targetEndX, targetEndY, - segment, visitedOppositeSegments); - } else { - routeFound = processRouteSegment(ctx, true, graphReverseSegments, visitedOppositeSegments, startX, startY, segment, - visitedDirectSegments); - } - if (graphReverseSegments.isEmpty() || graphDirectSegments.isEmpty() || routeFound) { - break; - } - if(runRecalculation) { - // nothing to do - inverse = false; - } else if (!init) { - inverse = !inverse; - init = true; - } else if (ctx.planRouteIn2Directions()) { - inverse = nonHeuristicSegmentsComparator.compare(graphDirectSegments.peek(), graphReverseSegments.peek()) > 0; - if (graphDirectSegments.size() * 1.3 > graphReverseSegments.size()) { - inverse = true; - } else if (graphDirectSegments.size() < 1.3 * graphReverseSegments.size()) { - inverse = false; - } - } else { - // different strategy : use onedirectional graph - inverse = ctx.getPlanRoadDirection() < 0; - } - if (inverse) { - graphSegments = graphReverseSegments; - } else { - graphSegments = graphDirectSegments; - } - // check if interrupted - if(ctx.calculationProgress != null && ctx.calculationProgress.isCancelled) { - throw new InterruptedException("Route calculation interrupted"); - } - } - println("Result is found"); - printDebugMemoryInformation(ctx, graphDirectSegments, graphReverseSegments, visitedDirectSegments, visitedOppositeSegments); - } - - private void updateCalculationProgress(final RoutingContext ctx, PriorityQueue graphDirectSegments, - PriorityQueue graphReverseSegments) { - if(ctx.calculationProgress != null) { - ctx.calculationProgress.reverseSegmentQueueSize = graphReverseSegments.size(); - ctx.calculationProgress.directSegmentQueueSize = graphDirectSegments.size(); - RouteSegment dirPeek = graphDirectSegments.peek(); - if(dirPeek != null) { - ctx.calculationProgress.distanceFromBegin = - Math.max(dirPeek.distanceFromStart, ctx.calculationProgress.distanceFromBegin); - } - RouteSegment revPeek = graphReverseSegments.peek(); - if(revPeek != null) { - ctx.calculationProgress.distanceFromEnd = - Math.max(revPeek.distanceFromStart, ctx.calculationProgress.distanceFromEnd); - } - } - } - - - private double h(final RoutingContext ctx, int targetEndX, int targetEndY, - int startX, int startY) { - double distance = squareRootDist(startX, startY, targetEndX, targetEndY); - return distance / ctx.getRouter().getMaxDefaultSpeed(); - } - - protected static double h(RoutingContext ctx, double distToFinalPoint, RouteSegment next) { - return distToFinalPoint / ctx.getRouter().getMaxDefaultSpeed(); - } - - private static void println(String logMsg) { -// log.info(logMsg); - System.out.println(logMsg); - } - - private static void printInfo(String logMsg) { - log.warn(logMsg); - } - - public void printDebugMemoryInformation(RoutingContext ctx, PriorityQueue graphDirectSegments, PriorityQueue graphReverseSegments, - TLongObjectHashMap visitedDirectSegments,TLongObjectHashMap visitedOppositeSegments) { - printInfo("Time to calculate : " + (System.nanoTime() - ctx.timeToCalculate) / 1e6 + ", time to load : " + ctx.timeToLoad / 1e6 + ", time to load headers : " + ctx.timeToLoadHeaders / 1e6); - int maxLoadedTiles = Math.max(ctx.maxLoadedTiles, ctx.getCurrentlyLoadedTiles()); - printInfo("Current loaded tiles : " + ctx.getCurrentlyLoadedTiles() + ", maximum loaded tiles " + maxLoadedTiles); - printInfo("Loaded tiles " + ctx.loadedTiles + " (distinct "+ctx.distinctLoadedTiles+ "), unloaded tiles " + ctx.unloadedTiles + - ", loaded more than once same tiles " - + ctx.loadedPrevUnloadedTiles ); - printInfo("Visited roads, " + ctx.visitedSegments + ", relaxed roads " + ctx.relaxedSegments); - if (graphDirectSegments != null && graphReverseSegments != null) { - printInfo("Priority queues sizes : " + graphDirectSegments.size() + "/" + graphReverseSegments.size()); - } - if (visitedDirectSegments != null && visitedOppositeSegments != null) { - printInfo("Visited segments sizes: " + visitedDirectSegments.size() + "/" + visitedOppositeSegments.size()); - } - - } - - - private boolean processRouteSegment(final RoutingContext ctx, boolean reverseWaySearch, - PriorityQueue graphSegments, TLongObjectHashMap visitedSegments, int targetEndX, int targetEndY, - RouteSegment segment, TLongObjectHashMap oppositeSegments) throws IOException { - // Always start from segmentStart (!), not from segmentEnd - // It makes difference only for the first start segment - // Middle point will always be skipped from observation considering already visited - final RouteDataObject road = segment.road; - final int middle = segment.getSegmentStart(); - float obstaclePlusTime = 0; - float obstacleMinusTime = 0; - - - // This is correct way of checking but it has problem with relaxing strategy -// long ntf = (segment.road.getId() << ROUTE_POINTS) + segment.segmentStart; -// visitedSegments.put(ntf, segment); -// if (oppositeSegments.contains(ntf) && oppositeSegments.get(ntf) != null) { -// RouteSegment opposite = oppositeSegments.get(ntf); -// if (opposite.segmentStart == segment.segmentStart) { - // if (reverseWaySearch) { - // reverse : segment.parentSegmentEnd - segment.parentRoute - // } else { - // reverse : opposite.parentSegmentEnd - oppositie.parentRoute - // } -// return true; -// } -// } - - // 0. mark route segment as visited - long nt = (road.getId() << ROUTE_POINTS) + middle; - // avoid empty segments to connect but mark the point as visited - visitedSegments.put(nt, null); - - int oneway = ctx.getRouter().isOneWay(road); - boolean minusAllowed; - boolean plusAllowed; - if(ctx.firstRoadId == nt) { - if(ctx.firstRoadDirection < 0) { - obstaclePlusTime += 500; - } else if(ctx.firstRoadDirection > 0) { - obstacleMinusTime += 500; - } - } - if (!reverseWaySearch) { - minusAllowed = oneway <= 0; - plusAllowed = oneway >= 0; - } else { - minusAllowed = oneway >= 0; - plusAllowed = oneway <= 0; - } - - // +/- diff from middle point - int d = plusAllowed ? 1 : -1; - if(segment.parentRoute != null) { - if(plusAllowed && middle < segment.getRoad().getPointsLength() - 1) { - obstaclePlusTime = (float) ctx.getRouter().calculateTurnTime(segment, segment.getRoad().getPointsLength() - 1, - segment.parentRoute, segment.parentSegmentEnd); - } - if(minusAllowed && middle > 0) { - obstacleMinusTime = (float) ctx.getRouter().calculateTurnTime(segment, 0, - segment.parentRoute, segment.parentSegmentEnd); - } - } - // Go through all point of the way and find ways to continue - // ! Actually there is small bug when there is restriction to move forward on way (it doesn't take into account) - float posSegmentDist = 0; - float negSegmentDist = 0; - while (minusAllowed || plusAllowed) { - // 1. calculate point not equal to middle - // (algorithm should visit all point on way if it is not oneway) - int segmentEnd = middle + d; - boolean positive = d > 0; - if (!minusAllowed && d > 0) { - d++; - } else if (!plusAllowed && d < 0) { - d--; - } else { - if (d <= 0) { - d = -d + 1; - } else { - d = -d; - } - } - if (segmentEnd < 0) { - minusAllowed = false; - continue; - } - if (segmentEnd >= road.getPointsLength()) { - plusAllowed = false; - continue; - } - // if we found end point break cycle - long nts = (road.getId() << ROUTE_POINTS) + segmentEnd; - visitedSegments.put(nts, segment); - - // 2. calculate point and try to load neighbor ways if they are not loaded - int x = road.getPoint31XTile(segmentEnd); - int y = road.getPoint31YTile(segmentEnd); - if(positive) { - posSegmentDist += squareRootDist(x, y, - road.getPoint31XTile(segmentEnd - 1), road.getPoint31YTile(segmentEnd - 1)); - } else { - negSegmentDist += squareRootDist(x, y, - road.getPoint31XTile(segmentEnd + 1), road.getPoint31YTile(segmentEnd + 1)); - } - - // 2.1 calculate possible obstacle plus time - if(positive){ - double obstacle = ctx.getRouter().defineRoutingObstacle(road, segmentEnd); - if (obstacle < 0) { - plusAllowed = false; - continue; - } - obstaclePlusTime += obstacle; - } else { - double obstacle = ctx.getRouter().defineRoutingObstacle(road, segmentEnd); - if (obstacle < 0) { - minusAllowed = false; - continue; - } - obstacleMinusTime += obstacle; - } -// int overhead = 0; - // could be expensive calculation - int overhead = (ctx.visitedSegments - ctx.relaxedSegments ) * - STANDARD_ROAD_IN_QUEUE_OVERHEAD; - if(overhead > ctx.config.memoryLimitation * 0.95){ - throw new OutOfMemoryError("There is no enough memory " + ctx.config.memoryLimitation/(1<<20) + " Mb"); - } - RouteSegment next = ctx.loadRouteSegment(x, y, ctx.config.memoryLimitation - overhead); - // 3. get intersected ways - if (next != null) { - // TO-DO U-Turn - if((next == segment || next.road.id == road.id) && next.next == null) { - // simplification if there is no real intersection - continue; - } - // Using A* routing algorithm - // g(x) - calculate distance to that point and calculate time - - float priority = ctx.getRouter().defineSpeedPriority(road); - float speed = ctx.getRouter().defineRoutingSpeed(road) * priority; - if (speed == 0) { - speed = ctx.getRouter().getMinDefaultSpeed() * priority; - } - float distOnRoadToPass = positive? posSegmentDist : negSegmentDist; - float distStartObstacles = segment.distanceFromStart + ( positive ? obstaclePlusTime : obstacleMinusTime) + - distOnRoadToPass / speed; - - float distToFinalPoint = (float) squareRootDist(x, y, targetEndX, targetEndY); - boolean routeFound = processIntersections(ctx, graphSegments, visitedSegments, oppositeSegments, - distStartObstacles, distToFinalPoint, segment, segmentEnd, next, reverseWaySearch); - if(routeFound){ - return routeFound; - } - - } - } - return false; - } - - - private boolean proccessRestrictions(RoutingContext ctx, RouteDataObject road, RouteSegment inputNext, boolean reverseWay) { - ctx.segmentsToVisitPrescripted.clear(); - ctx.segmentsToVisitNotForbidden.clear(); - boolean exclusiveRestriction = false; - RouteSegment next = inputNext; - if (!reverseWay && road.getRestrictionLength() == 0) { - return false; - } - if(!ctx.getRouter().restrictionsAware()) { - return false; - } - while (next != null) { - int type = -1; - if (!reverseWay) { - for (int i = 0; i < road.getRestrictionLength(); i++) { - if (road.getRestrictionId(i) == next.road.id) { - type = road.getRestrictionType(i); - break; - } - } - } else { - for (int i = 0; i < next.road.getRestrictionLength(); i++) { - int rt = next.road.getRestrictionType(i); - long restrictedTo = next.road.getRestrictionId(i); - if (restrictedTo == road.id) { - type = rt; - break; - } - - // Check if there is restriction only to the other than current road - if (rt == MapRenderingTypes.RESTRICTION_ONLY_RIGHT_TURN || rt == MapRenderingTypes.RESTRICTION_ONLY_LEFT_TURN - || rt == MapRenderingTypes.RESTRICTION_ONLY_STRAIGHT_ON) { - // check if that restriction applies to considered junk - RouteSegment foundNext = inputNext; - while (foundNext != null) { - if (foundNext.getRoad().id == restrictedTo) { - break; - } - foundNext = foundNext.next; - } - if (foundNext != null) { - type = REVERSE_WAY_RESTRICTION_ONLY; // special constant - } - } - } - } - if (type == REVERSE_WAY_RESTRICTION_ONLY) { - // next = next.next; continue; - } else if (type == -1 && exclusiveRestriction) { - // next = next.next; continue; - } else if (type == MapRenderingTypes.RESTRICTION_NO_LEFT_TURN || type == MapRenderingTypes.RESTRICTION_NO_RIGHT_TURN - || type == MapRenderingTypes.RESTRICTION_NO_STRAIGHT_ON || type == MapRenderingTypes.RESTRICTION_NO_U_TURN) { - // next = next.next; continue; - } else if (type == -1) { - // case no restriction - ctx.segmentsToVisitNotForbidden.add(next); - } else { - // case exclusive restriction (only_right, only_straight, ...) - // 1. in case we are going backward we should not consider only_restriction - // as exclusive because we have many "in" roads and one "out" - // 2. in case we are going forward we have one "in" and many "out" - if (!reverseWay) { - exclusiveRestriction = true; - ctx.segmentsToVisitNotForbidden.clear(); - ctx.segmentsToVisitPrescripted.add(next); - } else { - ctx.segmentsToVisitNotForbidden.add(next); - } - } - next = next.next; - } - ctx.segmentsToVisitPrescripted.addAll(ctx.segmentsToVisitNotForbidden); - return true; - } - - - - - private boolean processIntersections(RoutingContext ctx, PriorityQueue graphSegments, - TLongObjectHashMap visitedSegments, TLongObjectHashMap oppositeSegments, - float distFromStart, float distToFinalPoint, - RouteSegment segment, int segmentEnd, RouteSegment inputNext, - boolean reverseWay) { - - boolean thereAreRestrictions = proccessRestrictions(ctx, segment.road, inputNext, reverseWay); - Iterator nextIterator = null; - if (thereAreRestrictions) { - nextIterator = ctx.segmentsToVisitPrescripted.iterator(); - } - // Calculate possible ways to put into priority queue - RouteSegment next = inputNext; - boolean hasNext = nextIterator == null || nextIterator.hasNext(); - while (hasNext) { - if (nextIterator != null) { - next = nextIterator.next(); - } - long nts = (next.road.getId() << ROUTE_POINTS) + next.getSegmentStart(); - - // 1. Check if opposite segment found so we can stop calculations - if (oppositeSegments.contains(nts) && oppositeSegments.get(nts) != null) { - // restrictions checked - RouteSegment opposite = oppositeSegments.get(nts); - // additional check if opposite way not the same as current one - if (next.getSegmentStart() != segmentEnd || - opposite.getRoad().getId() != segment.getRoad().getId()) { - FinalRouteSegment frs = new FinalRouteSegment(segment.getRoad(), segment.getSegmentStart()); - float distStartObstacles = segment.distanceFromStart; - frs.setParentRoute(segment.getParentRoute()); - frs.setParentSegmentEnd(segment.getParentSegmentEnd()); - frs.reverseWaySearch = reverseWay; - frs.distanceFromStart = opposite.distanceFromStart + distStartObstacles; - RouteSegment op = new RouteSegment(segment.getRoad(), segmentEnd); - op.setParentRoute(opposite); - op.setParentSegmentEnd(next.getSegmentStart()); - frs.distanceToEnd = 0; - frs.opposite = op; - ctx.finalRouteSegment = frs; - return true; - } - } - // road.id could be equal on roundabout, but we should accept them - boolean alreadyVisited = visitedSegments.contains(nts); - if (!alreadyVisited) { - float distanceToEnd = (float) h(ctx, distToFinalPoint, next); - if (next.parentRoute == null - || ctx.roadPriorityComparator(next.distanceFromStart, next.distanceToEnd, distFromStart, distanceToEnd) > 0) { - if (next.parentRoute != null) { - // already in queue remove it - if (!graphSegments.remove(next)) { - // exist in different queue! - next = new RouteSegment(next.getRoad(), next.getSegmentStart()); - } - } - next.distanceFromStart = distFromStart; - next.distanceToEnd = distanceToEnd; - // put additional information to recover whole route after - next.setParentRoute(segment); - next.setParentSegmentEnd(segmentEnd); - graphSegments.add(next); - } - if (ctx.visitor != null) { -// ctx.visitor.visitSegment(next, false); - } - } else { - // the segment was already visited! We need to follow better route if it exists - // that is very strange situation and almost exception (it can happen when we underestimate distnceToEnd) - if (distFromStart < next.distanceFromStart && next.road.id != segment.road.id) { - // That code is incorrect (when segment is processed itself, - // then it tries to make wrong u-turn) - - // this situation should be very carefully checked in future (seems to be fixed) - // System.out.println(segment.getRoad().getName() + " " + next.getRoad().getName()); - // System.out.println(next.distanceFromStart + " ! " + distFromStart); - next.distanceFromStart = distFromStart; - next.setParentRoute(segment); - next.setParentSegmentEnd(segmentEnd); - if (ctx.visitor != null) { -// ctx.visitor.visitSegment(next, next.getSegmentStart(), false); - } - } - } - - // iterate to next road - if (nextIterator == null) { - next = next.next; - hasNext = next != null; - } else { - hasNext = nextIterator.hasNext(); - } - } - return false; - } - - - - /*public */static int roadPriorityComparator(double o1DistanceFromStart, double o1DistanceToEnd, - double o2DistanceFromStart, double o2DistanceToEnd, double heuristicCoefficient ) { - // f(x) = g(x) + h(x) --- g(x) - distanceFromStart, h(x) - distanceToEnd (not exact) - return Double.compare(o1DistanceFromStart + heuristicCoefficient * o1DistanceToEnd, - o2DistanceFromStart + heuristicCoefficient * o2DistanceToEnd); - } - -} diff --git a/OsmAnd-java/src/main/java/net/osmand/router/RoutePlannerFrontEnd.java b/OsmAnd-java/src/main/java/net/osmand/router/RoutePlannerFrontEnd.java index 17d29c872a..cad29d75e3 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/RoutePlannerFrontEnd.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/RoutePlannerFrontEnd.java @@ -1,6 +1,14 @@ package net.osmand.router; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; + import net.osmand.NativeLibrary; import net.osmand.PlatformUtil; import net.osmand.binary.BinaryMapIndexReader; @@ -15,24 +23,15 @@ import net.osmand.util.MapUtils; import org.apache.commons.logging.Log; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.List; - public class RoutePlannerFrontEnd { - private boolean useOldVersion; protected static final Log log = PlatformUtil.getLog(RoutePlannerFrontEnd.class); public boolean useSmartRouteRecalculation = true; - public RoutePlannerFrontEnd(boolean useOldVersion) { - this.useOldVersion = useOldVersion; + + public RoutePlannerFrontEnd() { } - + public enum RouteCalculationMode { BASE, NORMAL, @@ -345,11 +344,7 @@ public class RoutePlannerFrontEnd { } else { refreshProgressDistance(ctx); // Split into 2 methods to let GC work in between - if (useOldVersion) { - new BinaryRoutePlannerOld().searchRouteInternal(ctx, start, end); - } else { - ctx.finalRouteSegment = new BinaryRoutePlanner().searchRouteInternal(ctx, start, end, recalculationEnd); - } + ctx.finalRouteSegment = new BinaryRoutePlanner().searchRouteInternal(ctx, start, end, recalculationEnd); // 4. Route is found : collect all segments and prepare result return new RouteResultPreparation().prepareResult(ctx, ctx.finalRouteSegment); } diff --git a/OsmAnd-java/src/main/java/net/osmand/router/RoutingContext.java b/OsmAnd-java/src/main/java/net/osmand/router/RoutingContext.java index 840f321962..ddd44e738c 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/RoutingContext.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/RoutingContext.java @@ -197,9 +197,6 @@ public class RoutingContext { return config.planRoadDirection; } - public void setPlanRoadDirection(int planRoadDirection) { - config.planRoadDirection = planRoadDirection; - } public int roadPriorityComparator(double o1DistanceFromStart, double o1DistanceToEnd, double o2DistanceFromStart, double o2DistanceToEnd) { return BinaryRoutePlanner.roadPriorityComparator(o1DistanceFromStart, o1DistanceToEnd, o2DistanceFromStart, o2DistanceToEnd, diff --git a/OsmAnd-java/src/main/java/net/osmand/router/TestRouting.java b/OsmAnd-java/src/main/java/net/osmand/router/TestRouting.java index 77f5fc1142..78222819d9 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/TestRouting.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/TestRouting.java @@ -28,7 +28,6 @@ public class TestRouting { public static boolean TEST_WO_HEURISTIC = false; public static boolean TEST_BOTH_DIRECTION = false; public static NativeLibrary lib = null; - public static boolean oldRouting = false; private static String vehicle = "car"; @@ -203,7 +202,7 @@ public class TestRouting { return; } RoutingConfiguration rconfig = config.build(vehicle, MEMORY_TEST_LIMIT); - RoutePlannerFrontEnd router = new RoutePlannerFrontEnd(oldRouting); + RoutePlannerFrontEnd router = new RoutePlannerFrontEnd(); RoutingContext ctx = router.buildRoutingContext(rconfig, lib, rs); String skip = parser.getAttributeValue("", "skip_comment"); @@ -308,7 +307,7 @@ public class TestRouting { long ts = System.currentTimeMillis(); Builder config = RoutingConfiguration.getDefault(); RoutingConfiguration rconfig = config.build(vehicle, MEMORY_TEST_LIMIT); - RoutePlannerFrontEnd router = new RoutePlannerFrontEnd(oldRouting); + RoutePlannerFrontEnd router = new RoutePlannerFrontEnd(); RoutingContext ctx = router.buildRoutingContext(rconfig, lib, rs); RouteSegment startSegment = router.findRouteSegment(startLat, startLon, ctx, null); RouteSegment endSegment = router.findRouteSegment(endLat, endLon, ctx, null); diff --git a/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutePlanner.java b/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutePlanner.java new file mode 100644 index 0000000000..97038debe2 --- /dev/null +++ b/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutePlanner.java @@ -0,0 +1,608 @@ +package net.osmand.router; + +import gnu.trove.iterator.TIntIterator; +import gnu.trove.list.array.TIntArrayList; +import gnu.trove.map.hash.TIntObjectHashMap; +import gnu.trove.map.hash.TLongObjectHashMap; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; + +import net.osmand.binary.BinaryMapIndexReader; +import net.osmand.binary.BinaryMapIndexReader.SearchRequest; +import net.osmand.data.LatLon; +import net.osmand.data.TransportRoute; +import net.osmand.data.TransportStop; +import net.osmand.osm.edit.Node; +import net.osmand.osm.edit.Way; +import net.osmand.util.MapUtils; + +public class TransportRoutePlanner { + + + public static void main(String[] args) throws IOException { + File fl = new File(System.getProperty("maps.dir"), "Netherlands_noord-holland_europe_2.obf"); + RandomAccessFile raf = new RandomAccessFile(fl, "r"); + BinaryMapIndexReader reader = new BinaryMapIndexReader(raf, fl); + + LatLon start = new LatLon(52.28094, 4.853248); +// LatLon end = new LatLon(52.320988, 4.87256); + LatLon end = new LatLon(52.349308, 4.9017425); + + TransportRoutingConfiguration cfg = new TransportRoutingConfiguration(); + cfg.maxNumberOfChanges = 3; + cfg.walkRadius = 1500; +// cfg.walkChangeRadius = 500; + TransportRoutingContext ctx = new TransportRoutingContext(cfg, reader); + TransportRoutePlanner planner = new TransportRoutePlanner(); + planner.buildRoute(ctx, start, end); + + } + + public List buildRoute(TransportRoutingContext ctx, LatLon start, LatLon end) throws IOException { + ctx.startCalcTime = System.currentTimeMillis(); + List startStops = ctx.getTransportStops(start); + List endStops = ctx.getTransportStops(end); + + TLongObjectHashMap endSegments = new TLongObjectHashMap(); + for(TransportRouteSegment s : endStops) { + endSegments.put(s.getId(), s); + } + PriorityQueue queue = new PriorityQueue(new SegmentsComparator(ctx)); + for(TransportRouteSegment r : startStops){ + r.walkDist = (float) MapUtils.getDistance(r.getLocation(), start); + r.distFromStart = r.walkDist / ctx.cfg.walkSpeed; + queue.add(r); + } + double finishTime = ctx.cfg.maxRouteTime; + List results = new ArrayList(); + + while (!queue.isEmpty()) { + TransportRouteSegment segment = queue.poll(); + TransportRouteSegment ex = ctx.visitedSegments.get(segment.getId()); + if(ex != null) { + if(ex.distFromStart > segment.distFromStart) { + System.err.println(String.format("%.1f (%s) > %.1f (%s)", ex.distFromStart, ex, segment.distFromStart, segment)); + } + continue; + } + ctx.visitedRoutesCount++; + ctx.visitedSegments.put(segment.getId(), segment); + if (segment.getDepth() > ctx.cfg.maxNumberOfChanges) { + continue; + } + if (segment.distFromStart > finishTime + ctx.cfg.finishTimeSeconds) { + break; + } + long segmentId = segment.getId(); + TransportRouteSegment finish = null; + double minDist = 0; + double travelDist = 0; + double travelTime = 0; + TransportStop prevStop = segment.getStop(segment.segStart); + List sgms = new ArrayList(); + for (int ind = 1 + segment.segStart; ind < segment.getLength(); ind++) { + segmentId ++; + ctx.visitedSegments.put(segmentId, segment); + TransportStop stop = segment.getStop(ind); + // could be geometry size + double segmentDist = MapUtils.getDistance(prevStop.getLocation(), stop.getLocation()); + travelDist += segmentDist; + travelTime += ctx.cfg.stopTime + segmentDist / ctx.cfg.travelSpeed; + sgms.clear(); + sgms = ctx.getTransportStops(stop.x31, stop.y31, true, sgms); + for (TransportRouteSegment sgm : sgms) { + if (segment.wasVisited(sgm)) { + continue; + } + TransportRouteSegment rrs = new TransportRouteSegment(sgm); + rrs.parentRoute = segment; + rrs.parentStop = ind; + rrs.walkDist = MapUtils.getDistance(rrs.getLocation(), stop.getLocation()); + rrs.parentTravelTime = travelTime; + rrs.parentTravelDist = travelDist; + + double walkTime = rrs.walkDist / ctx.cfg.walkSpeed + ctx.cfg.changeTime; + rrs.distFromStart = segment.distFromStart + travelTime + walkTime; + + queue.add(rrs); + } + TransportRouteSegment f = endSegments.get(segmentId); + double distToEnd = MapUtils.getDistance(stop.getLocation(), end); + if (f != null && distToEnd < ctx.cfg.walkRadius) { + if (finish == null || minDist > distToEnd) { + minDist = distToEnd; + finish = new TransportRouteSegment(f); + finish.parentRoute = segment; + finish.parentStop = ind; + finish.walkDist = distToEnd; + finish.parentTravelTime = travelTime; + finish.parentTravelDist = travelDist; + + double walkTime = distToEnd / ctx.cfg.walkSpeed; + finish.distFromStart = segment.distFromStart + travelTime + walkTime; + + } + } + prevStop = stop; + } + if (finish != null) { + if (finishTime > finish.distFromStart) { + finishTime = finish.distFromStart; + } + if(finish.distFromStart < finishTime + ctx.cfg.finishTimeSeconds) { + results.add(finish); + } + } + } + + return prepareResults(ctx, results); + } + + private List prepareResults(TransportRoutingContext ctx, List results) { + Collections.sort(results, new SegmentsComparator(ctx)); + List lst = new ArrayList(); + System.out.println("FIX !!! " + ctx.wrongLoadedWays + " " + ctx.loadedWays + " " + + (ctx.loadTime / (1000 * 1000)) + " ms"); + System.out.println(String.format("Calculated %.1f seconds, found %d results, visited %d routes, loaded %d tiles (%d ms read, %d ms total),", + (System.currentTimeMillis() - ctx.startCalcTime) / 1000.0, results.size(), ctx.visitedRoutesCount, + ctx.quadTree.size(), ctx.readTime / (1000 * 1000), ctx.loadTime / (1000 * 1000))); + for(TransportRouteSegment res : results) { + TransportRouteResult route = new TransportRouteResult(ctx); + route.routeTime = res.distFromStart; + route.finishWalkDist = res.walkDist; + TransportRouteSegment p = res; + while (p != null) { + if (p.parentRoute != null) { + TransportRouteResultSegment sg = new TransportRouteResultSegment(p.parentRoute.road, + p.parentRoute.segStart, p.parentStop, p.parentRoute.walkDist); + route.segments.add(0, sg); + } + p = p.parentRoute; + } + // test if faster routes fully included + boolean include = false; + for(TransportRouteResult s : lst) { + if(includeRoute(s, route)) { + include = true; + break; + } + } + if(!include) { + lst.add(route); + System.out.println(route.toString()); + } else { +// System.err.println(route.toString()); + } + } + return lst; + } + + private boolean includeRoute(TransportRouteResult fastRoute, TransportRouteResult testRoute) { + if(testRoute.segments.size() < fastRoute.segments.size()) { + return false; + } + int j = 0; + for(int i = 0; i < fastRoute.segments.size(); i++, j++) { + TransportRouteResultSegment fs = fastRoute.segments.get(i); + while(j < testRoute.segments.size()) { + TransportRouteResultSegment ts = testRoute.segments.get(j); + if(fs.route.getId().longValue() != ts.route.getId().longValue()) { + j++; + } else { + break; + } + } + if(j >= testRoute.segments.size()) { + return false; + } + } + + return true; + } + + private static class SegmentsComparator implements Comparator { + + public SegmentsComparator(TransportRoutingContext ctx) { + } + + @Override + public int compare(TransportRouteSegment o1, TransportRouteSegment o2) { + return Double.compare(o1.distFromStart, o2.distFromStart); + } + } + + + public static class TransportRouteResultSegment { + public final TransportRoute route; + public final int start; + public final int end; + public final double walkDist ; + + + public TransportRouteResultSegment(TransportRoute route, int start, int end, double walkDist) { + this.route = route; + this.start = start; + this.end = end; + this.walkDist = walkDist; + } + + public TransportStop getStart() { + return route.getForwardStops().get(start); + } + + public TransportStop getEnd() { + return route.getForwardStops().get(end); + } + + public List getGeometry() { + List list = new ArrayList(); + route.mergeForwardWays(); + List fw = route.getForwardWays(); + double minStart = 150; + double minEnd = 150; + LatLon str = getStart().getLocation(); + LatLon en = getEnd().getLocation(); + int endInd = -1; + List res = new ArrayList(); + for(int i = 0; i < fw.size() ; i++) { + List nodes = fw.get(i).getNodes(); + for(int j = 0; j < nodes.size(); j++) { + Node n = nodes.get(j); + if(MapUtils.getDistance(str, n.getLatitude(), n.getLongitude()) < minStart) { + minStart = MapUtils.getDistance(str, n.getLatitude(), n.getLongitude()); + res.clear(); + } + res.add(n); + if(MapUtils.getDistance(en, n.getLatitude(), n.getLongitude()) < minEnd) { + endInd = res.size(); + minEnd = MapUtils.getDistance(en, n.getLatitude(), n.getLongitude()); + } + } + } + Way way = new Way(-1); + if (res.isEmpty()) { + for (int i = start; i <= end; i++) { + LatLon l = getStop(i).getLocation(); + Node n = new Node(l.getLatitude(), l.getLongitude(), -1); + way.addNode(n); + } + list.add(way); + } else { + for(int k = 0; k < res.size() && k < endInd; k++) { + way.addNode(res.get(k)); + } + } + list.add(way); + return list; + + } + + public double getTravelDist() { + double d = 0; + for (int k = start; k < end; k++) { + d += MapUtils.getDistance(route.getForwardStops().get(k).getLocation(), + route.getForwardStops().get(k + 1).getLocation()); + } + return d; + } + + public TransportStop getStop(int i) { + return route.getForwardStops().get(i); + } + } + + public static class TransportRouteResult { + + List segments = new ArrayList(4); + double finishWalkDist; + double routeTime; + private final TransportRoutingConfiguration cfg; + + public TransportRouteResult(TransportRoutingContext ctx) { + cfg = ctx.cfg; + } + + public List getSegments() { + return segments; + } + + public double getWalkDist() { + double d = finishWalkDist; + for (TransportRouteResultSegment s : segments) { + d += s.walkDist; + } + return d; + } + + public double getRouteTime() { + return routeTime; + } + + public int getStops() { + int stops = 0; + for(TransportRouteResultSegment s : segments) { + stops += (s.end - s.start); + } + return stops; + } + + public double getTravelDist() { + double d = 0; + for (TransportRouteResultSegment s : segments) { + d += s.getTravelDist(); + } + return d; + } + + public double getTravelTime() { + return getTravelDist() / cfg.travelSpeed + cfg.stopTime * getStops() + + cfg.changeTime * getChanges(); + } + + public double getWalkTime() { + return getWalkDist() / cfg.walkSpeed; + } + + public int getChanges() { + return segments.size() - 1; + } + + @Override + public String toString() { + StringBuilder bld = new StringBuilder(); + bld.append(String.format("Route %d stops, %d changes, %.2f min: %.2f m (%.1f min) to walk, %.2f m (%.1f min) to travel\n", + getStops(), getChanges(), routeTime / 60, getWalkDist(), getWalkTime() / 60.0, + getTravelDist(), getTravelTime() / 60.0)); + for(int i = 0; i < segments.size(); i++) { + TransportRouteResultSegment s = segments.get(i); + bld.append(String.format(" %d. %s: walk %.1f m to '%s' and travel to '%s' by %s %d stops \n", + i + 1, s.route.getRef(), s.walkDist, s.getStart().getName(), s.getEnd().getName(), s.route.getName(), (s.end - s.start))); + } + bld.append(String.format(" F. Walk %.1f m to reach your destination", finishWalkDist)); + return bld.toString(); + } + + + } + + public static class TransportRouteSegment { + + final int segStart; + final TransportRoute road; + private static final int SHIFT = 10; // assume less than 1024 stops + + TransportRouteSegment parentRoute = null; + int parentStop; + double parentTravelTime; // travel time + double parentTravelDist; // inaccurate + // walk distance to start route location (or finish in case last segment) + double walkDist = 0; + + // main field accumulated all time spent from beginning of journey + double distFromStart = 0; + + + public TransportRouteSegment(TransportRoute road, int stopIndex) { + this.road = road; + this.segStart = (short) stopIndex; + } + + public TransportRouteSegment(TransportRouteSegment c) { + this.road = c.road; + this.segStart = c.segStart; + } + + + public boolean wasVisited(TransportRouteSegment rrs) { + if (rrs.road.getId().longValue() == road.getId().longValue()) { + return true; + } + if(parentRoute != null) { + return parentRoute.wasVisited(rrs); + } + return false; + } + + + public TransportStop getStop(int i) { + return road.getForwardStops().get(i); + } + + + public LatLon getLocation() { + return road.getForwardStops().get(segStart).getLocation(); + } + + + public int getLength() { + return road.getForwardStops().size(); + } + + + public long getId() { + long l = road.getId() << SHIFT; + if(l < 0 ) { + throw new IllegalStateException("too long id " + road.getId()); + } + if(segStart >= (1 << SHIFT)) { + throw new IllegalStateException("too many stops " + road.getId() + " " + segStart); + } + return l + segStart; + } + + + public int getDepth() { + if(parentRoute != null) { + return parentRoute.getDepth() + 1; + } + return 1; + } + + @Override + public String toString() { + return String.format("Route: %s, stop: %s", road.getName(), road.getForwardStops().get(segStart).getName()); + } + + } + + public static class TransportRoutingContext { + + public RouteCalculationProgress calculationProgress; + public TLongObjectHashMap visitedSegments = new TLongObjectHashMap(); + public TransportRoutingConfiguration cfg; + + + public TLongObjectHashMap> quadTree; + public final Map> map = + new LinkedHashMap>(); + + // stats + public long startCalcTime; + public int visitedRoutesCount; + public int wrongLoadedWays; + public int loadedWays; + public long loadTime; + public long readTime; + + + + private final int walkRadiusIn31; + private final int walkChangeRadiusIn31; + + + + + public TransportRoutingContext(TransportRoutingConfiguration cfg, BinaryMapIndexReader... readers) { + this.cfg = cfg; + walkRadiusIn31 = (int) (cfg.walkRadius / MapUtils.getTileDistanceWidth(31)); + walkChangeRadiusIn31 = (int) (cfg.walkChangeRadius / MapUtils.getTileDistanceWidth(31)); + quadTree = new TLongObjectHashMap>(); + for (BinaryMapIndexReader r : readers) { + map.put(r, new TIntObjectHashMap()); + } + } + + public List getTransportStops(LatLon loc) throws IOException { + int y = MapUtils.get31TileNumberY(loc.getLatitude()); + int x = MapUtils.get31TileNumberX(loc.getLongitude()); + return getTransportStops(x, y, false, new ArrayList()); + } + + public List getTransportStops(int x, int y, boolean change, List res) throws IOException { + return loadNativeTransportStops(x, y, change, res); + } + + private List loadNativeTransportStops(int sx, int sy, boolean change, List res) throws IOException { + long nanoTime = System.nanoTime(); + int d = change ? walkChangeRadiusIn31 : walkRadiusIn31; + int lx = (sx - d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES); + int rx = (sx + d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES); + int ty = (sy - d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES); + int by = (sy + d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES); + for(int x = lx; x <= rx; x++) { + for(int y = ty; y <= by; y++) { + int tileId = x << (cfg.ZOOM_TO_LOAD_TILES + 1) + y; + List list = quadTree.get(tileId); + if(list == null) { + list = loadTile(x, y); + quadTree.put(tileId, list); + } + for(TransportRouteSegment r : list) { + TransportStop st = r.getStop(r.segStart); + if (Math.abs(st.x31 - sx) > walkRadiusIn31 || Math.abs(st.y31 - sy) > walkRadiusIn31) { + wrongLoadedWays++; + } else { + loadedWays++; + res.add(r); + } + } + } + } + loadTime += System.nanoTime() - nanoTime; + return res; + } + + + private List loadTile(int x, int y) throws IOException { + long nanoTime = System.nanoTime(); + List lst = new ArrayList(); + int pz = (31 - cfg.ZOOM_TO_LOAD_TILES); + SearchRequest sr = BinaryMapIndexReader.buildSearchTransportRequest(x << pz, (x + 1) << pz, + y << pz, (y + 1) << pz, -1, null); + TIntArrayList allPoints = new TIntArrayList(); + TIntArrayList allPointsUnique = new TIntArrayList(); + // should it be global? + TLongObjectHashMap loadedTransportStops = new TLongObjectHashMap(); + for(BinaryMapIndexReader r : map.keySet()) { + sr.clearSearchResults(); + List stops = r.searchTransportIndex(sr); + for(TransportStop s : stops) { + if(!loadedTransportStops.contains(s.getId())) { + loadedTransportStops.put(s.getId(), s); + allPoints.addAll(s.getReferencesToRoutes()); + } + } + makeUnique(allPoints, allPointsUnique); + if(allPointsUnique.size() > 0) { + loadTransportSegments(allPointsUnique, r, stops, lst); + } + } + readTime += System.nanoTime() - nanoTime; + return lst; + } + + private void loadTransportSegments(TIntArrayList allPointsUnique, BinaryMapIndexReader r, + List stops, List lst) throws IOException { + TIntObjectHashMap routes = r.getTransportRoutes(allPointsUnique.toArray()); + map.get(r).putAll(routes); + for(TransportStop s : stops) { + for (int ref : s.getReferencesToRoutes()) { + TransportRoute route = routes.get(ref); + if (route != null) { + int stopIndex = -1; + double dist = TransportRoute.SAME_STOP; + for (int k = 0; k < route.getForwardStops().size(); k++) { + TransportStop st = route.getForwardStops().get(k); + double d = MapUtils.getDistance(st.getLocation(), s.getLocation()); + if (d < dist) { + stopIndex = k; + dist = d; + } + } + if (stopIndex != -1) { + TransportRouteSegment segment = new TransportRouteSegment(route, stopIndex); + lst.add(segment); + } else { + System.err.println("missing"); + } + + } + } + } + } + + private void makeUnique(TIntArrayList allPoints, TIntArrayList allPointsUnique) { + allPoints.sort(); + int p = 0; + TIntIterator it = allPoints.iterator(); + while(it.hasNext()) { + int nxt = it.next(); + if(p != nxt) { + allPointsUnique.add(nxt); + p = nxt; + } + } + } + + } + +} diff --git a/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutingConfiguration.java b/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutingConfiguration.java new file mode 100644 index 0000000000..f4e04618b0 --- /dev/null +++ b/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutingConfiguration.java @@ -0,0 +1,26 @@ +package net.osmand.router; + +public class TransportRoutingConfiguration { + + + public int ZOOM_TO_LOAD_TILES = 14; + + public int walkRadius = 1500; // ? 3000 + + public int walkChangeRadius = 300; + + public double walkSpeed = 3.6 / 3.6; // m/s + + public double travelSpeed = 36 / 3.6; // m/s + + public int stopTime = 30; + + public int changeTime = 300; + + public int maxNumberOfChanges = 5; + + public int finishTimeSeconds = 1200; + + public int maxRouteTime = 60 * 60 * 1000; // 1000 hours + +} diff --git a/OsmAnd-java/src/test/java/net/osmand/router/RouteResultPreparationTest.java b/OsmAnd-java/src/test/java/net/osmand/router/RouteResultPreparationTest.java index a8ba2aa293..658bf28243 100644 --- a/OsmAnd-java/src/test/java/net/osmand/router/RouteResultPreparationTest.java +++ b/OsmAnd-java/src/test/java/net/osmand/router/RouteResultPreparationTest.java @@ -59,7 +59,7 @@ public class RouteResultPreparationTest { RandomAccessFile raf = new RandomAccessFile(fl, "r"); - fe = new RoutePlannerFrontEnd(false); + fe = new RoutePlannerFrontEnd(); RoutingConfiguration.Builder builder = RoutingConfiguration.getDefault(); Map params = new LinkedHashMap(); params.put("car", "true"); diff --git a/OsmAnd-java/src/test/java/net/osmand/router/RouteTestingTest.java b/OsmAnd-java/src/test/java/net/osmand/router/RouteTestingTest.java index 05dfdf598e..b4f343da62 100644 --- a/OsmAnd-java/src/test/java/net/osmand/router/RouteTestingTest.java +++ b/OsmAnd-java/src/test/java/net/osmand/router/RouteTestingTest.java @@ -60,7 +60,7 @@ public class RouteTestingTest { public void testRouting() throws Exception { String fl = "src/test/resources/Routing_test.obf"; RandomAccessFile raf = new RandomAccessFile(fl, "r"); - RoutePlannerFrontEnd fe = new RoutePlannerFrontEnd(false); + RoutePlannerFrontEnd fe = new RoutePlannerFrontEnd(); BinaryMapIndexReader[] binaryMapIndexReaders = { new BinaryMapIndexReader(raf, new File(fl)) }; RoutingConfiguration.Builder builder = RoutingConfiguration.getDefault(); diff --git a/OsmAnd-telegram/res/layout/fragment_live_now_tab.xml b/OsmAnd-telegram/res/layout/fragment_live_now_tab.xml index b9b6feb1d9..0995bb0735 100644 --- a/OsmAnd-telegram/res/layout/fragment_live_now_tab.xml +++ b/OsmAnd-telegram/res/layout/fragment_live_now_tab.xml @@ -5,12 +5,30 @@ android:layout_height="match_parent" android:orientation="vertical"> - + android:layout_height="match_parent"> + + + +