Merge branch 'master' of ssh://github.com/osmandapp/Osmand into js_voice_routing

This commit is contained in:
PaulStets 2018-08-07 08:51:19 +03:00
commit 7d808a38a9
42 changed files with 1074 additions and 665 deletions

View file

@ -1731,6 +1731,7 @@ public class BinaryMapIndexReader {
searchResults = new ArrayList<T>(); searchResults = new ArrayList<T>();
cacheCoordinates.clear(); cacheCoordinates.clear();
cacheTypes.clear(); cacheTypes.clear();
stringTable = null;
land = false; land = false;
ocean = false; ocean = false;
numberOfVisitedObjects = 0; numberOfVisitedObjects = 0;

View file

@ -436,7 +436,7 @@ public class BinaryMapTransportReaderAdapter {
} }
} }
dataObject.setId(did); 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; return dataObject;
} }
@ -461,7 +461,7 @@ public class BinaryMapTransportReaderAdapter {
req.cacheTypes.clear(); req.cacheTypes.clear();
TransportStop dataObject = new TransportStop(); 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); dataObject.setFileOffset(shift);
while(true){ while(true){
int t = codedIS.readTag(); int t = codedIS.readTag();

View file

@ -142,7 +142,7 @@ public class GeocodingUtilities {
public List<GeocodingResult> reverseGeocodingSearch(RoutingContext ctx, double lat, double lon, boolean allowEmptyNames) throws IOException { 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<GeocodingResult> lst = new ArrayList<GeocodingUtilities.GeocodingResult>();
List<RouteSegmentPoint> listR = new ArrayList<BinaryRoutePlanner.RouteSegmentPoint>(); List<RouteSegmentPoint> listR = new ArrayList<BinaryRoutePlanner.RouteSegmentPoint>();
rp.findRouteSegment(lat, lon, ctx, listR); rp.findRouteSegment(lat, lon, ctx, listR);

View file

@ -1,8 +1,16 @@
package net.osmand.data; package net.osmand.data;
import java.util.ArrayList; import gnu.trove.list.array.TIntArrayList;
import java.util.List;
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.osm.edit.Way;
import net.osmand.util.MapUtils; import net.osmand.util.MapUtils;
@ -14,6 +22,7 @@ public class TransportRoute extends MapObject {
private Integer dist = null; private Integer dist = null;
private String color; private String color;
private List<Way> forwardWays; private List<Way> forwardWays;
public static final double SAME_STOP = 25;
public TransportRoute(){ public TransportRoute(){
} }
@ -26,6 +35,110 @@ public class TransportRoute extends MapObject {
return forwardWays; 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() { public String getColor() {
return color; return color;
} }

View file

@ -1,9 +1,14 @@
package net.osmand.data; package net.osmand.data;
import net.osmand.util.MapUtils;
public class TransportStop extends MapObject { public class TransportStop extends MapObject {
private int[] referencesToRoutes = null; private int[] referencesToRoutes = null;
private Amenity amenity; private Amenity amenity;
public int distance; public int distance;
public int x31;
public int y31;
public TransportStop(){ public TransportStop(){
} }
@ -23,4 +28,15 @@ public class TransportStop extends MapObject {
public void setAmenity(Amenity amenity) { public void setAmenity(Amenity amenity) {
this.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));
}
} }

View file

@ -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);
}
}

View file

@ -1,6 +1,14 @@
package net.osmand.router; 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.NativeLibrary;
import net.osmand.PlatformUtil; import net.osmand.PlatformUtil;
import net.osmand.binary.BinaryMapIndexReader; import net.osmand.binary.BinaryMapIndexReader;
@ -15,22 +23,13 @@ import net.osmand.util.MapUtils;
import org.apache.commons.logging.Log; 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 { public class RoutePlannerFrontEnd {
private boolean useOldVersion;
protected static final Log log = PlatformUtil.getLog(RoutePlannerFrontEnd.class); protected static final Log log = PlatformUtil.getLog(RoutePlannerFrontEnd.class);
public boolean useSmartRouteRecalculation = true; public boolean useSmartRouteRecalculation = true;
public RoutePlannerFrontEnd(boolean useOldVersion) {
this.useOldVersion = useOldVersion; public RoutePlannerFrontEnd() {
} }
public enum RouteCalculationMode { public enum RouteCalculationMode {
@ -345,11 +344,7 @@ public class RoutePlannerFrontEnd {
} else { } else {
refreshProgressDistance(ctx); refreshProgressDistance(ctx);
// Split into 2 methods to let GC work in between // 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 // 4. Route is found : collect all segments and prepare result
return new RouteResultPreparation().prepareResult(ctx, ctx.finalRouteSegment); return new RouteResultPreparation().prepareResult(ctx, ctx.finalRouteSegment);
} }

View file

@ -197,9 +197,6 @@ public class RoutingContext {
return config.planRoadDirection; return config.planRoadDirection;
} }
public void setPlanRoadDirection(int planRoadDirection) {
config.planRoadDirection = planRoadDirection;
}
public int roadPriorityComparator(double o1DistanceFromStart, double o1DistanceToEnd, double o2DistanceFromStart, double o2DistanceToEnd) { public int roadPriorityComparator(double o1DistanceFromStart, double o1DistanceToEnd, double o2DistanceFromStart, double o2DistanceToEnd) {
return BinaryRoutePlanner.roadPriorityComparator(o1DistanceFromStart, o1DistanceToEnd, o2DistanceFromStart, o2DistanceToEnd, return BinaryRoutePlanner.roadPriorityComparator(o1DistanceFromStart, o1DistanceToEnd, o2DistanceFromStart, o2DistanceToEnd,

View file

@ -28,7 +28,6 @@ public class TestRouting {
public static boolean TEST_WO_HEURISTIC = false; public static boolean TEST_WO_HEURISTIC = false;
public static boolean TEST_BOTH_DIRECTION = false; public static boolean TEST_BOTH_DIRECTION = false;
public static NativeLibrary lib = null; public static NativeLibrary lib = null;
public static boolean oldRouting = false;
private static String vehicle = "car"; private static String vehicle = "car";
@ -203,7 +202,7 @@ public class TestRouting {
return; return;
} }
RoutingConfiguration rconfig = config.build(vehicle, MEMORY_TEST_LIMIT); RoutingConfiguration rconfig = config.build(vehicle, MEMORY_TEST_LIMIT);
RoutePlannerFrontEnd router = new RoutePlannerFrontEnd(oldRouting); RoutePlannerFrontEnd router = new RoutePlannerFrontEnd();
RoutingContext ctx = router.buildRoutingContext(rconfig, RoutingContext ctx = router.buildRoutingContext(rconfig,
lib, rs); lib, rs);
String skip = parser.getAttributeValue("", "skip_comment"); String skip = parser.getAttributeValue("", "skip_comment");
@ -308,7 +307,7 @@ public class TestRouting {
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
Builder config = RoutingConfiguration.getDefault(); Builder config = RoutingConfiguration.getDefault();
RoutingConfiguration rconfig = config.build(vehicle, MEMORY_TEST_LIMIT); RoutingConfiguration rconfig = config.build(vehicle, MEMORY_TEST_LIMIT);
RoutePlannerFrontEnd router = new RoutePlannerFrontEnd(oldRouting); RoutePlannerFrontEnd router = new RoutePlannerFrontEnd();
RoutingContext ctx = router.buildRoutingContext(rconfig, lib, rs); RoutingContext ctx = router.buildRoutingContext(rconfig, lib, rs);
RouteSegment startSegment = router.findRouteSegment(startLat, startLon, ctx, null); RouteSegment startSegment = router.findRouteSegment(startLat, startLon, ctx, null);
RouteSegment endSegment = router.findRouteSegment(endLat, endLon, ctx, null); RouteSegment endSegment = router.findRouteSegment(endLat, endLon, ctx, null);

View file

@ -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;
}
}
}
}
}

View file

@ -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
}

View file

@ -59,7 +59,7 @@ public class RouteResultPreparationTest {
RandomAccessFile raf = new RandomAccessFile(fl, "r"); RandomAccessFile raf = new RandomAccessFile(fl, "r");
fe = new RoutePlannerFrontEnd(false); fe = new RoutePlannerFrontEnd();
RoutingConfiguration.Builder builder = RoutingConfiguration.getDefault(); RoutingConfiguration.Builder builder = RoutingConfiguration.getDefault();
Map<String, String> params = new LinkedHashMap<String, String>(); Map<String, String> params = new LinkedHashMap<String, String>();
params.put("car", "true"); params.put("car", "true");

View file

@ -60,7 +60,7 @@ public class RouteTestingTest {
public void testRouting() throws Exception { public void testRouting() throws Exception {
String fl = "src/test/resources/Routing_test.obf"; String fl = "src/test/resources/Routing_test.obf";
RandomAccessFile raf = new RandomAccessFile(fl, "r"); RandomAccessFile raf = new RandomAccessFile(fl, "r");
RoutePlannerFrontEnd fe = new RoutePlannerFrontEnd(false); RoutePlannerFrontEnd fe = new RoutePlannerFrontEnd();
BinaryMapIndexReader[] binaryMapIndexReaders = { new BinaryMapIndexReader(raf, new File(fl)) }; BinaryMapIndexReader[] binaryMapIndexReaders = { new BinaryMapIndexReader(raf, new File(fl)) };
RoutingConfiguration.Builder builder = RoutingConfiguration.getDefault(); RoutingConfiguration.Builder builder = RoutingConfiguration.getDefault();

View file

@ -11,7 +11,7 @@
<application <application
android:name="net.osmand.telegram.TelegramApplication" android:name="net.osmand.telegram.TelegramApplication"
android:allowBackup="true" android:allowBackup="true"
android:icon="@mipmap/product_icon" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:launchMode="singleTask" android:launchMode="singleTask"
android:screenOrientation="unspecified" android:screenOrientation="unspecified"

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
@ -17,17 +18,21 @@
android:paddingBottom="@dimen/list_view_bottom_padding" android:paddingBottom="@dimen/list_view_bottom_padding"
android:scrollbars="vertical"/> android:scrollbars="vertical"/>
<Button <net.osmand.telegram.ui.views.TextViewEx
android:id="@+id/open_osmand_btn" android:id="@+id/open_osmand_btn"
style="@style/DialogActionButtonActive"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal" android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="@dimen/content_padding_big" android:background="@drawable/img_shadow_fab"
android:drawableLeft="@drawable/ic_action_osmand_plus" android:drawableLeft="@drawable/ic_action_osmand_plus"
android:drawablePadding="@dimen/content_padding_standard" android:drawablePadding="@dimen/content_padding_half"
android:drawableStart="@drawable/ic_action_osmand_plus" android:drawableStart="@drawable/ic_action_osmand_plus"
android:text="@string/open_osmand"/> android:gravity="center"
android:paddingLeft="32dp"
android:paddingRight="32dp"
android:text="@string/open_osmand"
android:textColor="@color/white"
app:typeface="@string/font_roboto_medium"/>
</FrameLayout> </FrameLayout>

View file

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

View file

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

View file

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View file

@ -8,6 +8,7 @@ import org.drinkless.td.libcore.telegram.Client.ResultHandler
import org.drinkless.td.libcore.telegram.TdApi import org.drinkless.td.libcore.telegram.TdApi
import org.drinkless.td.libcore.telegram.TdApi.AuthorizationState import org.drinkless.td.libcore.telegram.TdApi.AuthorizationState
import java.io.File import java.io.File
import java.text.SimpleDateFormat
import java.util.* import java.util.*
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executors import java.util.concurrent.Executors
@ -30,6 +31,19 @@ class TelegramHelper private constructor() {
private const val DEVICE_PREFIX = "Device: " private const val DEVICE_PREFIX = "Device: "
private const val LOCATION_PREFIX = "Location: " private const val LOCATION_PREFIX = "Location: "
private const val FEW_SECONDS_AGO = "few seconds ago"
private const val SECONDS_AGO_SUFFIX = " seconds ago"
private const val MINUTES_AGO_SUFFIX = " minutes ago"
private const val HOURS_AGO_SUFFIX = " hours ago"
private const val UTC_FORMAT_SUFFIX = " UTC"
private val UTC_DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd", Locale.US).apply {
timeZone = TimeZone.getTimeZone("UTC")
}
private val UTC_TIME_FORMAT = SimpleDateFormat("HH:mm:ss", Locale.US).apply {
timeZone = TimeZone.getTimeZone("UTC")
}
// min and max values for the Telegram API // min and max values for the Telegram API
const val MIN_LOCATION_MESSAGE_LIVE_PERIOD_SEC = 61 const val MIN_LOCATION_MESSAGE_LIVE_PERIOD_SEC = 61
const val MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC = 60 * 60 * 24 - 1 // one day const val MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC = 60 * 60 * 24 - 1 // one day
@ -714,11 +728,27 @@ class TelegramHelper private constructor() {
private fun parseOsmAndBotLocation(message: TdApi.Message): MessageOsmAndBotLocation { private fun parseOsmAndBotLocation(message: TdApi.Message): MessageOsmAndBotLocation {
val messageLocation = message.content as TdApi.MessageLocation val messageLocation = message.content as TdApi.MessageLocation
val res = MessageOsmAndBotLocation() return MessageOsmAndBotLocation().apply {
res.name = getOsmAndBotDeviceName(message) name = getOsmAndBotDeviceName(message)
res.lat = messageLocation.location.latitude lat = messageLocation.location.latitude
res.lon = messageLocation.location.longitude lon = messageLocation.location.longitude
return res val date = message.editDate
lastUpdated = if (date != 0) {
date
} else {
message.date
}
}
}
private fun parseOsmAndBotLocationContent(oldContent:MessageOsmAndBotLocation, content: TdApi.MessageContent): MessageOsmAndBotLocation {
val messageLocation = content as TdApi.MessageLocation
return MessageOsmAndBotLocation().apply {
name = oldContent.name
lat = messageLocation.location.latitude
lon = messageLocation.location.longitude
lastUpdated = (System.currentTimeMillis() / 1000).toInt()
}
} }
private fun parseOsmAndBotLocation(text: String): MessageOsmAndBotLocation { private fun parseOsmAndBotLocation(text: String): MessageOsmAndBotLocation {
@ -732,8 +762,15 @@ class TelegramHelper private constructor() {
val locStr = s.removePrefix(LOCATION_PREFIX) val locStr = s.removePrefix(LOCATION_PREFIX)
try { try {
val (latS, lonS) = locStr.split(" ") val (latS, lonS) = locStr.split(" ")
val updatedS = locStr.substring(locStr.indexOf("("), locStr.length)
val timeSecs = parseTime(updatedS.removePrefix("(").removeSuffix(")"))
res.lat = latS.dropLast(1).toDouble() res.lat = latS.dropLast(1).toDouble()
res.lon = lonS.toDouble() res.lon = lonS.toDouble()
if (timeSecs < MESSAGE_ACTIVE_TIME_SEC) {
res.lastUpdated = (System.currentTimeMillis() / 1000 - timeSecs).toInt()
} else {
res.lastUpdated = timeSecs
}
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }
@ -743,6 +780,40 @@ class TelegramHelper private constructor() {
return res return res
} }
private fun parseTime(timeS: String): Int {
try {
when {
timeS.endsWith(FEW_SECONDS_AGO) -> return 5
timeS.endsWith(SECONDS_AGO_SUFFIX) -> {
val locStr = timeS.removeSuffix(SECONDS_AGO_SUFFIX)
return locStr.toInt()
}
timeS.endsWith(MINUTES_AGO_SUFFIX) -> {
val locStr = timeS.removeSuffix(MINUTES_AGO_SUFFIX)
val minutes = locStr.toInt()
return minutes * 60
}
timeS.endsWith(HOURS_AGO_SUFFIX) -> {
val locStr = timeS.removeSuffix(HOURS_AGO_SUFFIX)
val hours = locStr.toInt()
return hours * 60 * 60
}
timeS.endsWith(UTC_FORMAT_SUFFIX) -> {
val locStr = timeS.removeSuffix(UTC_FORMAT_SUFFIX)
val (latS, lonS) = locStr.split(" ")
val date = UTC_DATE_FORMAT.parse(latS)
val time = UTC_TIME_FORMAT.parse(lonS)
val res = date.time + time.time
return res.toInt()
}
}
} catch (e: Exception) {
e.printStackTrace()
}
return 0
}
class MessageOsmAndBotLocation : TdApi.MessageContent() { class MessageOsmAndBotLocation : TdApi.MessageContent() {
var name: String = "" var name: String = ""
@ -751,6 +822,8 @@ class TelegramHelper private constructor() {
internal set internal set
var lon: Double = Double.NaN var lon: Double = Double.NaN
internal set internal set
var lastUpdated: Int = 0
internal set
override fun getConstructor() = -1 override fun getConstructor() = -1
@ -966,7 +1039,7 @@ class TelegramHelper private constructor() {
parseOsmAndBotLocation(newContent.text.text) parseOsmAndBotLocation(newContent.text.text)
} else if (newContent is TdApi.MessageLocation && } else if (newContent is TdApi.MessageLocation &&
(isOsmAndBot(message.senderUserId) || isOsmAndBot(message.viaBotUserId))) { (isOsmAndBot(message.senderUserId) || isOsmAndBot(message.viaBotUserId))) {
parseOsmAndBotLocation(message) parseOsmAndBotLocationContent(message.content as MessageOsmAndBotLocation, newContent)
} else { } else {
newContent newContent
} }

View file

@ -52,6 +52,10 @@ object TelegramUiHelper {
placeholderId = R.drawable.img_user_picture placeholderId = R.drawable.img_user_picture
} }
val type = chat.type val type = chat.type
val message = messages.firstOrNull()
if (message != null) {
res.lastUpdated = message.editDate
}
if (type is TdApi.ChatTypePrivate || type is TdApi.ChatTypeSecret) { if (type is TdApi.ChatTypePrivate || type is TdApi.ChatTypeSecret) {
val userId = getUserIdFromChatType(type) val userId = getUserIdFromChatType(type)
val chatWithBot = helper.isBot(userId) val chatWithBot = helper.isBot(userId)
@ -59,7 +63,7 @@ object TelegramUiHelper {
res.chatWithBot = chatWithBot res.chatWithBot = chatWithBot
if (!chatWithBot) { if (!chatWithBot) {
res.userId = userId res.userId = userId
val content = messages.firstOrNull()?.content val content = message?.content
if (content is TdApi.MessageLocation) { if (content is TdApi.MessageLocation) {
res.latLon = LatLon(content.location.latitude, content.location.longitude) res.latLon = LatLon(content.location.latitude, content.location.longitude)
} }
@ -108,6 +112,7 @@ object TelegramUiHelper {
name = content.name name = content.name
latLon = LatLon(content.lat, content.lon) latLon = LatLon(content.lat, content.lon)
placeholderId = R.drawable.img_user_picture placeholderId = R.drawable.img_user_picture
lastUpdated = content.lastUpdated
} }
} else { } else {
null null
@ -135,6 +140,7 @@ object TelegramUiHelper {
photoPath = helper.getUserPhotoPath(user) photoPath = helper.getUserPhotoPath(user)
placeholderId = R.drawable.img_user_picture placeholderId = R.drawable.img_user_picture
userId = message.senderUserId userId = message.senderUserId
lastUpdated = message.editDate
} }
} }
@ -152,6 +158,8 @@ object TelegramUiHelper {
internal set internal set
var userId: Int = 0 var userId: Int = 0
internal set internal set
var lastUpdated: Int = 0
internal set
abstract fun canBeOpenedOnMap(): Boolean abstract fun canBeOpenedOnMap(): Boolean

View file

@ -10,8 +10,8 @@ import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.animation.LinearInterpolator
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.Button
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import net.osmand.Location import net.osmand.Location
@ -33,6 +33,7 @@ import org.drinkless.td.libcore.telegram.TdApi
private const val CHAT_VIEW_TYPE = 0 private const val CHAT_VIEW_TYPE = 0
private const val LOCATION_ITEM_VIEW_TYPE = 1 private const val LOCATION_ITEM_VIEW_TYPE = 1
private const val LOCATION_TIMEOUT_TO_BE_STALE = 60 * 15 // 15 minutes
class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessagesListener, class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessagesListener,
FullInfoUpdatesListener, TelegramLocationListener, TelegramCompassListener { FullInfoUpdatesListener, TelegramLocationListener, TelegramCompassListener {
@ -47,6 +48,8 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
private lateinit var adapter: LiveNowListAdapter private lateinit var adapter: LiveNowListAdapter
private lateinit var locationViewCache: UpdateLocationViewCache private lateinit var locationViewCache: UpdateLocationViewCache
private lateinit var openOsmAndBtn: View
private var location: Location? = null private var location: Location? = null
private var heading: Float? = null private var heading: Float? = null
private var locationUiUpdateAllowed: Boolean = true private var locationUiUpdateAllowed: Boolean = true
@ -66,15 +69,21 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) { override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
super.onScrollStateChanged(recyclerView, newState) super.onScrollStateChanged(recyclerView, newState)
locationUiUpdateAllowed = newState == RecyclerView.SCROLL_STATE_IDLE locationUiUpdateAllowed = newState == RecyclerView.SCROLL_STATE_IDLE
when (newState) {
RecyclerView.SCROLL_STATE_DRAGGING -> animateOpenOsmAndBtn(false)
RecyclerView.SCROLL_STATE_IDLE -> animateOpenOsmAndBtn(true)
}
} }
}) })
} }
mainView.findViewById<Button>(R.id.open_osmand_btn).setOnClickListener { openOsmAndBtn = mainView.findViewById<View>(R.id.open_osmand_btn).apply {
val intent = activity?.packageManager?.getLaunchIntentForPackage(OsmandAidlHelper.OSMAND_PACKAGE_NAME) setOnClickListener {
if (intent != null) { activity?.packageManager?.getLaunchIntentForPackage(OsmandAidlHelper.OSMAND_PACKAGE_NAME)
?.also { intent ->
startActivity(intent) startActivity(intent)
} }
} }
}
return mainView return mainView
} }
@ -230,6 +239,16 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
} }
} }
private fun animateOpenOsmAndBtn(show: Boolean) {
val scale = if (show) 1f else 0f
openOsmAndBtn.animate()
.scaleX(scale)
.scaleY(scale)
.setDuration(200)
.setInterpolator(LinearInterpolator())
.start()
}
inner class LiveNowListAdapter : RecyclerView.Adapter<BaseViewHolder>() { inner class LiveNowListAdapter : RecyclerView.Adapter<BaseViewHolder>() {
private val menuList = private val menuList =
@ -282,7 +301,7 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage
} }
if (location != null && item.latLon != null) { if (location != null && item.latLon != null) {
holder.locationViewContainer?.visibility = View.VISIBLE holder.locationViewContainer?.visibility = View.VISIBLE
// TODO: locationViewCache.outdatedLocation locationViewCache.outdatedLocation = System.currentTimeMillis() / 1000 - item.lastUpdated > LOCATION_TIMEOUT_TO_BE_STALE
app.uiUtils.updateLocationView( app.uiUtils.updateLocationView(
holder.directionIcon, holder.directionIcon,
holder.distanceText, holder.distanceText,

View file

@ -118,10 +118,6 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene
} }
} }
}) })
telegramHelper.listener = this
if (!telegramHelper.isInit()) {
telegramHelper.init()
}
if (osmandAidlHelper.isOsmandBound() && !osmandAidlHelper.isOsmandConnected()) { if (osmandAidlHelper.isOsmandBound() && !osmandAidlHelper.isOsmandConnected()) {
osmandAidlHelper.connectOsmand() osmandAidlHelper.connectOsmand()
@ -143,6 +139,11 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene
super.onResume() super.onResume()
paused = false paused = false
telegramHelper.listener = this
if (!telegramHelper.isInit()) {
telegramHelper.init()
}
app.locationProvider.checkIfLastKnownLocationIsValid() app.locationProvider.checkIfLastKnownLocationIsValid()
if (AndroidUtils.isLocationPermissionAvailable(this)) { if (AndroidUtils.isLocationPermissionAvailable(this)) {

View file

@ -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">Sovrapposizioni fluorescenti</string>
<string name="use_fluorescent_overlays_descr">Usa colori fluorescenti per visualizzare tracce e percorsi.</string> <string name="use_fluorescent_overlays_descr">Usa colori fluorescenti per visualizzare tracce e percorsi.</string>
<string name="offline_edition">Modifiche offline</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="full_version_thanks">Grazie per avere acquistato la versione completa di OsmAnd!</string>
<string name="fonts_header">Caratteri della mappa</string> <string name="fonts_header">Caratteri della mappa</string>
<string name="right_side_navigation">Guida a destra</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="depth_contour_descr">Mappe linee isoipse marine e i punti nautici.</string>
<string name="sea_depth_thanks">Grazie per avere acquistato le isoipse nautiche!</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_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_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> <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="optional_point_name">Nome del punto facoltativo</string>
<string name="transport_nearby_routes_within">Percorsi vicini entro</string> <string name="transport_nearby_routes_within">Percorsi vicini entro</string>
<string name="transport_nearby_routes">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_import_error">Errore importazione mappa</string>
<string name="map_imported_successfully">Mappa importata</string> <string name="map_imported_successfully">Mappa importata</string>
<string name="winter_and_ski_renderer">Inverno e sci</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> \n"</string>
<string name="quick_action_edit_actions">Modifica le azioni</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> <string name="error_notification_desc">Per favore invia una schermata di questa notifica a support@osmand.net</string>
<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> </resources>

View file

@ -3008,7 +3008,7 @@
<string name="how_to_open_link">Hvordan åpne lenken?</string> <string name="how_to_open_link">Hvordan åpne lenken?</string>
<string name="read_wikipedia_offline">Les Wikipedia frakoblet</string> <string name="read_wikipedia_offline">Les Wikipedia frakoblet</string>
<string name="download_all">Last ned alt</string> <string name="download_all">Last ned alt</string>
<string name="shared_string_restart">Omstart</string> <string name="shared_string_restart">Programomstart</string>
<string name="purchase_cancelled_dialog_title">Du har kansellert ditt OsmAnd Live-abonnement</string> <string name="purchase_cancelled_dialog_title">Du har kansellert ditt OsmAnd Live-abonnement</string>
<string name="purchase_cancelled_dialog_descr">Forny abonnement for å fortsette å bruke alle funksjonene:</string> <string name="purchase_cancelled_dialog_descr">Forny abonnement for å fortsette å bruke alle funksjonene:</string>
@ -3023,7 +3023,7 @@
<string name="shared_string_gpx_files">GPX-filer</string> <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="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="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">Send en skjeravbildning av dette varselet til support@osmand.net</string>
<string name="coord_input_edit_point">Rediger punkt</string> <string name="coord_input_edit_point">Rediger punkt</string>
<string name="coord_input_add_point">Legg til punkt</string> <string name="coord_input_add_point">Legg til punkt</string>
<string name="coord_input_save_as_track">Lagre som spor</string> <string name="coord_input_save_as_track">Lagre som spor</string>
@ -3037,4 +3037,19 @@
<string name="thank_you_for_feedback">Takk for din tilbakemelding</string> <string name="thank_you_for_feedback">Takk for din tilbakemelding</string>
<string name="search_no_results_feedback">Resultatløst? <string name="search_no_results_feedback">Resultatløst?
\nGi oss tilbakemelding</string> \nGi oss tilbakemelding</string>
<string name="poi_cannot_be_found">Fant ikke node eller vei.</string>
<string name="release_3_1">• Navigering: Fiks for fremdriftsindikator, raskt bytte av start og sluttpunkt på ruten
\n
\n• Kartmarkører: Fiks for å slå av og på grupper, mulighet til å skjule markører fra kartet
\n
\n• OSM-rediger: Mulighet til å redigere koder for ikke-punktsobjekter og veier, fiks for manglende kommentarer på notater, sikkerhetskopiering av endringer
\n
\n• Forbedret Wikipedia- og Wikivoyage -fortolkning, oppdaterte filer er allerede tilgjengelige
\n
\n• Kontekstmeny: Fiks for transportskjoldsfarge i nattmodus, fiks for tilleggsmenystørrelser
\n
\n• Båtnavigasjon: Støtte for vannvei-fairway
\n
\n• Andre feilrettinger
\n</string>
</resources> </resources>

View file

@ -1661,7 +1661,7 @@
<string name="poi_toilets_access_community">Acesso aos banheiros: comunidade</string> <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_toilets_access_public">Acesso aos banheiros: público</string>
<string name="poi_diaper_yes">Trocador de fralda</string> <string name="poi_diaper_yes">Com trocador de fralda</string>
<string name="poi_diaper_no">Sem trocador de fralda</string> <string name="poi_diaper_no">Sem trocador de fralda</string>
<string name="poi_diaper_room">Sala para trocar fralda</string> <string name="poi_diaper_room">Sala para trocar fralda</string>

View file

@ -17,12 +17,12 @@
<string name="poi_service_general">Одржавање</string> <string name="poi_service_general">Одржавање</string>
<string name="poi_information_contents">Садржаји</string> <string name="poi_information_contents">Садржаји</string>
<string name="poi_clock_option">Додатно</string> <string name="poi_clock_option">Додатно</string>
<string name="poi_scout_camp">Камп скаута</string> <string name="poi_scout_camp">Камп извиђача</string>
<string name="poi_resort_type">Тип</string> <string name="poi_resort_type">Тип</string>
<string name="poi_piste_difficulty">Тежина стазе</string> <string name="poi_piste_difficulty">Тежина стазе</string>
<string name="poi_piste_grooming">Равнање стазе</string> <string name="poi_piste_grooming">Равнање стазе</string>
<string name="poi_theatre_genre">Жанр</string> <string name="poi_theatre_genre">Жанр</string>
<string name="poi_outdoor_seating">Седење напољу</string> <string name="poi_outdoor_seating">Спољашња клупа</string>
<string name="poi_fee">Накнада</string> <string name="poi_fee">Накнада</string>
<string name="poi_smoking">Пушење</string> <string name="poi_smoking">Пушење</string>
<string name="poi_delivery">Достава</string> <string name="poi_delivery">Достава</string>
@ -1492,4 +1492,55 @@
<string name="poi_denomination_dutch_reformed">Холандски реформисти</string> <string name="poi_denomination_dutch_reformed">Холандски реформисти</string>
<string name="poi_denomination_apostolic">Апостолска црква</string> <string name="poi_denomination_apostolic">Апостолска црква</string>
<string name="poi_denomination_reform">Реформисти</string> <string name="poi_denomination_reform">Реформисти</string>
<string name="poi_fuel_avia_type">Врста горива (авиа)</string>
<string name="poi_tactile_paving">Додирна поставка</string>
<string name="poi_brushless">Без четкица</string>
<string name="poi_observatory_designation">Одредиште</string>
<string name="poi_pharmacy_dispensing">Апотека</string>
<string name="poi_denomination">Деноминација</string>
<string name="poi_backcountry">Рурални део</string>
<string name="poi_beauty_salon_service">Услуга</string>
<string name="poi_diet">Храна</string>
<string name="poi_health_specialty">Специјалитет здрави</string>
<string name="poi_power_supply">Извор напајања</string>
<string name="poi_motorcycle_services">Услуге</string>
<string name="poi_operational_status">Функционални статус</string>
<string name="poi_water_supply_type">Тип доставе воде</string>
<string name="poi_water_place_access">Место приступа води</string>
<string name="poi_checkpoint_type">Тип контролног места</string>
<string name="poi_shop">Продавница</string>
<string name="poi_shop_food">Продавница потребштина и маркет</string>
<string name="poi_emergency">Хитна помоћ</string>
<string name="poi_emergency_infrastructure">Инфраструктура хитне помоћи</string>
<string name="poi_node_networks">Пешачки/бициклистички мрежни чворови</string>
<string name="poi_traffic_enforcement">Ограничење саобраћаја</string>
<string name="poi_man_made">Направљено ручно</string>
<string name="poi_transport_construction">Саобраћајна конструкција</string>
<string name="poi_water_supply">Достава воде</string>
<string name="poi_landuse">Коришћење земљишта</string>
<string name="poi_accomodation">Смештај</string>
<string name="poi_entertainment">Забавно</string>
<string name="poi_service">Услуга</string>
<string name="poi_craft">Радионица</string>
<string name="poi_convenience">Продавница потребштина</string>
<string name="poi_deli">Продавница деликатеса</string>
<string name="poi_greengrocer">Продавница зеленила</string>
<string name="poi_ice_cream">Простор са сладоледом</string>
<string name="poi_pastry">Продавница пецива</string>
<string name="poi_dairy">Млекара</string>
<string name="poi_vending_machine">Аутомат продаје</string>
<string name="poi_bag">Продавница торби</string>
<string name="poi_bathroom_furnishing">Намештај за купатило</string>
<string name="poi_bed">Продавница кревета</string>
<string name="poi_charity">Продавница прилога</string>
<string name="poi_dive">Ронилачка опрема</string>
<string name="poi_doityourself">Продавница за кућно унапређење</string>
<string name="poi_fishing">Опрема за пецање</string>
<string name="poi_free_flying">Продавница ствари слободног летења</string>
<string name="poi_furniture">Продавница намештаја</string>
<string name="poi_garden_centre">Баштенски центар</string>
<string name="poi_garden_furniture">Продавница баштенског намештаја</string>
<string name="poi_shop_yes">Општа продавница</string>
<string name="poi_glaziery">Стаклара</string>
</resources> </resources>

View file

@ -2978,7 +2978,7 @@
\nЈавите нам</string> \nЈавите нам</string>
<string name="release_3_1">• Навођење: Исправка у траци прогреса, брза замена почетне и крајње тачке пута <string name="release_3_1">• Навођење: Исправка у траци прогреса, брза замена почетне и крајње тачке пута
\n \n
\n • Ознаке на карти: fix turn on/off groups, ability to hide markers from the map \n • Ознаке на карти: исправи укључење/искључење група, способност скривања маркера са мапе
\n \n
\n • OSM измене: Могућност измена ознака за објекте и путеве који нису обичне тачке, исправка недостајућих коментара на белешкама, прављење резервне копије измена \n • OSM измене: Могућност измена ознака за објекте и путеве који нису обичне тачке, исправка недостајућих коментара на белешкама, прављење резервне копије измена
\n \n

View file

@ -136,10 +136,10 @@ public class CurrentPositionHelper {
} }
RoutingConfiguration cfg = app.getDefaultRoutingConfig().build(p, 10, RoutingConfiguration cfg = app.getDefaultRoutingConfig().build(p, 10,
new HashMap<String, String>()); 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, RoutingConfiguration defCfg = app.getDefaultRoutingConfig().build("geocoding", 10,
new HashMap<String, String>()); new HashMap<String, String>());
defCtx = new RoutePlannerFrontEnd(false).buildRoutingContext(defCfg, null, rs); defCtx = new RoutePlannerFrontEnd().buildRoutingContext(defCfg, null, rs);
} else { } else {
ctx = null; ctx = null;
defCtx = null; defCtx = null;

View file

@ -756,6 +756,7 @@ public class MapContextMenu extends MenuTitleController implements StateChangedL
} }
} }
@Nullable
public WeakReference<MapContextMenuFragment> findMenuFragment() { public WeakReference<MapContextMenuFragment> findMenuFragment() {
Fragment fragment = mapActivity != null Fragment fragment = mapActivity != null
? mapActivity.getSupportFragmentManager().findFragmentByTag(MapContextMenuFragment.TAG) : null; ? mapActivity.getSupportFragmentManager().findFragmentByTag(MapContextMenuFragment.TAG) : null;

View file

@ -12,6 +12,7 @@ import android.graphics.drawable.GradientDrawable;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.support.v4.graphics.drawable.DrawableCompat; import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v7.view.ContextThemeWrapper; import android.support.v7.view.ContextThemeWrapper;
@ -89,6 +90,7 @@ public class MenuBuilder {
protected List<Amenity> nearestWiki = new ArrayList<>(); protected List<Amenity> nearestWiki = new ArrayList<>();
private List<OsmandPlugin> menuPlugins = new ArrayList<>(); private List<OsmandPlugin> menuPlugins = new ArrayList<>();
private List<TransportStopRoute> routes = new ArrayList<>(); private List<TransportStopRoute> routes = new ArrayList<>();
@Nullable
private CardsRowBuilder onlinePhotoCardsRow; private CardsRowBuilder onlinePhotoCardsRow;
private List<AbstractCard> onlinePhotoCards; private List<AbstractCard> onlinePhotoCards;
@ -416,6 +418,9 @@ public class MenuBuilder {
} }
private void startLoadingImages() { private void startLoadingImages() {
if (onlinePhotoCardsRow == null) {
return;
}
onlinePhotoCards = new ArrayList<>(); onlinePhotoCards = new ArrayList<>();
onlinePhotoCardsRow.setProgressCard(); onlinePhotoCardsRow.setProgressCard();
execute(new GetImageCardsTask(mapActivity, getLatLon(), getAdditionalCardParams(), execute(new GetImageCardsTask(mapActivity, getLatLon(), getAdditionalCardParams(),
@ -433,7 +438,9 @@ public class MenuBuilder {
if (cardList.size() == 0) { if (cardList.size() == 0) {
cards.add(new NoImagesCard(mapActivity)); cards.add(new NoImagesCard(mapActivity));
} }
if (onlinePhotoCardsRow != null) {
onlinePhotoCardsRow.setCards(cards); onlinePhotoCardsRow.setCards(cards);
}
onlinePhotoCards = cards; onlinePhotoCards = cards;
} }
} }

View file

@ -44,16 +44,17 @@ public class MapMultiSelectionMenu extends BaseMenuController {
this.pointDescription = pointDescription; this.pointDescription = pointDescription;
this.object = object; this.object = object;
this.mapActivity = mapActivity; this.mapActivity = mapActivity;
if (mapActivity != null) {
init(); init();
} }
}
protected void init() { protected void init() {
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
controller = MenuController.getMenuController(mapActivity, latLon, pointDescription, object, MenuType.MULTI_LINE); controller = MenuController.getMenuController(mapActivity, latLon, pointDescription, object, MenuType.MULTI_LINE);
controller.setActive(true); controller.setActive(true);
initTitle(); initTitle();
} }
}
protected void deinit() { protected void deinit() {
controller = null; controller = null;
@ -160,7 +161,10 @@ public class MapMultiSelectionMenu extends BaseMenuController {
private void clearMenu() { private void clearMenu() {
clearSelectedObjects(); clearSelectedObjects();
objects.clear(); objects.clear();
getMapActivity().refreshMap(); MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
mapActivity.refreshMap();
}
} }
public void show(LatLon latLon, Map<Object, IContextMenuProvider> selectedObjects) { public void show(LatLon latLon, Map<Object, IContextMenuProvider> selectedObjects) {
@ -170,8 +174,11 @@ public class MapMultiSelectionMenu extends BaseMenuController {
this.latLon = latLon; this.latLon = latLon;
createCollection(selectedObjects); createCollection(selectedObjects);
updateNightMode(); updateNightMode();
MapMultiSelectionMenuFragment.showInstance(getMapActivity()); MapActivity mapActivity = getMapActivity();
getMapActivity().refreshMap(); if (mapActivity != null) {
MapMultiSelectionMenuFragment.showInstance(mapActivity);
mapActivity.refreshMap();
}
} }
public boolean isVisible() { public boolean isVisible() {
@ -179,8 +186,14 @@ public class MapMultiSelectionMenu extends BaseMenuController {
return fragment != null; return fragment != null;
} }
@Nullable
public Fragment getFragmentByTag() { public Fragment getFragmentByTag() {
return getMapActivity().getSupportFragmentManager().findFragmentByTag(MapMultiSelectionMenuFragment.TAG); MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
return mapActivity.getSupportFragmentManager().findFragmentByTag(MapMultiSelectionMenuFragment.TAG);
} else {
return null;
}
} }
public void hide() { public void hide() {
@ -199,9 +212,12 @@ public class MapMultiSelectionMenu extends BaseMenuController {
public void openContextMenu(@NonNull MenuObject menuObject) { public void openContextMenu(@NonNull MenuObject menuObject) {
IContextMenuProvider provider = selectedObjects.remove(menuObject.getObject()); IContextMenuProvider provider = selectedObjects.remove(menuObject.getObject());
hide(); hide();
ContextMenuLayer contextMenuLayer = getMapActivity().getMapLayers().getContextMenuLayer(); MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
ContextMenuLayer contextMenuLayer = mapActivity.getMapLayers().getContextMenuLayer();
contextMenuLayer.showContextMenu(menuObject.getLatLon(), menuObject.getPointDescription(), menuObject.getObject(), provider); contextMenuLayer.showContextMenu(menuObject.getLatLon(), menuObject.getPointDescription(), menuObject.getObject(), provider);
} }
}
private void clearSelectedObjects() { private void clearSelectedObjects() {
for(IContextMenuProvider p : selectedObjects.values()) { for(IContextMenuProvider p : selectedObjects.values()) {

View file

@ -658,7 +658,7 @@ public class RouteProvider {
protected RouteCalculationResult findVectorMapsRoute(final RouteCalculationParams params, boolean calcGPXRoute) throws IOException { protected RouteCalculationResult findVectorMapsRoute(final RouteCalculationParams params, boolean calcGPXRoute) throws IOException {
BinaryMapIndexReader[] files = params.ctx.getResourceManager().getRoutingMapFiles(); BinaryMapIndexReader[] files = params.ctx.getResourceManager().getRoutingMapFiles();
RoutePlannerFrontEnd router = new RoutePlannerFrontEnd(false); RoutePlannerFrontEnd router = new RoutePlannerFrontEnd();
OsmandSettings settings = params.ctx.getSettings(); OsmandSettings settings = params.ctx.getSettings();
router.setUseFastRecalculation(settings.USE_FAST_RECALCULATION.get()); router.setUseFastRecalculation(settings.USE_FAST_RECALCULATION.get());

View file

@ -10,6 +10,7 @@ import android.graphics.PointF;
import android.os.Build; import android.os.Build;
import android.os.Vibrator; import android.os.Vibrator;
import android.support.annotation.DimenRes; import android.support.annotation.DimenRes;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.support.v4.util.Pair; import android.support.v4.util.Pair;
import android.view.MotionEvent; import android.view.MotionEvent;
@ -32,12 +33,15 @@ import net.osmand.plus.R;
import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.mapcontextmenu.MapContextMenu; import net.osmand.plus.mapcontextmenu.MapContextMenu;
import net.osmand.plus.mapcontextmenu.MapContextMenuFragment;
import net.osmand.plus.mapcontextmenu.other.MapMultiSelectionMenu;
import net.osmand.plus.measurementtool.MeasurementToolLayer; import net.osmand.plus.measurementtool.MeasurementToolLayer;
import net.osmand.plus.quickaction.QuickAction; import net.osmand.plus.quickaction.QuickAction;
import net.osmand.plus.quickaction.QuickActionFactory; import net.osmand.plus.quickaction.QuickActionFactory;
import net.osmand.plus.quickaction.QuickActionRegistry; import net.osmand.plus.quickaction.QuickActionRegistry;
import net.osmand.plus.quickaction.QuickActionsWidget; import net.osmand.plus.quickaction.QuickActionsWidget;
import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -381,15 +385,20 @@ public class MapQuickActionLayer extends OsmandMapLayer implements QuickActionRe
} }
private void setupQuickActionBtnVisibility() { private void setupQuickActionBtnVisibility() {
MapContextMenu contextMenu = mapActivity.getContextMenu();
MapMultiSelectionMenu multiSelectionMenu = contextMenu.getMultiSelectionMenu();
WeakReference<MapContextMenuFragment> contextMenuMenuFragmentRef = contextMenu.findMenuFragment();
MapContextMenuFragment contextMenuMenuFragment = contextMenuMenuFragmentRef != null ? contextMenuMenuFragmentRef.get() : null;
Fragment multiMenuFragment = multiSelectionMenu.getFragmentByTag();
boolean hideQuickButton = !isLayerOn || boolean hideQuickButton = !isLayerOn ||
contextMenuLayer.isInChangeMarkerPositionMode() || contextMenuLayer.isInChangeMarkerPositionMode() ||
contextMenuLayer.isInGpxDetailsMode() || contextMenuLayer.isInGpxDetailsMode() ||
measurementToolLayer.isInMeasurementMode() || measurementToolLayer.isInMeasurementMode() ||
mapMarkersLayer.isInPlanRouteMode() || mapMarkersLayer.isInPlanRouteMode() ||
mapActivity.getContextMenu().isVisible() && !mapActivity.getContextMenu().findMenuFragment().get().isRemoving() || contextMenu.isVisible() && contextMenuMenuFragment != null && !contextMenuMenuFragment.isRemoving() ||
mapActivity.getContextMenu().isVisible() && mapActivity.getContextMenu().findMenuFragment().get().isAdded() || contextMenu.isVisible() && contextMenuMenuFragment != null && contextMenuMenuFragment.isAdded() ||
mapActivity.getContextMenu().getMultiSelectionMenu().isVisible() && mapActivity.getContextMenu().getMultiSelectionMenu().getFragmentByTag().isAdded() || multiSelectionMenu.isVisible() && multiMenuFragment != null && multiMenuFragment.isAdded() ||
mapActivity.getContextMenu().getMultiSelectionMenu().isVisible() && !mapActivity.getContextMenu().getMultiSelectionMenu().getFragmentByTag().isRemoving(); multiSelectionMenu.isVisible() && multiMenuFragment != null && !multiMenuFragment.isRemoving();
quickActionButton.setVisibility(hideQuickButton ? View.GONE : View.VISIBLE); quickActionButton.setVisibility(hideQuickButton ? View.GONE : View.VISIBLE);
} }

View file

@ -117,7 +117,7 @@ public class CurrentPositionHelper {
if (rs.length > 0) { if (rs.length > 0) {
RoutingConfiguration defCfg = RoutingConfiguration.getDefault().build("geocoding", 10, RoutingConfiguration defCfg = RoutingConfiguration.getDefault().build("geocoding", 10,
new HashMap<String, String>()); new HashMap<String, String>());
defCtx = new RoutePlannerFrontEnd(false).buildRoutingContext(defCfg, null, rs); defCtx = new RoutePlannerFrontEnd().buildRoutingContext(defCfg, null, rs);
} else { } else {
defCtx = null; defCtx = null;
} }