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

This commit is contained in:
Alexey Pelykh 2014-03-04 19:33:04 +02:00
commit 40d5335dbd
37 changed files with 733 additions and 255 deletions

View file

@ -18,6 +18,7 @@ public class RenderingContext {
} }
} }
public int renderedState = 0;
// FIELDS OF THAT CLASS ARE USED IN C++ // FIELDS OF THAT CLASS ARE USED IN C++
public boolean interrupted = false; public boolean interrupted = false;
public boolean nightMode = false; public boolean nightMode = false;

View file

@ -33,6 +33,7 @@ public class BinaryMapDataObject {
this.coordinates = coordinates; this.coordinates = coordinates;
} }
public String getName(){ public String getName(){
if(objectNames == null){ if(objectNames == null){
return ""; return "";

View file

@ -1541,6 +1541,10 @@ public class BinaryMapIndexReader {
return top; return top;
} }
public int getZoom() {
return zoom;
}
public void clearSearchResults(){ public void clearSearchResults(){
// recreate whole list to allow GC collect old data // recreate whole list to allow GC collect old data
searchResults = new ArrayList<T>(); searchResults = new ArrayList<T>();
@ -1598,6 +1602,10 @@ public class BinaryMapIndexReader {
} }
} }
public boolean isRegisteredRule(int id) {
return decodingRules.containsKey(id);
}
public void initMapEncodingRule(int type, int id, String tag, String val) { public void initMapEncodingRule(int type, int id, String tag, String val) {
if(!encodingRules.containsKey(tag)){ if(!encodingRules.containsKey(tag)){
encodingRules.put(tag, new HashMap<String, Integer>()); encodingRules.put(tag, new HashMap<String, Integer>());
@ -2070,7 +2078,7 @@ public class BinaryMapIndexReader {
} }
public List<RouteSubregion> searchRouteIndexTree(SearchRequest<RouteDataObject> req, List<RouteSubregion> list) throws IOException { public List<RouteSubregion> searchRouteIndexTree(SearchRequest<?> req, List<RouteSubregion> list) throws IOException {
req.numberOfVisitedObjects = 0; req.numberOfVisitedObjects = 0;
req.numberOfAcceptedObjects = 0; req.numberOfAcceptedObjects = 0;
req.numberOfAcceptedSubtrees = 0; req.numberOfAcceptedSubtrees = 0;

View file

@ -674,7 +674,7 @@ public class BinaryMapRouteReaderAdapter {
} }
} }
public void initRouteTypesIfNeeded(SearchRequest<RouteDataObject> req, List<RouteSubregion> list) throws IOException { public void initRouteTypesIfNeeded(SearchRequest<?> req, List<RouteSubregion> list) throws IOException {
for (RouteSubregion rs : list) { for (RouteSubregion rs : list) {
if (req.intersects(rs.left, rs.top, rs.right, rs.bottom)) { if (req.intersects(rs.left, rs.top, rs.right, rs.bottom)) {
initRouteRegion(rs.routeReg); initRouteRegion(rs.routeReg);
@ -736,7 +736,7 @@ public class BinaryMapRouteReaderAdapter {
} }
} }
public List<RouteSubregion> searchRouteRegionTree(SearchRequest<RouteDataObject> req, List<RouteSubregion> list, public List<RouteSubregion> searchRouteRegionTree(SearchRequest<?> req, List<RouteSubregion> list,
List<RouteSubregion> toLoad) throws IOException { List<RouteSubregion> toLoad) throws IOException {
for (RouteSubregion rs : list) { for (RouteSubregion rs : list) {
if (req.intersects(rs.left, rs.top, rs.right, rs.bottom)) { if (req.intersects(rs.left, rs.top, rs.right, rs.bottom)) {

View file

@ -61,6 +61,10 @@ public class RouteDataObject {
return null; return null;
} }
public TIntObjectHashMap<String> getNames() {
return names;
}
public String getRef(){ public String getRef(){
if(names != null ) { if(names != null ) {
return names.get(region.refTypeRule); return names.get(region.refTypeRule);

View file

@ -98,7 +98,7 @@ public class BinaryRoutePlanner {
graphReverseSegments.size()) * STANDARD_ROAD_IN_QUEUE_OVERHEAD; graphReverseSegments.size()) * STANDARD_ROAD_IN_QUEUE_OVERHEAD;
if(TRACE_ROUTING){ if(TRACE_ROUTING){
printRoad(">", segment); printRoad(">", segment, !forwardSearch);
} }
if(segment instanceof FinalRouteSegment) { if(segment instanceof FinalRouteSegment) {
if(RoutingContext.SHOW_GC_SIZE){ if(RoutingContext.SHOW_GC_SIZE){
@ -136,9 +136,9 @@ public class BinaryRoutePlanner {
} }
if (ctx.planRouteIn2Directions()) { if (ctx.planRouteIn2Directions()) {
forwardSearch = (nonHeuristicSegmentsComparator.compare(graphDirectSegments.peek(), graphReverseSegments.peek()) < 0); forwardSearch = (nonHeuristicSegmentsComparator.compare(graphDirectSegments.peek(), graphReverseSegments.peek()) < 0);
if (graphDirectSegments.size() * 1.3 > graphReverseSegments.size()) { if (graphDirectSegments.size() * 2 > graphReverseSegments.size()) {
forwardSearch = false; forwardSearch = false;
} else if (graphDirectSegments.size() < 1.3 * graphReverseSegments.size()) { } else if (graphDirectSegments.size() < 2 * graphReverseSegments.size()) {
forwardSearch = true; forwardSearch = true;
} }
} else { } else {
@ -236,14 +236,18 @@ public class BinaryRoutePlanner {
} }
private void printRoad(String prefix, RouteSegment segment) { private void printRoad(String prefix, RouteSegment segment, Boolean reverseWaySearch) {
String pr; String pr;
if(segment.parentRoute != null){ if(segment.parentRoute != null){
pr = " pend="+segment.parentSegmentEnd +" parent=" + segment.parentRoute.road; pr = " pend="+segment.parentSegmentEnd +" parent=" + segment.parentRoute.road;
} else { } else {
pr = ""; pr = "";
} }
println(prefix +"" + segment.road + " dir="+segment.getDirectionAssigned()+" ind=" + segment.getSegmentStart() + String p = "";
if(reverseWaySearch != null) {
p = (reverseWaySearch?"B" : "F");
}
println(p+prefix +"" + segment.road + " dir="+segment.getDirectionAssigned()+" ind=" + segment.getSegmentStart() +
" ds=" + ((float)segment.distanceFromStart) + " es="+((float)segment.distanceToEnd) + pr); " ds=" + ((float)segment.distanceFromStart) + " es="+((float)segment.distanceToEnd) + pr);
} }
@ -304,7 +308,7 @@ public class BinaryRoutePlanner {
final RouteDataObject road = segment.road; final RouteDataObject road = segment.road;
boolean initDirectionAllowed = checkIfInitialMovementAllowedOnSegment(ctx, reverseWaySearch, visitedSegments, segment, road); boolean initDirectionAllowed = checkIfInitialMovementAllowedOnSegment(ctx, reverseWaySearch, visitedSegments, segment, road);
if(TEST_SPECIFIC && road.getId() == TEST_ID ) { if(TEST_SPECIFIC && road.getId() == TEST_ID ) {
printRoad(" ! " + +segment.distanceFromStart + " ", segment); printRoad(" ! " + +segment.distanceFromStart + " ", segment, reverseWaySearch);
} }
boolean directionAllowed = initDirectionAllowed; boolean directionAllowed = initDirectionAllowed;
if(!directionAllowed) { if(!directionAllowed) {
@ -367,15 +371,23 @@ public class BinaryRoutePlanner {
// long nt = System.nanoTime(); // long nt = System.nanoTime();
// float devDistance = ctx.precalculatedRouteDirection.getDeviationDistance(x, y); // float devDistance = ctx.precalculatedRouteDirection.getDeviationDistance(x, y);
// // 1. linear method // // 1. linear method
// // segmentDist = segmentDist * (1 + ctx.precalculatedRouteDirection.getDeviationDistance(x, y) / ctx.config.DEVIATION_RADIUS); // segmentDist = segmentDist * (1 + devDistance / ctx.config.DEVIATION_RADIUS);
// // 2. exponential method // // 2. exponential method
// segmentDist = segmentDist * (float) Math.pow(1.5, devDistance / 500); // segmentDist = segmentDist * (float) Math.pow(1.5, devDistance / 500);
// 3. next by method
// segmentDist = devDistance ;
// ctx.timeNanoToCalcDeviation += (System.nanoTime() - nt); // ctx.timeNanoToCalcDeviation += (System.nanoTime() - nt);
} }
// could be expensive calculation // could be expensive calculation
// 3. get intersected ways // 3. get intersected ways
final RouteSegment roadNext = ctx.loadRouteSegment(x, y, ctx.config.memoryLimitation - ctx.memoryOverhead); final RouteSegment roadNext = ctx.loadRouteSegment(x, y, ctx.config.memoryLimitation - ctx.memoryOverhead);
float distStartObstacles = segment.distanceFromStart + calculateTimeWithObstacles(ctx, road, segmentDist , obstaclesTime); float distStartObstacles = segment.distanceFromStart + calculateTimeWithObstacles(ctx, road, segmentDist , obstaclesTime);
if(ctx.precalculatedRouteDirection != null && ctx.precalculatedRouteDirection.isFollowNext()) {
// reset to f
// distStartObstacles = 0;
// more precise but slower
distStartObstacles = ctx.precalculatedRouteDirection.getDeviationDistance(x, y) / ctx.getRouter().getMaxDefaultSpeed();
}
// We don't check if there are outgoing connections // We don't check if there are outgoing connections
previous = processIntersections(ctx, graphSegments, visitedSegments, distStartObstacles, previous = processIntersections(ctx, graphSegments, visitedSegments, distStartObstacles,
@ -433,7 +445,7 @@ public class BinaryRoutePlanner {
frs.opposite = opposite; frs.opposite = opposite;
graphSegments.add(frs); graphSegments.add(frs);
if(TRACE_ROUTING){ if(TRACE_ROUTING){
printRoad(" >> Final segment : ", frs); printRoad(" >> Final segment : ", frs, reverseWaySearch);
} }
return true; return true;
} }
@ -553,7 +565,7 @@ public class BinaryRoutePlanner {
private RouteSegment processIntersections(RoutingContext ctx, PriorityQueue<RouteSegment> graphSegments, private RouteSegment processIntersections(RoutingContext ctx, PriorityQueue<RouteSegment> graphSegments,
TLongObjectHashMap<RouteSegment> visitedSegments, float distFromStart, RouteSegment segment, TLongObjectHashMap<RouteSegment> visitedSegments, float distFromStart, RouteSegment segment,
short segmentPoint, RouteSegment inputNext, boolean reverseWaySearch, boolean doNotAddIntersections, short segmentPoint, RouteSegment inputNext, boolean reverseWaySearch, boolean doNotAddIntersections,
boolean[] processFurther) { boolean[] processFurther) {
boolean thereAreRestrictions ; boolean thereAreRestrictions ;
@ -634,7 +646,7 @@ public class BinaryRoutePlanner {
printRoad(" !? distFromStart=" + +distFromStart + " from " + segment.getRoad().getId() + printRoad(" !? distFromStart=" + +distFromStart + " from " + segment.getRoad().getId() +
" dir=" + segment.getDirectionAssigned() + " dir=" + segment.getDirectionAssigned() +
" distToEnd=" + distanceToEnd + " distToEnd=" + distanceToEnd +
" segmentPoint="+ segmentPoint + " -- ", next); " segmentPoint="+ segmentPoint + " -- ", next, true);
} }
if (!visitedSegments.containsKey(calculateRoutePointId(next, next.isPositive()))) { if (!visitedSegments.containsKey(calculateRoutePointId(next, next.isPositive()))) {
if (next.getParentRoute() == null if (next.getParentRoute() == null
@ -643,7 +655,7 @@ public class BinaryRoutePlanner {
next.distanceFromStart = distFromStart; next.distanceFromStart = distFromStart;
next.distanceToEnd = distanceToEnd; next.distanceToEnd = distanceToEnd;
if (TRACE_ROUTING) { if (TRACE_ROUTING) {
printRoad(" >>", next); printRoad(" >>", next, null);
} }
// put additional information to recover whole route after // put additional information to recover whole route after
next.setParentRoute(segment); next.setParentRoute(segment);

View file

@ -6,6 +6,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import net.osmand.binary.RouteDataObject; import net.osmand.binary.RouteDataObject;
import net.osmand.data.LatLon;
import net.osmand.data.QuadPoint; import net.osmand.data.QuadPoint;
import net.osmand.data.QuadRect; import net.osmand.data.QuadRect;
import net.osmand.data.QuadTree; import net.osmand.data.QuadTree;
@ -18,6 +19,7 @@ public class PrecalculatedRouteDirection {
private float minSpeed; private float minSpeed;
private float maxSpeed; private float maxSpeed;
private float[] tms; private float[] tms;
private boolean followNext;
private static final int SHIFT = (1 << (31 - 17)); private static final int SHIFT = (1 << (31 - 17));
private static final int[] SHIFTS = new int[]{1 << (31 - 15), 1 << (31 - 13), 1 << (31 - 12), private static final int[] SHIFTS = new int[]{1 << (31 - 15), 1 << (31 - 13), 1 << (31 - 12),
1 << (31 - 11), 1 << (31 - 7)}; 1 << (31 - 11), 1 << (31 - 7)};
@ -42,6 +44,11 @@ public class PrecalculatedRouteDirection {
init(ls); init(ls);
} }
private PrecalculatedRouteDirection(LatLon[] ls, float maxSpeed) {
this.maxSpeed = maxSpeed;
init(ls);
}
private PrecalculatedRouteDirection(PrecalculatedRouteDirection parent, int s1, int s2) { private PrecalculatedRouteDirection(PrecalculatedRouteDirection parent, int s1, int s2) {
this.minSpeed = parent.minSpeed; this.minSpeed = parent.minSpeed;
this.maxSpeed = parent.maxSpeed; this.maxSpeed = parent.maxSpeed;
@ -81,6 +88,10 @@ public class PrecalculatedRouteDirection {
return null; return null;
} }
public static PrecalculatedRouteDirection build(LatLon[] ls, float maxSpeed){
return new PrecalculatedRouteDirection(ls, maxSpeed);
}
private void init(List<RouteSegmentResult> ls) { private void init(List<RouteSegmentResult> ls) {
TIntArrayList px = new TIntArrayList(); TIntArrayList px = new TIntArrayList();
@ -105,6 +116,19 @@ public class PrecalculatedRouteDirection {
init(px, py, speedSegments); init(px, py, speedSegments);
} }
private void init(LatLon[] ls) {
TIntArrayList px = new TIntArrayList();
TIntArrayList py = new TIntArrayList();
List<Float> speedSegments = new ArrayList<Float>();
for (LatLon s : ls) {
float routeSpd = maxSpeed; // (s.getDistance() / s.getRoutingTime())
px.add(MapUtils.get31TileNumberX(s.getLongitude()));
py.add(MapUtils.get31TileNumberY(s.getLatitude()));
speedSegments.add(routeSpd);
}
init(px, py, speedSegments);
}
private void init(TIntArrayList px, TIntArrayList py, List<Float> speedSegments) { private void init(TIntArrayList px, TIntArrayList py, List<Float> speedSegments) {
float totaltm = 0; float totaltm = 0;
List<Float> times = new ArrayList<Float>(); List<Float> times = new ArrayList<Float>();
@ -217,6 +241,15 @@ public class PrecalculatedRouteDirection {
return ((long) x31) << 32l + ((long)y31); return ((long) x31) << 32l + ((long)y31);
} }
public void setFollowNext(boolean followNext) {
this.followNext = followNext;
}
public boolean isFollowNext() {
return followNext;
}
public PrecalculatedRouteDirection adopt(RoutingContext ctx) { public PrecalculatedRouteDirection adopt(RoutingContext ctx) {
int ind1 = getIndex(ctx.startX, ctx.startY); int ind1 = getIndex(ctx.startX, ctx.startY);
int ind2 = getIndex(ctx.targetX, ctx.targetY); int ind2 = getIndex(ctx.targetX, ctx.targetY);
@ -235,6 +268,7 @@ public class PrecalculatedRouteDirection {
// routeDirection.startY31 = ctx.startY; // routeDirection.startY31 = ctx.startY;
routeDirection.endPoint = calc(ctx.targetX, ctx.targetX); routeDirection.endPoint = calc(ctx.targetX, ctx.targetX);
routeDirection.endFinishTime = (float) BinaryRoutePlanner.squareRootDist(pointsX[ind2], pointsY[ind2], ctx.targetX, ctx.targetY) / maxSpeed; routeDirection.endFinishTime = (float) BinaryRoutePlanner.squareRootDist(pointsX[ind2], pointsY[ind2], ctx.targetX, ctx.targetY) / maxSpeed;
routeDirection.followNext = followNext;
// routeDirection.endX31 = ctx.targetX; // routeDirection.endX31 = ctx.targetX;
// routeDirection.endY31 = ctx.targetY; // routeDirection.endY31 = ctx.targetY;

View file

@ -84,13 +84,17 @@ public class RoutePlannerFrontEnd {
} }
public List<RouteSegmentResult> searchRoute(final RoutingContext ctx, LatLon start, LatLon end, List<LatLon> intermediates) throws IOException, InterruptedException { public List<RouteSegmentResult> searchRoute(final RoutingContext ctx, LatLon start, LatLon end, List<LatLon> intermediates) throws IOException, InterruptedException {
return searchRoute(ctx, start, end, intermediates, null);
}
public List<RouteSegmentResult> searchRoute(final RoutingContext ctx, LatLon start, LatLon end, List<LatLon> intermediates,
PrecalculatedRouteDirection routeDirection) throws IOException, InterruptedException {
if(ctx.calculationProgress == null) { if(ctx.calculationProgress == null) {
ctx.calculationProgress = new RouteCalculationProgress(); ctx.calculationProgress = new RouteCalculationProgress();
} }
boolean intermediatesEmpty = intermediates == null || intermediates.isEmpty(); boolean intermediatesEmpty = intermediates == null || intermediates.isEmpty();
PrecalculatedRouteDirection routeDirection = null;
double maxDistance = MapUtils.getDistance(start, end); double maxDistance = MapUtils.getDistance(start, end);
if(!intermediatesEmpty) { if(!intermediatesEmpty) {
LatLon b = start; LatLon b = start;
@ -99,7 +103,8 @@ public class RoutePlannerFrontEnd {
b = l; b = l;
} }
} }
if(ctx.calculationMode == RouteCalculationMode.COMPLEX && maxDistance > Math.max(ctx.config.DEVIATION_RADIUS * 4, 30000)) { if(ctx.calculationMode == RouteCalculationMode.COMPLEX && routeDirection == null
&& maxDistance > Math.max(ctx.config.DEVIATION_RADIUS * 4, 30000)) {
RoutingContext nctx = buildRoutingContext(ctx.config, ctx.nativeLib, ctx.getMaps(), RouteCalculationMode.BASE); RoutingContext nctx = buildRoutingContext(ctx.config, ctx.nativeLib, ctx.getMaps(), RouteCalculationMode.BASE);
nctx.calculationProgress = ctx.calculationProgress ; nctx.calculationProgress = ctx.calculationProgress ;
List<RouteSegmentResult> ls = searchRoute(nctx, start, end, intermediates); List<RouteSegmentResult> ls = searchRoute(nctx, start, end, intermediates);

View file

@ -9,6 +9,7 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import net.osmand.binary.BinaryMapIndexReader; import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteTypeRule;
import net.osmand.binary.RouteDataObject; import net.osmand.binary.RouteDataObject;
import net.osmand.data.LatLon; import net.osmand.data.LatLon;
import net.osmand.router.BinaryRoutePlanner.FinalRouteSegment; import net.osmand.router.BinaryRoutePlanner.FinalRouteSegment;
@ -295,11 +296,30 @@ public class RouteResultPreparation {
additional.append("description = \"").append(res.getDescription()).append("\" "); additional.append("description = \"").append(res.getDescription()).append("\" ");
println(MessageFormat.format("\t<segment id=\"{0}\" start=\"{1}\" end=\"{2}\" {3}/>", (res.getObject().getId()) + "", println(MessageFormat.format("\t<segment id=\"{0}\" start=\"{1}\" end=\"{2}\" {3}/>", (res.getObject().getId()) + "",
res.getStartPointIndex() + "", res.getEndPointIndex() + "", additional.toString())); res.getStartPointIndex() + "", res.getEndPointIndex() + "", additional.toString()));
printAdditionalPointInfo(res);
} }
} }
println("</test>"); println("</test>");
} }
private void printAdditionalPointInfo(RouteSegmentResult res) {
boolean plus = res.getStartPointIndex() < res.getEndPointIndex();
for(int k = res.getStartPointIndex(); k != res.getEndPointIndex(); ) {
int[] tp = res.getObject().getPointTypes(k);
if(tp != null) {
for(int t = 0; t < tp.length; t++) {
RouteTypeRule rr = res.getObject().region.quickGetEncodingRule(tp[t]);
println("\t<point tag=\""+rr.getTag()+"\"" + " value=\""+rr.getValue()+"\"/>");
}
}
if(plus) {
k++;
} else {
k--;
}
}
}
private void addTurnInfo(boolean leftside, List<RouteSegmentResult> result) { private void addTurnInfo(boolean leftside, List<RouteSegmentResult> result) {
int prevSegment = -1; int prevSegment = -1;

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 528 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 649 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -9,6 +9,15 @@
3. All your modified/created strings are in the top of the file (to make easier find what\'s translated). 3. All your modified/created strings are in the top of the file (to make easier find what\'s translated).
PLEASE: Have a look at http://code.google.com/p/osmand/wiki/UIConsistency, it may really improve your and our work :-) Thx - Hardy PLEASE: Have a look at http://code.google.com/p/osmand/wiki/UIConsistency, it may really improve your and our work :-) Thx - Hardy
--> -->
<string name="android_19_location_disabled">Since KitKat version you can\'t download and update map in previous storage location (%s). Do you want to change to allowed one and copy all files there?
\n Note : old files will stay untouched.
\n Note : it will not be possible to share files between OsmAnd and OsmAnd+. </string>
<string name="application_dir_change_warning2">OsmAnd could try to move the data to new destination. Do you want it?</string>
<string name="copying_osmand_one_file_descr">Copying file (%s) to new destination...</string>
<string name="copying_osmand_files_descr">Copying OsmAnd files to new destination (%s)</string>
<string name="copying_osmand_files">Copying OsmAnd files</string>
<string name="calculate_osmand_route_gpx">Calculate OsmAnd offline route</string>
<string name="app_mode_truck">Truck</string>
<string name="guidance_preferences_descr">Navigation preferences</string> <string name="guidance_preferences_descr">Navigation preferences</string>
<string name="routing_preferences_descr">Routing preferences</string> <string name="routing_preferences_descr">Routing preferences</string>
<string name="speech_rate_descr">Specify speech rate for TTS</string> <string name="speech_rate_descr">Specify speech rate for TTS</string>
@ -1250,7 +1259,7 @@ Afghanistan, Albania, Algeria, Andorra, Angola, Anguilla, Antigua and Barbuda, A
<string name="osmand_routing_experimental">OsmAnd offline navigation is an experimental feature and it does not work for distances of more than about 20 km.\n\nNavigation service is temporarily switched to online CloudMade.</string> <string name="osmand_routing_experimental">OsmAnd offline navigation is an experimental feature and it does not work for distances of more than about 20 km.\n\nNavigation service is temporarily switched to online CloudMade.</string>
<string name="specified_dir_doesnt_exist">Can not find specified directory.</string> <string name="specified_dir_doesnt_exist">Can not find specified directory.</string>
<string name="application_dir">Storage directory</string> <string name="application_dir">Storage directory</string>
<string name="application_dir_change_warning">Changing the storage directory will not move or delete the data. This must be performed separately and outside OsmAnd. Continue anyway?</string>
<string name="osmand_net_previously_installed">A previous OsmAnd version is installed. All offline data will be supported by the new application. But Favorite points should be exported in the old application and later imported by the new one.</string> <string name="osmand_net_previously_installed">A previous OsmAnd version is installed. All offline data will be supported by the new application. But Favorite points should be exported in the old application and later imported by the new one.</string>
<string name="build_installed">Build {0} successfully installed ({1}).</string> <string name="build_installed">Build {0} successfully installed ({1}).</string>
<string name="downloading_build">Downloading build&#8230;</string> <string name="downloading_build">Downloading build&#8230;</string>

View file

@ -45,6 +45,10 @@ public class ApplicationMode {
carLocation().parent(CAR). carLocation().parent(CAR).
icon(R.drawable.ic_motorcycle, R.drawable.ic_action_motorcycle_light, R.drawable.ic_action_motorcycle_dark).reg(); icon(R.drawable.ic_motorcycle, R.drawable.ic_action_motorcycle_light, R.drawable.ic_action_motorcycle_dark).reg();
public static final ApplicationMode TRUCK = create(R.string.app_mode_truck, "truck").speed(15.3f, 40).
carLocation().parent(CAR).
icon(R.drawable.ic_truck, R.drawable.ic_action_truck_light, R.drawable.ic_action_truck_dark).reg();
static { static {
ApplicationMode[] exceptPedestrian = new ApplicationMode[] { DEFAULT, CAR, BICYCLE, BOAT, AIRCRAFT }; ApplicationMode[] exceptPedestrian = new ApplicationMode[] { DEFAULT, CAR, BICYCLE, BOAT, AIRCRAFT };
ApplicationMode[] exceptAirBoat = new ApplicationMode[] { DEFAULT, CAR, BICYCLE}; ApplicationMode[] exceptAirBoat = new ApplicationMode[] { DEFAULT, CAR, BICYCLE};

View file

@ -8,8 +8,8 @@ import net.osmand.CallbackWithObject;
import net.osmand.Location; import net.osmand.Location;
import net.osmand.access.AccessibleToast; import net.osmand.access.AccessibleToast;
import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.routing.RouteProvider;
import net.osmand.plus.routing.RouteProvider.GPXRouteParams; import net.osmand.plus.routing.RouteProvider.GPXRouteParams;
import net.osmand.plus.routing.RouteProvider.GPXRouteParams.GPXRouteParamsBuilder;
import net.osmand.plus.routing.RoutingHelper; import net.osmand.plus.routing.RoutingHelper;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.AlertDialog.Builder; import android.app.AlertDialog.Builder;
@ -72,8 +72,11 @@ public class OsmAndLocationSimulation {
new CallbackWithObject<GPXUtilities.GPXFile>() { new CallbackWithObject<GPXUtilities.GPXFile>() {
@Override @Override
public boolean processResult(GPXUtilities.GPXFile result) { public boolean processResult(GPXUtilities.GPXFile result) {
GPXRouteParams prms = new RouteProvider.GPXRouteParams(result, false, ch GPXRouteParamsBuilder builder = GPXRouteParams.GPXRouteParamsBuilder.newBuilder(result, app.getSettings());
.isChecked(), app.getSettings()); if(ch.isChecked()){
builder.announceWaypoints();
}
GPXRouteParams prms = builder.build();
startAnimationThread(app.getRoutingHelper(), ma, prms.getPoints(), true, startAnimationThread(app.getRoutingHelper(), ma, prms.getPoints(), true,
speedup.getProgress() + 1); speedup.getProgress() + 1);
return true; return true;

View file

@ -651,7 +651,7 @@ public class OsmandSettings {
public float getSettingsZoomScale(float density){ public float getSettingsZoomScale(float density){
// by default scale between [0, 1[ density (because of lots map complains) // by default scale between [0, 1[ density (because of lots map complains)
return MAP_ZOOM_SCALE_BY_DENSITY.get() + (float)Math.min(Math.sqrt(Math.max(0, density - 1)), 1); return MAP_ZOOM_SCALE_BY_DENSITY.get() + (float)Math.sqrt(Math.max(0, density - 1));
} }
@ -739,6 +739,7 @@ public class OsmandSettings {
public final OsmandPreference<Boolean> SPEAK_SPEED_LIMIT = new BooleanPreference("speak_speed_limit", true).makeProfile().cache(); public final OsmandPreference<Boolean> SPEAK_SPEED_LIMIT = new BooleanPreference("speak_speed_limit", true).makeProfile().cache();
public final OsmandPreference<Boolean> SPEAK_GPX_WPT = new BooleanPreference("speak_gpx_wpt", true).makeGlobal().cache(); public final OsmandPreference<Boolean> SPEAK_GPX_WPT = new BooleanPreference("speak_gpx_wpt", true).makeGlobal().cache();
public final OsmandPreference<Boolean> CALC_GPX_ROUTE = new BooleanPreference("calc_gpx_route", false).makeGlobal().cache();
@ -992,8 +993,22 @@ public class OsmandSettings {
public static final String EXTERNAL_STORAGE_DIR = "external_storage_dir"; //$NON-NLS-1$ public static final String EXTERNAL_STORAGE_DIR = "external_storage_dir"; //$NON-NLS-1$
public File getExternalStorageDirectory() { public File getExternalStorageDirectory() {
return new File(settingsAPI.getString(globalPreferences,EXTERNAL_STORAGE_DIR, String defaultLocation = ctx.getExternalServiceAPI().getExternalStorageDirectory();
ctx.getExternalServiceAPI().getExternalStorageDirectory())); if(Build.VERSION.SDK_INT >= VERSION_DEFAULTLOCATION_CHANGED && !new File(defaultLocation, IndexConstants.APP_DIR).exists()) {
defaultLocation += "/Android/data/" + ctx.getPackageName();
}
return new File(settingsAPI.getString(globalPreferences, EXTERNAL_STORAGE_DIR,
defaultLocation));
}
public static final int VERSION_DEFAULTLOCATION_CHANGED = 19;
public String getDefaultExternalStorageLocation() {
String defaultLocation = ctx.getExternalServiceAPI().getExternalStorageDirectory();
if(Build.VERSION.SDK_INT >= VERSION_DEFAULTLOCATION_CHANGED) {
defaultLocation += "/Android/data/" + ctx.getPackageName();
}
return defaultLocation;
} }
public boolean setExternalStorageDirectory(String externalStorageDir) { public boolean setExternalStorageDirectory(String externalStorageDir) {

View file

@ -11,6 +11,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TimeZone; import java.util.TimeZone;
import net.osmand.IndexConstants;
import net.osmand.access.AccessibleAlertBuilder;
import net.osmand.access.AccessibleToast; import net.osmand.access.AccessibleToast;
import net.osmand.plus.ClientContext; import net.osmand.plus.ClientContext;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
@ -18,6 +20,7 @@ import net.osmand.plus.OsmandPlugin;
import net.osmand.plus.OsmandSettings; import net.osmand.plus.OsmandSettings;
import net.osmand.plus.R; import net.osmand.plus.R;
import net.osmand.plus.Version; import net.osmand.plus.Version;
import net.osmand.plus.activities.SettingsGeneralActivity.MoveFilesToDifferentDirectory;
import net.osmand.plus.base.BasicProgressAsyncTask; import net.osmand.plus.base.BasicProgressAsyncTask;
import net.osmand.plus.base.SuggestExternalDirectoryDialog; import net.osmand.plus.base.SuggestExternalDirectoryDialog;
import net.osmand.plus.download.DownloadActivityType; import net.osmand.plus.download.DownloadActivityType;
@ -36,6 +39,7 @@ import android.content.DialogInterface.OnClickListener;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask.Status; import android.os.AsyncTask.Status;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
@ -89,6 +93,7 @@ public class DownloadIndexActivity extends OsmandExpandableListActivity {
private TextView progressPercent; private TextView progressPercent;
private ImageView cancel; private ImageView cancel;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -162,7 +167,10 @@ public class DownloadIndexActivity extends OsmandExpandableListActivity {
DownloadIndexAdapter adapter = new DownloadIndexAdapter(this, list); DownloadIndexAdapter adapter = new DownloadIndexAdapter(this, list);
setListAdapter(adapter); setListAdapter(adapter);
if(getMyApplication().getResourceManager().getIndexFileNames().isEmpty()) { if(getMyApplication().getResourceManager().getIndexFileNames().isEmpty()) {
boolean showedDialog = SuggestExternalDirectoryDialog.showDialog(this, null, null); boolean showedDialog = false;
if(Build.VERSION.SDK_INT < OsmandSettings.VERSION_DEFAULTLOCATION_CHANGED) {
SuggestExternalDirectoryDialog.showDialog(this, null, null);
}
if(!showedDialog) { if(!showedDialog) {
showDialogOfFreeDownloadsIfNeeded(); showDialogOfFreeDownloadsIfNeeded();
} }
@ -182,7 +190,39 @@ public class DownloadIndexActivity extends OsmandExpandableListActivity {
return true; return true;
} }
}); });
if(Build.VERSION.SDK_INT >= OsmandSettings.VERSION_DEFAULTLOCATION_CHANGED) {
if(!settings.getExternalStorageDirectory().getAbsolutePath().equals(settings.getDefaultExternalStorageLocation())) {
AccessibleAlertBuilder ab = new AccessibleAlertBuilder(this);
ab.setMessage(getString(R.string.android_19_location_disabled, settings.getExternalStorageDirectory()));
ab.setPositiveButton(R.string.default_buttons_yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
copyFilesForAndroid19();
}
});
ab.setNegativeButton(R.string.default_buttons_cancel, null);
ab.show();
}
}
}
private void copyFilesForAndroid19() {
final String newLoc = settings.getDefaultExternalStorageLocation();
MoveFilesToDifferentDirectory task =
new MoveFilesToDifferentDirectory(DownloadIndexActivity.this,
new File(settings.getExternalStorageDirectory(), IndexConstants.APP_DIR),
new File(newLoc, IndexConstants.APP_DIR)) {
protected Boolean doInBackground(Void[] params) {
Boolean result = super.doInBackground(params);
if(result) {
settings.setExternalStorageDirectory(newLoc);
getMyApplication().getResourceManager().resetStoreDirectory();
getMyApplication().getResourceManager().reloadIndexes(progress) ;
}
return result;
};
};
task.execute();
} }
@Override @Override

View file

@ -367,10 +367,10 @@ public class MapActivity extends AccessibleActivity {
public void changeZoom(int stp){ public void changeZoom(int stp){
// delta = Math.round(delta * OsmandMapTileView.ZOOM_DELTA) * OsmandMapTileView.ZOOM_DELTA_1; // delta = Math.round(delta * OsmandMapTileView.ZOOM_DELTA) * OsmandMapTileView.ZOOM_DELTA_1;
boolean changeLocation = true; boolean changeLocation = false;
if (settings.AUTO_ZOOM_MAP.get() == AutoZoomMap.NONE) { // if (settings.AUTO_ZOOM_MAP.get() == AutoZoomMap.NONE) {
changeLocation = false; // changeLocation = false;
} // }
final int newZoom = mapView.getZoom() + stp; final int newZoom = mapView.getZoom() + stp;
mapView.getAnimatedDraggingThread().startZooming(newZoom, changeLocation); mapView.getAnimatedDraggingThread().startZooming(newZoom, changeLocation);
if (app.accessibilityEnabled()) if (app.accessibilityEnabled())

View file

@ -4,30 +4,27 @@ package net.osmand.plus.activities;
import java.io.File; import java.io.File;
import java.util.Date; import java.util.Date;
import android.content.SharedPreferences;
import android.widget.ScrollView;
import net.osmand.IndexConstants; import net.osmand.IndexConstants;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.OsmandPlugin; import net.osmand.plus.OsmandPlugin;
import net.osmand.plus.R; import net.osmand.plus.R;
import net.osmand.plus.Version; import net.osmand.plus.Version;
import net.osmand.plus.rastermaps.SettingsRasterMapsActivity;
import net.osmand.util.Algorithms;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.AlertDialog.Builder; import android.app.AlertDialog.Builder;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Bundle; import android.os.Bundle;
import android.preference.Preference; import android.preference.Preference;
import android.preference.PreferenceScreen; import android.preference.PreferenceScreen;
import android.preference.Preference.OnPreferenceClickListener;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan; import android.text.style.ClickableSpan;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.View; import android.view.View;
import android.widget.ScrollView;
import android.widget.TextView; import android.widget.TextView;
public class SettingsActivity extends SettingsBaseActivity { public class SettingsActivity extends SettingsBaseActivity {

View file

@ -2,23 +2,32 @@ package net.osmand.plus.activities;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List; import java.util.List;
import net.osmand.CallbackWithObject;
import net.osmand.IProgress; import net.osmand.IProgress;
import net.osmand.IndexConstants;
import net.osmand.access.AccessibleToast; import net.osmand.access.AccessibleToast;
import net.osmand.plus.ApplicationMode; import net.osmand.plus.ApplicationMode;
import net.osmand.plus.ClientContext; import net.osmand.plus.ClientContext;
import net.osmand.plus.OsmandSettings; import net.osmand.plus.OsmandSettings;
import net.osmand.plus.OsmandSettings.DrivingRegion; import net.osmand.plus.OsmandSettings.DrivingRegion;
import net.osmand.plus.OsmandSettings.MetricsConstants; import net.osmand.plus.OsmandSettings.MetricsConstants;
import net.osmand.plus.ProgressDialogImplementation;
import net.osmand.plus.R; import net.osmand.plus.R;
import net.osmand.plus.Version; import net.osmand.plus.Version;
import net.osmand.plus.base.SuggestExternalDirectoryDialog; import net.osmand.plus.base.SuggestExternalDirectoryDialog;
import net.osmand.plus.render.NativeOsmandLibrary; import net.osmand.plus.render.NativeOsmandLibrary;
import net.osmand.plus.voice.CommandPlayer; import net.osmand.plus.voice.CommandPlayer;
import net.osmand.render.RenderingRulesStorage; import net.osmand.render.RenderingRulesStorage;
import net.osmand.util.Algorithms;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.AlertDialog.Builder; import android.app.AlertDialog.Builder;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener; import android.content.DialogInterface.OnClickListener;
import android.content.Intent; import android.content.Intent;
@ -101,12 +110,12 @@ public class SettingsGeneralActivity extends SettingsBaseActivity {
dialog.dismiss(); dialog.dismiss();
showOtherDialog(); showOtherDialog();
} }
}, new Runnable() { }, new CallbackWithObject<String>() {
@Override @Override
public void run() { public boolean processResult(String result) {
Toast.makeText(SettingsGeneralActivity.this, getString(R.string.application_dir_change_warning), warnAboutChangingStorage(result);
Toast.LENGTH_LONG).show(); return true;
reloadIndexes();
} }
}); });
return false; return false;
@ -265,6 +274,97 @@ public class SettingsGeneralActivity extends SettingsBaseActivity {
return true; return true;
} }
public static class MoveFilesToDifferentDirectory extends AsyncTask<Void, Void, Boolean> {
private File to;
private Context ctx;
private File from;
protected ProgressDialogImplementation progress;
private Runnable runOnSuccess;
public MoveFilesToDifferentDirectory(Context ctx, File from, File to) {
this.ctx = ctx;
this.from = from;
this.to = to;
}
public void setRunOnSuccess(Runnable runOnSuccess) {
this.runOnSuccess = runOnSuccess;
}
@Override
protected void onPreExecute() {
progress = ProgressDialogImplementation.createProgressDialog(
ctx, ctx.getString(R.string.copying_osmand_files),
ctx.getString(R.string.copying_osmand_files_descr, to.getPath()),
ProgressDialog.STYLE_HORIZONTAL);
}
@Override
protected void onPostExecute(Boolean result) {
if(result != null && result.booleanValue() && runOnSuccess != null) {
runOnSuccess.run();
}
if(progress.getDialog().isShowing()) {
progress.getDialog().dismiss();
}
}
private void movingFiles(File f, File t, int depth) throws IOException {
if(depth <= 2) {
progress.startTask(ctx.getString(R.string.copying_osmand_one_file_descr, t.getName()), -1);
}
if (f.isDirectory()) {
t.mkdirs();
File[] lf = f.listFiles();
if (lf != null) {
for (int i = 0; i < lf.length; i++) {
if (lf[i] != null) {
movingFiles(lf[i], new File(t, lf[i].getName()), depth + 1);
}
}
}
f.delete();
} else if (f.isFile()) {
if(t.exists()) {
Algorithms.removeAllFiles(t);
}
boolean rnm = false;
try {
rnm = f.renameTo(t);
} catch(RuntimeException e) {
}
if (!rnm) {
FileInputStream fin = new FileInputStream(f);
FileOutputStream fout = new FileOutputStream(t);
try {
Algorithms.streamCopy(fin, fout);
} finally {
fin.close();
fout.close();
}
f.delete();
}
}
if(depth <= 2) {
progress.finishTask();
}
}
@Override
protected Boolean doInBackground(Void... params) {
to.mkdirs();
try {
movingFiles(from, to, 0);
} catch (IOException e) {
Toast.makeText(ctx, R.string.input_output_error, Toast.LENGTH_LONG);
return false;
}
return true;
}
}
private void warnAboutChangingStorage(final String newValue) { private void warnAboutChangingStorage(final String newValue) {
final String newDir = newValue != null ? newValue.trim() : newValue; final String newDir = newValue != null ? newValue.trim() : newValue;
File path = new File(newDir); File path = new File(newDir);
@ -273,23 +373,43 @@ public class SettingsGeneralActivity extends SettingsBaseActivity {
AccessibleToast.makeText(this, R.string.specified_dir_doesnt_exist, Toast.LENGTH_LONG).show(); AccessibleToast.makeText(this, R.string.specified_dir_doesnt_exist, Toast.LENGTH_LONG).show();
return; return;
} }
Builder builder = new AlertDialog.Builder(this); Builder builder = new AlertDialog.Builder(this);
builder.setMessage(getString(R.string.application_dir_change_warning)); builder.setMessage(getString(R.string.application_dir_change_warning2));
builder.setPositiveButton(R.string.default_buttons_yes, new OnClickListener() { builder.setPositiveButton(R.string.default_buttons_yes, new OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
// edit the preference MoveFilesToDifferentDirectory task =
settings.setExternalStorageDirectory(newDir); new MoveFilesToDifferentDirectory(SettingsGeneralActivity.this,
getMyApplication().getResourceManager().resetStoreDirectory(); new File(settings.getExternalStorageDirectory(), IndexConstants.APP_DIR), new File(newDir,
reloadIndexes(); IndexConstants.APP_DIR));
updateApplicationDirTextAndSummary(); task.setRunOnSuccess(new Runnable() {
@Override
public void run() {
updateSettingsToNewDir(newDir);
}
});
task.execute();
}
});
builder.setNeutralButton(R.string.default_buttons_no, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
updateSettingsToNewDir(newDir);
} }
}); });
builder.setNegativeButton(R.string.default_buttons_cancel, null); builder.setNegativeButton(R.string.default_buttons_cancel, null);
builder.show(); builder.show();
} }
private void updateSettingsToNewDir(final String newDir) {
// edit the preference
settings.setExternalStorageDirectory(newDir);
getMyApplication().getResourceManager().resetStoreDirectory();
reloadIndexes();
updateApplicationDirTextAndSummary();
}
public void reloadIndexes() { public void reloadIndexes() {
setSupportProgressBarIndeterminateVisibility(true); setSupportProgressBarIndeterminateVisibility(true);
final CharSequence oldTitle = getSupportActionBar().getTitle(); final CharSequence oldTitle = getSupportActionBar().getTitle();

View file

@ -233,7 +233,7 @@ public class SettingsNavigationActivity extends SettingsBaseActivity {
if (MORE_VALUE.equals(newValue)) { if (MORE_VALUE.equals(newValue)) {
// listPref.set(oldValue); // revert the change.. // listPref.set(oldValue); // revert the change..
final Intent intent = new Intent(this, DownloadIndexActivity.class); final Intent intent = new Intent(this, DownloadIndexActivity.class);
intent.putExtra(DownloadIndexActivity.FILTER_KEY, "voice"); intent.putExtra(DownloadIndexActivity.FILTER_KEY, getString(R.string.voice));
startActivity(intent); startActivity(intent);
} else { } else {
super.onPreferenceChange(preference, newValue); super.onPreferenceChange(preference, newValue);

View file

@ -19,6 +19,7 @@ import net.osmand.plus.TargetPointsHelper;
import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.routing.RouteProvider.GPXRouteParams; import net.osmand.plus.routing.RouteProvider.GPXRouteParams;
import net.osmand.plus.routing.RouteProvider.RouteService; import net.osmand.plus.routing.RouteProvider.RouteService;
import net.osmand.plus.routing.RouteProvider.GPXRouteParams.GPXRouteParamsBuilder;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.AlertDialog.Builder; import android.app.AlertDialog.Builder;
@ -66,10 +67,11 @@ public class NavigateAction {
public boolean navigateUsingGPX(final ApplicationMode appMode, final LatLon endForRouting, public boolean navigateUsingGPX(final ApplicationMode appMode, final LatLon endForRouting,
final GPXFile result) { final GPXFile result) {
Builder builder = new AlertDialog.Builder(mapActivity); Builder builder = new AlertDialog.Builder(mapActivity);
final boolean[] props = new boolean[]{false, false, false, settings.SPEAK_GPX_WPT.get()}; final boolean[] props = new boolean[]{false, false, false, settings.SPEAK_GPX_WPT.get(), settings.CALC_GPX_ROUTE.get()};
builder.setMultiChoiceItems(new String[] { getString(R.string.gpx_option_reverse_route), builder.setMultiChoiceItems(new String[] { getString(R.string.gpx_option_reverse_route),
getString(R.string.gpx_option_destination_point), getString(R.string.gpx_option_from_start_point), getString(R.string.gpx_option_destination_point), getString(R.string.gpx_option_from_start_point),
getString(R.string.announce_gpx_waypoints) }, props, getString(R.string.announce_gpx_waypoints),
getString(R.string.calculate_osmand_route_gpx)}, props,
new OnMultiChoiceClickListener() { new OnMultiChoiceClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) { public void onClick(DialogInterface dialog, int which, boolean isChecked) {
@ -83,9 +85,20 @@ public class NavigateAction {
boolean passWholeWay = props[2]; boolean passWholeWay = props[2];
boolean useDestination = props[1]; boolean useDestination = props[1];
boolean announceGpxWpt = props[3]; boolean announceGpxWpt = props[3];
boolean calculateOsmAndRoute = props[4];
settings.SPEAK_GPX_WPT.set(announceGpxWpt); settings.SPEAK_GPX_WPT.set(announceGpxWpt);
GPXRouteParams gpxRoute = new GPXRouteParams(result, reverse, announceGpxWpt, settings); settings.CALC_GPX_ROUTE.set(calculateOsmAndRoute);
GPXRouteParamsBuilder bld = GPXRouteParamsBuilder.newBuilder(result, settings);
if(reverse) {
bld.reverse();
}
if(announceGpxWpt) {
bld.announceWaypoints();
}
if(calculateOsmAndRoute) {
bld.calculateOsmAndRoute();
}
GPXRouteParams gpxRoute = bld.build();
Location loc = getLastKnownLocation(); Location loc = getLastKnownLocation();
if(passWholeWay && loc != null){ if(passWholeWay && loc != null){
gpxRoute.setStartPoint(loc); gpxRoute.setStartPoint(loc);

View file

@ -16,6 +16,7 @@ import net.osmand.plus.R;
import net.osmand.plus.TargetPointsHelper; import net.osmand.plus.TargetPointsHelper;
import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.routing.RouteProvider.GPXRouteParams; import net.osmand.plus.routing.RouteProvider.GPXRouteParams;
import net.osmand.plus.routing.RouteProvider.GPXRouteParams.GPXRouteParamsBuilder;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.AlertDialog.Builder; import android.app.AlertDialog.Builder;
import android.content.DialogInterface; import android.content.DialogInterface;
@ -123,14 +124,26 @@ public class FailSafeFuntions {
@Override @Override
protected void onPostExecute(GPXFile result) { protected void onPostExecute(GPXFile result) {
final GPXRouteParams gpxRoute = result == null ? null : new GPXRouteParams(result, false, final GPXRouteParams gpxRoute;
settings.SPEAK_GPX_WPT.get(), settings); if (result != null) {
GPXRouteParamsBuilder builder = GPXRouteParamsBuilder.newBuilder(result, settings);
if (settings.SPEAK_GPX_WPT.get()) {
builder.announceWaypoints();
}
if(settings.CALC_GPX_ROUTE.get()) {
builder.calculateOsmAndRoute();
}
gpxRoute = builder.build();
} else {
gpxRoute = null;
}
LatLon endPoint = pointToNavigate != null ? pointToNavigate : gpxRoute.getLastPoint(); LatLon endPoint = pointToNavigate != null ? pointToNavigate : gpxRoute.getLastPoint();
net.osmand.Location startPoint = gpxRoute == null ? null : gpxRoute.getStartPointForRoute(); net.osmand.Location startPoint = gpxRoute == null ? null : gpxRoute.getStartPointForRoute();
if (endPoint == null) { if (endPoint == null) {
notRestoreRoutingMode(ma, app); notRestoreRoutingMode(ma, app);
} else { } else {
ma.followRoute(settings.getApplicationMode(), endPoint, targetPoints.getIntermediatePoints(), startPoint, gpxRoute); ma.followRoute(settings.getApplicationMode(), endPoint,
targetPoints.getIntermediatePoints(), startPoint, gpxRoute);
} }
} }
}; };

View file

@ -4,6 +4,7 @@ import java.io.InputStream;
import java.util.HashSet; import java.util.HashSet;
import java.util.Locale; import java.util.Locale;
import net.osmand.CallbackWithObject;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R; import net.osmand.plus.R;
@ -17,7 +18,7 @@ public class SuggestExternalDirectoryDialog {
public static boolean showDialog(Activity a, final DialogInterface.OnClickListener otherListener, public static boolean showDialog(Activity a, final DialogInterface.OnClickListener otherListener,
final Runnable reloadListener){ final CallbackWithObject<String> selector){
final boolean showOther = otherListener != null; final boolean showOther = otherListener != null;
final OsmandApplication app = (OsmandApplication) a.getApplication(); final OsmandApplication app = (OsmandApplication) a.getApplication();
Builder bld = new AlertDialog.Builder(a); Builder bld = new AlertDialog.Builder(a);
@ -46,10 +47,11 @@ public class SuggestExternalDirectoryDialog {
otherListener.onClick(dialog, which); otherListener.onClick(dialog, which);
} else { } else {
dialog.dismiss(); dialog.dismiss();
app.getSettings().setExternalStorageDirectory(extMounts[which]); if(selector != null) {
app.getResourceManager().resetStoreDirectory(); selector.processResult(extMounts[which]);
if(reloadListener != null) { } else {
reloadListener.run(); app.getSettings().setExternalStorageDirectory(extMounts[which]);
app.getResourceManager().resetStoreDirectory();
} }
} }
} }

View file

@ -10,6 +10,7 @@ import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TimeZone;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import net.osmand.AndroidUtils; import net.osmand.AndroidUtils;
@ -182,7 +183,7 @@ public class DownloadOsmandIndexesHelper {
} }
} }
private static String reparseDate(Context ctx, String date) { protected static String reparseDate(Context ctx, String date) {
try { try {
Date d = simpleDateFormat.parse(date); Date d = simpleDateFormat.parse(date);
return AndroidUtils.formatDate(ctx, d.getTime()); return AndroidUtils.formatDate(ctx, d.getTime());

View file

@ -10,6 +10,7 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TimeZone;
import net.osmand.IndexConstants; import net.osmand.IndexConstants;
import net.osmand.PlatformUtil; import net.osmand.PlatformUtil;
@ -218,7 +219,9 @@ public class IndexItem implements Comparable<IndexItem> {
entry.zipStream = zipStream; entry.zipStream = zipStream;
entry.unzipFolder = unzipDir; entry.unzipFolder = unzipDir;
try { try {
Date d = DateFormat.getDateFormat((Context) ctx).parse(date); final java.text.DateFormat format = DateFormat.getDateFormat((Context) ctx);
format.setTimeZone(TimeZone.getTimeZone("GMT+01:00"));
Date d = format.parse(date);
entry.dateModified = d.getTime(); entry.dateModified = d.getTime();
} catch (ParseException e1) { } catch (ParseException e1) {
log.error("ParseException", e1); log.error("ParseException", e1);

View file

@ -301,7 +301,13 @@ public class OpenstreetmapRemoteUtil implements OpenstreetmapUtil {
OsmBaseStorage st = new OsmBaseStorage(); OsmBaseStorage st = new OsmBaseStorage();
st.parseOSM(new ByteArrayInputStream(res.getBytes("UTF-8")), null, null, true); //$NON-NLS-1$ st.parseOSM(new ByteArrayInputStream(res.getBytes("UTF-8")), null, null, true); //$NON-NLS-1$
EntityId id = new Entity.EntityId(EntityType.NODE, nodeId); EntityId id = new Entity.EntityId(EntityType.NODE, nodeId);
// Node entity = (Node) st.getRegisteredEntities().get(id); Node entity = (Node) st.getRegisteredEntities().get(id);
// merge non existing tags
for(String rtag : entity.getTagKeySet()) {
if(!n.getTagKeySet().contains(rtag)) {
n.putTag(rtag, entity.getTag(rtag));
}
}
entityInfo = st.getRegisteredEntityInfo().get(id); entityInfo = st.getRegisteredEntityInfo().get(id);
return entityInfo; return entityInfo;
} }

View file

@ -1,9 +1,11 @@
package net.osmand.plus.render; package net.osmand.plus.render;
import gnu.trove.iterator.TIntObjectIterator;
import gnu.trove.list.TLongList; import gnu.trove.list.TLongList;
import gnu.trove.list.array.TIntArrayList; import gnu.trove.list.array.TIntArrayList;
import gnu.trove.list.array.TLongArrayList; import gnu.trove.list.array.TLongArrayList;
import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.set.TLongSet; import gnu.trove.set.TLongSet;
import gnu.trove.set.hash.TLongHashSet; import gnu.trove.set.hash.TLongHashSet;
@ -20,18 +22,19 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import net.osmand.IProgress; import net.osmand.IProgress;
import net.osmand.ResultMatcher;
import net.osmand.NativeLibrary.NativeSearchResult; import net.osmand.NativeLibrary.NativeSearchResult;
import net.osmand.PlatformUtil; import net.osmand.PlatformUtil;
import net.osmand.ResultMatcher;
import net.osmand.access.AccessibleToast; import net.osmand.access.AccessibleToast;
import net.osmand.binary.BinaryMapDataObject; import net.osmand.binary.BinaryMapDataObject;
import net.osmand.binary.BinaryMapIndexReader; import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteTypeRule;
import net.osmand.binary.RouteDataObject;
import net.osmand.binary.BinaryMapIndexReader.MapIndex; import net.osmand.binary.BinaryMapIndexReader.MapIndex;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest; import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.binary.BinaryMapIndexReader.TagValuePair; import net.osmand.binary.BinaryMapIndexReader.TagValuePair;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion; import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteSubregion; import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteSubregion;
import net.osmand.binary.RouteDataObject;
import net.osmand.data.QuadPointDouble; import net.osmand.data.QuadPointDouble;
import net.osmand.data.QuadRect; import net.osmand.data.QuadRect;
import net.osmand.data.RotatedTileBox; import net.osmand.data.RotatedTileBox;
@ -66,10 +69,10 @@ public class MapRenderRepositories {
// It is needed to not draw object twice if user have map index that intersects by boundaries // It is needed to not draw object twice if user have map index that intersects by boundaries
public static boolean checkForDuplicateObjectIds = true; public static boolean checkForDuplicateObjectIds = true;
private final static Log log = PlatformUtil.getLog(MapRenderRepositories.class); private final static Log log = PlatformUtil.getLog(MapRenderRepositories.class);
private final OsmandApplication context; private final OsmandApplication context;
private final static int BASEMAP_ZOOM = 11; private final static int zoomOnlyForBasemaps = 11;
static int zoomForBaseRouteRendering = 14;
private Handler handler; private Handler handler;
private Map<String, BinaryMapIndexReader> files = new ConcurrentHashMap<String, BinaryMapIndexReader>(); private Map<String, BinaryMapIndexReader> files = new ConcurrentHashMap<String, BinaryMapIndexReader>();
private Set<String> nativeFiles = new HashSet<String>(); private Set<String> nativeFiles = new HashSet<String>();
@ -91,6 +94,8 @@ public class MapRenderRepositories {
private RotatedTileBox prevBmpLocation = null; private RotatedTileBox prevBmpLocation = null;
// already rendered bitmap // already rendered bitmap
private Bitmap prevBmp; private Bitmap prevBmp;
// to track necessity of map download (1 (if basemap) + 2 (if normal map)
private int previousRenderedState;
// location of rendered bitmap // location of rendered bitmap
private RotatedTileBox bmpLocation = null; private RotatedTileBox bmpLocation = null;
@ -98,6 +103,7 @@ public class MapRenderRepositories {
private Bitmap bmp; private Bitmap bmp;
// Field used in C++ // Field used in C++
private boolean interrupted = false; private boolean interrupted = false;
private int renderedState = 0; // (1 (if basemap) + 2 (if normal map)
private RenderingContext currentRenderingContext; private RenderingContext currentRenderingContext;
private SearchRequest<BinaryMapDataObject> searchRequest; private SearchRequest<BinaryMapDataObject> searchRequest;
private OsmandSettings prefs; private OsmandSettings prefs;
@ -252,7 +258,7 @@ public class MapRenderRepositories {
} }
NativeSearchResult resultHandler = library.searchObjectsForRendering(leftX, rightX, topY, bottomY, zoom, renderingReq, NativeSearchResult resultHandler = library.searchObjectsForRendering(leftX, rightX, topY, bottomY, zoom, renderingReq,
checkForDuplicateObjectIds, this, /*context.getString(R.string.switch_to_raster_map_to_see)*/ ""); checkForDuplicateObjectIds, this, "");
if (checkWhetherInterrupted()) { if (checkWhetherInterrupted()) {
resultHandler.deleteNativeResult(); resultHandler.deleteNativeResult();
return false; return false;
@ -268,6 +274,77 @@ public class MapRenderRepositories {
return true; return true;
} }
private void readRouteDataAsMapObjects(SearchRequest<BinaryMapDataObject> sr, BinaryMapIndexReader c,
final ArrayList<BinaryMapDataObject> tempResult, final TLongSet ids) {
final boolean basemap = c.isBasemap();
try {
for (RouteRegion reg : c.getRoutingIndexes()) {
List<RouteSubregion> parent = sr.getZoom() < 15 ? reg.getBaseSubregions() : reg.getSubregions();
List<RouteSubregion> searchRouteIndexTree = c.searchRouteIndexTree(sr, parent);
final MapIndex nmi = new MapIndex();
c.loadRouteIndexData(searchRouteIndexTree, new ResultMatcher<RouteDataObject>() {
@Override
public boolean publish(RouteDataObject r) {
if (basemap) {
renderedState |= 1;
} else {
renderedState |= 2;
}
if (checkForDuplicateObjectIds && !basemap) {
if (ids.contains(r.getId()) && r.getId() > 0) {
// do not add object twice
return false;
}
ids.add(r.getId());
}
int[] coordinantes = new int[r.getPointsLength() * 2];
int[] roTypes = r.getTypes();
for(int k = 0; k < roTypes.length; k++) {
int type = roTypes[k];
registerMissingType(nmi, r, type);
}
for(int k = 0; k < coordinantes.length/2; k++ ) {
coordinantes[2 * k] = r.getPoint31XTile(k);
coordinantes[2 * k + 1] = r.getPoint31YTile(k);
}
BinaryMapDataObject mo = new BinaryMapDataObject(coordinantes, roTypes, new int[0][], r.getId());
TIntObjectHashMap<String> names = r.getNames();
if(names != null) {
TIntObjectIterator<String> it = names.iterator();
while(it.hasNext()) {
it.advance();
registerMissingType(nmi, r, it.key());
mo.putObjectName(it.key(), it.value());
}
}
mo.setMapIndex(nmi);
tempResult.add(mo);
return false;
}
private void registerMissingType(final MapIndex nmi, RouteDataObject r, int type) {
if (!nmi.isRegisteredRule(type)) {
RouteTypeRule rr = r.region.quickGetEncodingRule(type);
String tag = rr.getTag();
int additional = ("highway".equals(tag) || "route".equals(tag) || "railway".equals(tag)
|| "aeroway".equals(tag) || "aerialway".equals(tag)) ? 0 : 1;
nmi.initMapEncodingRule(additional, type, rr.getTag(), rr.getValue());
}
}
@Override
public boolean isCancelled() {
return !interrupted;
}
});
}
} catch (IOException e) {
log.debug("Search failed " + c.getRegionNames(), e); //$NON-NLS-1$
}
}
private boolean loadVectorData(QuadRect dataBox, final int zoom, final RenderingRuleSearchRequest renderingReq) { private boolean loadVectorData(QuadRect dataBox, final int zoom, final RenderingRuleSearchRequest renderingReq) {
double cBottomLatitude = dataBox.bottom; double cBottomLatitude = dataBox.bottom;
double cTopLatitude = dataBox.top; double cTopLatitude = dataBox.top;
@ -277,18 +354,102 @@ public class MapRenderRepositories {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
System.gc(); // to clear previous objects System.gc(); // to clear previous objects
int count = 0;
ArrayList<BinaryMapDataObject> tempResult = new ArrayList<BinaryMapDataObject>(); ArrayList<BinaryMapDataObject> tempResult = new ArrayList<BinaryMapDataObject>();
ArrayList<BinaryMapDataObject> basemapResult = new ArrayList<BinaryMapDataObject>(); ArrayList<BinaryMapDataObject> basemapResult = new ArrayList<BinaryMapDataObject>();
TLongSet ids = new TLongHashSet();
int[] count = new int[]{0};
boolean[] ocean = new boolean[]{false};
boolean[] land = new boolean[]{false};
List<BinaryMapDataObject> coastLines = new ArrayList<BinaryMapDataObject>(); List<BinaryMapDataObject> coastLines = new ArrayList<BinaryMapDataObject>();
List<BinaryMapDataObject> basemapCoastLines = new ArrayList<BinaryMapDataObject>(); List<BinaryMapDataObject> basemapCoastLines = new ArrayList<BinaryMapDataObject>();
int leftX = MapUtils.get31TileNumberX(cLeftLongitude); int leftX = MapUtils.get31TileNumberX(cLeftLongitude);
int rightX = MapUtils.get31TileNumberX(cRightLongitude); int rightX = MapUtils.get31TileNumberX(cRightLongitude);
int bottomY = MapUtils.get31TileNumberY(cBottomLatitude); int bottomY = MapUtils.get31TileNumberY(cBottomLatitude);
int topY = MapUtils.get31TileNumberY(cTopLatitude); int topY = MapUtils.get31TileNumberY(cTopLatitude);
BinaryMapIndexReader.SearchFilter searchFilter = new BinaryMapIndexReader.SearchFilter() { TLongSet ids = new TLongHashSet();
MapIndex mi = readMapObjectsForRendering(zoom, renderingReq, tempResult, basemapResult, ids, count, ocean,
land, coastLines, basemapCoastLines, leftX, rightX, bottomY, topY);
int renderRouteDataFile = 0;
if (renderingReq.searchRenderingAttribute("showRoadMapsAttribute")) {
renderRouteDataFile = renderingReq.getIntPropertyValue(renderingReq.ALL.R_ATTR_INT_VALUE);
}
if (checkWhetherInterrupted()) {
return false;
}
if (renderRouteDataFile >= 0 && zoom >= zoomOnlyForBasemaps ) {
searchRequest = BinaryMapIndexReader.buildSearchRequest(leftX, rightX, topY, bottomY, zoom, null);
for (BinaryMapIndexReader c : files.values()) {
// false positive case when we have 2 sep maps Country-roads & Country
if(c.getMapIndexes().size() == 0 || renderRouteDataFile == 1) {
readRouteDataAsMapObjects(searchRequest, c, tempResult, ids);
}
}
log.info(String.format("Route objects %s", tempResult.size() +""));
}
String coastlineTime = "";
boolean addBasemapCoastlines = true;
boolean emptyData = zoom > zoomOnlyForBasemaps && tempResult.isEmpty() && coastLines.isEmpty();
boolean basemapMissing = zoom <= zoomOnlyForBasemaps && basemapCoastLines.isEmpty() && mi == null;
boolean detailedLandData = zoom >= zoomForBaseRouteRendering && tempResult.size() > 0 && renderRouteDataFile < 0;
if (!coastLines.isEmpty()) {
long ms = System.currentTimeMillis();
boolean coastlinesWereAdded = processCoastlines(coastLines, leftX, rightX, bottomY, topY, zoom,
basemapCoastLines.isEmpty(), true, tempResult);
addBasemapCoastlines = (!coastlinesWereAdded && !detailedLandData) || zoom <= zoomOnlyForBasemaps;
coastlineTime = "(coastline " + (System.currentTimeMillis() - ms) + " ms )";
} else {
addBasemapCoastlines = !detailedLandData;
}
if (addBasemapCoastlines) {
long ms = System.currentTimeMillis();
boolean coastlinesWereAdded = processCoastlines(basemapCoastLines, leftX, rightX, bottomY, topY, zoom,
true, true, tempResult);
addBasemapCoastlines = !coastlinesWereAdded;
coastlineTime = "(coastline " + (System.currentTimeMillis() - ms) + " ms )";
}
if (addBasemapCoastlines && mi != null) {
BinaryMapDataObject o = new BinaryMapDataObject(new int[]{leftX, topY, rightX, topY, rightX, bottomY, leftX, bottomY, leftX,
topY}, new int[]{ocean[0] && !land[0] ? mi.coastlineEncodingType : (mi.landEncodingType)}, null, -1);
o.setMapIndex(mi);
tempResult.add(o);
}
if (emptyData || basemapMissing) {
// message
MapIndex mapIndex;
if (!tempResult.isEmpty()) {
mapIndex = tempResult.get(0).getMapIndex();
} else {
mapIndex = new MapIndex();
mapIndex.initMapEncodingRule(0, 1, "natural", "coastline");
mapIndex.initMapEncodingRule(0, 2, "name", "");
}
}
if (zoom <= zoomOnlyForBasemaps || emptyData) {
tempResult.addAll(basemapResult);
}
if (count[0] > 0) {
log.info(String.format("BLat=%s, TLat=%s, LLong=%s, RLong=%s, zoom=%s", //$NON-NLS-1$
cBottomLatitude, cTopLatitude, cLeftLongitude, cRightLongitude, zoom));
log.info(String.format("Searching: %s ms %s (%s results found)", System.currentTimeMillis() - now, coastlineTime, count[0])); //$NON-NLS-1$
}
cObjects = tempResult;
cObjectsBox = dataBox;
return true;
}
private MapIndex readMapObjectsForRendering(final int zoom, final RenderingRuleSearchRequest renderingReq,
ArrayList<BinaryMapDataObject> tempResult, ArrayList<BinaryMapDataObject> basemapResult,
TLongSet ids, int[] count, boolean[] ocean, boolean[] land, List<BinaryMapDataObject> coastLines,
List<BinaryMapDataObject> basemapCoastLines, int leftX, int rightX, int bottomY, int topY) {
BinaryMapIndexReader.SearchFilter searchFilter = new BinaryMapIndexReader.SearchFilter() {
@Override @Override
public boolean accept(TIntArrayList types, BinaryMapIndexReader.MapIndex root) { public boolean accept(TIntArrayList types, BinaryMapIndexReader.MapIndex root) {
for (int j = 0; j < types.size(); j++) { for (int j = 0; j < types.size(); j++) {
@ -318,11 +479,10 @@ public class MapRenderRepositories {
if (zoom > 16) { if (zoom > 16) {
searchFilter = null; searchFilter = null;
} }
boolean ocean = false;
boolean land = false;
MapIndex mi = null; MapIndex mi = null;
searchRequest = BinaryMapIndexReader.buildSearchRequest(leftX, rightX, topY, bottomY, zoom, searchFilter); searchRequest = BinaryMapIndexReader.buildSearchRequest(leftX, rightX, topY, bottomY, zoom, searchFilter);
for (BinaryMapIndexReader c : files.values()) { for (BinaryMapIndexReader c : files.values()) {
boolean basemap = c.isBasemap();
searchRequest.clearSearchResults(); searchRequest.clearSearchResults();
List<BinaryMapDataObject> res; List<BinaryMapDataObject> res;
try { try {
@ -331,99 +491,52 @@ public class MapRenderRepositories {
res = new ArrayList<BinaryMapDataObject>(); res = new ArrayList<BinaryMapDataObject>();
log.debug("Search failed " + c.getRegionNames(), e); //$NON-NLS-1$ log.debug("Search failed " + c.getRegionNames(), e); //$NON-NLS-1$
} }
if(res.size() > 0) {
if(basemap) {
renderedState |= 1;
} else {
renderedState |= 2;
}
}
for (BinaryMapDataObject r : res) { for (BinaryMapDataObject r : res) {
if (checkForDuplicateObjectIds) { if (checkForDuplicateObjectIds && !basemap) {
if (ids.contains(r.getId()) && r.getId() > 0) { if (ids.contains(r.getId()) && r.getId() > 0) {
// do not add object twice // do not add object twice
continue; continue;
} }
ids.add(r.getId()); ids.add(r.getId());
} }
count++; count[0]++;
if (r.containsType(r.getMapIndex().coastlineEncodingType)) { if (r.containsType(r.getMapIndex().coastlineEncodingType)) {
if (c.isBasemap()) { if (basemap) {
basemapCoastLines.add(r); basemapCoastLines.add(r);
} else { } else {
coastLines.add(r); coastLines.add(r);
} }
} else { } else {
// do not mess coastline and other types // do not mess coastline and other types
if (c.isBasemap()) { if (basemap) {
basemapResult.add(r); basemapResult.add(r);
} else { } else {
tempResult.add(r); tempResult.add(r);
} }
} }
if (checkWhetherInterrupted()) { if (checkWhetherInterrupted()) {
return false; return null;
} }
} }
if (searchRequest.isOcean()) { if (searchRequest.isOcean()) {
mi = c.getMapIndexes().get(0); mi = c.getMapIndexes().get(0);
ocean = true; ocean[0] = true;
} }
if (searchRequest.isLand()) { if (searchRequest.isLand()) {
mi = c.getMapIndexes().get(0); mi = c.getMapIndexes().get(0);
land = true; land[0] = true;
} }
} }
return mi;
String coastlineTime = "";
boolean addBasemapCoastlines = true;
boolean emptyData = zoom > BASEMAP_ZOOM && tempResult.isEmpty() && coastLines.isEmpty();
boolean basemapMissing = zoom <= BASEMAP_ZOOM && basemapCoastLines.isEmpty() && mi == null;
boolean detailedLandData = zoom >= 14 && tempResult.size() > 0;
if (!coastLines.isEmpty()) {
long ms = System.currentTimeMillis();
boolean coastlinesWereAdded = processCoastlines(coastLines, leftX, rightX, bottomY, topY, zoom,
basemapCoastLines.isEmpty(), true, tempResult);
addBasemapCoastlines = (!coastlinesWereAdded && !detailedLandData) || zoom <= BASEMAP_ZOOM;
coastlineTime = "(coastline " + (System.currentTimeMillis() - ms) + " ms )";
} else {
addBasemapCoastlines = !detailedLandData;
}
if (addBasemapCoastlines) {
long ms = System.currentTimeMillis();
boolean coastlinesWereAdded = processCoastlines(basemapCoastLines, leftX, rightX, bottomY, topY, zoom,
true, true, tempResult);
addBasemapCoastlines = !coastlinesWereAdded;
coastlineTime = "(coastline " + (System.currentTimeMillis() - ms) + " ms )";
}
if (addBasemapCoastlines && mi != null) {
BinaryMapDataObject o = new BinaryMapDataObject(new int[]{leftX, topY, rightX, topY, rightX, bottomY, leftX, bottomY, leftX,
topY}, new int[]{ocean && !land ? mi.coastlineEncodingType : (mi.landEncodingType)}, null, -1);
o.setMapIndex(mi);
tempResult.add(o);
}
if (emptyData || basemapMissing) {
// message
MapIndex mapIndex;
if (!tempResult.isEmpty()) {
mapIndex = tempResult.get(0).getMapIndex();
} else {
mapIndex = new MapIndex();
mapIndex.initMapEncodingRule(0, 1, "natural", "coastline");
mapIndex.initMapEncodingRule(0, 2, "name", "");
}
}
if (zoom <= BASEMAP_ZOOM || emptyData) {
tempResult.addAll(basemapResult);
}
if (count > 0) {
log.info(String.format("BLat=%s, TLat=%s, LLong=%s, RLong=%s, zoom=%s", //$NON-NLS-1$
cBottomLatitude, cTopLatitude, cLeftLongitude, cRightLongitude, zoom));
log.info(String.format("Searching: %s ms %s (%s results found)", System.currentTimeMillis() - now, coastlineTime, count)); //$NON-NLS-1$
}
cObjects = tempResult;
cObjectsBox = dataBox;
return true;
} }
private void validateLatLonBox(QuadRect box) { private void validateLatLonBox(QuadRect box) {
@ -442,60 +555,13 @@ public class MapRenderRepositories {
} }
// only single thread to read !
public synchronized boolean checkIfMapIsEmpty(int leftX, int rightX, int topY, int bottomY, int zoom){
final boolean[] empty = new boolean[] {true};
SearchRequest<BinaryMapDataObject> searchRequest = BinaryMapIndexReader.buildSearchRequest(leftX, rightX, topY, bottomY, zoom,
null, new ResultMatcher<BinaryMapDataObject>() {
@Override
public boolean publish(BinaryMapDataObject object) {
empty[0] = false;
return false;
}
@Override public boolean isLastMapRenderedEmpty(boolean checkBaseMap){
public boolean isCancelled() { if(checkBaseMap) {
return !empty[0]; return prevBmp != null && previousRenderedState == 0;
} } else {
}); return prevBmp != null && previousRenderedState == 1;
SearchRequest<RouteDataObject> searchRouteRequest = BinaryMapIndexReader.buildSearchRouteRequest(leftX, rightX, topY, bottomY,
new ResultMatcher<RouteDataObject>() {
@Override
public boolean publish(RouteDataObject object) {
empty[0] = false;
return false;
}
@Override
public boolean isCancelled() {
return !empty[0];
}
});
for (BinaryMapIndexReader c : files.values()) {
if (!c.isBasemap()) {
try {
c.searchMapIndex(searchRequest);
} catch (IOException e) {
// lots of FalsePositive cases
return false;
}
if (!empty[0]) {
return false;
}
for (RouteRegion r : c.getRoutingIndexes()) {
try {
List<RouteSubregion> regs = c.searchRouteIndexTree(searchRouteRequest, r.getSubregions());
if(!regs.isEmpty()) {
return false;
}
} catch (IOException e) {
// lots of FalsePositive cases
return false;
}
}
}
} }
return empty[0];
} }
public synchronized void loadMap(RotatedTileBox tileRect, List<IMapDownloaderCallback> notifyList) { public synchronized void loadMap(RotatedTileBox tileRect, List<IMapDownloaderCallback> notifyList) {
@ -539,10 +605,10 @@ public class MapRenderRepositories {
// prevent editing // prevent editing
requestedBox = new RotatedTileBox(tileRect); requestedBox = new RotatedTileBox(tileRect);
// calculate data box // calculate data box
QuadRect dataBox = requestedBox.getLatLonBounds(); QuadRect dataBox = requestedBox.getLatLonBounds();
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
if (cObjectsBox.left > dataBox.left || cObjectsBox.top > dataBox.top || cObjectsBox.right < dataBox.right if (cObjectsBox.left > dataBox.left || cObjectsBox.top > dataBox.top || cObjectsBox.right < dataBox.right
|| cObjectsBox.bottom < dataBox.bottom || (nativeLib != null) == (cNativeObjects == null)) { || cObjectsBox.bottom < dataBox.bottom || (nativeLib != null) == (cNativeObjects == null)) {
// increase data box in order for rotate // increase data box in order for rotate
@ -556,6 +622,7 @@ public class MapRenderRepositories {
dataBox.bottom -= hi; dataBox.bottom -= hi;
} }
validateLatLonBox(dataBox); validateLatLonBox(dataBox);
renderedState = 0;
boolean loaded; boolean loaded;
if(nativeLib != null) { if(nativeLib != null) {
cObjects = new LinkedList<BinaryMapDataObject>(); cObjects = new LinkedList<BinaryMapDataObject>();
@ -618,6 +685,7 @@ public class MapRenderRepositories {
Bitmap reuse = prevBmp; Bitmap reuse = prevBmp;
this.prevBmp = this.bmp; this.prevBmp = this.bmp;
this.prevBmpLocation = this.bmpLocation; this.prevBmpLocation = this.bmpLocation;
this.previousRenderedState = renderedState;
if (reuse != null && reuse.getWidth() == currentRenderingContext.width && reuse.getHeight() == currentRenderingContext.height) { if (reuse != null && reuse.getWidth() == currentRenderingContext.width && reuse.getHeight() == currentRenderingContext.height) {
bmp = reuse; bmp = reuse;
bmp.eraseColor(currentRenderingContext.defaultColor); bmp.eraseColor(currentRenderingContext.defaultColor);
@ -726,6 +794,7 @@ public class MapRenderRepositories {
cObjectsBox = new QuadRect(); cObjectsBox = new QuadRect();
requestedBox = prevBmpLocation = null; requestedBox = prevBmpLocation = null;
previousRenderedState = 0;
// Do not clear main bitmap to not cause a screen refresh // Do not clear main bitmap to not cause a screen refresh
// prevBmp = null; // prevBmp = null;
// bmp = null; // bmp = null;

View file

@ -3,6 +3,7 @@ package net.osmand.plus.routing;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.StringReader; import java.io.StringReader;
@ -48,6 +49,7 @@ import net.osmand.router.GeneralRouter;
import net.osmand.router.GeneralRouter.GeneralRouterProfile; import net.osmand.router.GeneralRouter.GeneralRouterProfile;
import net.osmand.router.GeneralRouter.RoutingParameter; import net.osmand.router.GeneralRouter.RoutingParameter;
import net.osmand.router.GeneralRouter.RoutingParameterType; import net.osmand.router.GeneralRouter.RoutingParameterType;
import net.osmand.router.PrecalculatedRouteDirection;
import net.osmand.router.RoutePlannerFrontEnd; import net.osmand.router.RoutePlannerFrontEnd;
import net.osmand.router.RoutePlannerFrontEnd.RouteCalculationMode; import net.osmand.router.RoutePlannerFrontEnd.RouteCalculationMode;
import net.osmand.router.RouteSegmentResult; import net.osmand.router.RouteSegmentResult;
@ -115,9 +117,9 @@ public class RouteProvider {
List<Location> points = new ArrayList<Location>(); List<Location> points = new ArrayList<Location>();
List<RouteDirectionInfo> directions; List<RouteDirectionInfo> directions;
DataTileManager<WptPt> wpt; DataTileManager<WptPt> wpt;
boolean calculateOsmAndRoute = false;
public GPXRouteParams(GPXFile file, boolean reverse, boolean announceWaypoints, OsmandSettings settings){ private GPXRouteParams(){
prepareEverything(file, reverse, announceWaypoints, settings.DRIVING_REGION.get().leftHandDriving);
} }
public void setStartPoint(Location startPoint) { public void setStartPoint(Location startPoint) {
@ -144,6 +146,45 @@ public class RouteProvider {
return null; return null;
} }
public static class GPXRouteParamsBuilder {
private GPXRouteParams obj = new GPXRouteParams();
private GPXFile file;
private boolean leftHandDriving;
private boolean reverse;
private boolean announceWaypoints;
private GPXRouteParamsBuilder(GPXFile f, OsmandSettings settings) {
this.file = f;
leftHandDriving = settings.DRIVING_REGION.get().leftHandDriving;
// obj = new GPXRouteParams(file, reverse, announceWaypoints, settings)
// TODO Auto-generated constructor stub
}
public GPXRouteParamsBuilder reverse() {
this.reverse = true;
return this;
}
public GPXRouteParamsBuilder announceWaypoints() {
this.announceWaypoints = true;
return this;
}
public GPXRouteParamsBuilder calculateOsmAndRoute() {
obj.calculateOsmAndRoute = true;
return this;
}
public static GPXRouteParamsBuilder newBuilder(GPXFile f, OsmandSettings settings) {
return new GPXRouteParamsBuilder(f, settings);
}
public GPXRouteParams build(){
obj.prepareEverything(file, reverse, announceWaypoints, leftHandDriving);
return obj;
}
}
private void prepareEverything(GPXFile file, boolean reverse, boolean announceWaypoints, boolean leftSide){ private void prepareEverything(GPXFile file, boolean reverse, boolean announceWaypoints, boolean leftSide){
if(file.isCloudmadeRouteFile() || OSMAND_ROUTER.equals(file.author)){ if(file.isCloudmadeRouteFile() || OSMAND_ROUTER.equals(file.author)){
directions = parseCloudmadeRoute(points, file, OSMAND_ROUTER.equals(file.author), leftSide, 10); directions = parseCloudmadeRoute(points, file, OSMAND_ROUTER.equals(file.author), leftSide, 10);
@ -210,7 +251,10 @@ public class RouteProvider {
} }
try { try {
RouteCalculationResult res; RouteCalculationResult res;
if(params.gpxRoute != null && !params.gpxRoute.points.isEmpty()){ boolean calcGPXRoute = params.gpxRoute != null && !params.gpxRoute.points.isEmpty();
if (params.type == RouteService.OSMAND || (calcGPXRoute && params.gpxRoute.calculateOsmAndRoute)) {
res = findVectorMapsRoute(params, calcGPXRoute);
} else if(calcGPXRoute){
res = calculateGpxRoute(params); res = calculateGpxRoute(params);
} else if (params.type == RouteService.YOURS) { } else if (params.type == RouteService.YOURS) {
res = findYOURSRoute(params); res = findYOURSRoute(params);
@ -218,8 +262,6 @@ public class RouteProvider {
res = findORSRoute(params); res = findORSRoute(params);
} else if (params.type == RouteService.OSRM) { } else if (params.type == RouteService.OSRM) {
res = findOSRMRoute(params); res = findOSRMRoute(params);
} else if (params.type == RouteService.OSMAND) {
res = findVectorMapsRoute(params);
} else if (params.type == RouteService.BROUTER) { } else if (params.type == RouteService.BROUTER) {
res = findBROUTERRoute(params); res = findBROUTERRoute(params);
} else { } else {
@ -246,45 +288,20 @@ public class RouteProvider {
private RouteCalculationResult calculateGpxRoute(RouteCalculationParams pars) { private RouteCalculationResult calculateGpxRoute(RouteCalculationParams pars) {
RouteCalculationResult res; RouteCalculationResult res;
// get the closest point to start and to end // get the closest point to start and to end
float minDist = Integer.MAX_VALUE;
int startI = 0;
GPXRouteParams params = pars.gpxRoute; GPXRouteParams params = pars.gpxRoute;
List<Location> gpxRoute = params.points; List<Location> gpxRoute = params.points;
int endI = gpxRoute.size(); int[] startI = new int[]{0};
if (pars.start != null) { int[] endI = new int[]{gpxRoute.size()};
for (int i = 0; i < gpxRoute.size(); i++) { ArrayList<Location> sublist = findGpxLocations(pars, startI, endI);
float d = gpxRoute.get(i).distanceTo(pars.start);
if (d < minDist) {
startI = i;
minDist = d;
}
}
} else {
pars.start = gpxRoute.get(0);
}
Location l = new Location("temp"); //$NON-NLS-1$
l.setLatitude(pars.end.getLatitude());
l.setLongitude(pars.end.getLongitude());
minDist = Integer.MAX_VALUE;
// get in reverse order taking into account ways with cycle
for (int i = gpxRoute.size() - 1; i >= startI; i--) {
float d = gpxRoute.get(i).distanceTo(l);
if (d < minDist) {
endI = i + 1;
// slightly modify to allow last point to be added
minDist = d - 40;
}
}
ArrayList<Location> sublist = new ArrayList<Location>(gpxRoute.subList(startI, endI));
pars.intermediates = null; pars.intermediates = null;
if(params.directions == null){ if(params.directions == null){
res = new RouteCalculationResult(sublist, null, pars, params.wpt); res = new RouteCalculationResult(sublist, null, pars, params.wpt);
} else { } else {
List<RouteDirectionInfo> subdirections = new ArrayList<RouteDirectionInfo>(); List<RouteDirectionInfo> subdirections = new ArrayList<RouteDirectionInfo>();
for (RouteDirectionInfo info : params.directions) { for (RouteDirectionInfo info : params.directions) {
if(info.routePointOffset >= startI && info.routePointOffset < endI){ if(info.routePointOffset >= startI[0] && info.routePointOffset < endI[0]){
RouteDirectionInfo ch = new RouteDirectionInfo(info.getAverageSpeed(), info.getTurnType()); RouteDirectionInfo ch = new RouteDirectionInfo(info.getAverageSpeed(), info.getTurnType());
ch.routePointOffset = info.routePointOffset - startI; ch.routePointOffset = info.routePointOffset - startI[0];
ch.setDescriptionRoute(info.getDescriptionRoute()); ch.setDescriptionRoute(info.getDescriptionRoute());
// recalculate // recalculate
@ -298,6 +315,49 @@ public class RouteProvider {
return res; return res;
} }
private ArrayList<Location> findGpxLocations(RouteCalculationParams pars, int[] startI, int[] endI) {
GPXRouteParams params = pars.gpxRoute;
List<Location> gpxRoute = params.points;
float minDist = Integer.MAX_VALUE;
int start = 0;
int end = gpxRoute.size();
if (pars.start != null) {
for (int i = 0; i < gpxRoute.size(); i++) {
float d = gpxRoute.get(i).distanceTo(pars.start);
if (d < minDist) {
start = i;
minDist = d;
}
}
} else {
pars.start = gpxRoute.get(0);
}
Location l = new Location("temp"); //$NON-NLS-1$
l.setLatitude(pars.end.getLatitude());
l.setLongitude(pars.end.getLongitude());
minDist = Integer.MAX_VALUE;
// get in reverse order taking into account ways with cycle
for (int i = gpxRoute.size() - 1; i >= start; i--) {
float d = gpxRoute.get(i).distanceTo(l);
if (d < minDist) {
end = i + 1;
// slightly modify to allow last point to be added
minDist = d - 40;
}
}
ArrayList<Location> sublist = new ArrayList<Location>(gpxRoute.subList(start, end));
if(startI != null) {
startI[0] = start;
}
if(endI != null) {
endI[0] = end;
}
return sublist;
}
protected String getString(ClientContext ctx, int resId){ protected String getString(ClientContext ctx, int resId){
if(ctx == null){ if(ctx == null){
return ""; //$NON-NLS-1$ return ""; //$NON-NLS-1$
@ -370,10 +430,63 @@ public class RouteProvider {
return new RouteCalculationResult(res, null, params, null); return new RouteCalculationResult(res, null, params, null);
} }
protected RouteCalculationResult findVectorMapsRoute(final RouteCalculationParams params) throws IOException { protected RouteCalculationResult findVectorMapsRoute(final RouteCalculationParams params, boolean calcGPXRoute) throws IOException {
BinaryMapIndexReader[] files = params.ctx.getTodoAPI().getRoutingMapFiles(); BinaryMapIndexReader[] files = params.ctx.getTodoAPI().getRoutingMapFiles();
RoutePlannerFrontEnd router = new RoutePlannerFrontEnd(false); RoutePlannerFrontEnd router = new RoutePlannerFrontEnd(false);
OsmandSettings settings = params.ctx.getSettings(); OsmandSettings settings = params.ctx.getSettings();
GeneralRouter generalRouter = SettingsNavigationActivity.getRouter(params.mode);
if(generalRouter == null) {
return applicationModeNotSupported(params);
}
RoutingConfiguration cf = initOsmAndRoutingConfig(params, settings, generalRouter);
if(cf == null){
return applicationModeNotSupported(params);
}
PrecalculatedRouteDirection precalculated = null;
if(calcGPXRoute) {
ArrayList<Location> sublist = findGpxLocations(params, null, null);
LatLon[] latLon = new LatLon[sublist.size()];
for(int k = 0; k < latLon.length; k ++) {
latLon[k] = new LatLon(sublist.get(k).getLatitude(), sublist.get(k).getLongitude());
}
precalculated = PrecalculatedRouteDirection.build(latLon, generalRouter.getMaxDefaultSpeed());
precalculated.setFollowNext(true);
//cf.planRoadDirection = 1;
}
// BUILD context
RoutingContext ctx = router.buildRoutingContext(cf, params.ctx.getInternalAPI().getNativeLibrary(), files,
RouteCalculationMode.NORMAL);
RoutingContext complexCtx = null;
boolean complex = params.mode.isDerivedRoutingFrom(ApplicationMode.CAR) && !settings.DISABLE_COMPLEX_ROUTING.get()
&& precalculated == null;
if(complex) {
complexCtx = router.buildRoutingContext(cf, params.ctx.getInternalAPI().getNativeLibrary(), files,
RouteCalculationMode.COMPLEX);
complexCtx.calculationProgress = params.calculationProgress;
complexCtx.leftSideNavigation = params.leftSide;
}
ctx.leftSideNavigation = params.leftSide;
ctx.calculationProgress = params.calculationProgress;
if(params.previousToRecalculate != null) {
// not used any more
// ctx.previouslyCalculatedRoute = params.previousToRecalculate.getOriginalRoute();
}
LatLon st = new LatLon(params.start.getLatitude(), params.start.getLongitude());
LatLon en = new LatLon(params.end.getLatitude(), params.end.getLongitude());
List<LatLon> inters = new ArrayList<LatLon>();
if (params.intermediates != null) {
inters = new ArrayList<LatLon>(params.intermediates);
}
return calcOfflineRouteImpl(params, router, ctx, complexCtx, st, en, inters, precalculated);
}
private RoutingConfiguration initOsmAndRoutingConfig(final RouteCalculationParams params, OsmandSettings settings,
GeneralRouter generalRouter) throws IOException, FileNotFoundException {
File routingXml = params.ctx.getAppPath(IndexConstants.ROUTING_XML_FILE); File routingXml = params.ctx.getAppPath(IndexConstants.ROUTING_XML_FILE);
RoutingConfiguration.Builder config ; RoutingConfiguration.Builder config ;
if (routingXml.exists() && routingXml.canRead()) { if (routingXml.exists() && routingXml.canRead()) {
@ -393,12 +506,9 @@ public class RouteProvider {
} else if(params.mode.isDerivedRoutingFrom(ApplicationMode.CAR)){ } else if(params.mode.isDerivedRoutingFrom(ApplicationMode.CAR)){
p = GeneralRouterProfile.CAR; p = GeneralRouterProfile.CAR;
} else { } else {
return applicationModeNotSupported(params); return null;
}
GeneralRouter generalRouter = SettingsNavigationActivity.getRouter(params.mode);
if(generalRouter == null) {
return applicationModeNotSupported(params);
} }
Map<String, String> paramsR = new LinkedHashMap<String, String>(); Map<String, String> paramsR = new LinkedHashMap<String, String>();
for(Map.Entry<String, RoutingParameter> e : generalRouter.getParameters().entrySet()){ for(Map.Entry<String, RoutingParameter> e : generalRouter.getParameters().entrySet()){
String key = e.getKey(); String key = e.getKey();
@ -427,32 +537,20 @@ public class RouteProvider {
RoutingConfiguration cf = config.build(p.name().toLowerCase(), params.start.hasBearing() ? RoutingConfiguration cf = config.build(p.name().toLowerCase(), params.start.hasBearing() ?
params.start.getBearing() / 180d * Math.PI : null, params.start.getBearing() / 180d * Math.PI : null,
memoryLimit, paramsR); memoryLimit, paramsR);
boolean complex = params.mode.isDerivedRoutingFrom(ApplicationMode.CAR) && !settings.DISABLE_COMPLEX_ROUTING.get(); return cf;
RoutingContext ctx = router.buildRoutingContext(cf, params.ctx.getInternalAPI().getNativeLibrary(), files, }
RouteCalculationMode.NORMAL);
RoutingContext complexCtx = null;
if(complex) {
complexCtx = router.buildRoutingContext(cf, params.ctx.getInternalAPI().getNativeLibrary(), files,
RouteCalculationMode.COMPLEX); private RouteCalculationResult calcOfflineRouteImpl(final RouteCalculationParams params,
complexCtx.calculationProgress = params.calculationProgress; RoutePlannerFrontEnd router, RoutingContext ctx, RoutingContext complexCtx, LatLon st, LatLon en,
} List<LatLon> inters, PrecalculatedRouteDirection precalculated) throws IOException {
ctx.leftSideNavigation = params.leftSide;
ctx.calculationProgress = params.calculationProgress;
if(params.previousToRecalculate != null) {
// not used any more
// ctx.previouslyCalculatedRoute = params.previousToRecalculate.getOriginalRoute();
}
LatLon st = new LatLon(params.start.getLatitude(), params.start.getLongitude());
LatLon en = new LatLon(params.end.getLatitude(), params.end.getLongitude());
List<LatLon> inters = new ArrayList<LatLon>();
if (params.intermediates != null) {
inters = new ArrayList<LatLon>(params.intermediates);
}
try { try {
List<RouteSegmentResult> result ; List<RouteSegmentResult> result ;
if(complexCtx != null) { if(complexCtx != null) {
try { try {
result = router.searchRoute(complexCtx, st, en, inters); result = router.searchRoute(complexCtx, st, en, inters, precalculated);
// discard ctx and replace with calculated // discard ctx and replace with calculated
ctx = complexCtx; ctx = complexCtx;
} catch(final RuntimeException e) { } catch(final RuntimeException e) {

View file

@ -165,7 +165,7 @@ public class DownloadedRegionsLayer extends OsmandMapLayer {
int right = MapUtils.get31TileNumberX(tileBox.getRightBottomLatLon().getLongitude()); int right = MapUtils.get31TileNumberX(tileBox.getRightBottomLatLon().getLongitude());
int top = MapUtils.get31TileNumberY(tileBox.getLeftTopLatLon().getLatitude()); int top = MapUtils.get31TileNumberY(tileBox.getLeftTopLatLon().getLatitude());
int bottom = MapUtils.get31TileNumberY(tileBox.getRightBottomLatLon().getLatitude()); int bottom = MapUtils.get31TileNumberY(tileBox.getRightBottomLatLon().getLatitude());
final boolean empty = rm.getRenderer().checkIfMapIsEmpty(left, right, top, bottom, tileBox.getZoom()); final boolean empty = rm.getRenderer().isLastMapRenderedEmpty(false);
noMapsPresent = empty; noMapsPresent = empty;
if (!empty && tileBox.getZoom() >= ZOOM_TO_SHOW_MAP_NAMES) { if (!empty && tileBox.getZoom() >= ZOOM_TO_SHOW_MAP_NAMES) {
return Collections.emptyList(); return Collections.emptyList();

View file

@ -393,7 +393,7 @@ public class MapControlsLayer extends OsmandMapLayer {
final AlertDialog.Builder bld = new AlertDialog.Builder(view.getContext()); final AlertDialog.Builder bld = new AlertDialog.Builder(view.getContext());
float scale = view.getZoomScale(); float scale = view.getZoomScale();
int p = (int) ((scale > 0 ? 1 : -1) * Math.round(scale * scale * 100)) + 100; int p = (int) ((scale > 0 ? 1 : -1) * Math.round(scale * scale * 100)) + 100;
final TIntArrayList tlist = new TIntArrayList(new int[] { 75, 100, 150, 200, 300, 400 }); final TIntArrayList tlist = new TIntArrayList(new int[] { 75, 100, 150, 200, 300, 400, 500 });
final List<String> values = new ArrayList<String>(); final List<String> values = new ArrayList<String>();
int i = -1; int i = -1;
for (int k = 0; k <= tlist.size(); k++) { for (int k = 0; k <= tlist.size(); k++) {