Merge branch 'master' of ssh://github.com/osmandapp/Osmand into TelegramUiImprovements
This commit is contained in:
commit
4853fcd17b
28 changed files with 1041 additions and 663 deletions
|
@ -1731,6 +1731,7 @@ public class BinaryMapIndexReader {
|
|||
searchResults = new ArrayList<T>();
|
||||
cacheCoordinates.clear();
|
||||
cacheTypes.clear();
|
||||
stringTable = null;
|
||||
land = false;
|
||||
ocean = false;
|
||||
numberOfVisitedObjects = 0;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -142,7 +142,7 @@ public class GeocodingUtilities {
|
|||
|
||||
|
||||
public List<GeocodingResult> reverseGeocodingSearch(RoutingContext ctx, double lat, double lon, boolean allowEmptyNames) throws IOException {
|
||||
RoutePlannerFrontEnd rp = new RoutePlannerFrontEnd(false);
|
||||
RoutePlannerFrontEnd rp = new RoutePlannerFrontEnd();
|
||||
List<GeocodingResult> lst = new ArrayList<GeocodingUtilities.GeocodingResult>();
|
||||
List<RouteSegmentPoint> listR = new ArrayList<BinaryRoutePlanner.RouteSegmentPoint>();
|
||||
rp.findRouteSegment(lat, lon, ctx, listR);
|
||||
|
|
|
@ -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<Way> 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<Way> 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<Way, int[]> orderWays = new HashMap<Way, int[]>();
|
||||
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<Way>() {
|
||||
@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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<RouteSegment> segmentsComparator = new Comparator<RouteSegment>(){
|
||||
@Override
|
||||
public int compare(RouteSegment o1, RouteSegment o2) {
|
||||
return ctx.roadPriorityComparator(o1.distanceFromStart, o1.distanceToEnd, o2.distanceFromStart, o2.distanceToEnd);
|
||||
}
|
||||
};
|
||||
|
||||
Comparator<RouteSegment> nonHeuristicSegmentsComparator = new Comparator<RouteSegment>(){
|
||||
@Override
|
||||
public int compare(RouteSegment o1, RouteSegment o2) {
|
||||
return roadPriorityComparator(o1.distanceFromStart, o1.distanceToEnd, o2.distanceFromStart, o2.distanceToEnd, 0.5);
|
||||
}
|
||||
};
|
||||
|
||||
PriorityQueue<RouteSegment> graphDirectSegments = new PriorityQueue<RouteSegment>(50, segmentsComparator);
|
||||
PriorityQueue<RouteSegment> graphReverseSegments = new PriorityQueue<RouteSegment>(50, segmentsComparator);
|
||||
|
||||
// Set to not visit one segment twice (stores road.id << X + segmentStart)
|
||||
TLongObjectHashMap<RouteSegment> visitedDirectSegments = new TLongObjectHashMap<RouteSegment>();
|
||||
TLongObjectHashMap<RouteSegment> visitedOppositeSegments = new TLongObjectHashMap<RouteSegment>();
|
||||
|
||||
boolean runRecalculation = ctx.previouslyCalculatedRoute != null && ctx.previouslyCalculatedRoute.size() > 0
|
||||
&& ctx.config.recalculateDistance != 0;
|
||||
if (runRecalculation) {
|
||||
RouteSegment previous = null;
|
||||
List<RouteSegmentResult> rlist = new ArrayList<RouteSegmentResult>();
|
||||
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<RouteSegment> 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<RouteSegment> graphDirectSegments,
|
||||
PriorityQueue<RouteSegment> 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<RouteSegment> graphDirectSegments, PriorityQueue<RouteSegment> graphReverseSegments,
|
||||
TLongObjectHashMap<RouteSegment> visitedDirectSegments,TLongObjectHashMap<RouteSegment> 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<RouteSegment> graphSegments, TLongObjectHashMap<RouteSegment> visitedSegments, int targetEndX, int targetEndY,
|
||||
RouteSegment segment, TLongObjectHashMap<RouteSegment> 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<RouteSegment> graphSegments,
|
||||
TLongObjectHashMap<RouteSegment> visitedSegments, TLongObjectHashMap<RouteSegment> oppositeSegments,
|
||||
float distFromStart, float distToFinalPoint,
|
||||
RouteSegment segment, int segmentEnd, RouteSegment inputNext,
|
||||
boolean reverseWay) {
|
||||
|
||||
boolean thereAreRestrictions = proccessRestrictions(ctx, segment.road, inputNext, reverseWay);
|
||||
Iterator<RouteSegment> 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<TransportRouteResult> buildRoute(TransportRoutingContext ctx, LatLon start, LatLon end) throws IOException {
|
||||
ctx.startCalcTime = System.currentTimeMillis();
|
||||
List<TransportRouteSegment> startStops = ctx.getTransportStops(start);
|
||||
List<TransportRouteSegment> endStops = ctx.getTransportStops(end);
|
||||
|
||||
TLongObjectHashMap<TransportRouteSegment> endSegments = new TLongObjectHashMap<TransportRouteSegment>();
|
||||
for(TransportRouteSegment s : endStops) {
|
||||
endSegments.put(s.getId(), s);
|
||||
}
|
||||
PriorityQueue<TransportRouteSegment> queue = new PriorityQueue<TransportRouteSegment>(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<TransportRouteSegment> results = new ArrayList<TransportRouteSegment>();
|
||||
|
||||
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<TransportRouteSegment> sgms = new ArrayList<TransportRouteSegment>();
|
||||
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<TransportRouteResult> prepareResults(TransportRoutingContext ctx, List<TransportRouteSegment> results) {
|
||||
Collections.sort(results, new SegmentsComparator(ctx));
|
||||
List<TransportRouteResult> lst = new ArrayList<TransportRouteResult>();
|
||||
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<TransportRouteSegment> {
|
||||
|
||||
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<Way> getGeometry() {
|
||||
List<Way> list = new ArrayList<Way>();
|
||||
route.mergeForwardWays();
|
||||
List<Way> fw = route.getForwardWays();
|
||||
double minStart = 150;
|
||||
double minEnd = 150;
|
||||
LatLon str = getStart().getLocation();
|
||||
LatLon en = getEnd().getLocation();
|
||||
int endInd = -1;
|
||||
List<Node> res = new ArrayList<Node>();
|
||||
for(int i = 0; i < fw.size() ; i++) {
|
||||
List<Node> 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<TransportRouteResultSegment> segments = new ArrayList<TransportRouteResultSegment>(4);
|
||||
double finishWalkDist;
|
||||
double routeTime;
|
||||
private final TransportRoutingConfiguration cfg;
|
||||
|
||||
public TransportRouteResult(TransportRoutingContext ctx) {
|
||||
cfg = ctx.cfg;
|
||||
}
|
||||
|
||||
public List<TransportRouteResultSegment> 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<TransportRouteSegment> visitedSegments = new TLongObjectHashMap<TransportRouteSegment>();
|
||||
public TransportRoutingConfiguration cfg;
|
||||
|
||||
|
||||
public TLongObjectHashMap<List<TransportRouteSegment>> quadTree;
|
||||
public final Map<BinaryMapIndexReader, TIntObjectHashMap<TransportRoute>> map =
|
||||
new LinkedHashMap<BinaryMapIndexReader, TIntObjectHashMap<TransportRoute>>();
|
||||
|
||||
// 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<List<TransportRouteSegment>>();
|
||||
for (BinaryMapIndexReader r : readers) {
|
||||
map.put(r, new TIntObjectHashMap<TransportRoute>());
|
||||
}
|
||||
}
|
||||
|
||||
public List<TransportRouteSegment> getTransportStops(LatLon loc) throws IOException {
|
||||
int y = MapUtils.get31TileNumberY(loc.getLatitude());
|
||||
int x = MapUtils.get31TileNumberX(loc.getLongitude());
|
||||
return getTransportStops(x, y, false, new ArrayList<TransportRouteSegment>());
|
||||
}
|
||||
|
||||
public List<TransportRouteSegment> getTransportStops(int x, int y, boolean change, List<TransportRouteSegment> res) throws IOException {
|
||||
return loadNativeTransportStops(x, y, change, res);
|
||||
}
|
||||
|
||||
private List<TransportRouteSegment> loadNativeTransportStops(int sx, int sy, boolean change, List<TransportRouteSegment> 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<TransportRouteSegment> 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<TransportRouteSegment> loadTile(int x, int y) throws IOException {
|
||||
long nanoTime = System.nanoTime();
|
||||
List<TransportRouteSegment> lst = new ArrayList<TransportRouteSegment>();
|
||||
int pz = (31 - cfg.ZOOM_TO_LOAD_TILES);
|
||||
SearchRequest<TransportStop> 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<TransportStop> loadedTransportStops = new TLongObjectHashMap<TransportStop>();
|
||||
for(BinaryMapIndexReader r : map.keySet()) {
|
||||
sr.clearSearchResults();
|
||||
List<TransportStop> 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<TransportStop> stops, List<TransportRouteSegment> lst) throws IOException {
|
||||
TIntObjectHashMap<TransportRoute> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
|
@ -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<String, String> params = new LinkedHashMap<String, String>();
|
||||
params.put("car", "true");
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -5,12 +5,30 @@
|
|||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="@dimen/list_view_bottom_padding"
|
||||
android:scrollbars="vertical"/>
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="@dimen/list_view_bottom_padding"
|
||||
android:scrollbars="vertical"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/open_osmand_btn"
|
||||
style="@style/DialogActionButtonActive"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:layout_marginBottom="@dimen/content_padding_big"
|
||||
android:drawableLeft="@drawable/ic_action_osmand_plus"
|
||||
android:drawablePadding="@dimen/content_padding_standard"
|
||||
android:drawableStart="@drawable/ic_action_osmand_plus"
|
||||
android:text="@string/open_osmand"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<resources>
|
||||
<string name="open_osmand">Open OsmAnd</string>
|
||||
<string name="shared_string_live">Live</string>
|
||||
<string name="shared_string_bot">Bot</string>
|
||||
<string name="get_telegram_title">Registration in Telegram</string>
|
||||
|
|
|
@ -85,6 +85,7 @@ class TelegramHelper private constructor() {
|
|||
|
||||
var listener: TelegramListener? = null
|
||||
private val incomingMessagesListeners = HashSet<TelegramIncomingMessagesListener>()
|
||||
private val fullInfoUpdatesListeners = HashSet<FullInfoUpdatesListener>()
|
||||
|
||||
fun addIncomingMessagesListener(listener: TelegramIncomingMessagesListener) {
|
||||
incomingMessagesListeners.add(listener)
|
||||
|
@ -94,6 +95,14 @@ class TelegramHelper private constructor() {
|
|||
incomingMessagesListeners.remove(listener)
|
||||
}
|
||||
|
||||
fun addFullInfoUpdatesListener(listener: FullInfoUpdatesListener) {
|
||||
fullInfoUpdatesListeners.add(listener)
|
||||
}
|
||||
|
||||
fun removeFullInfoUpdatesListener(listener: FullInfoUpdatesListener) {
|
||||
fullInfoUpdatesListeners.remove(listener)
|
||||
}
|
||||
|
||||
fun getChatList(): TreeSet<OrderedChat> {
|
||||
synchronized(chatList) {
|
||||
return TreeSet(chatList.filter { !it.isChannel })
|
||||
|
@ -130,9 +139,21 @@ class TelegramHelper private constructor() {
|
|||
return res
|
||||
}
|
||||
|
||||
fun getBasicGroupFullInfo(id: Int) = basicGroupsFullInfo[id]
|
||||
fun getBasicGroupFullInfo(id: Int): TdApi.BasicGroupFullInfo? {
|
||||
val res = basicGroupsFullInfo[id]
|
||||
if (res == null) {
|
||||
requestBasicGroupFullInfo(id)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
fun getSupergroupFullInfo(id: Int) = supergroupsFullInfo[id]
|
||||
fun getSupergroupFullInfo(id: Int): TdApi.SupergroupFullInfo? {
|
||||
val res = supergroupsFullInfo[id]
|
||||
if (res == null) {
|
||||
requestSupergroupFullInfo(id)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
fun isGroup(chat: TdApi.Chat): Boolean {
|
||||
return chat.type is TdApi.ChatTypeSupergroup || chat.type is TdApi.ChatTypeBasicGroup
|
||||
|
@ -180,6 +201,11 @@ class TelegramHelper private constructor() {
|
|||
fun updateLocationMessages()
|
||||
}
|
||||
|
||||
interface FullInfoUpdatesListener {
|
||||
fun onBasicGroupFullInfoUpdated(groupId: Int, info: TdApi.BasicGroupFullInfo)
|
||||
fun onSupergroupFullInfoUpdated(groupId: Int, info: TdApi.SupergroupFullInfo)
|
||||
}
|
||||
|
||||
interface TelegramAuthorizationRequestListener {
|
||||
fun onRequestTelegramAuthenticationParameter(parameterType: TelegramAuthenticationParameterType)
|
||||
fun onTelegramAuthorizationRequestError(code: Int, message: String)
|
||||
|
@ -366,6 +392,42 @@ class TelegramHelper private constructor() {
|
|||
listener?.onTelegramChatsRead()
|
||||
}
|
||||
|
||||
private fun requestBasicGroupFullInfo(id: Int) {
|
||||
client?.send(TdApi.GetBasicGroupFullInfo(id)) { obj ->
|
||||
when (obj.constructor) {
|
||||
TdApi.Error.CONSTRUCTOR -> {
|
||||
val error = obj as TdApi.Error
|
||||
if (error.code != IGNORED_ERROR_CODE) {
|
||||
listener?.onTelegramError(error.code, error.message)
|
||||
}
|
||||
}
|
||||
TdApi.BasicGroupFullInfo.CONSTRUCTOR -> {
|
||||
val info = obj as TdApi.BasicGroupFullInfo
|
||||
basicGroupsFullInfo[id] = info
|
||||
fullInfoUpdatesListeners.forEach { it.onBasicGroupFullInfoUpdated(id, info) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestSupergroupFullInfo(id: Int) {
|
||||
client?.send(TdApi.GetSupergroupFullInfo(id)) { obj ->
|
||||
when (obj.constructor) {
|
||||
TdApi.Error.CONSTRUCTOR -> {
|
||||
val error = obj as TdApi.Error
|
||||
if (error.code != IGNORED_ERROR_CODE) {
|
||||
listener?.onTelegramError(error.code, error.message)
|
||||
}
|
||||
}
|
||||
TdApi.SupergroupFullInfo.CONSTRUCTOR -> {
|
||||
val info = obj as TdApi.SupergroupFullInfo
|
||||
supergroupsFullInfo[id] = info
|
||||
fullInfoUpdatesListeners.forEach { it.onSupergroupFullInfoUpdated(id, info) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestCurrentUser(){
|
||||
client?.send(TdApi.GetMe()) { obj ->
|
||||
when (obj.constructor) {
|
||||
|
@ -1030,11 +1092,21 @@ class TelegramHelper private constructor() {
|
|||
}
|
||||
TdApi.UpdateBasicGroupFullInfo.CONSTRUCTOR -> {
|
||||
val updateBasicGroupFullInfo = obj as TdApi.UpdateBasicGroupFullInfo
|
||||
basicGroupsFullInfo[updateBasicGroupFullInfo.basicGroupId] = updateBasicGroupFullInfo.basicGroupFullInfo
|
||||
val id = updateBasicGroupFullInfo.basicGroupId
|
||||
if (basicGroupsFullInfo.containsKey(id)) {
|
||||
val info = updateBasicGroupFullInfo.basicGroupFullInfo
|
||||
basicGroupsFullInfo[id] = info
|
||||
fullInfoUpdatesListeners.forEach { it.onBasicGroupFullInfoUpdated(id, info) }
|
||||
}
|
||||
}
|
||||
TdApi.UpdateSupergroupFullInfo.CONSTRUCTOR -> {
|
||||
val updateSupergroupFullInfo = obj as TdApi.UpdateSupergroupFullInfo
|
||||
supergroupsFullInfo[updateSupergroupFullInfo.supergroupId] = updateSupergroupFullInfo.supergroupFullInfo
|
||||
val id = updateSupergroupFullInfo.supergroupId
|
||||
if (supergroupsFullInfo.containsKey(id)) {
|
||||
val info = updateSupergroupFullInfo.supergroupFullInfo
|
||||
supergroupsFullInfo[id] = info
|
||||
fullInfoUpdatesListeners.forEach { it.onSupergroupFullInfoUpdated(id, info) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Button
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import net.osmand.Location
|
||||
|
@ -18,6 +19,7 @@ import net.osmand.telegram.R
|
|||
import net.osmand.telegram.TelegramApplication
|
||||
import net.osmand.telegram.TelegramLocationProvider.TelegramCompassListener
|
||||
import net.osmand.telegram.TelegramLocationProvider.TelegramLocationListener
|
||||
import net.osmand.telegram.helpers.OsmandAidlHelper
|
||||
import net.osmand.telegram.helpers.TelegramHelper.*
|
||||
import net.osmand.telegram.helpers.TelegramUiHelper
|
||||
import net.osmand.telegram.helpers.TelegramUiHelper.ChatItem
|
||||
|
@ -33,7 +35,7 @@ private const val CHAT_VIEW_TYPE = 0
|
|||
private const val LOCATION_ITEM_VIEW_TYPE = 1
|
||||
|
||||
class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessagesListener,
|
||||
TelegramLocationListener, TelegramCompassListener {
|
||||
FullInfoUpdatesListener, TelegramLocationListener, TelegramCompassListener {
|
||||
|
||||
private val app: TelegramApplication
|
||||
get() = activity?.application as TelegramApplication
|
||||
|
@ -68,6 +70,12 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
|
|||
}
|
||||
})
|
||||
}
|
||||
mainView.findViewById<Button>(R.id.open_osmand_btn).setOnClickListener {
|
||||
val intent = activity?.packageManager?.getLaunchIntentForPackage(OsmandAidlHelper.OSMAND_PACKAGE_NAME)
|
||||
if (intent != null) {
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
return mainView
|
||||
}
|
||||
|
||||
|
@ -76,12 +84,14 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
|
|||
locationViewCache = app.uiUtils.getUpdateLocationViewCache()
|
||||
updateList()
|
||||
telegramHelper.addIncomingMessagesListener(this)
|
||||
telegramHelper.addFullInfoUpdatesListener(this)
|
||||
startLocationUpdate()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
telegramHelper.removeIncomingMessagesListener(this)
|
||||
telegramHelper.removeFullInfoUpdatesListener(this)
|
||||
stopLocationUpdate()
|
||||
}
|
||||
|
||||
|
@ -131,6 +141,14 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
|
|||
|
||||
override fun updateLocationMessages() {}
|
||||
|
||||
override fun onBasicGroupFullInfoUpdated(groupId: Int, info: TdApi.BasicGroupFullInfo) {
|
||||
app.runInUIThread { updateList() }
|
||||
}
|
||||
|
||||
override fun onSupergroupFullInfoUpdated(groupId: Int, info: TdApi.SupergroupFullInfo) {
|
||||
app.runInUIThread { updateList() }
|
||||
}
|
||||
|
||||
override fun updateLocation(location: Location?) {
|
||||
val loc = this.location
|
||||
val newLocation = loc == null && location != null
|
||||
|
@ -307,8 +325,9 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
|
|||
item.privateChat -> "" // FIXME
|
||||
else -> {
|
||||
val live = getString(R.string.shared_string_live)
|
||||
// val all = getString(R.string.shared_string_all)
|
||||
"$live ${item.liveMembersCount}"
|
||||
val all = getString(R.string.shared_string_all)
|
||||
val liveStr = "$live ${item.liveMembersCount}"
|
||||
if (item.membersCount > 0) "$liveStr • $all ${item.membersCount}" else liveStr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><resources>
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
|
||||
<string name="switch_to_raster_map_to_see">Off-line vektorové mapy toto místo neobsahují. Mapová data můžete stáhnout v \'Nastavení\' (\'Stáhnout mapy\'), nebo se přepněte na modul \'Online mapy\'.</string>
|
||||
<string name="send_files_to_osm">Nahrát GPX soubory do OSM?</string>
|
||||
|
@ -2516,7 +2517,7 @@ Zobrazená oblast: %1$s x %2$s</string>
|
|||
<string name="import_track">Import trasy</string>
|
||||
<string name="import_track_desc">Soubor %$1s neobsahuje body trasy, importovat jako trasu?</string>
|
||||
<string name="move_point">Přesunout bod</string>
|
||||
<string name="add_segment_to_the_track">Přidat do GPX trasy</string>
|
||||
<string name="add_segment_to_the_track">Přidat do GPX souboru</string>
|
||||
<string name="move_all_to_history">Přesunout vše do historie</string>
|
||||
|
||||
<string name="show_direction">Indikace vzdálenosti</string>
|
||||
|
@ -2686,7 +2687,7 @@ Zobrazená oblast: %1$s x %2$s</string>
|
|||
<string name="osmand_extended_description_part1">OsmAnd (OSM Automated Navigation Directions) je mapová a navigační aplikace s přístupem k volným a kvalitním celosvětovým údajům z OpenStreetMap (OSM).
|
||||
\n
|
||||
\nVyužívejte hlasovou a optickou navigaci, zobrazení POI (bodů zájmu), vytváření a správu GPX tras, znázornění vrstevnic a nadmořské výšky (pomocí doplňkového modulu), výběr z režimů auto, cyklista a pěší, editaci OSM a mnoho dalšího.</string>
|
||||
<string name="toast_empty_name_error">Místo nemá žádný název</string>
|
||||
<string name="toast_empty_name_error">Bezejmenné místo</string>
|
||||
<string name="tunnel_warning">Blíží se tunel</string>
|
||||
<string name="show_tunnels">Tunely</string>
|
||||
<string name="make_as_start_point">Nastavit toto jako počáteční bod</string>
|
||||
|
@ -2709,8 +2710,8 @@ Zobrazená oblast: %1$s x %2$s</string>
|
|||
<string name="optional_point_name">Volitelný název bodu</string>
|
||||
<string name="transport_nearby_routes_within">Trasy v okruhu</string>
|
||||
<string name="transport_nearby_routes">POBLÍŽ</string>
|
||||
<string name="distance_farthest">Vzdálenost: Nejvzdálenější jako první</string>
|
||||
<string name="distance_nearest">Vzdálenost: Nejbližší jako první</string>
|
||||
<string name="distance_farthest">Vzdálenost: nejvzdálenější jako první</string>
|
||||
<string name="distance_nearest">Vzdálenost: nejbližší jako první</string>
|
||||
<string name="rendering_attr_whiteWaterSports_name">Divoká voda</string>
|
||||
<string name="osmand_extended_description_part2">GPS navigace
|
||||
\n• Můžete si vybrat mezi offline režimem (bez roamingových poplatků v zahraničí) a online režimem (rychlejší)
|
||||
|
@ -2761,7 +2762,7 @@ Zobrazená oblast: %1$s x %2$s</string>
|
|||
<string name="unlock_all_features">Odemknout všechny funkce OsmAnd</string>
|
||||
|
||||
<string name="purchase_dialog_title">Vyberte si předplatné</string>
|
||||
<string name="purchase_dialog_travel_description">Chcete-li číst články o cestování offline, je třeba koupit jednu z následujících položek:</string>
|
||||
<string name="purchase_dialog_travel_description">Chcete-li dostávat offline články o cestování, je třeba koupit jednu z následujících položek:</string>
|
||||
<string name="purchase_dialog_subtitle">Vyberte vhodnou položku:</string>
|
||||
<string name="shared_string_dont">Nedělat</string>
|
||||
<string name="shared_string_do">Dělat</string>
|
||||
|
@ -2775,18 +2776,18 @@ Zobrazená oblast: %1$s x %2$s</string>
|
|||
<string name="online_webpage_warning">Stránka je k dispozici pouze online. Chcete ji otevřít v prohlížeči?</string>
|
||||
<string name="images_cache">Mezipaměť obrázků</string>
|
||||
<string name="delete_search_history">Vymazat historii hledání</string>
|
||||
<string name="download_images">Zobrazit obrázky</string>
|
||||
<string name="download_images">Stáhnout obrázky</string>
|
||||
<string name="download_maps_travel">Cestovní průvodce</string>
|
||||
<string name="shared_string_wikivoyage">Wikivoyage</string>
|
||||
<string name="article_removed">Článek odstraněn</string>
|
||||
<string name="wikivoyage_search_hint">Vyhledávání: Stát, město, kraj</string>
|
||||
<string name="wikivoyage_search_hint">Vyhledávání: stát, město, kraj</string>
|
||||
<string name="shared_string_read">Číst</string>
|
||||
<string name="saved_articles">Články v záložkách</string>
|
||||
<string name="shared_string_explore">Prozkoumat</string>
|
||||
<string name="shared_string_contents">Obsah</string>
|
||||
<string name="index_item_world_wikivoyage">Celosvětové články Wikivoyage</string>
|
||||
<string name="contour_lines_hillshade_maps">Vrstevnice & stínování terénu</string>
|
||||
<string name="shared_string_restart">Restartovat</string>
|
||||
<string name="shared_string_restart">Restartovat aplikaci</string>
|
||||
<string name="show_images">Zobrazit obrázky</string>
|
||||
<string name="purchase_cancelled_dialog_descr">Obnovte předplatné, abyste nadále mohli využívat všechny funkce:</string>
|
||||
|
||||
|
@ -2798,8 +2799,8 @@ Zobrazená oblast: %1$s x %2$s</string>
|
|||
<string name="download_file">Stáhnout soubor</string>
|
||||
<string name="start_editing">Začít s úpravami</string>
|
||||
<string name="get_unlimited_access">Získejte neomezený přístup</string>
|
||||
<string name="monthly_map_updates">Aktualizace map: <b>Každý měsíc</b></string>
|
||||
<string name="daily_map_updates">Aktualizace map: <b>Každou hodinu</b></string>
|
||||
<string name="monthly_map_updates">Aktualizace map: <b>každý měsíc</b></string>
|
||||
<string name="daily_map_updates">Aktualizace map: <b>každou hodinu</b></string>
|
||||
<string name="download_wikipedia_description">Stáhnout články Wikipedie pro %1$s a číst je v režimu offline.</string>
|
||||
<string name="download_wikipedia_label">Stahování dat z Wikipedie</string>
|
||||
<string name="open_in_browser_wiki">Otevřít článek online</string>
|
||||
|
@ -2819,4 +2820,4 @@ Zobrazená oblast: %1$s x %2$s</string>
|
|||
\n • Mapové značky: import vybraných skupin z GPX souborů, nový vzhled zadávání souřadnic
|
||||
\n
|
||||
\n • Předplatné OsmAnd Live nyní zahrnuje všechny funkce OsmAnd</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
@ -3086,4 +3086,8 @@ Abgedeckte Fläche: %1$s x %2$s</string>
|
|||
\n • Bootnavigation: Unterstützung für Fahrrinnen
|
||||
\n
|
||||
\n • Andere Fehlerbehebungen</string>
|
||||
<string name="thank_you_for_feedback">Danke für Ihre Rückmeldung</string>
|
||||
<string name="poi_cannot_be_found">Knoten oder Weg kann nicht gefunden werden.</string>
|
||||
<string name="search_no_results_feedback">Keine Suchergebnisse?
|
||||
\nGeben Sie uns eine Rückmeldung</string>
|
||||
</resources>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><resources><string name="add_opening_hours">Lisa lahioleku ajad</string>
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources><string name="add_opening_hours">Lisa lahioleku ajad</string>
|
||||
<string name="description">Kirjeldus</string>
|
||||
<string name="contact_info">Kontakti info</string>
|
||||
|
||||
|
@ -171,4 +172,42 @@
|
|||
<string name="quick_action_showhide_osmbugs_title">Näita/peida OSM Notes</string>
|
||||
<string name="quick_action_osmbugs_show">Näita OSM Notes</string>
|
||||
<string name="quick_action_osmbugs_hide">Peida OSM Notes</string>
|
||||
</resources>
|
||||
<string name="thank_you_for_feedback">Täname tagasiside eest</string>
|
||||
<string name="poi_cannot_be_found">Sõlme või teed ei leitud.</string>
|
||||
<string name="search_no_results_feedback">Otsingutulemused puuduvad? Anna meile tagasisidet</string>
|
||||
<string name="increase_search_radius_to">Suurenda otsingu raadiust kuni %1$s</string>
|
||||
<string name="send_search_query">Saada otsingu päring?</string>
|
||||
<string name="shared_string_world">Maailm</string>
|
||||
<string name="point_deleted">Punkt %1$s kustutatud</string>
|
||||
<string name="coord_input_edit_point">Muuda punkti</string>
|
||||
<string name="coord_input_add_point">Lisa punkt</string>
|
||||
<string name="coord_input_save_as_track">Salvesta jäljena</string>
|
||||
<string name="coord_input_save_as_track_descr">Lisasid %1$s punkti. Sisesta faili nimi ja vajuta \"Salvesta\".</string>
|
||||
<string name="error_notification_desc">Palun saada ekraanipilt sellest teatest aadressil support@osmand.net</string>
|
||||
<string name="quick_action_edit_actions">Muuda tegevusi</string>
|
||||
<string name="shared_string_bookmark">Järjehoidja</string>
|
||||
<string name="hide_full_description">Peida täielik kirjeldus</string>
|
||||
<string name="show_full_description">Näita täielikku kirjeldust</string>
|
||||
<string name="open_wikipedia_link_online">Ava Wikipedia link veebis</string>
|
||||
<string name="open_wikipedia_link_online_description">Link avatakse brauseris.</string>
|
||||
<string name="how_to_open_link">Kuidas link avada?</string>
|
||||
<string name="read_wikipedia_offline">Loe Wikipediat ilma võrguühenduseta</string>
|
||||
<string name="download_all">Lae kõik alla</string>
|
||||
<string name="shared_string_restart">Rakenduse taaskäivitamine</string>
|
||||
<string name="show_images">Näita pilte</string>
|
||||
<string name="maps_you_need">Kaardid, mida vajad</string>
|
||||
<string name="osmand_team">OsmAnd meeskond</string>
|
||||
<string name="popular_destinations">Populaarsed sihtkohad</string>
|
||||
<string name="paid_app">Makstud rakendus</string>
|
||||
<string name="paid_plugin">Makstud lisa</string>
|
||||
<string name="update_is_available">Uuendus saadaval</string>
|
||||
<string name="download_file">Lae fail alla</string>
|
||||
<string name="start_editing">Alusta muutmist</string>
|
||||
<string name="get_unlimited_access">Hangi piiramatu ligipääs</string>
|
||||
<string name="monthly_map_updates">Kaardiuuendused: <b>iga kuu</b></string>
|
||||
<string name="daily_map_updates">Kaardiuuendused: <b>iga tund</b></string>
|
||||
<string name="in_app_purchase">Rakenduse sisene ost</string>
|
||||
<string name="in_app_purchase_desc">Ühekordne makse</string>
|
||||
<string name="purchase_unlim_title">Osta - %1$s</string>
|
||||
<string name="purchase_subscription_title">Liitu - %1$s</string>
|
||||
</resources>
|
||||
|
|
|
@ -618,7 +618,7 @@
|
|||
<string name="poi_harbour_basin">حوضه بندرگاه</string>
|
||||
<string name="poi_seamark_harbour">بندرگاه</string>
|
||||
<string name="poi_landmark">لندمارک</string>
|
||||
<string name="poi_seamark_light">]چراغ دریایی</string>
|
||||
<string name="poi_seamark_light">چراغ دریایی</string>
|
||||
<string name="poi_seamark_light_major">چراغ بزرگ دریایی</string>
|
||||
<string name="poi_seamark_light_minor">چراغ کوچک دریایی</string>
|
||||
<string name="poi_seamark_light_float">چراغ شناور دریایی</string>
|
||||
|
|
|
@ -738,7 +738,7 @@
|
|||
<string name="fav_export_confirmation">قبلاً از نقاط برگزیده یک خروجی تهیه کردهاید و فایلش وجود دارد. خروجی جدید را جایگزین آن میکنید؟</string>
|
||||
|
||||
|
||||
<string name="osmand_development_plugin_description">بهوسیلهٔ این افزونه میتوانید تنظیمات توسعهای و امکانات عیبیابی را مشاهده کنید؛ مانند شبیهسازی مسیریابی، آزمایش عملکرد رندرینگ یا امتحانکردن پیامهای صوتی.
|
||||
<string name="osmand_development_plugin_description">بهوسیلهٔ این افزونه میتوانید تنظیمات توسعهای و امکانات عیبیابی را مشاهده کنید؛ مانند شبیهسازی مسیریابی، آزمایش عملکرد رندر یا امتحانکردن پیامهای صوتی.
|
||||
\nاین تنظیمات برای توسعهدهندگان فراهم شده است و بهکار کاربران عادی نمیآید.</string>
|
||||
<string name="animate_route_off">پایان شبیهسازی</string>
|
||||
<string name="animate_route">شروع شبیهسازی</string>
|
||||
|
@ -892,7 +892,7 @@
|
|||
<string name="monitoring_control_start">GPX</string>
|
||||
<string name="rendering_attr_noPolygons_description">همهٔ عوارض زمین را روی نقشه ناپدید کنید.</string>
|
||||
<string name="rendering_attr_noPolygons_name">چندضلعیها</string>
|
||||
<string name="rendering_attr_appMode_name">حالت رندرکردن</string>
|
||||
<string name="rendering_attr_appMode_name">حالت رندرگیری</string>
|
||||
<string name="rendering_attr_appMode_description">بهینهسازی نقشه برای</string>
|
||||
<string name="rendering_attr_contourLines_description">از این زوم نمایان شود (نیازمند دادههای منحنی تراز):</string>
|
||||
<string name="rendering_attr_contourLines_name">نشاندادن منحنیهای تراز</string>
|
||||
|
@ -1170,8 +1170,8 @@
|
|||
<string name="local_index_no_items_to_do">هیچموردی برای %1$s وجود ندارد</string>
|
||||
<string name="local_index_action_do">دارید %2$s مورد را %1$s میکنید. ادامه میدهید؟</string>
|
||||
|
||||
<string name="trace_rendering">اطلاعات عیبیابی رندرینگ</string>
|
||||
<string name="trace_rendering_descr">عملکرد رندرینگ را نمایش میدهد.</string>
|
||||
<string name="trace_rendering">اطلاعات عیبیابی رندرگیری</string>
|
||||
<string name="trace_rendering_descr">عملکرد رندرگیری را نمایش میدهد.</string>
|
||||
|
||||
<string name="choose_audio_stream_descr">بلندگوی پخش راهنمای صوتی را انتخاب کنید.</string>
|
||||
<string name="voice_stream_voice_call">منبع صدای تماس تلفنی (همچنین برای وقفهانداختن در دستگاه پخش خودرو)</string>
|
||||
|
@ -1195,11 +1195,11 @@
|
|||
|
||||
<string name="send_location_email_pattern">برای مشاهدهٔ مکان، پیوند اینترنتی %1$s یا پیوند اندرویدی %2$s را باز کنید</string>
|
||||
<string name="amenity_type_geocache">Geocache</string>
|
||||
<string name="continuous_rendering_descr">بهجای نمایش یکبارهٔ تصویر از رندرینگ پیوسته استفاده شود.</string>
|
||||
<string name="continuous_rendering_descr">بهجای نمایش یکبارهٔ تصویر از رندرکردن پیوسته استفاده شود.</string>
|
||||
<string name="renderer_load_sucess">رندرکننده بارگذاری شد</string>
|
||||
<string name="renderer_load_exception">بارگذاری رندرکننده ناموفق بود</string>
|
||||
<string name="renderers">رسمکنندهٔ بُرداری</string>
|
||||
<string name="renderers_descr">شکلوشمایل رندر را انتخاب نمایید.</string>
|
||||
<string name="renderers">رندرکنندهٔ بُرداری</string>
|
||||
<string name="renderers_descr">شکلوشمایل رندرگیری را انتخاب نمایید.</string>
|
||||
<string name="download_type_to_filter">فیلتر</string>
|
||||
<string name="transport_context_menu">جستوجوی وسیلهٔ حملونقل در ایستگاه</string>
|
||||
<string name="rotate_map_to_bearing_descr">نحوهٔ چرخش نقشه را انتخاب کنید.</string>
|
||||
|
@ -1751,8 +1751,8 @@
|
|||
<string name="download_tab_downloads">همهٔ دانلودها</string>
|
||||
<string name="download_tab_updates">بهروزرسانیها</string>
|
||||
<string name="shared_string_dismiss">بیخیال</string>
|
||||
<string name="use_opengl_render">استفاده از رندرینگ OpenGL</string>
|
||||
<string name="use_opengl_render_descr">از رندرینگ سرعتیافتهٔ سختافزاری OpenGL استفاده کن (شاید مصرف باتری افزایش یابد یا روی دستگاههای خیلی قدیمی کار نکند).</string>
|
||||
<string name="use_opengl_render">استفاده از رندرگیری OpenGL</string>
|
||||
<string name="use_opengl_render_descr">از رندرگیری سرعتیافتهٔ سختافزاری OpenGL استفاده کن (شاید مصرف باتری افزایش یابد یا روی دستگاههای خیلی قدیمی کار نکند).</string>
|
||||
<string name="lock_screen_request_explanation">%1$s به این مجوز نیاز دارد تا برای صرفهجویی در انرژی، بتواند نمایشگر را خاموش کند.</string>
|
||||
<string name="wake_on_voice_descr">قبل از رسیدن به پیچ، نمایشگر روشن شود (اگر خاموش است).</string>
|
||||
<string name="shared_string_never">هرگز</string>
|
||||
|
@ -2241,7 +2241,7 @@
|
|||
<string name="active_markers">نشانههای فعال</string>
|
||||
<string name="map_markers">نشانههای نقشه</string>
|
||||
<string name="map_marker">نشانهٔ نقشه</string>
|
||||
<string name="consider_turning_polygons_off">توصیه میکنیم رسم چندضلعیها را غیرفعال کنید.</string>
|
||||
<string name="consider_turning_polygons_off">توصیه میکنیم رندرشدن چندضلعیها را غیرفعال کنید.</string>
|
||||
<string name="rendering_attr_showMtbRoutes_name">نمایش مسیرهای دوچرخهسواری کوهستانی (MTB)</string>
|
||||
<string name="show_polygons">چندضلعیها را نشان بده</string>
|
||||
<string name="shared_string_status">وضعیت</string>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><resources>
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="use_fluorescent_overlays">Sovrapposizioni fluorescenti</string>
|
||||
<string name="use_fluorescent_overlays_descr">Usa colori fluorescenti per visualizzare tracce e percorsi.</string>
|
||||
<string name="offline_edition">Modifiche offline</string>
|
||||
|
@ -2555,8 +2556,8 @@ Rappresenta l\'area: %1$s x %2$s</string>
|
|||
<string name="full_version_thanks">Grazie per avere acquistato la versione completa di OsmAnd!</string>
|
||||
<string name="fonts_header">Caratteri della mappa</string>
|
||||
<string name="right_side_navigation">Guida a destra</string>
|
||||
<string name="depth_contour_descr">Insieme di mappe che contengono le linee isoipse marine e i punti nautici.</string>
|
||||
<string name="sea_depth_thanks">Grazie per avere acquistato le isoipse nautiche!</string>
|
||||
<string name="depth_contour_descr">Mappe linee isoipse marine e i punti nautici.</string>
|
||||
<string name="sea_depth_thanks">Grazie per avere acquistato le \'Linee isoipse nautiche\'</string>
|
||||
<string name="index_item_depth_contours_osmand_ext">Isoipse nautiche</string>
|
||||
<string name="index_item_depth_points_southern_hemisphere">Punti nautici sotto il livello del mare dell\'emisfero Australe</string>
|
||||
<string name="index_item_depth_points_northern_hemisphere">Punti nautici sotto il livello del mare dell\'emisfero boreale</string>
|
||||
|
@ -2919,7 +2920,7 @@ Rappresenta l\'area: %1$s x %2$s</string>
|
|||
<string name="optional_point_name">Nome del punto facoltativo</string>
|
||||
<string name="transport_nearby_routes_within">Percorsi vicini entro</string>
|
||||
<string name="transport_nearby_routes">Entro</string>
|
||||
<string name="enter_the_file_name">Immetti il nome del file.</string>
|
||||
<string name="enter_the_file_name">Digit il nome del file.</string>
|
||||
<string name="map_import_error">Errore importazione mappa</string>
|
||||
<string name="map_imported_successfully">Mappa importata</string>
|
||||
<string name="winter_and_ski_renderer">Inverno e sci</string>
|
||||
|
@ -3078,4 +3079,32 @@ Rappresenta l\'area: %1$s x %2$s</string>
|
|||
\n"</string>
|
||||
<string name="quick_action_edit_actions">Modifica le azioni</string>
|
||||
<string name="error_notification_desc">Per favore invia una schermata di questa notifica a support@osmand.net</string>
|
||||
</resources>
|
||||
<string name="poi_cannot_be_found">Il nodo o il percorso non è stato trovato.</string>
|
||||
<string name="search_no_results_feedback">Nessun risultato di ricerca?
|
||||
\nDacci un ritorno</string>
|
||||
<string name="release_3_1">• Navigazione: corretta la barra di avanzamento, veloce inversione dei punti d\'inizio e fine percorso
|
||||
\n
|
||||
\n • Marcatori mappa: corretto la visualizzazione o meno dei gruppi, la possibilità di non visualizzare i marcatori dalla mappa
|
||||
\n
|
||||
\n • Modifiche OSM: possibilità di modificare elementi non puntuali e segmenti, correzione di commenti mancanti nelle note, backup delle modifiche
|
||||
\n
|
||||
\n • Migliorati Wikipedia e predizione Wikivoyage, aggiornati file già disponibili
|
||||
\n
|
||||
\n • Menu contestuale: corretti il colore degli scudetti nella modalità notturna e le dimensioni dei menu addizionali
|
||||
\n
|
||||
\n • Navigazione marittima: supporto ai fiumi navigabili
|
||||
\n
|
||||
\n • Altre correzioni di errori
|
||||
\n
|
||||
\n</string>
|
||||
<string name="commiting_way">Sottoponi la via…</string>
|
||||
<string name="increase_search_radius_to">Incrementa il raggio di ricerca a %1$s</string>
|
||||
<string name="send_search_query_description">Invieremo le tue ricerche: <b>\"%1$s\"</b>, così come la tua localizzazione.<br/><br/> Non raccogliamo informazioni personali, abbiamo solo bisogno dei dati delle ricerche per migliorare l\'algoritmo di ricerca.<br/></string>
|
||||
<string name="send_search_query">Inviare i dati delle ricerche?</string>
|
||||
<string name="shared_string_world">Mondo</string>
|
||||
<string name="point_deleted">Punto %1$s eliminato</string>
|
||||
<string name="coord_input_edit_point">Modifica il punto</string>
|
||||
<string name="coord_input_add_point">Aggiungi punto</string>
|
||||
<string name="coord_input_save_as_track">Salva come traccia</string>
|
||||
<string name="coord_input_save_as_track_descr">Hai aggiunto %1$s punti. Digita il nome del file e tappa su \"Salva\".</string>
|
||||
</resources>
|
||||
|
|
|
@ -3023,7 +3023,7 @@
|
|||
<string name="shared_string_gpx_files">GPX-filer</string>
|
||||
<string name="get_osmand_live">Skaff deg OsmAnd Live for å låse opp alle funksjoner: Daglige kartoppdateringer med ubegrensede nedlastinger, alle programtilleggene, Wikipedia, Wikivoyage og mye mer.</string>
|
||||
<string name="quick_action_edit_actions">Rediger handlinger</string>
|
||||
<string name="error_notification_desc">Send skjermavbildning av denne merknaden til support@osmand.net</string>
|
||||
<string name="error_notification_desc">Vennligst send et skjermdump av dette varselet til support@osmand.net</string>
|
||||
<string name="coord_input_edit_point">Rediger punkt</string>
|
||||
<string name="coord_input_add_point">Legg til punkt</string>
|
||||
<string name="coord_input_save_as_track">Lagre som spor</string>
|
||||
|
@ -3037,4 +3037,18 @@
|
|||
<string name="thank_you_for_feedback">Takk for din tilbakemelding</string>
|
||||
<string name="search_no_results_feedback">Resultatløst?
|
||||
\nGi oss tilbakemelding</string>
|
||||
<string name="poi_cannot_be_found">Node eller måte kan ikke bli funnet.</string>
|
||||
<string name="release_3_1">• Navigering: Løs fremdriftslinje, rask bytte av start og sluttpunkt på ruten
|
||||
\n
|
||||
\n• Kartmarkører: fikse på / av grupper, mulighet til å skjule markører fra kartet
|
||||
\n
|
||||
\n• OSM Rediger: Mulighet til å redigere koder for ikke-punktsobjekter og måter, fikse manglende kommentarer på notater, sikkerhetskopiering av endringer
|
||||
\n
|
||||
\n• Forbedre Wikipedia og Wikivoyage parsing, oppdaterte filer er allerede tilgjengelige
|
||||
\n
|
||||
\n• Kontekstmeny: Fiks transportskjold farge i nattmodus, fikse tilleggsmenystørrelser
|
||||
\n
|
||||
\n• Båtnavigasjon: støtte for vannvei fairway
|
||||
\n
|
||||
\n• Andre feilrettinger</string>
|
||||
</resources>
|
||||
|
|
|
@ -1661,7 +1661,7 @@
|
|||
<string name="poi_toilets_access_community">Acesso aos banheiros: comunidade</string>
|
||||
<string name="poi_toilets_access_public">Acesso aos banheiros: público</string>
|
||||
|
||||
<string name="poi_diaper_yes">Trocador de fralda</string>
|
||||
<string name="poi_diaper_yes">Possui trocador de fralda</string>
|
||||
<string name="poi_diaper_no">Sem trocador de fralda</string>
|
||||
<string name="poi_diaper_room">Sala para trocar fralda</string>
|
||||
|
||||
|
|
|
@ -2965,15 +2965,17 @@ Zodpovedá oblasti: %1$s x %2$s</string>
|
|||
<string name="coord_input_save_as_track_descr">Pridali ste %1$s bodov. Zadajte názov súboru a stlačte \"Uložiť\".</string>
|
||||
<string name="point_deleted">Bod %1$s vymazaný</string>
|
||||
<string name="shared_string_world">Celý svet</string>
|
||||
<string name="release_3_1">• Navigácia: Opravený indikátor postupu, rýchle prepínanie začiatočného a koncového bodu trasy
|
||||
<string name="release_3_1">• Navigácia: Opravený indikátor postupu, rýchle prepínanie začiatočného a koncového bodu trasy
|
||||
\n
|
||||
\n • Mapové značky: opravené zapínanie a vypínanie skupín, možnosť skryť značky z mapy
|
||||
\n • Mapové značky: opravené zapínanie a vypínanie skupín, možnosť skryť značky z mapy
|
||||
\n
|
||||
\n • Úpravy OSM: Možnosť meniť značky nebodových objektov, oprava chýbajúcich komentárov na poznámkach, záloha úprav
|
||||
\n • Úpravy OSM: Možnosť meniť značky nebodových objektov a ciest, oprava chýbajúcich komentárov na poznámkach, záloha úprav
|
||||
\n
|
||||
\n • Vylepšené čítanie Wikipédie a Wikivoyage, aktualizované súbory sú už k dispozícii
|
||||
\n • Vylepšené čítanie Wikipédie a Wikivoyage, aktualizované súbory sú už k dispozícii
|
||||
\n
|
||||
\n • Kontextové menu: opravené farby znakov ciest v nočnom režime, opravené doplnkové veľkosti menu
|
||||
\n • Kontextové menu: opravené farby znakov ciest v nočnom režime, opravené doplnkové veľkosti menu
|
||||
\n
|
||||
\n • Navigácia lodí: podpora pre vodné cesty
|
||||
\n
|
||||
\n • Opravy ďalších chýb
|
||||
\n
|
||||
|
@ -2983,4 +2985,8 @@ Zodpovedá oblasti: %1$s x %2$s</string>
|
|||
<string name="search_no_results_description">Žiadne výsledky?
|
||||
\nPovedzte nám o tom.</string>
|
||||
<string name="send_search_query">Odoslať vyhľadávaciu požiadavku?</string>
|
||||
<string name="thank_you_for_feedback">Ďakujeme za vašu spätnú väzbu</string>
|
||||
<string name="poi_cannot_be_found">Nebol nájdený bod ani cesta.</string>
|
||||
<string name="search_no_results_feedback">Žiadne výsledky vyhľadávania?
|
||||
\nDajte nám spätnú väzbu</string>
|
||||
</resources>
|
||||
|
|
|
@ -136,10 +136,10 @@ public class CurrentPositionHelper {
|
|||
}
|
||||
RoutingConfiguration cfg = app.getDefaultRoutingConfig().build(p, 10,
|
||||
new HashMap<String, String>());
|
||||
ctx = new RoutePlannerFrontEnd(false).buildRoutingContext(cfg, null, rs);
|
||||
ctx = new RoutePlannerFrontEnd().buildRoutingContext(cfg, null, rs);
|
||||
RoutingConfiguration defCfg = app.getDefaultRoutingConfig().build("geocoding", 10,
|
||||
new HashMap<String, String>());
|
||||
defCtx = new RoutePlannerFrontEnd(false).buildRoutingContext(defCfg, null, rs);
|
||||
defCtx = new RoutePlannerFrontEnd().buildRoutingContext(defCfg, null, rs);
|
||||
} else {
|
||||
ctx = null;
|
||||
defCtx = null;
|
||||
|
|
|
@ -658,7 +658,7 @@ public class RouteProvider {
|
|||
|
||||
protected RouteCalculationResult findVectorMapsRoute(final RouteCalculationParams params, boolean calcGPXRoute) throws IOException {
|
||||
BinaryMapIndexReader[] files = params.ctx.getResourceManager().getRoutingMapFiles();
|
||||
RoutePlannerFrontEnd router = new RoutePlannerFrontEnd(false);
|
||||
RoutePlannerFrontEnd router = new RoutePlannerFrontEnd();
|
||||
OsmandSettings settings = params.ctx.getSettings();
|
||||
router.setUseFastRecalculation(settings.USE_FAST_RECALCULATION.get());
|
||||
|
||||
|
|
Loading…
Reference in a new issue