Merge branch 'master' of ssh://github.com/osmandapp/Osmand
|
@ -18,6 +18,7 @@ public class RenderingContext {
|
|||
}
|
||||
}
|
||||
|
||||
public int renderedState = 0;
|
||||
// FIELDS OF THAT CLASS ARE USED IN C++
|
||||
public boolean interrupted = false;
|
||||
public boolean nightMode = false;
|
||||
|
|
|
@ -33,6 +33,7 @@ public class BinaryMapDataObject {
|
|||
this.coordinates = coordinates;
|
||||
}
|
||||
|
||||
|
||||
public String getName(){
|
||||
if(objectNames == null){
|
||||
return "";
|
||||
|
|
|
@ -1541,6 +1541,10 @@ public class BinaryMapIndexReader {
|
|||
return top;
|
||||
}
|
||||
|
||||
public int getZoom() {
|
||||
return zoom;
|
||||
}
|
||||
|
||||
public void clearSearchResults(){
|
||||
// recreate whole list to allow GC collect old data
|
||||
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) {
|
||||
if(!encodingRules.containsKey(tag)){
|
||||
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.numberOfAcceptedObjects = 0;
|
||||
req.numberOfAcceptedSubtrees = 0;
|
||||
|
|
|
@ -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) {
|
||||
if (req.intersects(rs.left, rs.top, rs.right, rs.bottom)) {
|
||||
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 {
|
||||
for (RouteSubregion rs : list) {
|
||||
if (req.intersects(rs.left, rs.top, rs.right, rs.bottom)) {
|
||||
|
|
|
@ -61,6 +61,10 @@ public class RouteDataObject {
|
|||
return null;
|
||||
}
|
||||
|
||||
public TIntObjectHashMap<String> getNames() {
|
||||
return names;
|
||||
}
|
||||
|
||||
public String getRef(){
|
||||
if(names != null ) {
|
||||
return names.get(region.refTypeRule);
|
||||
|
|
|
@ -98,7 +98,7 @@ public class BinaryRoutePlanner {
|
|||
graphReverseSegments.size()) * STANDARD_ROAD_IN_QUEUE_OVERHEAD;
|
||||
|
||||
if(TRACE_ROUTING){
|
||||
printRoad(">", segment);
|
||||
printRoad(">", segment, !forwardSearch);
|
||||
}
|
||||
if(segment instanceof FinalRouteSegment) {
|
||||
if(RoutingContext.SHOW_GC_SIZE){
|
||||
|
@ -136,9 +136,9 @@ public class BinaryRoutePlanner {
|
|||
}
|
||||
if (ctx.planRouteIn2Directions()) {
|
||||
forwardSearch = (nonHeuristicSegmentsComparator.compare(graphDirectSegments.peek(), graphReverseSegments.peek()) < 0);
|
||||
if (graphDirectSegments.size() * 1.3 > graphReverseSegments.size()) {
|
||||
if (graphDirectSegments.size() * 2 > graphReverseSegments.size()) {
|
||||
forwardSearch = false;
|
||||
} else if (graphDirectSegments.size() < 1.3 * graphReverseSegments.size()) {
|
||||
} else if (graphDirectSegments.size() < 2 * graphReverseSegments.size()) {
|
||||
forwardSearch = true;
|
||||
}
|
||||
} 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;
|
||||
if(segment.parentRoute != null){
|
||||
pr = " pend="+segment.parentSegmentEnd +" parent=" + segment.parentRoute.road;
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -304,7 +308,7 @@ public class BinaryRoutePlanner {
|
|||
final RouteDataObject road = segment.road;
|
||||
boolean initDirectionAllowed = checkIfInitialMovementAllowedOnSegment(ctx, reverseWaySearch, visitedSegments, segment, road);
|
||||
if(TEST_SPECIFIC && road.getId() == TEST_ID ) {
|
||||
printRoad(" ! " + +segment.distanceFromStart + " ", segment);
|
||||
printRoad(" ! " + +segment.distanceFromStart + " ", segment, reverseWaySearch);
|
||||
}
|
||||
boolean directionAllowed = initDirectionAllowed;
|
||||
if(!directionAllowed) {
|
||||
|
@ -367,15 +371,23 @@ public class BinaryRoutePlanner {
|
|||
// long nt = System.nanoTime();
|
||||
// float devDistance = ctx.precalculatedRouteDirection.getDeviationDistance(x, y);
|
||||
// // 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
|
||||
// segmentDist = segmentDist * (float) Math.pow(1.5, devDistance / 500);
|
||||
// 3. next by method
|
||||
// segmentDist = devDistance ;
|
||||
// ctx.timeNanoToCalcDeviation += (System.nanoTime() - nt);
|
||||
}
|
||||
// could be expensive calculation
|
||||
// 3. get intersected ways
|
||||
final RouteSegment roadNext = ctx.loadRouteSegment(x, y, ctx.config.memoryLimitation - ctx.memoryOverhead);
|
||||
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
|
||||
previous = processIntersections(ctx, graphSegments, visitedSegments, distStartObstacles,
|
||||
|
@ -433,7 +445,7 @@ public class BinaryRoutePlanner {
|
|||
frs.opposite = opposite;
|
||||
graphSegments.add(frs);
|
||||
if(TRACE_ROUTING){
|
||||
printRoad(" >> Final segment : ", frs);
|
||||
printRoad(" >> Final segment : ", frs, reverseWaySearch);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -634,7 +646,7 @@ public class BinaryRoutePlanner {
|
|||
printRoad(" !? distFromStart=" + +distFromStart + " from " + segment.getRoad().getId() +
|
||||
" dir=" + segment.getDirectionAssigned() +
|
||||
" distToEnd=" + distanceToEnd +
|
||||
" segmentPoint="+ segmentPoint + " -- ", next);
|
||||
" segmentPoint="+ segmentPoint + " -- ", next, true);
|
||||
}
|
||||
if (!visitedSegments.containsKey(calculateRoutePointId(next, next.isPositive()))) {
|
||||
if (next.getParentRoute() == null
|
||||
|
@ -643,7 +655,7 @@ public class BinaryRoutePlanner {
|
|||
next.distanceFromStart = distFromStart;
|
||||
next.distanceToEnd = distanceToEnd;
|
||||
if (TRACE_ROUTING) {
|
||||
printRoad(" >>", next);
|
||||
printRoad(" >>", next, null);
|
||||
}
|
||||
// put additional information to recover whole route after
|
||||
next.setParentRoute(segment);
|
||||
|
|
|
@ -6,6 +6,7 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import net.osmand.binary.RouteDataObject;
|
||||
import net.osmand.data.LatLon;
|
||||
import net.osmand.data.QuadPoint;
|
||||
import net.osmand.data.QuadRect;
|
||||
import net.osmand.data.QuadTree;
|
||||
|
@ -18,6 +19,7 @@ public class PrecalculatedRouteDirection {
|
|||
private float minSpeed;
|
||||
private float maxSpeed;
|
||||
private float[] tms;
|
||||
private boolean followNext;
|
||||
private static final int SHIFT = (1 << (31 - 17));
|
||||
private static final int[] SHIFTS = new int[]{1 << (31 - 15), 1 << (31 - 13), 1 << (31 - 12),
|
||||
1 << (31 - 11), 1 << (31 - 7)};
|
||||
|
@ -42,6 +44,11 @@ public class PrecalculatedRouteDirection {
|
|||
init(ls);
|
||||
}
|
||||
|
||||
private PrecalculatedRouteDirection(LatLon[] ls, float maxSpeed) {
|
||||
this.maxSpeed = maxSpeed;
|
||||
init(ls);
|
||||
}
|
||||
|
||||
private PrecalculatedRouteDirection(PrecalculatedRouteDirection parent, int s1, int s2) {
|
||||
this.minSpeed = parent.minSpeed;
|
||||
this.maxSpeed = parent.maxSpeed;
|
||||
|
@ -81,6 +88,10 @@ public class PrecalculatedRouteDirection {
|
|||
return null;
|
||||
}
|
||||
|
||||
public static PrecalculatedRouteDirection build(LatLon[] ls, float maxSpeed){
|
||||
return new PrecalculatedRouteDirection(ls, maxSpeed);
|
||||
}
|
||||
|
||||
|
||||
private void init(List<RouteSegmentResult> ls) {
|
||||
TIntArrayList px = new TIntArrayList();
|
||||
|
@ -105,6 +116,19 @@ public class PrecalculatedRouteDirection {
|
|||
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) {
|
||||
float totaltm = 0;
|
||||
List<Float> times = new ArrayList<Float>();
|
||||
|
@ -217,6 +241,15 @@ public class PrecalculatedRouteDirection {
|
|||
return ((long) x31) << 32l + ((long)y31);
|
||||
}
|
||||
|
||||
public void setFollowNext(boolean followNext) {
|
||||
this.followNext = followNext;
|
||||
}
|
||||
|
||||
public boolean isFollowNext() {
|
||||
return followNext;
|
||||
}
|
||||
|
||||
|
||||
public PrecalculatedRouteDirection adopt(RoutingContext ctx) {
|
||||
int ind1 = getIndex(ctx.startX, ctx.startY);
|
||||
int ind2 = getIndex(ctx.targetX, ctx.targetY);
|
||||
|
@ -235,6 +268,7 @@ public class PrecalculatedRouteDirection {
|
|||
// routeDirection.startY31 = ctx.startY;
|
||||
routeDirection.endPoint = calc(ctx.targetX, ctx.targetX);
|
||||
routeDirection.endFinishTime = (float) BinaryRoutePlanner.squareRootDist(pointsX[ind2], pointsY[ind2], ctx.targetX, ctx.targetY) / maxSpeed;
|
||||
routeDirection.followNext = followNext;
|
||||
|
||||
// routeDirection.endX31 = ctx.targetX;
|
||||
// routeDirection.endY31 = ctx.targetY;
|
||||
|
|
|
@ -84,13 +84,17 @@ public class RoutePlannerFrontEnd {
|
|||
}
|
||||
|
||||
|
||||
|
||||
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) {
|
||||
ctx.calculationProgress = new RouteCalculationProgress();
|
||||
}
|
||||
boolean intermediatesEmpty = intermediates == null || intermediates.isEmpty();
|
||||
PrecalculatedRouteDirection routeDirection = null;
|
||||
double maxDistance = MapUtils.getDistance(start, end);
|
||||
if(!intermediatesEmpty) {
|
||||
LatLon b = start;
|
||||
|
@ -99,7 +103,8 @@ public class RoutePlannerFrontEnd {
|
|||
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);
|
||||
nctx.calculationProgress = ctx.calculationProgress ;
|
||||
List<RouteSegmentResult> ls = searchRoute(nctx, start, end, intermediates);
|
||||
|
|
|
@ -9,6 +9,7 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
|
||||
import net.osmand.binary.BinaryMapIndexReader;
|
||||
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteTypeRule;
|
||||
import net.osmand.binary.RouteDataObject;
|
||||
import net.osmand.data.LatLon;
|
||||
import net.osmand.router.BinaryRoutePlanner.FinalRouteSegment;
|
||||
|
@ -295,11 +296,30 @@ public class RouteResultPreparation {
|
|||
additional.append("description = \"").append(res.getDescription()).append("\" ");
|
||||
println(MessageFormat.format("\t<segment id=\"{0}\" start=\"{1}\" end=\"{2}\" {3}/>", (res.getObject().getId()) + "",
|
||||
res.getStartPointIndex() + "", res.getEndPointIndex() + "", additional.toString()));
|
||||
printAdditionalPointInfo(res);
|
||||
}
|
||||
}
|
||||
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) {
|
||||
int prevSegment = -1;
|
||||
|
|
BIN
OsmAnd/res/drawable-hdpi/ic_action_truck_dark.png
Normal file
After Width: | Height: | Size: 859 B |
BIN
OsmAnd/res/drawable-hdpi/ic_action_truck_light.png
Normal file
After Width: | Height: | Size: 998 B |
BIN
OsmAnd/res/drawable-hdpi/ic_truck.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
OsmAnd/res/drawable-mdpi/ic_action_truck_dark.png
Normal file
After Width: | Height: | Size: 528 B |
BIN
OsmAnd/res/drawable-mdpi/ic_action_truck_light.png
Normal file
After Width: | Height: | Size: 649 B |
BIN
OsmAnd/res/drawable-mdpi/ic_truck.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
OsmAnd/res/drawable-xhdpi/ic_action_truck_dark.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
OsmAnd/res/drawable-xhdpi/ic_action_truck_light.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
OsmAnd/res/drawable-xhdpi/ic_truck.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
|
@ -9,6 +9,15 @@
|
|||
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
|
||||
-->
|
||||
<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="routing_preferences_descr">Routing preferences</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="specified_dir_doesnt_exist">Can not find specified 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="build_installed">Build {0} successfully installed ({1}).</string>
|
||||
<string name="downloading_build">Downloading build…</string>
|
||||
|
|
|
@ -45,6 +45,10 @@ public class ApplicationMode {
|
|||
carLocation().parent(CAR).
|
||||
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 {
|
||||
ApplicationMode[] exceptPedestrian = new ApplicationMode[] { DEFAULT, CAR, BICYCLE, BOAT, AIRCRAFT };
|
||||
ApplicationMode[] exceptAirBoat = new ApplicationMode[] { DEFAULT, CAR, BICYCLE};
|
||||
|
|
|
@ -8,8 +8,8 @@ import net.osmand.CallbackWithObject;
|
|||
import net.osmand.Location;
|
||||
import net.osmand.access.AccessibleToast;
|
||||
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.GPXRouteParamsBuilder;
|
||||
import net.osmand.plus.routing.RoutingHelper;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.AlertDialog.Builder;
|
||||
|
@ -72,8 +72,11 @@ public class OsmAndLocationSimulation {
|
|||
new CallbackWithObject<GPXUtilities.GPXFile>() {
|
||||
@Override
|
||||
public boolean processResult(GPXUtilities.GPXFile result) {
|
||||
GPXRouteParams prms = new RouteProvider.GPXRouteParams(result, false, ch
|
||||
.isChecked(), app.getSettings());
|
||||
GPXRouteParamsBuilder builder = GPXRouteParams.GPXRouteParamsBuilder.newBuilder(result, app.getSettings());
|
||||
if(ch.isChecked()){
|
||||
builder.announceWaypoints();
|
||||
}
|
||||
GPXRouteParams prms = builder.build();
|
||||
startAnimationThread(app.getRoutingHelper(), ma, prms.getPoints(), true,
|
||||
speedup.getProgress() + 1);
|
||||
return true;
|
||||
|
|
|
@ -651,7 +651,7 @@ public class OsmandSettings {
|
|||
|
||||
public float getSettingsZoomScale(float density){
|
||||
// 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_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 File getExternalStorageDirectory() {
|
||||
return new File(settingsAPI.getString(globalPreferences,EXTERNAL_STORAGE_DIR,
|
||||
ctx.getExternalServiceAPI().getExternalStorageDirectory()));
|
||||
String defaultLocation = 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) {
|
||||
|
|
|
@ -11,6 +11,8 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import net.osmand.IndexConstants;
|
||||
import net.osmand.access.AccessibleAlertBuilder;
|
||||
import net.osmand.access.AccessibleToast;
|
||||
import net.osmand.plus.ClientContext;
|
||||
import net.osmand.plus.OsmandApplication;
|
||||
|
@ -18,6 +20,7 @@ import net.osmand.plus.OsmandPlugin;
|
|||
import net.osmand.plus.OsmandSettings;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.Version;
|
||||
import net.osmand.plus.activities.SettingsGeneralActivity.MoveFilesToDifferentDirectory;
|
||||
import net.osmand.plus.base.BasicProgressAsyncTask;
|
||||
import net.osmand.plus.base.SuggestExternalDirectoryDialog;
|
||||
import net.osmand.plus.download.DownloadActivityType;
|
||||
|
@ -36,6 +39,7 @@ import android.content.DialogInterface.OnClickListener;
|
|||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask.Status;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
|
@ -89,6 +93,7 @@ public class DownloadIndexActivity extends OsmandExpandableListActivity {
|
|||
private TextView progressPercent;
|
||||
private ImageView cancel;
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
@ -162,7 +167,10 @@ public class DownloadIndexActivity extends OsmandExpandableListActivity {
|
|||
DownloadIndexAdapter adapter = new DownloadIndexAdapter(this, list);
|
||||
setListAdapter(adapter);
|
||||
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) {
|
||||
showDialogOfFreeDownloadsIfNeeded();
|
||||
}
|
||||
|
@ -182,7 +190,39 @@ public class DownloadIndexActivity extends OsmandExpandableListActivity {
|
|||
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
|
||||
|
|
|
@ -367,10 +367,10 @@ public class MapActivity extends AccessibleActivity {
|
|||
|
||||
public void changeZoom(int stp){
|
||||
// delta = Math.round(delta * OsmandMapTileView.ZOOM_DELTA) * OsmandMapTileView.ZOOM_DELTA_1;
|
||||
boolean changeLocation = true;
|
||||
if (settings.AUTO_ZOOM_MAP.get() == AutoZoomMap.NONE) {
|
||||
changeLocation = false;
|
||||
}
|
||||
boolean changeLocation = false;
|
||||
// if (settings.AUTO_ZOOM_MAP.get() == AutoZoomMap.NONE) {
|
||||
// changeLocation = false;
|
||||
// }
|
||||
final int newZoom = mapView.getZoom() + stp;
|
||||
mapView.getAnimatedDraggingThread().startZooming(newZoom, changeLocation);
|
||||
if (app.accessibilityEnabled())
|
||||
|
|
|
@ -4,30 +4,27 @@ package net.osmand.plus.activities;
|
|||
import java.io.File;
|
||||
import java.util.Date;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.widget.ScrollView;
|
||||
import net.osmand.IndexConstants;
|
||||
import net.osmand.plus.OsmandApplication;
|
||||
import net.osmand.plus.OsmandPlugin;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.Version;
|
||||
import net.osmand.plus.rastermaps.SettingsRasterMapsActivity;
|
||||
import net.osmand.util.Algorithms;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.AlertDialog.Builder;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceScreen;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.text.SpannableString;
|
||||
import android.text.format.DateFormat;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class SettingsActivity extends SettingsBaseActivity {
|
||||
|
|
|
@ -2,23 +2,32 @@ package net.osmand.plus.activities;
|
|||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import net.osmand.CallbackWithObject;
|
||||
import net.osmand.IProgress;
|
||||
import net.osmand.IndexConstants;
|
||||
import net.osmand.access.AccessibleToast;
|
||||
import net.osmand.plus.ApplicationMode;
|
||||
import net.osmand.plus.ClientContext;
|
||||
import net.osmand.plus.OsmandSettings;
|
||||
import net.osmand.plus.OsmandSettings.DrivingRegion;
|
||||
import net.osmand.plus.OsmandSettings.MetricsConstants;
|
||||
import net.osmand.plus.ProgressDialogImplementation;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.Version;
|
||||
import net.osmand.plus.base.SuggestExternalDirectoryDialog;
|
||||
import net.osmand.plus.render.NativeOsmandLibrary;
|
||||
import net.osmand.plus.voice.CommandPlayer;
|
||||
import net.osmand.render.RenderingRulesStorage;
|
||||
import net.osmand.util.Algorithms;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.AlertDialog.Builder;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.Intent;
|
||||
|
@ -101,12 +110,12 @@ public class SettingsGeneralActivity extends SettingsBaseActivity {
|
|||
dialog.dismiss();
|
||||
showOtherDialog();
|
||||
}
|
||||
}, new Runnable() {
|
||||
}, new CallbackWithObject<String>() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(SettingsGeneralActivity.this, getString(R.string.application_dir_change_warning),
|
||||
Toast.LENGTH_LONG).show();
|
||||
reloadIndexes();
|
||||
public boolean processResult(String result) {
|
||||
warnAboutChangingStorage(result);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return false;
|
||||
|
@ -265,6 +274,97 @@ public class SettingsGeneralActivity extends SettingsBaseActivity {
|
|||
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) {
|
||||
final String newDir = newValue != null ? newValue.trim() : newValue;
|
||||
File path = new File(newDir);
|
||||
|
@ -273,22 +373,42 @@ public class SettingsGeneralActivity extends SettingsBaseActivity {
|
|||
AccessibleToast.makeText(this, R.string.specified_dir_doesnt_exist, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
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() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
MoveFilesToDifferentDirectory task =
|
||||
new MoveFilesToDifferentDirectory(SettingsGeneralActivity.this,
|
||||
new File(settings.getExternalStorageDirectory(), IndexConstants.APP_DIR), new File(newDir,
|
||||
IndexConstants.APP_DIR));
|
||||
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.show();
|
||||
}
|
||||
|
||||
private void updateSettingsToNewDir(final String newDir) {
|
||||
// edit the preference
|
||||
settings.setExternalStorageDirectory(newDir);
|
||||
getMyApplication().getResourceManager().resetStoreDirectory();
|
||||
reloadIndexes();
|
||||
updateApplicationDirTextAndSummary();
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(R.string.default_buttons_cancel, null);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
public void reloadIndexes() {
|
||||
setSupportProgressBarIndeterminateVisibility(true);
|
||||
|
|
|
@ -233,7 +233,7 @@ public class SettingsNavigationActivity extends SettingsBaseActivity {
|
|||
if (MORE_VALUE.equals(newValue)) {
|
||||
// listPref.set(oldValue); // revert the change..
|
||||
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);
|
||||
} else {
|
||||
super.onPreferenceChange(preference, newValue);
|
||||
|
|
|
@ -19,6 +19,7 @@ import net.osmand.plus.TargetPointsHelper;
|
|||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.routing.RouteProvider.GPXRouteParams;
|
||||
import net.osmand.plus.routing.RouteProvider.RouteService;
|
||||
import net.osmand.plus.routing.RouteProvider.GPXRouteParams.GPXRouteParamsBuilder;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.AlertDialog.Builder;
|
||||
|
@ -66,10 +67,11 @@ public class NavigateAction {
|
|||
public boolean navigateUsingGPX(final ApplicationMode appMode, final LatLon endForRouting,
|
||||
final GPXFile result) {
|
||||
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),
|
||||
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() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
|
||||
|
@ -83,9 +85,20 @@ public class NavigateAction {
|
|||
boolean passWholeWay = props[2];
|
||||
boolean useDestination = props[1];
|
||||
boolean announceGpxWpt = props[3];
|
||||
boolean calculateOsmAndRoute = props[4];
|
||||
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();
|
||||
if(passWholeWay && loc != null){
|
||||
gpxRoute.setStartPoint(loc);
|
||||
|
|
|
@ -16,6 +16,7 @@ import net.osmand.plus.R;
|
|||
import net.osmand.plus.TargetPointsHelper;
|
||||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.routing.RouteProvider.GPXRouteParams;
|
||||
import net.osmand.plus.routing.RouteProvider.GPXRouteParams.GPXRouteParamsBuilder;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.AlertDialog.Builder;
|
||||
import android.content.DialogInterface;
|
||||
|
@ -123,14 +124,26 @@ public class FailSafeFuntions {
|
|||
|
||||
@Override
|
||||
protected void onPostExecute(GPXFile result) {
|
||||
final GPXRouteParams gpxRoute = result == null ? null : new GPXRouteParams(result, false,
|
||||
settings.SPEAK_GPX_WPT.get(), settings);
|
||||
final GPXRouteParams gpxRoute;
|
||||
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();
|
||||
net.osmand.Location startPoint = gpxRoute == null ? null : gpxRoute.getStartPointForRoute();
|
||||
if (endPoint == null) {
|
||||
notRestoreRoutingMode(ma, app);
|
||||
} else {
|
||||
ma.followRoute(settings.getApplicationMode(), endPoint, targetPoints.getIntermediatePoints(), startPoint, gpxRoute);
|
||||
ma.followRoute(settings.getApplicationMode(), endPoint,
|
||||
targetPoints.getIntermediatePoints(), startPoint, gpxRoute);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@ import java.io.InputStream;
|
|||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
|
||||
import net.osmand.CallbackWithObject;
|
||||
import net.osmand.plus.OsmandApplication;
|
||||
import net.osmand.plus.R;
|
||||
|
||||
|
@ -17,7 +18,7 @@ public class SuggestExternalDirectoryDialog {
|
|||
|
||||
|
||||
public static boolean showDialog(Activity a, final DialogInterface.OnClickListener otherListener,
|
||||
final Runnable reloadListener){
|
||||
final CallbackWithObject<String> selector){
|
||||
final boolean showOther = otherListener != null;
|
||||
final OsmandApplication app = (OsmandApplication) a.getApplication();
|
||||
Builder bld = new AlertDialog.Builder(a);
|
||||
|
@ -46,10 +47,11 @@ public class SuggestExternalDirectoryDialog {
|
|||
otherListener.onClick(dialog, which);
|
||||
} else {
|
||||
dialog.dismiss();
|
||||
if(selector != null) {
|
||||
selector.processResult(extMounts[which]);
|
||||
} else {
|
||||
app.getSettings().setExternalStorageDirectory(extMounts[which]);
|
||||
app.getResourceManager().resetStoreDirectory();
|
||||
if(reloadListener != null) {
|
||||
reloadListener.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import java.util.Date;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
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 {
|
||||
Date d = simpleDateFormat.parse(date);
|
||||
return AndroidUtils.formatDate(ctx, d.getTime());
|
||||
|
|
|
@ -10,6 +10,7 @@ import java.util.ArrayList;
|
|||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import net.osmand.IndexConstants;
|
||||
import net.osmand.PlatformUtil;
|
||||
|
@ -218,7 +219,9 @@ public class IndexItem implements Comparable<IndexItem> {
|
|||
entry.zipStream = zipStream;
|
||||
entry.unzipFolder = unzipDir;
|
||||
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();
|
||||
} catch (ParseException e1) {
|
||||
log.error("ParseException", e1);
|
||||
|
|
|
@ -301,7 +301,13 @@ public class OpenstreetmapRemoteUtil implements OpenstreetmapUtil {
|
|||
OsmBaseStorage st = new OsmBaseStorage();
|
||||
st.parseOSM(new ByteArrayInputStream(res.getBytes("UTF-8")), null, null, true); //$NON-NLS-1$
|
||||
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);
|
||||
return entityInfo;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package net.osmand.plus.render;
|
||||
|
||||
|
||||
import gnu.trove.iterator.TIntObjectIterator;
|
||||
import gnu.trove.list.TLongList;
|
||||
import gnu.trove.list.array.TIntArrayList;
|
||||
import gnu.trove.list.array.TLongArrayList;
|
||||
import gnu.trove.map.hash.TIntObjectHashMap;
|
||||
import gnu.trove.set.TLongSet;
|
||||
import gnu.trove.set.hash.TLongHashSet;
|
||||
|
||||
|
@ -20,18 +22,19 @@ import java.util.Set;
|
|||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.osmand.IProgress;
|
||||
import net.osmand.ResultMatcher;
|
||||
import net.osmand.NativeLibrary.NativeSearchResult;
|
||||
import net.osmand.PlatformUtil;
|
||||
import net.osmand.ResultMatcher;
|
||||
import net.osmand.access.AccessibleToast;
|
||||
import net.osmand.binary.BinaryMapDataObject;
|
||||
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.SearchRequest;
|
||||
import net.osmand.binary.BinaryMapIndexReader.TagValuePair;
|
||||
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion;
|
||||
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteSubregion;
|
||||
import net.osmand.binary.RouteDataObject;
|
||||
import net.osmand.data.QuadPointDouble;
|
||||
import net.osmand.data.QuadRect;
|
||||
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
|
||||
public static boolean checkForDuplicateObjectIds = true;
|
||||
|
||||
|
||||
private final static Log log = PlatformUtil.getLog(MapRenderRepositories.class);
|
||||
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 Map<String, BinaryMapIndexReader> files = new ConcurrentHashMap<String, BinaryMapIndexReader>();
|
||||
private Set<String> nativeFiles = new HashSet<String>();
|
||||
|
@ -91,6 +94,8 @@ public class MapRenderRepositories {
|
|||
private RotatedTileBox prevBmpLocation = null;
|
||||
// already rendered bitmap
|
||||
private Bitmap prevBmp;
|
||||
// to track necessity of map download (1 (if basemap) + 2 (if normal map)
|
||||
private int previousRenderedState;
|
||||
|
||||
// location of rendered bitmap
|
||||
private RotatedTileBox bmpLocation = null;
|
||||
|
@ -98,6 +103,7 @@ public class MapRenderRepositories {
|
|||
private Bitmap bmp;
|
||||
// Field used in C++
|
||||
private boolean interrupted = false;
|
||||
private int renderedState = 0; // (1 (if basemap) + 2 (if normal map)
|
||||
private RenderingContext currentRenderingContext;
|
||||
private SearchRequest<BinaryMapDataObject> searchRequest;
|
||||
private OsmandSettings prefs;
|
||||
|
@ -252,7 +258,7 @@ public class MapRenderRepositories {
|
|||
}
|
||||
|
||||
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()) {
|
||||
resultHandler.deleteNativeResult();
|
||||
return false;
|
||||
|
@ -268,6 +274,77 @@ public class MapRenderRepositories {
|
|||
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) {
|
||||
double cBottomLatitude = dataBox.bottom;
|
||||
double cTopLatitude = dataBox.top;
|
||||
|
@ -277,18 +354,102 @@ public class MapRenderRepositories {
|
|||
long now = System.currentTimeMillis();
|
||||
|
||||
System.gc(); // to clear previous objects
|
||||
int count = 0;
|
||||
ArrayList<BinaryMapDataObject> tempResult = 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> basemapCoastLines = new ArrayList<BinaryMapDataObject>();
|
||||
int leftX = MapUtils.get31TileNumberX(cLeftLongitude);
|
||||
int rightX = MapUtils.get31TileNumberX(cRightLongitude);
|
||||
int bottomY = MapUtils.get31TileNumberY(cBottomLatitude);
|
||||
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
|
||||
public boolean accept(TIntArrayList types, BinaryMapIndexReader.MapIndex root) {
|
||||
for (int j = 0; j < types.size(); j++) {
|
||||
|
@ -318,11 +479,10 @@ public class MapRenderRepositories {
|
|||
if (zoom > 16) {
|
||||
searchFilter = null;
|
||||
}
|
||||
boolean ocean = false;
|
||||
boolean land = false;
|
||||
MapIndex mi = null;
|
||||
searchRequest = BinaryMapIndexReader.buildSearchRequest(leftX, rightX, topY, bottomY, zoom, searchFilter);
|
||||
for (BinaryMapIndexReader c : files.values()) {
|
||||
boolean basemap = c.isBasemap();
|
||||
searchRequest.clearSearchResults();
|
||||
List<BinaryMapDataObject> res;
|
||||
try {
|
||||
|
@ -331,99 +491,52 @@ public class MapRenderRepositories {
|
|||
res = new ArrayList<BinaryMapDataObject>();
|
||||
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) {
|
||||
if (checkForDuplicateObjectIds) {
|
||||
if (checkForDuplicateObjectIds && !basemap) {
|
||||
if (ids.contains(r.getId()) && r.getId() > 0) {
|
||||
// do not add object twice
|
||||
continue;
|
||||
}
|
||||
ids.add(r.getId());
|
||||
}
|
||||
count++;
|
||||
count[0]++;
|
||||
|
||||
if (r.containsType(r.getMapIndex().coastlineEncodingType)) {
|
||||
if (c.isBasemap()) {
|
||||
if (basemap) {
|
||||
basemapCoastLines.add(r);
|
||||
} else {
|
||||
coastLines.add(r);
|
||||
}
|
||||
} else {
|
||||
// do not mess coastline and other types
|
||||
if (c.isBasemap()) {
|
||||
if (basemap) {
|
||||
basemapResult.add(r);
|
||||
} else {
|
||||
tempResult.add(r);
|
||||
}
|
||||
}
|
||||
if (checkWhetherInterrupted()) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (searchRequest.isOcean()) {
|
||||
mi = c.getMapIndexes().get(0);
|
||||
ocean = true;
|
||||
ocean[0] = true;
|
||||
}
|
||||
if (searchRequest.isLand()) {
|
||||
mi = c.getMapIndexes().get(0);
|
||||
land = true;
|
||||
land[0] = true;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
return mi;
|
||||
}
|
||||
|
||||
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 isCancelled() {
|
||||
return !empty[0];
|
||||
public boolean isLastMapRenderedEmpty(boolean checkBaseMap){
|
||||
if(checkBaseMap) {
|
||||
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) {
|
||||
|
@ -539,10 +605,10 @@ public class MapRenderRepositories {
|
|||
// prevent editing
|
||||
requestedBox = new RotatedTileBox(tileRect);
|
||||
|
||||
|
||||
// calculate data box
|
||||
QuadRect dataBox = requestedBox.getLatLonBounds();
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
if (cObjectsBox.left > dataBox.left || cObjectsBox.top > dataBox.top || cObjectsBox.right < dataBox.right
|
||||
|| cObjectsBox.bottom < dataBox.bottom || (nativeLib != null) == (cNativeObjects == null)) {
|
||||
// increase data box in order for rotate
|
||||
|
@ -556,6 +622,7 @@ public class MapRenderRepositories {
|
|||
dataBox.bottom -= hi;
|
||||
}
|
||||
validateLatLonBox(dataBox);
|
||||
renderedState = 0;
|
||||
boolean loaded;
|
||||
if(nativeLib != null) {
|
||||
cObjects = new LinkedList<BinaryMapDataObject>();
|
||||
|
@ -618,6 +685,7 @@ public class MapRenderRepositories {
|
|||
Bitmap reuse = prevBmp;
|
||||
this.prevBmp = this.bmp;
|
||||
this.prevBmpLocation = this.bmpLocation;
|
||||
this.previousRenderedState = renderedState;
|
||||
if (reuse != null && reuse.getWidth() == currentRenderingContext.width && reuse.getHeight() == currentRenderingContext.height) {
|
||||
bmp = reuse;
|
||||
bmp.eraseColor(currentRenderingContext.defaultColor);
|
||||
|
@ -726,6 +794,7 @@ public class MapRenderRepositories {
|
|||
cObjectsBox = new QuadRect();
|
||||
|
||||
requestedBox = prevBmpLocation = null;
|
||||
previousRenderedState = 0;
|
||||
// Do not clear main bitmap to not cause a screen refresh
|
||||
// prevBmp = null;
|
||||
// bmp = null;
|
||||
|
|
|
@ -3,6 +3,7 @@ package net.osmand.plus.routing;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringReader;
|
||||
|
@ -48,6 +49,7 @@ import net.osmand.router.GeneralRouter;
|
|||
import net.osmand.router.GeneralRouter.GeneralRouterProfile;
|
||||
import net.osmand.router.GeneralRouter.RoutingParameter;
|
||||
import net.osmand.router.GeneralRouter.RoutingParameterType;
|
||||
import net.osmand.router.PrecalculatedRouteDirection;
|
||||
import net.osmand.router.RoutePlannerFrontEnd;
|
||||
import net.osmand.router.RoutePlannerFrontEnd.RouteCalculationMode;
|
||||
import net.osmand.router.RouteSegmentResult;
|
||||
|
@ -115,9 +117,9 @@ public class RouteProvider {
|
|||
List<Location> points = new ArrayList<Location>();
|
||||
List<RouteDirectionInfo> directions;
|
||||
DataTileManager<WptPt> wpt;
|
||||
boolean calculateOsmAndRoute = false;
|
||||
|
||||
public GPXRouteParams(GPXFile file, boolean reverse, boolean announceWaypoints, OsmandSettings settings){
|
||||
prepareEverything(file, reverse, announceWaypoints, settings.DRIVING_REGION.get().leftHandDriving);
|
||||
private GPXRouteParams(){
|
||||
}
|
||||
|
||||
public void setStartPoint(Location startPoint) {
|
||||
|
@ -144,6 +146,45 @@ public class RouteProvider {
|
|||
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){
|
||||
if(file.isCloudmadeRouteFile() || OSMAND_ROUTER.equals(file.author)){
|
||||
directions = parseCloudmadeRoute(points, file, OSMAND_ROUTER.equals(file.author), leftSide, 10);
|
||||
|
@ -210,7 +251,10 @@ public class RouteProvider {
|
|||
}
|
||||
try {
|
||||
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);
|
||||
} else if (params.type == RouteService.YOURS) {
|
||||
res = findYOURSRoute(params);
|
||||
|
@ -218,8 +262,6 @@ public class RouteProvider {
|
|||
res = findORSRoute(params);
|
||||
} else if (params.type == RouteService.OSRM) {
|
||||
res = findOSRMRoute(params);
|
||||
} else if (params.type == RouteService.OSMAND) {
|
||||
res = findVectorMapsRoute(params);
|
||||
} else if (params.type == RouteService.BROUTER) {
|
||||
res = findBROUTERRoute(params);
|
||||
} else {
|
||||
|
@ -246,45 +288,20 @@ public class RouteProvider {
|
|||
private RouteCalculationResult calculateGpxRoute(RouteCalculationParams pars) {
|
||||
RouteCalculationResult res;
|
||||
// get the closest point to start and to end
|
||||
float minDist = Integer.MAX_VALUE;
|
||||
int startI = 0;
|
||||
GPXRouteParams params = pars.gpxRoute;
|
||||
List<Location> gpxRoute = params.points;
|
||||
int endI = 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) {
|
||||
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));
|
||||
int[] startI = new int[]{0};
|
||||
int[] endI = new int[]{gpxRoute.size()};
|
||||
ArrayList<Location> sublist = findGpxLocations(pars, startI, endI);
|
||||
pars.intermediates = null;
|
||||
if(params.directions == null){
|
||||
res = new RouteCalculationResult(sublist, null, pars, params.wpt);
|
||||
} else {
|
||||
List<RouteDirectionInfo> subdirections = new ArrayList<RouteDirectionInfo>();
|
||||
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());
|
||||
ch.routePointOffset = info.routePointOffset - startI;
|
||||
ch.routePointOffset = info.routePointOffset - startI[0];
|
||||
ch.setDescriptionRoute(info.getDescriptionRoute());
|
||||
|
||||
// recalculate
|
||||
|
@ -298,6 +315,49 @@ public class RouteProvider {
|
|||
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){
|
||||
if(ctx == null){
|
||||
return ""; //$NON-NLS-1$
|
||||
|
@ -370,10 +430,63 @@ public class RouteProvider {
|
|||
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();
|
||||
RoutePlannerFrontEnd router = new RoutePlannerFrontEnd(false);
|
||||
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);
|
||||
RoutingConfiguration.Builder config ;
|
||||
if (routingXml.exists() && routingXml.canRead()) {
|
||||
|
@ -393,12 +506,9 @@ public class RouteProvider {
|
|||
} else if(params.mode.isDerivedRoutingFrom(ApplicationMode.CAR)){
|
||||
p = GeneralRouterProfile.CAR;
|
||||
} else {
|
||||
return applicationModeNotSupported(params);
|
||||
}
|
||||
GeneralRouter generalRouter = SettingsNavigationActivity.getRouter(params.mode);
|
||||
if(generalRouter == null) {
|
||||
return applicationModeNotSupported(params);
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, String> paramsR = new LinkedHashMap<String, String>();
|
||||
for(Map.Entry<String, RoutingParameter> e : generalRouter.getParameters().entrySet()){
|
||||
String key = e.getKey();
|
||||
|
@ -427,32 +537,20 @@ public class RouteProvider {
|
|||
RoutingConfiguration cf = config.build(p.name().toLowerCase(), params.start.hasBearing() ?
|
||||
params.start.getBearing() / 180d * Math.PI : null,
|
||||
memoryLimit, paramsR);
|
||||
boolean complex = params.mode.isDerivedRoutingFrom(ApplicationMode.CAR) && !settings.DISABLE_COMPLEX_ROUTING.get();
|
||||
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);
|
||||
complexCtx.calculationProgress = params.calculationProgress;
|
||||
}
|
||||
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 cf;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private RouteCalculationResult calcOfflineRouteImpl(final RouteCalculationParams params,
|
||||
RoutePlannerFrontEnd router, RoutingContext ctx, RoutingContext complexCtx, LatLon st, LatLon en,
|
||||
List<LatLon> inters, PrecalculatedRouteDirection precalculated) throws IOException {
|
||||
try {
|
||||
List<RouteSegmentResult> result ;
|
||||
if(complexCtx != null) {
|
||||
try {
|
||||
result = router.searchRoute(complexCtx, st, en, inters);
|
||||
result = router.searchRoute(complexCtx, st, en, inters, precalculated);
|
||||
// discard ctx and replace with calculated
|
||||
ctx = complexCtx;
|
||||
} catch(final RuntimeException e) {
|
||||
|
|
|
@ -165,7 +165,7 @@ public class DownloadedRegionsLayer extends OsmandMapLayer {
|
|||
int right = MapUtils.get31TileNumberX(tileBox.getRightBottomLatLon().getLongitude());
|
||||
int top = MapUtils.get31TileNumberY(tileBox.getLeftTopLatLon().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;
|
||||
if (!empty && tileBox.getZoom() >= ZOOM_TO_SHOW_MAP_NAMES) {
|
||||
return Collections.emptyList();
|
||||
|
|
|
@ -393,7 +393,7 @@ public class MapControlsLayer extends OsmandMapLayer {
|
|||
final AlertDialog.Builder bld = new AlertDialog.Builder(view.getContext());
|
||||
float scale = view.getZoomScale();
|
||||
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>();
|
||||
int i = -1;
|
||||
for (int k = 0; k <= tlist.size(); k++) {
|
||||
|
|