Refactor route / gpx geometry
This commit is contained in:
parent
1e22187dde
commit
151f2f31c6
79 changed files with 3029 additions and 2502 deletions
|
@ -2,27 +2,16 @@ package net.osmand.router;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Locale;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
|
||||
import gnu.trove.iterator.TIntIterator;
|
||||
import gnu.trove.list.array.TIntArrayList;
|
||||
import gnu.trove.map.hash.TIntObjectHashMap;
|
||||
import gnu.trove.map.hash.TLongObjectHashMap;
|
||||
import net.osmand.NativeLibrary;
|
||||
import net.osmand.binary.BinaryMapIndexReader;
|
||||
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
|
||||
import net.osmand.data.IncompleteTransportRoute;
|
||||
|
||||
import net.osmand.data.LatLon;
|
||||
import net.osmand.data.QuadRect;
|
||||
import net.osmand.data.TransportRoute;
|
||||
|
@ -31,9 +20,6 @@ import net.osmand.data.TransportStop;
|
|||
import net.osmand.data.TransportStopExit;
|
||||
import net.osmand.osm.edit.Node;
|
||||
import net.osmand.osm.edit.Way;
|
||||
import net.osmand.router.TransportRoutePlanner.TransportRouteResult;
|
||||
import net.osmand.router.TransportRoutePlanner.TransportRouteResultSegment;
|
||||
import net.osmand.router.TransportRoutePlanner.TransportRouteSegment;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
||||
public class TransportRoutePlanner {
|
||||
|
@ -224,11 +210,10 @@ public class TransportRoutePlanner {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private List<TransportRouteResult> prepareResults(TransportRoutingContext ctx, List<TransportRouteSegment> results) {
|
||||
Collections.sort(results, new SegmentsComparator(ctx));
|
||||
List<TransportRouteResult> lst = new ArrayList<TransportRouteResult>();
|
||||
System.out.println(String.format("Calculated %.1f seconds, found %d results, visited %d routes / %d stops, loaded %d tiles (%d ms read, %d ms total), loaded ways %d (%d wrong)",
|
||||
System.out.println(String.format(Locale.US, "Calculated %.1f seconds, found %d results, visited %d routes / %d stops, loaded %d tiles (%d ms read, %d ms total), loaded ways %d (%d wrong)",
|
||||
(System.currentTimeMillis() - ctx.startCalcTime) / 1000.0, results.size(),
|
||||
ctx.visitedRoutesCount, ctx.visitedStops,
|
||||
ctx.quadTree.size(), ctx.readTime / (1000 * 1000), ctx.loadTime / (1000 * 1000),
|
||||
|
@ -314,7 +299,6 @@ public class TransportRoutePlanner {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public static class TransportRouteResultSegment {
|
||||
|
||||
private static final boolean DISPLAY_FULL_SEGMENT_ROUTE = false;
|
||||
|
@ -487,156 +471,12 @@ public class TransportRoutePlanner {
|
|||
}
|
||||
}
|
||||
|
||||
public static class TransportRouteResult {
|
||||
|
||||
List<TransportRouteResultSegment> segments = new ArrayList<TransportRouteResultSegment>(4);
|
||||
double finishWalkDist;
|
||||
double routeTime;
|
||||
private final TransportRoutingConfiguration cfg;
|
||||
|
||||
public TransportRouteResult(TransportRoutingContext ctx) {
|
||||
cfg = ctx.cfg;
|
||||
}
|
||||
|
||||
public TransportRouteResult(TransportRoutingConfiguration cfg) {
|
||||
this.cfg = cfg;
|
||||
}
|
||||
|
||||
public List<TransportRouteResultSegment> getSegments() {
|
||||
return segments;
|
||||
}
|
||||
|
||||
public void setFinishWalkDist(double finishWalkDist) {
|
||||
this.finishWalkDist = finishWalkDist;
|
||||
}
|
||||
|
||||
public void setRouteTime(double routeTime) {
|
||||
this.routeTime = routeTime;
|
||||
}
|
||||
|
||||
public void addSegment(TransportRouteResultSegment seg) {
|
||||
segments.add(seg);
|
||||
}
|
||||
|
||||
public double getWalkDist() {
|
||||
double d = finishWalkDist;
|
||||
for (TransportRouteResultSegment s : segments) {
|
||||
d += s.walkDist;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
public double getFinishWalkDist() {
|
||||
return finishWalkDist;
|
||||
}
|
||||
|
||||
public double getWalkSpeed() {
|
||||
return cfg.walkSpeed;
|
||||
}
|
||||
|
||||
public double getRouteTime() {
|
||||
return routeTime;
|
||||
}
|
||||
|
||||
public int getStops() {
|
||||
int stops = 0;
|
||||
for(TransportRouteResultSegment s : segments) {
|
||||
stops += (s.end - s.start);
|
||||
}
|
||||
return stops;
|
||||
}
|
||||
|
||||
public boolean isRouteStop(TransportStop stop) {
|
||||
for(TransportRouteResultSegment s : segments) {
|
||||
if (s.getTravelStops().contains(stop)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public TransportRouteResultSegment getRouteStopSegment(TransportStop stop) {
|
||||
for(TransportRouteResultSegment s : segments) {
|
||||
if (s.getTravelStops().contains(stop)) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public double getTravelDist() {
|
||||
double d = 0;
|
||||
for (TransportRouteResultSegment s : segments) {
|
||||
d += s.getTravelDist();
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
public double getTravelTime() {
|
||||
double t = 0;
|
||||
for (TransportRouteResultSegment s : segments) {
|
||||
if (cfg.useSchedule) {
|
||||
TransportSchedule sts = s.route.getSchedule();
|
||||
for (int k = s.start; k < s.end; k++) {
|
||||
t += sts.getAvgStopIntervals()[k] * 10;
|
||||
}
|
||||
} else {
|
||||
t += cfg.getBoardingTime();
|
||||
t += s.getTravelTime();
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
public double getWalkTime() {
|
||||
return getWalkDist() / cfg.walkSpeed;
|
||||
}
|
||||
|
||||
public double getChangeTime() {
|
||||
return cfg.getChangeTime();
|
||||
}
|
||||
|
||||
public double getBoardingTime() {
|
||||
return cfg.getBoardingTime();
|
||||
}
|
||||
|
||||
public int getChanges() {
|
||||
return segments.size() - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder bld = new StringBuilder();
|
||||
bld.append(String.format("Route %d stops, %d changes, %.2f min: %.2f m (%.1f min) to walk, %.2f m (%.1f min) to travel\n",
|
||||
getStops(), getChanges(), routeTime / 60, getWalkDist(), getWalkTime() / 60.0,
|
||||
getTravelDist(), getTravelTime() / 60.0));
|
||||
for(int i = 0; i < segments.size(); i++) {
|
||||
TransportRouteResultSegment s = segments.get(i);
|
||||
String time = "";
|
||||
String arriveTime = "";
|
||||
if(s.depTime != -1) {
|
||||
time = String.format("at %s", formatTransporTime(s.depTime));
|
||||
}
|
||||
int aTime = s.getArrivalTime();
|
||||
if(aTime != -1) {
|
||||
arriveTime = String.format("and arrive at %s", formatTransporTime(aTime));
|
||||
}
|
||||
bld.append(String.format(" %d. %s [%d]: walk %.1f m to '%s' and travel %s to '%s' by %s %d stops %s\n",
|
||||
i + 1, s.route.getRef(), s.route.getId() / 2, s.walkDist, s.getStart().getName(),
|
||||
time, s.getEnd().getName(),s.route.getName(), (s.end - s.start), arriveTime));
|
||||
}
|
||||
bld.append(String.format(" F. Walk %.1f m to reach your destination", finishWalkDist));
|
||||
return bld.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static String formatTransporTime(int i) {
|
||||
public static String formatTransportTime(int i) {
|
||||
int h = i / 60 / 6;
|
||||
int mh = i - h * 60 * 6;
|
||||
int m = mh / 6;
|
||||
int s = (mh - m * 6) * 10;
|
||||
String tm = String.format("%02d:%02d:%02d ", h, m, s);
|
||||
return tm;
|
||||
return String.format(Locale.US, "%02d:%02d:%02d ", h, m, s);
|
||||
}
|
||||
|
||||
public static class TransportRouteSegment {
|
||||
|
@ -653,13 +493,9 @@ public class TransportRoutePlanner {
|
|||
double parentTravelDist; // travel distance for parent route (inaccurate)
|
||||
// walk distance to start route location (or finish in case last segment)
|
||||
double walkDist = 0;
|
||||
|
||||
// main field accumulated all time spent from beginning of journey
|
||||
double distFromStart = 0;
|
||||
|
||||
|
||||
|
||||
|
||||
public TransportRouteSegment(TransportRoute road, int stopIndex) {
|
||||
this.road = road;
|
||||
this.segStart = (short) stopIndex;
|
||||
|
@ -678,7 +514,6 @@ public class TransportRoutePlanner {
|
|||
this.departureTime = c.departureTime;
|
||||
}
|
||||
|
||||
|
||||
public boolean wasVisited(TransportRouteSegment rrs) {
|
||||
if (rrs.road.getId().longValue() == road.getId().longValue() &&
|
||||
rrs.departureTime == departureTime) {
|
||||
|
@ -690,22 +525,18 @@ public class TransportRoutePlanner {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
public TransportStop getStop(int i) {
|
||||
return road.getForwardStops().get(i);
|
||||
}
|
||||
|
||||
|
||||
public LatLon getLocation() {
|
||||
return road.getForwardStops().get(segStart).getLocation();
|
||||
}
|
||||
|
||||
|
||||
public int getLength() {
|
||||
return road.getForwardStops().size();
|
||||
}
|
||||
|
||||
|
||||
public long getId() {
|
||||
long l = road.getId();
|
||||
|
||||
|
@ -727,7 +558,6 @@ public class TransportRoutePlanner {
|
|||
return l ;
|
||||
}
|
||||
|
||||
|
||||
public int getDepth() {
|
||||
if(parentRoute != null) {
|
||||
return parentRoute.getDepth() + 1;
|
||||
|
@ -738,162 +568,7 @@ public class TransportRoutePlanner {
|
|||
@Override
|
||||
public String toString() {
|
||||
return String.format("Route: %s, stop: %s %s", road.getName(), road.getForwardStops().get(segStart).getName(),
|
||||
departureTime == -1 ? "" : formatTransporTime(departureTime) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class TransportRoutingContext {
|
||||
|
||||
public NativeLibrary library;
|
||||
public RouteCalculationProgress calculationProgress;
|
||||
public TLongObjectHashMap<TransportRouteSegment> visitedSegments = new TLongObjectHashMap<TransportRouteSegment>();
|
||||
public TransportRoutingConfiguration cfg;
|
||||
public TLongObjectHashMap<TransportRoute> combinedRoutesCache = new TLongObjectHashMap<TransportRoute>();
|
||||
public Map<TransportStop, List<TransportRoute>> missingStopsCache = new HashMap<TransportStop, List<TransportRoute>>();
|
||||
|
||||
public TLongObjectHashMap<List<TransportRouteSegment>> quadTree;
|
||||
// Here we don't limit files by bbox, so it could be an issue while searching for multiple unused files
|
||||
// Incomplete routes usually don't need more files than around Max-BBOX of start/end,
|
||||
// so here an improvement could be introduced
|
||||
final TransportStopsRouteReader transportStopsReader;
|
||||
public int finishTimeSeconds;
|
||||
|
||||
// stats
|
||||
public long startCalcTime;
|
||||
public int visitedRoutesCount;
|
||||
public int visitedStops;
|
||||
public int wrongLoadedWays;
|
||||
public int loadedWays;
|
||||
public long loadTime;
|
||||
public long readTime;
|
||||
|
||||
|
||||
|
||||
private final int walkRadiusIn31;
|
||||
private final int walkChangeRadiusIn31;
|
||||
|
||||
|
||||
|
||||
public TransportRoutingContext(TransportRoutingConfiguration cfg, NativeLibrary library, BinaryMapIndexReader... readers) {
|
||||
this.cfg = cfg;
|
||||
walkRadiusIn31 = (int) (cfg.walkRadius / MapUtils.getTileDistanceWidth(31));
|
||||
walkChangeRadiusIn31 = (int) (cfg.walkChangeRadius / MapUtils.getTileDistanceWidth(31));
|
||||
quadTree = new TLongObjectHashMap<List<TransportRouteSegment>>();
|
||||
this.library = library;
|
||||
transportStopsReader = new TransportStopsRouteReader(Arrays.asList(readers));
|
||||
}
|
||||
|
||||
public List<TransportRouteSegment> getTransportStops(LatLon loc) throws IOException {
|
||||
int y = MapUtils.get31TileNumberY(loc.getLatitude());
|
||||
int x = MapUtils.get31TileNumberX(loc.getLongitude());
|
||||
return getTransportStops(x, y, false, new ArrayList<TransportRouteSegment>());
|
||||
}
|
||||
|
||||
public List<TransportRouteSegment> getTransportStops(int x, int y, boolean change, List<TransportRouteSegment> res) throws IOException {
|
||||
return loadNativeTransportStops(x, y, change, res);
|
||||
}
|
||||
|
||||
private List<TransportRouteSegment> loadNativeTransportStops(int sx, int sy, boolean change, List<TransportRouteSegment> res) throws IOException {
|
||||
long nanoTime = System.nanoTime();
|
||||
int d = change ? walkChangeRadiusIn31 : walkRadiusIn31;
|
||||
int lx = (sx - d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
|
||||
int rx = (sx + d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
|
||||
int ty = (sy - d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
|
||||
int by = (sy + d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
|
||||
for(int x = lx; x <= rx; x++) {
|
||||
for(int y = ty; y <= by; y++) {
|
||||
long tileId = (((long)x) << (cfg.ZOOM_TO_LOAD_TILES + 1)) + y;
|
||||
List<TransportRouteSegment> list = quadTree.get(tileId);
|
||||
if(list == null) {
|
||||
list = loadTile(x, y);
|
||||
quadTree.put(tileId, list);
|
||||
}
|
||||
for(TransportRouteSegment r : list) {
|
||||
TransportStop st = r.getStop(r.segStart);
|
||||
if (Math.abs(st.x31 - sx) > walkRadiusIn31 || Math.abs(st.y31 - sy) > walkRadiusIn31) {
|
||||
wrongLoadedWays++;
|
||||
} else {
|
||||
loadedWays++;
|
||||
res.add(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
loadTime += System.nanoTime() - nanoTime;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
private List<TransportRouteSegment> loadTile(int x, int y) throws IOException {
|
||||
long nanoTime = System.nanoTime();
|
||||
List<TransportRouteSegment> lst = new ArrayList<TransportRouteSegment>();
|
||||
int pz = (31 - cfg.ZOOM_TO_LOAD_TILES);
|
||||
SearchRequest<TransportStop> sr = BinaryMapIndexReader.buildSearchTransportRequest(x << pz, (x + 1) << pz,
|
||||
y << pz, (y + 1) << pz, -1, null);
|
||||
Collection<TransportStop> stops = transportStopsReader.readMergedTransportStops(sr);
|
||||
loadTransportSegments(stops, lst);
|
||||
readTime += System.nanoTime() - nanoTime;
|
||||
return lst;
|
||||
}
|
||||
|
||||
private void loadTransportSegments(Collection<TransportStop> stops, List<TransportRouteSegment> lst) throws IOException {
|
||||
for(TransportStop s : stops) {
|
||||
if (s.isDeleted() || s.getRoutes() == null) {
|
||||
continue;
|
||||
}
|
||||
for (TransportRoute route : s.getRoutes()) {
|
||||
int stopIndex = -1;
|
||||
double dist = TransportRoute.SAME_STOP;
|
||||
for (int k = 0; k < route.getForwardStops().size(); k++) {
|
||||
TransportStop st = route.getForwardStops().get(k);
|
||||
if(st.getId().longValue() == s.getId().longValue() ) {
|
||||
stopIndex = k;
|
||||
break;
|
||||
}
|
||||
double d = MapUtils.getDistance(st.getLocation(), s.getLocation());
|
||||
if (d < dist) {
|
||||
stopIndex = k;
|
||||
dist = d;
|
||||
}
|
||||
}
|
||||
if (stopIndex != -1) {
|
||||
if (cfg != null && cfg.useSchedule) {
|
||||
loadScheduleRouteSegment(lst, route, stopIndex);
|
||||
} else {
|
||||
TransportRouteSegment segment = new TransportRouteSegment(route, stopIndex);
|
||||
lst.add(segment);
|
||||
}
|
||||
} else {
|
||||
System.err.println(String.format("Routing error: missing stop '%s' in route '%s' id: %d",
|
||||
s.toString(), route.getRef(), route.getId() / 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadScheduleRouteSegment(List<TransportRouteSegment> lst, TransportRoute route, int stopIndex) {
|
||||
if(route.getSchedule() != null) {
|
||||
TIntArrayList ti = route.getSchedule().tripIntervals;
|
||||
int cnt = ti.size();
|
||||
int t = 0;
|
||||
// improve by using exact data
|
||||
int stopTravelTime = 0;
|
||||
TIntArrayList avgStopIntervals = route.getSchedule().avgStopIntervals;
|
||||
for (int i = 0; i < stopIndex; i++) {
|
||||
if (avgStopIntervals.size() > i) {
|
||||
stopTravelTime += avgStopIntervals.getQuick(i);
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < cnt; i++) {
|
||||
t += ti.getQuick(i);
|
||||
int startTime = t + stopTravelTime;
|
||||
if(startTime >= cfg.scheduleTimeOfDay && startTime <= cfg.scheduleTimeOfDay + cfg.scheduleMaxTime ) {
|
||||
TransportRouteSegment segment = new TransportRouteSegment(route, stopIndex, startTime);
|
||||
lst.add(segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
departureTime == -1 ? "" : formatTransportTime(departureTime) );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
package net.osmand.router;
|
||||
|
||||
import net.osmand.data.TransportSchedule;
|
||||
import net.osmand.data.TransportStop;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class TransportRouteResult {
|
||||
|
||||
List<TransportRoutePlanner.TransportRouteResultSegment> segments = new ArrayList<TransportRoutePlanner.TransportRouteResultSegment>(4);
|
||||
double finishWalkDist;
|
||||
double routeTime;
|
||||
private final TransportRoutingConfiguration cfg;
|
||||
|
||||
public TransportRouteResult(TransportRoutingContext ctx) {
|
||||
cfg = ctx.cfg;
|
||||
}
|
||||
|
||||
public TransportRouteResult(TransportRoutingConfiguration cfg) {
|
||||
this.cfg = cfg;
|
||||
}
|
||||
|
||||
public List<TransportRoutePlanner.TransportRouteResultSegment> getSegments() {
|
||||
return segments;
|
||||
}
|
||||
|
||||
public void setFinishWalkDist(double finishWalkDist) {
|
||||
this.finishWalkDist = finishWalkDist;
|
||||
}
|
||||
|
||||
public void setRouteTime(double routeTime) {
|
||||
this.routeTime = routeTime;
|
||||
}
|
||||
|
||||
public void addSegment(TransportRoutePlanner.TransportRouteResultSegment seg) {
|
||||
segments.add(seg);
|
||||
}
|
||||
|
||||
public double getWalkDist() {
|
||||
double d = finishWalkDist;
|
||||
for (TransportRoutePlanner.TransportRouteResultSegment s : segments) {
|
||||
d += s.walkDist;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
public double getFinishWalkDist() {
|
||||
return finishWalkDist;
|
||||
}
|
||||
|
||||
public double getWalkSpeed() {
|
||||
return cfg.walkSpeed;
|
||||
}
|
||||
|
||||
public double getRouteTime() {
|
||||
return routeTime;
|
||||
}
|
||||
|
||||
public int getStops() {
|
||||
int stops = 0;
|
||||
for(TransportRoutePlanner.TransportRouteResultSegment s : segments) {
|
||||
stops += (s.end - s.start);
|
||||
}
|
||||
return stops;
|
||||
}
|
||||
|
||||
public boolean isRouteStop(TransportStop stop) {
|
||||
for(TransportRoutePlanner.TransportRouteResultSegment s : segments) {
|
||||
if (s.getTravelStops().contains(stop)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public TransportRoutePlanner.TransportRouteResultSegment getRouteStopSegment(TransportStop stop) {
|
||||
for(TransportRoutePlanner.TransportRouteResultSegment s : segments) {
|
||||
if (s.getTravelStops().contains(stop)) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public double getTravelDist() {
|
||||
double d = 0;
|
||||
for (TransportRoutePlanner.TransportRouteResultSegment s : segments) {
|
||||
d += s.getTravelDist();
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
public double getTravelTime() {
|
||||
double t = 0;
|
||||
for (TransportRoutePlanner.TransportRouteResultSegment s : segments) {
|
||||
if (cfg.useSchedule) {
|
||||
TransportSchedule sts = s.route.getSchedule();
|
||||
for (int k = s.start; k < s.end; k++) {
|
||||
t += sts.getAvgStopIntervals()[k] * 10;
|
||||
}
|
||||
} else {
|
||||
t += cfg.getBoardingTime();
|
||||
t += s.getTravelTime();
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
public double getWalkTime() {
|
||||
return getWalkDist() / cfg.walkSpeed;
|
||||
}
|
||||
|
||||
public double getChangeTime() {
|
||||
return cfg.getChangeTime();
|
||||
}
|
||||
|
||||
public double getBoardingTime() {
|
||||
return cfg.getBoardingTime();
|
||||
}
|
||||
|
||||
public int getChanges() {
|
||||
return segments.size() - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder bld = new StringBuilder();
|
||||
bld.append(String.format(Locale.US, "Route %d stops, %d changes, %.2f min: %.2f m (%.1f min) to walk, %.2f m (%.1f min) to travel\n",
|
||||
getStops(), getChanges(), routeTime / 60, getWalkDist(), getWalkTime() / 60.0,
|
||||
getTravelDist(), getTravelTime() / 60.0));
|
||||
for(int i = 0; i < segments.size(); i++) {
|
||||
TransportRoutePlanner.TransportRouteResultSegment s = segments.get(i);
|
||||
String time = "";
|
||||
String arriveTime = "";
|
||||
if(s.depTime != -1) {
|
||||
time = String.format("at %s", TransportRoutePlanner.formatTransportTime(s.depTime));
|
||||
}
|
||||
int aTime = s.getArrivalTime();
|
||||
if(aTime != -1) {
|
||||
arriveTime = String.format("and arrive at %s", TransportRoutePlanner.formatTransportTime(aTime));
|
||||
}
|
||||
bld.append(String.format(Locale.US, " %d. %s [%d]: walk %.1f m to '%s' and travel %s to '%s' by %s %d stops %s\n",
|
||||
i + 1, s.route.getRef(), s.route.getId() / 2, s.walkDist, s.getStart().getName(),
|
||||
time, s.getEnd().getName(),s.route.getName(), (s.end - s.start), arriveTime));
|
||||
}
|
||||
bld.append(String.format(" F. Walk %.1f m to reach your destination", finishWalkDist));
|
||||
return bld.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
package net.osmand.router;
|
||||
|
||||
import net.osmand.NativeLibrary;
|
||||
import net.osmand.binary.BinaryMapIndexReader;
|
||||
import net.osmand.data.LatLon;
|
||||
import net.osmand.data.TransportRoute;
|
||||
import net.osmand.data.TransportStop;
|
||||
import net.osmand.router.TransportRoutePlanner.TransportRouteSegment;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import gnu.trove.list.array.TIntArrayList;
|
||||
import gnu.trove.map.hash.TLongObjectHashMap;
|
||||
|
||||
public class TransportRoutingContext {
|
||||
|
||||
public NativeLibrary library;
|
||||
public RouteCalculationProgress calculationProgress;
|
||||
public TLongObjectHashMap<TransportRouteSegment> visitedSegments = new TLongObjectHashMap<TransportRouteSegment>();
|
||||
public TransportRoutingConfiguration cfg;
|
||||
public TLongObjectHashMap<TransportRoute> combinedRoutesCache = new TLongObjectHashMap<TransportRoute>();
|
||||
public Map<TransportStop, List<TransportRoute>> missingStopsCache = new HashMap<TransportStop, List<TransportRoute>>();
|
||||
|
||||
public TLongObjectHashMap<List<TransportRouteSegment>> quadTree;
|
||||
// Here we don't limit files by bbox, so it could be an issue while searching for multiple unused files
|
||||
// Incomplete routes usually don't need more files than around Max-BBOX of start/end,
|
||||
// so here an improvement could be introduced
|
||||
final TransportStopsRouteReader transportStopsReader;
|
||||
public int finishTimeSeconds;
|
||||
|
||||
// stats
|
||||
public long startCalcTime;
|
||||
public int visitedRoutesCount;
|
||||
public int visitedStops;
|
||||
public int wrongLoadedWays;
|
||||
public int loadedWays;
|
||||
public long loadTime;
|
||||
public long readTime;
|
||||
|
||||
private final int walkRadiusIn31;
|
||||
private final int walkChangeRadiusIn31;
|
||||
|
||||
public TransportRoutingContext(TransportRoutingConfiguration cfg, NativeLibrary library, BinaryMapIndexReader... readers) {
|
||||
this.cfg = cfg;
|
||||
walkRadiusIn31 = (int) (cfg.walkRadius / MapUtils.getTileDistanceWidth(31));
|
||||
walkChangeRadiusIn31 = (int) (cfg.walkChangeRadius / MapUtils.getTileDistanceWidth(31));
|
||||
quadTree = new TLongObjectHashMap<List<TransportRouteSegment>>();
|
||||
this.library = library;
|
||||
transportStopsReader = new TransportStopsRouteReader(Arrays.asList(readers));
|
||||
}
|
||||
|
||||
public List<TransportRouteSegment> getTransportStops(LatLon loc) throws IOException {
|
||||
int y = MapUtils.get31TileNumberY(loc.getLatitude());
|
||||
int x = MapUtils.get31TileNumberX(loc.getLongitude());
|
||||
return getTransportStops(x, y, false, new ArrayList<TransportRouteSegment>());
|
||||
}
|
||||
|
||||
public List<TransportRouteSegment> getTransportStops(int x, int y, boolean change, List<TransportRouteSegment> res) throws IOException {
|
||||
return loadNativeTransportStops(x, y, change, res);
|
||||
}
|
||||
|
||||
private List<TransportRouteSegment> loadNativeTransportStops(int sx, int sy, boolean change, List<TransportRouteSegment> res) throws IOException {
|
||||
long nanoTime = System.nanoTime();
|
||||
int d = change ? walkChangeRadiusIn31 : walkRadiusIn31;
|
||||
int lx = (sx - d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
|
||||
int rx = (sx + d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
|
||||
int ty = (sy - d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
|
||||
int by = (sy + d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
|
||||
for(int x = lx; x <= rx; x++) {
|
||||
for(int y = ty; y <= by; y++) {
|
||||
long tileId = (((long)x) << (cfg.ZOOM_TO_LOAD_TILES + 1)) + y;
|
||||
List<TransportRouteSegment> list = quadTree.get(tileId);
|
||||
if(list == null) {
|
||||
list = loadTile(x, y);
|
||||
quadTree.put(tileId, list);
|
||||
}
|
||||
for(TransportRouteSegment r : list) {
|
||||
TransportStop st = r.getStop(r.segStart);
|
||||
if (Math.abs(st.x31 - sx) > walkRadiusIn31 || Math.abs(st.y31 - sy) > walkRadiusIn31) {
|
||||
wrongLoadedWays++;
|
||||
} else {
|
||||
loadedWays++;
|
||||
res.add(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
loadTime += System.nanoTime() - nanoTime;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
private List<TransportRouteSegment> loadTile(int x, int y) throws IOException {
|
||||
long nanoTime = System.nanoTime();
|
||||
List<TransportRouteSegment> lst = new ArrayList<TransportRouteSegment>();
|
||||
int pz = (31 - cfg.ZOOM_TO_LOAD_TILES);
|
||||
BinaryMapIndexReader.SearchRequest<TransportStop> sr = BinaryMapIndexReader.buildSearchTransportRequest(x << pz, (x + 1) << pz,
|
||||
y << pz, (y + 1) << pz, -1, null);
|
||||
Collection<TransportStop> stops = transportStopsReader.readMergedTransportStops(sr);
|
||||
loadTransportSegments(stops, lst);
|
||||
readTime += System.nanoTime() - nanoTime;
|
||||
return lst;
|
||||
}
|
||||
|
||||
private void loadTransportSegments(Collection<TransportStop> stops, List<TransportRouteSegment> lst) throws IOException {
|
||||
for(TransportStop s : stops) {
|
||||
if (s.isDeleted() || s.getRoutes() == null) {
|
||||
continue;
|
||||
}
|
||||
for (TransportRoute route : s.getRoutes()) {
|
||||
int stopIndex = -1;
|
||||
double dist = TransportRoute.SAME_STOP;
|
||||
for (int k = 0; k < route.getForwardStops().size(); k++) {
|
||||
TransportStop st = route.getForwardStops().get(k);
|
||||
if(st.getId().longValue() == s.getId().longValue() ) {
|
||||
stopIndex = k;
|
||||
break;
|
||||
}
|
||||
double d = MapUtils.getDistance(st.getLocation(), s.getLocation());
|
||||
if (d < dist) {
|
||||
stopIndex = k;
|
||||
dist = d;
|
||||
}
|
||||
}
|
||||
if (stopIndex != -1) {
|
||||
if (cfg != null && cfg.useSchedule) {
|
||||
loadScheduleRouteSegment(lst, route, stopIndex);
|
||||
} else {
|
||||
TransportRouteSegment segment = new TransportRouteSegment(route, stopIndex);
|
||||
lst.add(segment);
|
||||
}
|
||||
} else {
|
||||
System.err.println(String.format(Locale.US, "Routing error: missing stop '%s' in route '%s' id: %d",
|
||||
s.toString(), route.getRef(), route.getId() / 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadScheduleRouteSegment(List<TransportRouteSegment> lst, TransportRoute route, int stopIndex) {
|
||||
if(route.getSchedule() != null) {
|
||||
TIntArrayList ti = route.getSchedule().tripIntervals;
|
||||
int cnt = ti.size();
|
||||
int t = 0;
|
||||
// improve by using exact data
|
||||
int stopTravelTime = 0;
|
||||
TIntArrayList avgStopIntervals = route.getSchedule().avgStopIntervals;
|
||||
for (int i = 0; i < stopIndex; i++) {
|
||||
if (avgStopIntervals.size() > i) {
|
||||
stopTravelTime += avgStopIntervals.getQuick(i);
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < cnt; i++) {
|
||||
t += ti.getQuick(i);
|
||||
int startTime = t + stopTravelTime;
|
||||
if(startTime >= cfg.scheduleTimeOfDay && startTime <= cfg.scheduleTimeOfDay + cfg.scheduleMaxTime ) {
|
||||
TransportRouteSegment segment = new TransportRouteSegment(route, stopIndex, startTime);
|
||||
lst.add(segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,8 +18,8 @@ import net.osmand.plus.OsmandApplication;
|
|||
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.views.AidlMapLayer;
|
||||
import net.osmand.plus.views.MapInfoLayer;
|
||||
import net.osmand.plus.views.layers.AidlMapLayer;
|
||||
import net.osmand.plus.views.layers.MapInfoLayer;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.mapwidgets.widgets.TextInfoWidget;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
|
|
@ -76,8 +76,8 @@ import net.osmand.plus.settings.backend.ApplicationMode;
|
|||
import net.osmand.plus.settings.backend.OsmAndAppCustomization;
|
||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||
import net.osmand.plus.settings.backend.SettingsHelper;
|
||||
import net.osmand.plus.views.AidlMapLayer;
|
||||
import net.osmand.plus.views.MapInfoLayer;
|
||||
import net.osmand.plus.views.layers.AidlMapLayer;
|
||||
import net.osmand.plus.views.layers.MapInfoLayer;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.mapwidgets.widgets.TextInfoWidget;
|
||||
|
|
|
@ -147,9 +147,9 @@ import net.osmand.plus.settings.fragments.ProfileAppearanceFragment;
|
|||
import net.osmand.plus.track.TrackAppearanceFragment;
|
||||
import net.osmand.plus.views.AddGpxPointBottomSheetHelper.NewGpxPoint;
|
||||
import net.osmand.plus.views.AnimateDraggingMapThread;
|
||||
import net.osmand.plus.views.MapControlsLayer;
|
||||
import net.osmand.plus.views.MapInfoLayer;
|
||||
import net.osmand.plus.views.MapQuickActionLayer;
|
||||
import net.osmand.plus.views.layers.MapControlsLayer;
|
||||
import net.osmand.plus.views.layers.MapInfoLayer;
|
||||
import net.osmand.plus.views.layers.MapQuickActionLayer;
|
||||
import net.osmand.plus.views.OsmAndMapLayersView;
|
||||
import net.osmand.plus.views.OsmAndMapSurfaceView;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
|
|
|
@ -68,7 +68,7 @@ import net.osmand.plus.routing.RouteProvider.GPXRouteParamsBuilder;
|
|||
import net.osmand.plus.routing.RoutingHelper;
|
||||
import net.osmand.plus.settings.fragments.BaseSettingsFragment;
|
||||
import net.osmand.plus.views.BaseMapLayer;
|
||||
import net.osmand.plus.views.MapControlsLayer;
|
||||
import net.osmand.plus.views.layers.MapControlsLayer;
|
||||
import net.osmand.plus.views.MapTileLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.wikipedia.WikipediaDialogFragment;
|
||||
|
|
|
@ -41,24 +41,24 @@ import net.osmand.plus.rastermaps.OsmandRasterMapsPlugin;
|
|||
import net.osmand.plus.render.MapVectorLayer;
|
||||
import net.osmand.plus.render.RenderingIcons;
|
||||
import net.osmand.plus.routing.RoutingHelper;
|
||||
import net.osmand.plus.views.ContextMenuLayer;
|
||||
import net.osmand.plus.views.DownloadedRegionsLayer;
|
||||
import net.osmand.plus.views.FavouritesLayer;
|
||||
import net.osmand.plus.views.GPXLayer;
|
||||
import net.osmand.plus.views.ImpassableRoadsLayer;
|
||||
import net.osmand.plus.views.MapControlsLayer;
|
||||
import net.osmand.plus.views.MapInfoLayer;
|
||||
import net.osmand.plus.views.MapMarkersLayer;
|
||||
import net.osmand.plus.views.MapQuickActionLayer;
|
||||
import net.osmand.plus.views.MapTextLayer;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer;
|
||||
import net.osmand.plus.views.layers.DownloadedRegionsLayer;
|
||||
import net.osmand.plus.views.layers.FavouritesLayer;
|
||||
import net.osmand.plus.views.layers.GPXLayer;
|
||||
import net.osmand.plus.views.layers.ImpassableRoadsLayer;
|
||||
import net.osmand.plus.views.layers.MapControlsLayer;
|
||||
import net.osmand.plus.views.layers.MapInfoLayer;
|
||||
import net.osmand.plus.views.layers.MapMarkersLayer;
|
||||
import net.osmand.plus.views.layers.MapQuickActionLayer;
|
||||
import net.osmand.plus.views.layers.MapTextLayer;
|
||||
import net.osmand.plus.views.MapTileLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.POIMapLayer;
|
||||
import net.osmand.plus.views.PointLocationLayer;
|
||||
import net.osmand.plus.views.PointNavigationLayer;
|
||||
import net.osmand.plus.views.RouteLayer;
|
||||
import net.osmand.plus.views.RulerControlLayer;
|
||||
import net.osmand.plus.views.TransportStopsLayer;
|
||||
import net.osmand.plus.views.layers.POIMapLayer;
|
||||
import net.osmand.plus.views.layers.PointLocationLayer;
|
||||
import net.osmand.plus.views.layers.PointNavigationLayer;
|
||||
import net.osmand.plus.views.layers.RouteLayer;
|
||||
import net.osmand.plus.views.layers.RulerControlLayer;
|
||||
import net.osmand.plus.views.layers.TransportStopsLayer;
|
||||
import net.osmand.plus.views.mapwidgets.MapWidgetRegistry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
|
|
@ -17,8 +17,8 @@ import net.osmand.plus.R;
|
|||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.audionotes.AudioVideoNotesPlugin.Recording;
|
||||
import net.osmand.plus.base.PointImageDrawable;
|
||||
import net.osmand.plus.views.ContextMenuLayer;
|
||||
import net.osmand.plus.views.ContextMenuLayer.IContextMenuProvider;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer.IContextMenuProvider;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
|
|
@ -67,7 +67,7 @@ import net.osmand.plus.monitoring.OsmandMonitoringPlugin;
|
|||
import net.osmand.plus.myplaces.FavoritesActivity;
|
||||
import net.osmand.plus.quickaction.QuickActionType;
|
||||
import net.osmand.plus.settings.fragments.BaseSettingsFragment;
|
||||
import net.osmand.plus.views.MapInfoLayer;
|
||||
import net.osmand.plus.views.layers.MapInfoLayer;
|
||||
import net.osmand.plus.views.OsmandMapLayer.DrawSettings;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.mapwidgets.widgetstates.WidgetState;
|
||||
|
|
|
@ -13,8 +13,8 @@ import net.osmand.plus.activities.MapActivity;
|
|||
import net.osmand.plus.activities.MapActivityLayers;
|
||||
import net.osmand.plus.base.ContextMenuFragment.ContextMenuFragmentListener;
|
||||
import net.osmand.plus.helpers.AndroidUiHelper;
|
||||
import net.osmand.plus.views.MapControlsLayer;
|
||||
import net.osmand.plus.views.MapInfoLayer;
|
||||
import net.osmand.plus.views.layers.MapControlsLayer;
|
||||
import net.osmand.plus.views.layers.MapInfoLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.mapwidgets.widgets.RulerWidget;
|
||||
|
||||
|
|
|
@ -82,8 +82,8 @@ import net.osmand.plus.settings.backend.OsmandSettings;
|
|||
import net.osmand.plus.srtmplugin.ContourLinesMenu;
|
||||
import net.osmand.plus.srtmplugin.SRTMPlugin;
|
||||
import net.osmand.plus.srtmplugin.TerrainFragment;
|
||||
import net.osmand.plus.views.DownloadedRegionsLayer;
|
||||
import net.osmand.plus.views.MapInfoLayer;
|
||||
import net.osmand.plus.views.layers.DownloadedRegionsLayer;
|
||||
import net.osmand.plus.views.layers.MapInfoLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.mapwidgets.MapWidgetRegistry;
|
||||
import net.osmand.plus.wikipedia.WikipediaPoiMenu;
|
||||
|
|
|
@ -15,7 +15,7 @@ import net.osmand.plus.activities.ContributionVersionActivity;
|
|||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.dashboard.tools.DashFragmentData;
|
||||
import net.osmand.plus.settings.fragments.BaseSettingsFragment;
|
||||
import net.osmand.plus.views.MapInfoLayer;
|
||||
import net.osmand.plus.views.layers.MapInfoLayer;
|
||||
import net.osmand.plus.views.OsmandMapLayer.DrawSettings;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.mapwidgets.widgets.TextInfoWidget;
|
||||
|
|
|
@ -35,7 +35,7 @@ import net.osmand.plus.activities.MapActivity;
|
|||
import net.osmand.plus.mapcontextmenu.MapContextMenu;
|
||||
import net.osmand.plus.routing.RoutingHelper;
|
||||
import net.osmand.plus.routing.RoutingHelper.RouteSegmentSearchResult;
|
||||
import net.osmand.plus.views.ContextMenuLayer;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer;
|
||||
import net.osmand.router.RouteSegmentResult;
|
||||
import net.osmand.router.RoutingConfiguration;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
|
|
@ -51,7 +51,7 @@ import net.osmand.plus.mapcontextmenu.AdditionalActionsBottomSheetDialogFragment
|
|||
import net.osmand.plus.monitoring.OsmandMonitoringPlugin;
|
||||
import net.osmand.plus.routing.RoutingHelper;
|
||||
import net.osmand.plus.transport.TransportStopRoute;
|
||||
import net.osmand.plus.views.ContextMenuLayer;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarController;
|
||||
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarControllerType;
|
||||
|
|
|
@ -77,11 +77,11 @@ import net.osmand.plus.routepreparationmenu.MapRouteInfoMenu;
|
|||
import net.osmand.plus.transport.TransportStopRoute;
|
||||
import net.osmand.plus.views.AnimateDraggingMapThread;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.TransportStopsLayer;
|
||||
import net.osmand.plus.views.layers.TransportStopsLayer;
|
||||
import net.osmand.plus.views.controls.HorizontalSwipeConfirm;
|
||||
import net.osmand.plus.views.controls.SingleTapConfirm;
|
||||
import net.osmand.plus.widgets.style.CustomTypefaceSpan;
|
||||
import net.osmand.router.TransportRoutePlanner.TransportRouteResult;
|
||||
import net.osmand.router.TransportRouteResult;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
|
|
@ -55,8 +55,8 @@ import net.osmand.plus.mapcontextmenu.builders.cards.NoImagesCard;
|
|||
import net.osmand.plus.mapcontextmenu.controllers.TransportStopController;
|
||||
import net.osmand.plus.render.RenderingIcons;
|
||||
import net.osmand.plus.transport.TransportStopRoute;
|
||||
import net.osmand.plus.views.POIMapLayer;
|
||||
import net.osmand.plus.views.TransportStopsLayer;
|
||||
import net.osmand.plus.views.layers.POIMapLayer;
|
||||
import net.osmand.plus.views.layers.TransportStopsLayer;
|
||||
import net.osmand.plus.widgets.TextViewEx;
|
||||
import net.osmand.plus.widgets.tools.ClickableSpanTouchListener;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
|
|
@ -75,7 +75,7 @@ import net.osmand.plus.osmedit.OsmPoint;
|
|||
import net.osmand.plus.parkingpoint.ParkingPositionMenuController;
|
||||
import net.osmand.plus.resources.ResourceManager;
|
||||
import net.osmand.plus.transport.TransportStopRoute;
|
||||
import net.osmand.plus.views.DownloadedRegionsLayer.DownloadMapObject;
|
||||
import net.osmand.plus.views.layers.DownloadedRegionsLayer.DownloadMapObject;
|
||||
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarController;
|
||||
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarControllerType;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
|
|
@ -38,7 +38,7 @@ import net.osmand.plus.osmedit.OsmEditingPlugin;
|
|||
import net.osmand.plus.poi.PoiUIFilter;
|
||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||
import net.osmand.plus.settings.backend.OsmandSettings.MetricsConstants;
|
||||
import net.osmand.plus.views.POIMapLayer;
|
||||
import net.osmand.plus.views.layers.POIMapLayer;
|
||||
import net.osmand.plus.widgets.TextViewEx;
|
||||
import net.osmand.plus.widgets.tools.ClickableSpanTouchListener;
|
||||
import net.osmand.plus.wikipedia.WikiArticleHelper;
|
||||
|
|
|
@ -23,7 +23,7 @@ import net.osmand.plus.activities.MapActivity;
|
|||
import net.osmand.plus.activities.TrackActivity;
|
||||
import net.osmand.plus.mapcontextmenu.MenuBuilder;
|
||||
import net.osmand.plus.mapcontextmenu.CollapsableView;
|
||||
import net.osmand.plus.views.POIMapLayer;
|
||||
import net.osmand.plus.views.layers.POIMapLayer;
|
||||
import net.osmand.plus.widgets.TextViewEx;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
||||
|
|
|
@ -32,8 +32,8 @@ import net.osmand.plus.liveupdates.LiveUpdatesHelper;
|
|||
import net.osmand.plus.mapcontextmenu.MenuController;
|
||||
import net.osmand.plus.mapcontextmenu.builders.MapDataMenuBuilder;
|
||||
import net.osmand.plus.srtmplugin.SRTMPlugin;
|
||||
import net.osmand.plus.views.ContextMenuLayer.IContextMenuProvider;
|
||||
import net.osmand.plus.views.DownloadedRegionsLayer.DownloadMapObject;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer.IContextMenuProvider;
|
||||
import net.osmand.plus.views.layers.DownloadedRegionsLayer.DownloadMapObject;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
||||
import java.io.File;
|
||||
|
|
|
@ -17,7 +17,7 @@ import net.osmand.plus.mapcontextmenu.MapContextMenu;
|
|||
import net.osmand.plus.mapcontextmenu.MenuBuilder;
|
||||
import net.osmand.plus.mapcontextmenu.MenuController;
|
||||
import net.osmand.plus.transport.TransportStopRoute;
|
||||
import net.osmand.plus.views.TransportStopsLayer;
|
||||
import net.osmand.plus.views.layers.TransportStopsLayer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
|
@ -11,8 +11,8 @@ import net.osmand.plus.mapcontextmenu.BaseMenuController;
|
|||
import net.osmand.plus.mapcontextmenu.MenuController;
|
||||
import net.osmand.plus.mapcontextmenu.MenuController.MenuType;
|
||||
import net.osmand.plus.mapcontextmenu.MenuTitleController;
|
||||
import net.osmand.plus.views.ContextMenuLayer;
|
||||
import net.osmand.plus.views.ContextMenuLayer.IContextMenuProvider;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer.IContextMenuProvider;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
|
|
@ -44,7 +44,7 @@ import net.osmand.plus.helpers.GpxUiHelper;
|
|||
import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetAxisType;
|
||||
import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetType;
|
||||
import net.osmand.plus.helpers.GpxUiHelper.OrderedLineDataSet;
|
||||
import net.osmand.plus.views.GPXLayer;
|
||||
import net.osmand.plus.views.layers.GPXLayer;
|
||||
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory;
|
||||
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarController;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
|
|
@ -31,7 +31,7 @@ import net.osmand.plus.activities.MapActivity;
|
|||
import net.osmand.plus.activities.MapActivityLayers;
|
||||
import net.osmand.plus.base.BottomSheetDialogFragment;
|
||||
import net.osmand.plus.dashboard.DashboardOnMap;
|
||||
import net.osmand.plus.views.MapInfoLayer;
|
||||
import net.osmand.plus.views.layers.MapInfoLayer;
|
||||
import net.osmand.plus.views.MapTileLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.mapwidgets.MapWidgetRegistry.MapWidgetRegInfo;
|
||||
|
|
|
@ -26,7 +26,7 @@ import net.osmand.plus.OsmandPlugin;
|
|||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.rastermaps.OsmandRasterMapsPlugin;
|
||||
import net.osmand.plus.resources.ResourceManager;
|
||||
import net.osmand.plus.views.ContextMenuLayer.IContextMenuProvider;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer.IContextMenuProvider;
|
||||
import net.osmand.plus.views.MapTileLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
|
|
@ -57,7 +57,7 @@ import net.osmand.plus.measurementtool.SnapToRoadBottomSheetDialogFragment.SnapT
|
|||
import net.osmand.plus.routing.RoutingHelper;
|
||||
import net.osmand.plus.settings.backend.ApplicationMode;
|
||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||
import net.osmand.plus.views.MapMarkersLayer;
|
||||
import net.osmand.plus.views.layers.MapMarkersLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory;
|
||||
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarController;
|
||||
|
|
|
@ -76,9 +76,8 @@ import net.osmand.plus.measurementtool.command.RemovePointCommand;
|
|||
import net.osmand.plus.measurementtool.command.ReorderPointCommand;
|
||||
import net.osmand.plus.settings.backend.ApplicationMode;
|
||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||
import net.osmand.plus.views.MapControlsLayer;
|
||||
import net.osmand.plus.views.layers.MapControlsLayer;
|
||||
import net.osmand.plus.views.controls.ReorderItemTouchHelperCallback;
|
||||
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory;
|
||||
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarController;
|
||||
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarControllerType;
|
||||
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarView;
|
||||
|
|
|
@ -16,10 +16,11 @@ import net.osmand.data.RotatedTileBox;
|
|||
import net.osmand.plus.OsmAndFormatter;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.settings.backend.ApplicationMode;
|
||||
import net.osmand.plus.views.ContextMenuLayer;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.Renderable;
|
||||
import net.osmand.plus.views.layers.geometry.GeometryWay;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -246,7 +247,7 @@ public class MeasurementToolLayer extends OsmandMapLayer implements ContextMenuL
|
|||
ty.add(locY);
|
||||
}
|
||||
|
||||
calculatePath(tb, tx, ty, path);
|
||||
GeometryWay.calculatePath(tb, tx, ty, path);
|
||||
canvas.drawPath(path, lineAttrs.paint);
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ import net.osmand.plus.dashboard.tools.DashFragmentData;
|
|||
import net.osmand.plus.settings.backend.ApplicationMode;
|
||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||
import net.osmand.plus.settings.fragments.BaseSettingsFragment;
|
||||
import net.osmand.plus.views.MapInfoLayer;
|
||||
import net.osmand.plus.views.layers.MapInfoLayer;
|
||||
import net.osmand.plus.views.OsmandMapLayer.DrawSettings;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.mapwidgets.widgets.TextInfoWidget;
|
||||
|
|
|
@ -30,7 +30,7 @@ import net.osmand.plus.R;
|
|||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.osmedit.OsmBugsUtil.OsmBugResult;
|
||||
import net.osmand.plus.osmedit.OsmPoint.Action;
|
||||
import net.osmand.plus.views.ContextMenuLayer.IContextMenuProvider;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer.IContextMenuProvider;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
|
|
@ -19,7 +19,7 @@ import net.osmand.plus.R;
|
|||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.base.PointImageDrawable;
|
||||
import net.osmand.plus.render.RenderingIcons;
|
||||
import net.osmand.plus.views.ContextMenuLayer;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ import net.osmand.plus.settings.backend.ApplicationMode;
|
|||
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||
import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference;
|
||||
import net.osmand.plus.views.AnimateDraggingMapThread;
|
||||
import net.osmand.plus.views.MapInfoLayer;
|
||||
import net.osmand.plus.views.layers.MapInfoLayer;
|
||||
import net.osmand.plus.views.OsmandMapLayer.DrawSettings;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.mapwidgets.widgets.TextInfoWidget;
|
||||
|
|
|
@ -40,7 +40,7 @@ import net.osmand.plus.UiUtilities;
|
|||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.base.BaseOsmAndFragment;
|
||||
import net.osmand.plus.settings.backend.ApplicationMode;
|
||||
import net.osmand.plus.views.MapQuickActionLayer;
|
||||
import net.osmand.plus.views.layers.MapQuickActionLayer;
|
||||
import net.osmand.plus.views.controls.ReorderItemTouchHelperCallback;
|
||||
import net.osmand.plus.views.controls.ReorderItemTouchHelperCallback.OnItemMoveCallback;
|
||||
import net.osmand.plus.views.controls.ReorderItemTouchHelperCallback.UnmovableItem;
|
||||
|
|
|
@ -56,9 +56,9 @@ import net.osmand.plus.routing.RouteDirectionInfo;
|
|||
import net.osmand.plus.routing.RoutingHelper;
|
||||
import net.osmand.plus.routing.TransportRoutingHelper;
|
||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||
import net.osmand.plus.views.MapControlsLayer;
|
||||
import net.osmand.plus.views.layers.MapControlsLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.router.TransportRoutePlanner.TransportRouteResult;
|
||||
import net.osmand.router.TransportRouteResult;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
||||
import java.io.File;
|
||||
|
|
|
@ -103,7 +103,7 @@ import net.osmand.plus.search.QuickSearchHelper;
|
|||
import net.osmand.plus.widgets.TextViewExProgress;
|
||||
import net.osmand.router.GeneralRouter;
|
||||
import net.osmand.router.GeneralRouter.RoutingParameter;
|
||||
import net.osmand.router.TransportRoutePlanner.TransportRouteResult;
|
||||
import net.osmand.router.TransportRouteResult;
|
||||
import net.osmand.search.SearchUICore.SearchResultCollection;
|
||||
import net.osmand.search.core.SearchResult;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
|
|
@ -29,7 +29,7 @@ import net.osmand.plus.helpers.AndroidUiHelper;
|
|||
import net.osmand.plus.routing.RoutingHelper;
|
||||
import net.osmand.plus.routing.TransportRoutingHelper;
|
||||
import net.osmand.plus.widgets.TextViewExProgress;
|
||||
import net.osmand.router.TransportRoutePlanner.TransportRouteResult;
|
||||
import net.osmand.router.TransportRouteResult;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
|
|
@ -90,7 +90,7 @@ import net.osmand.render.RenderingRulesStorage;
|
|||
import net.osmand.router.RouteSegmentResult;
|
||||
import net.osmand.router.RouteStatisticsHelper;
|
||||
import net.osmand.router.RouteStatisticsHelper.RouteStatistics;
|
||||
import net.osmand.router.TransportRoutePlanner.TransportRouteResult;
|
||||
import net.osmand.router.TransportRouteResult;
|
||||
import net.osmand.router.TransportRoutePlanner.TransportRouteResultSegment;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ import net.osmand.plus.routing.TransportRoutingHelper;
|
|||
import net.osmand.plus.transport.TransportStopRoute;
|
||||
import net.osmand.plus.widgets.FlowLayout;
|
||||
import net.osmand.plus.widgets.style.CustomTypefaceSpan;
|
||||
import net.osmand.router.TransportRoutePlanner.TransportRouteResult;
|
||||
import net.osmand.router.TransportRouteResult;
|
||||
import net.osmand.router.TransportRoutePlanner.TransportRouteResultSegment;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
||||
|
|
|
@ -27,9 +27,9 @@ import net.osmand.router.GeneralRouter;
|
|||
import net.osmand.router.RouteCalculationProgress;
|
||||
import net.osmand.router.RoutingConfiguration;
|
||||
import net.osmand.router.TransportRoutePlanner;
|
||||
import net.osmand.router.TransportRoutePlanner.TransportRouteResult;
|
||||
import net.osmand.router.TransportRouteResult;
|
||||
import net.osmand.router.TransportRoutePlanner.TransportRouteResultSegment;
|
||||
import net.osmand.router.TransportRoutePlanner.TransportRoutingContext;
|
||||
import net.osmand.router.TransportRoutingContext;
|
||||
import net.osmand.router.TransportRoutingConfiguration;
|
||||
import net.osmand.router.NativeTransportRoutingResult;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
|
|
@ -19,6 +19,7 @@ import net.osmand.plus.mapcontextmenu.MapContextMenu;
|
|||
import net.osmand.plus.mapcontextmenu.editors.RtePtEditor;
|
||||
import net.osmand.plus.mapcontextmenu.editors.WptPtEditor;
|
||||
import net.osmand.plus.mapcontextmenu.editors.WptPtEditor.OnDismissListener;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer;
|
||||
|
||||
public class AddGpxPointBottomSheetHelper implements OnDismissListener {
|
||||
private final View view;
|
||||
|
|
|
@ -12,6 +12,7 @@ import net.osmand.data.PointDescription;
|
|||
import net.osmand.plus.UiUtilities;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer;
|
||||
|
||||
public class MoveMarkerBottomSheetHelper {
|
||||
private final View mView;
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
package net.osmand.plus.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Cap;
|
||||
import android.graphics.Paint.Join;
|
||||
|
@ -16,15 +13,12 @@ import android.graphics.PointF;
|
|||
import android.graphics.PorterDuff.Mode;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Pair;
|
||||
import android.view.MotionEvent;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
|
||||
import net.osmand.binary.BinaryMapIndexReader;
|
||||
import net.osmand.data.Amenity;
|
||||
|
@ -36,18 +30,15 @@ import net.osmand.osm.PoiCategory;
|
|||
import net.osmand.plus.ContextMenuAdapter;
|
||||
import net.osmand.plus.OsmandApplication;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.UiUtilities;
|
||||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.render.OsmandRenderer;
|
||||
import net.osmand.plus.render.OsmandRenderer.RenderingContext;
|
||||
import net.osmand.render.RenderingRuleSearchRequest;
|
||||
import net.osmand.render.RenderingRulesStorage;
|
||||
import net.osmand.util.MapAlgorithms;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -144,485 +135,14 @@ public abstract class OsmandMapLayer {
|
|||
}
|
||||
}
|
||||
|
||||
protected boolean isIn(float x, float y, int lx, int ty, int rx, int by) {
|
||||
return x >= lx && x <= rx && y >= ty && y <= by;
|
||||
}
|
||||
|
||||
public int calculatePath(RotatedTileBox tb, List<Float> xs, List<Float> ys, Path path) {
|
||||
List<Pair<Path, GeometryWayStyle>> paths = new ArrayList<>();
|
||||
int res = calculatePath(tb, xs, ys, null, paths);
|
||||
if (paths.size() > 0) {
|
||||
path.addPath(paths.get(0).first);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public static class GeometryWayContext {
|
||||
|
||||
private Context ctx;
|
||||
private float density;
|
||||
private boolean nightMode;
|
||||
|
||||
private Paint paintIcon;
|
||||
private Paint paintIconCustom;
|
||||
|
||||
private RenderingLineAttributes attrs;
|
||||
private RenderingLineAttributes attrsPT;
|
||||
private RenderingLineAttributes attrsW;
|
||||
|
||||
private Bitmap arrowBitmap;
|
||||
private Bitmap walkArrowBitmap;
|
||||
private Bitmap anchorBitmap;
|
||||
private Map<Pair<Integer, Drawable>, Bitmap> stopBitmapsCache = new HashMap<>();
|
||||
private Map<Integer, Bitmap> stopSmallBitmapsCache = new HashMap<>();
|
||||
|
||||
public GeometryWayContext(Context ctx, float density) {
|
||||
this.ctx = ctx;
|
||||
this.density = density;
|
||||
|
||||
paintIcon = new Paint();
|
||||
paintIcon.setFilterBitmap(true);
|
||||
paintIcon.setAntiAlias(true);
|
||||
paintIcon.setColor(Color.BLACK);
|
||||
paintIcon.setStrokeWidth(1f * density);
|
||||
|
||||
paintIconCustom = new Paint();
|
||||
paintIconCustom.setFilterBitmap(true);
|
||||
paintIconCustom.setAntiAlias(true);
|
||||
paintIconCustom.setColor(Color.BLACK);
|
||||
paintIconCustom.setStrokeWidth(1f * density);
|
||||
|
||||
attrsW = new RenderingLineAttributes("walkingRouteLine");
|
||||
attrsW.defaultWidth = (int) (12 * density);
|
||||
attrsW.defaultWidth3 = (int) (7 * density);
|
||||
attrsW.defaultColor = ctx.getResources().getColor(R.color.nav_track_walk_fill);
|
||||
attrsW.paint3.setStrokeCap(Cap.BUTT);
|
||||
attrsW.paint3.setColor(Color.WHITE);
|
||||
attrsW.paint2.setStrokeCap(Cap.BUTT);
|
||||
attrsW.paint2.setColor(Color.BLACK);
|
||||
|
||||
arrowBitmap = BitmapFactory.decodeResource(ctx.getResources(), R.drawable.map_route_direction_arrow, null);
|
||||
}
|
||||
|
||||
public OsmandApplication getApp() {
|
||||
return (OsmandApplication) ctx.getApplicationContext();
|
||||
}
|
||||
|
||||
public float getDensity() {
|
||||
return density;
|
||||
}
|
||||
|
||||
public boolean isNightMode() {
|
||||
return nightMode;
|
||||
}
|
||||
|
||||
private int calculateHash(Object... o) {
|
||||
return Arrays.hashCode(o);
|
||||
}
|
||||
|
||||
public void updatePaints(boolean nightMode,
|
||||
@NonNull RenderingLineAttributes attrs,
|
||||
@NonNull RenderingLineAttributes attrsPT,
|
||||
@NonNull RenderingLineAttributes attrsW) {
|
||||
this.attrs = attrs;
|
||||
this.attrsPT = attrsPT;
|
||||
this.attrsW = attrsW;
|
||||
paintIcon.setColorFilter(new PorterDuffColorFilter(attrs.paint2.getColor(), Mode.MULTIPLY));
|
||||
|
||||
this.nightMode = nightMode;
|
||||
recreateBitmaps();
|
||||
}
|
||||
|
||||
private boolean hasAttrs() {
|
||||
return attrs != null && attrsPT != null && attrsW != null;
|
||||
}
|
||||
|
||||
public int getStrokeColor(int sourceColor) {
|
||||
return ColorUtils.blendARGB(sourceColor, Color.BLACK, 0.6f);
|
||||
}
|
||||
|
||||
private void recreateBitmaps() {
|
||||
if (hasAttrs()) {
|
||||
float walkCircleH = attrsW.paint.getStrokeWidth() * 1.33f;
|
||||
float walkCircleW = attrsW.paint.getStrokeWidth();
|
||||
float walkCircleRadius = attrsW.paint.getStrokeWidth() / 2f;
|
||||
float routeShieldRadius = attrsPT.paint3.getStrokeWidth() / 2f;
|
||||
|
||||
// create anchor bitmap
|
||||
float margin = 2f * density;
|
||||
float width = routeShieldRadius * 2 + margin * 2;
|
||||
float height = routeShieldRadius * 2 + margin * 2;
|
||||
Bitmap bitmap = Bitmap.createBitmap((int)width, (int)height, Bitmap.Config.ARGB_8888);
|
||||
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
Paint paint = new Paint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStrokeWidth(1f * density);
|
||||
|
||||
float x = width / 2;
|
||||
float y = height / 2;
|
||||
paint.setColor(Color.WHITE);
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
canvas.drawCircle(x, y, routeShieldRadius, paint);
|
||||
paint.setColor(Color.BLACK);
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
canvas.drawCircle(x, y, routeShieldRadius, paint);
|
||||
|
||||
anchorBitmap = bitmap;
|
||||
|
||||
// create walk arrow bitmap
|
||||
width = walkCircleW + margin * 2;
|
||||
height = walkCircleH + margin * 2;
|
||||
bitmap = Bitmap.createBitmap((int)width, (int)height, Bitmap.Config.ARGB_8888);
|
||||
|
||||
canvas = new Canvas(bitmap);
|
||||
paint = new Paint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStrokeWidth(1f * density);
|
||||
|
||||
RectF rect = new RectF(margin, margin, width - margin, height - margin);
|
||||
paint.setColor(attrsW.paint.getColor());
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
canvas.drawRoundRect(rect, walkCircleRadius, walkCircleRadius, paint);
|
||||
paint.setColor(getStrokeColor(paint.getColor()));
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
canvas.drawRoundRect(rect, walkCircleRadius, walkCircleRadius, paint);
|
||||
|
||||
paint = new Paint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setColor(Color.WHITE);
|
||||
paint.setStrokeWidth(1f * density);
|
||||
paint.setAlpha(200);
|
||||
canvas.drawBitmap(arrowBitmap, width / 2 - arrowBitmap.getWidth() / 2f, height / 2 - arrowBitmap.getHeight() / 2f, paint);
|
||||
|
||||
walkArrowBitmap = bitmap;
|
||||
stopBitmapsCache = new HashMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
public Paint getPaintIcon() {
|
||||
return paintIcon;
|
||||
}
|
||||
|
||||
public Paint getPaintIconCustom() {
|
||||
return paintIconCustom;
|
||||
}
|
||||
|
||||
public Bitmap getArrowBitmap() {
|
||||
return arrowBitmap;
|
||||
}
|
||||
|
||||
public Bitmap getWalkArrowBitmap() {
|
||||
return walkArrowBitmap;
|
||||
}
|
||||
|
||||
public Bitmap getAnchorBitmap() {
|
||||
return anchorBitmap;
|
||||
}
|
||||
|
||||
public Bitmap getStopShieldBitmap(int color, Drawable stopDrawable) {
|
||||
Bitmap bmp = stopBitmapsCache.get(new Pair<>(color, stopDrawable));
|
||||
if (bmp == null) {
|
||||
int fillColor = UiUtilities.getContrastColor(getApp(), color, true);
|
||||
int strokeColor = getStrokeColor(color);
|
||||
|
||||
float routeShieldRadius = attrsPT.paint3.getStrokeWidth() / 2;
|
||||
float routeShieldCornerRadius = 3 * density;
|
||||
|
||||
float margin = 2f * density;
|
||||
float width = routeShieldRadius * 2 + margin * 2;
|
||||
float height = routeShieldRadius * 2 + margin * 2;
|
||||
bmp = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888);
|
||||
|
||||
Canvas canvas = new Canvas(bmp);
|
||||
Paint paint = new Paint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStrokeWidth(1f * density);
|
||||
|
||||
RectF rect = new RectF(margin, margin, width - margin, height - margin);
|
||||
paint.setColor(fillColor);
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
canvas.drawRoundRect(rect, routeShieldCornerRadius, routeShieldCornerRadius, paint);
|
||||
paint.setColor(strokeColor);
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
canvas.drawRoundRect(rect, routeShieldCornerRadius, routeShieldCornerRadius, paint);
|
||||
|
||||
if (stopDrawable != null) {
|
||||
stopDrawable.setColorFilter(new PorterDuffColorFilter(strokeColor, Mode.SRC_IN));
|
||||
float marginBitmap = 1f * density;
|
||||
rect.inset(marginBitmap, marginBitmap);
|
||||
stopDrawable.setBounds(0, 0, (int) rect.width(), (int) rect.height());
|
||||
canvas.translate(rect.left, rect.top);
|
||||
stopDrawable.draw(canvas);
|
||||
}
|
||||
stopBitmapsCache.put(new Pair<>(color, stopDrawable), bmp);
|
||||
}
|
||||
return bmp;
|
||||
}
|
||||
|
||||
public Bitmap getStopSmallShieldBitmap(int color) {
|
||||
Bitmap bmp = stopSmallBitmapsCache.get(color);
|
||||
if (bmp == null) {
|
||||
int fillColor = UiUtilities.getContrastColor(getApp(), color, true);
|
||||
int strokeColor = getStrokeColor(color);
|
||||
|
||||
float routeShieldRadius = attrsPT.paint3.getStrokeWidth() / 4;
|
||||
|
||||
float margin = 3f * density;
|
||||
float width = routeShieldRadius * 2 + margin * 2;
|
||||
float height = routeShieldRadius * 2 + margin * 2;
|
||||
bmp = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888);
|
||||
|
||||
Canvas canvas = new Canvas(bmp);
|
||||
Paint paint = new Paint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStrokeWidth(1f * density);
|
||||
paint.setColor(fillColor);
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
canvas.drawCircle(width / 2, height / 2, routeShieldRadius, paint);
|
||||
paint.setColor(strokeColor);
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
canvas.drawCircle(width / 2, height / 2, routeShieldRadius, paint);
|
||||
|
||||
stopSmallBitmapsCache.put(color, bmp);
|
||||
}
|
||||
return bmp;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class GeometryWayStyle {
|
||||
|
||||
private GeometryWayContext context;
|
||||
protected Integer color;
|
||||
|
||||
public GeometryWayStyle(GeometryWayContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public GeometryWayStyle(GeometryWayContext context, Integer color) {
|
||||
this.context = context;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public GeometryWayContext getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public Context getCtx() {
|
||||
return context.ctx;
|
||||
}
|
||||
|
||||
public Integer getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public Integer getStrokeColor() {
|
||||
return context.getStrokeColor(color);
|
||||
}
|
||||
|
||||
public Integer getPointColor() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isNightMode() {
|
||||
return context.nightMode;
|
||||
}
|
||||
|
||||
public boolean hasAnchors() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hasPathLine() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isTransportLine() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isWalkLine() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract Bitmap getPointBitmap();
|
||||
|
||||
public boolean hasPaintedPointBitmap() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (color != null ? color.hashCode() : 0) + (context.nightMode ? 1231 : 1237) + (hasAnchors() ? 12310 : 12370);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof GeometryWayStyle)) {
|
||||
return false;
|
||||
}
|
||||
GeometryWayStyle o = (GeometryWayStyle) other;
|
||||
if (color != null && o.color != null) {
|
||||
return color.equals(o.color);
|
||||
}
|
||||
return color == null && o.color == null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PathPoint {
|
||||
float x;
|
||||
float y;
|
||||
double angle;
|
||||
GeometryWayStyle style;
|
||||
|
||||
private Matrix matrix = new Matrix();
|
||||
|
||||
PathPoint(float x, float y, double angle, GeometryWayStyle style) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.angle = angle;
|
||||
this.style = style;
|
||||
}
|
||||
|
||||
protected Matrix getMatrix() {
|
||||
return matrix;
|
||||
}
|
||||
|
||||
void draw(Canvas canvas, GeometryWayContext context) {
|
||||
if (style != null && style.getPointBitmap() != null) {
|
||||
Bitmap bitmap = style.getPointBitmap();
|
||||
Integer pointColor = style.getPointColor();
|
||||
float paintH2 = bitmap.getHeight() / 2f;
|
||||
float paintW2 = bitmap.getWidth() / 2f;
|
||||
|
||||
matrix.reset();
|
||||
matrix.postRotate((float) angle, paintW2, paintH2);
|
||||
matrix.postTranslate(x - paintW2, y - paintH2);
|
||||
if (pointColor != null) {
|
||||
Paint paint = context.getPaintIconCustom();
|
||||
paint.setColorFilter(new PorterDuffColorFilter(pointColor, Mode.SRC_IN));
|
||||
canvas.drawBitmap(bitmap, matrix, paint);
|
||||
} else {
|
||||
if (style.hasPaintedPointBitmap()) {
|
||||
Paint paint = context.getPaintIconCustom();
|
||||
paint.setColorFilter(null);
|
||||
canvas.drawBitmap(bitmap, matrix, paint);
|
||||
} else {
|
||||
canvas.drawBitmap(bitmap, matrix, context.getPaintIcon());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void addLocation(RotatedTileBox tb, double latitude, double longitude, GeometryWayStyle style,
|
||||
List<Float> tx, List<Float> ty, List<Double> angles, List<Double> distances,
|
||||
double dist, List<GeometryWayStyle> styles) {
|
||||
float x = tb.getPixXFromLatLon(latitude, longitude);
|
||||
float y = tb.getPixYFromLatLon(latitude, longitude);
|
||||
float px = x;
|
||||
float py = y;
|
||||
int previous = tx.size() - 1;
|
||||
if (previous >= 0) {
|
||||
px = tx.get(previous);
|
||||
py = ty.get(previous);
|
||||
}
|
||||
double angle = 0;
|
||||
if (px != x || py != y) {
|
||||
double angleRad = Math.atan2(y - py, x - px);
|
||||
angle = (angleRad * 180 / Math.PI) + 90f;
|
||||
}
|
||||
double distSegment = Math.sqrt((y - py) * (y - py) + (x - px) * (x - px));
|
||||
if (dist != 0) {
|
||||
distSegment = dist;
|
||||
}
|
||||
tx.add(x);
|
||||
ty.add(y);
|
||||
angles.add(angle);
|
||||
distances.add(distSegment);
|
||||
styles.add(style);
|
||||
}
|
||||
|
||||
public int calculatePath(RotatedTileBox tb, List<Float> xs, List<Float> ys, List<GeometryWayStyle> styles, List<Pair<Path, GeometryWayStyle>> paths) {
|
||||
boolean segmentStarted = false;
|
||||
float prevX = xs.get(0);
|
||||
float prevY = ys.get(0);
|
||||
int height = tb.getPixHeight();
|
||||
int width = tb.getPixWidth();
|
||||
int cnt = 0;
|
||||
boolean hasStyles = styles != null && styles.size() == xs.size();
|
||||
GeometryWayStyle style = hasStyles ? styles.get(0) : null;
|
||||
Path path = new Path();
|
||||
boolean prevIn = isIn(prevX, prevY, 0, 0, width, height);
|
||||
for (int i = 1; i < xs.size(); i++) {
|
||||
float currX = xs.get(i);
|
||||
float currY = ys.get(i);
|
||||
boolean currIn = isIn(currX, currY, 0, 0, width, height);
|
||||
boolean draw = false;
|
||||
if (prevIn && currIn) {
|
||||
draw = true;
|
||||
} else {
|
||||
long intersection = MapAlgorithms.calculateIntersection((int)currX, (int)currY, (int)prevX, (int)prevY, 0, width, height, 0);
|
||||
if (intersection != -1) {
|
||||
if (prevIn && (i == 1)) {
|
||||
cnt++;
|
||||
path.moveTo(prevX, prevY);
|
||||
segmentStarted = true;
|
||||
}
|
||||
prevX = (int) (intersection >> 32);
|
||||
prevY = (int) (intersection & 0xffffffff);
|
||||
draw = true;
|
||||
}
|
||||
if (i == xs.size() - 1 && !currIn) {
|
||||
long inter = MapAlgorithms.calculateIntersection((int)prevX, (int)prevY, (int)currX, (int)currY, 0, width, height, 0);
|
||||
if (inter != -1) {
|
||||
currX = (int) (inter >> 32);
|
||||
currY = (int) (inter & 0xffffffff);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (draw) {
|
||||
if (!segmentStarted) {
|
||||
cnt++;
|
||||
path.moveTo(prevX, prevY);
|
||||
segmentStarted = true;
|
||||
}
|
||||
path.lineTo(currX, currY);
|
||||
} else {
|
||||
segmentStarted = false;
|
||||
}
|
||||
prevIn = currIn;
|
||||
prevX = currX;
|
||||
prevY = currY;
|
||||
|
||||
if (hasStyles) {
|
||||
GeometryWayStyle newStyle = styles.get(i);
|
||||
if (!style.equals(newStyle)) {
|
||||
paths.add(new Pair<>(path, style));
|
||||
path = new Path();
|
||||
if (segmentStarted) {
|
||||
path.moveTo(currX, currY);
|
||||
}
|
||||
style = newStyle;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!path.isEmpty()) {
|
||||
paths.add(new Pair<>(path, style));
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public QuadTree<QuadRect> initBoundIntersections(RotatedTileBox tileBox) {
|
||||
public static QuadTree<QuadRect> initBoundIntersections(RotatedTileBox tileBox) {
|
||||
QuadRect bounds = new QuadRect(0, 0, tileBox.getPixWidth(), tileBox.getPixHeight());
|
||||
bounds.inset(-bounds.width() / 4, -bounds.height() / 4);
|
||||
return new QuadTree<>(bounds, 4, 0.6f);
|
||||
}
|
||||
|
||||
public boolean intersects(QuadTree<QuadRect> boundIntersections, float x, float y, float width, float height) {
|
||||
public static boolean intersects(QuadTree<QuadRect> boundIntersections, float x, float y, float width, float height) {
|
||||
List<QuadRect> result = new ArrayList<>();
|
||||
QuadRect visibleRect = calculateRect(x, y, width, height);
|
||||
boundIntersections.queryInBox(new QuadRect(visibleRect.left, visibleRect.top, visibleRect.right, visibleRect.bottom), result);
|
||||
|
@ -650,7 +170,7 @@ public abstract class OsmandMapLayer {
|
|||
return new QuadRect(leftLongitude - lon, topLatitude + lat, rightLongitude + lon, bottomLatitude - lat);
|
||||
}
|
||||
|
||||
public QuadRect calculateRect(float x, float y, float width, float height) {
|
||||
public static QuadRect calculateRect(float x, float y, float width, float height) {
|
||||
QuadRect rf;
|
||||
double left = x - width / 2.0d;
|
||||
double top = y - height / 2.0d;
|
||||
|
@ -852,7 +372,7 @@ public abstract class OsmandMapLayer {
|
|||
|
||||
}
|
||||
|
||||
protected static class RenderingLineAttributes {
|
||||
public static class RenderingLineAttributes {
|
||||
protected int cachedHash;
|
||||
public Paint paint;
|
||||
public Paint customColorPaint;
|
||||
|
|
|
@ -49,6 +49,7 @@ import net.osmand.plus.helpers.TwoFingerTapDetector;
|
|||
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||
import net.osmand.plus.views.MultiTouchSupport.MultiTouchZoomListener;
|
||||
import net.osmand.plus.views.OsmandMapLayer.DrawSettings;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer;
|
||||
import net.osmand.render.RenderingRuleSearchRequest;
|
||||
import net.osmand.render.RenderingRuleStorageProperties;
|
||||
import net.osmand.render.RenderingRulesStorage;
|
||||
|
|
|
@ -2,6 +2,7 @@ package net.osmand.plus.views;
|
|||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
|
@ -9,6 +10,9 @@ import net.osmand.GPXUtilities;
|
|||
import net.osmand.GPXUtilities.WptPt;
|
||||
import net.osmand.data.QuadRect;
|
||||
import net.osmand.data.RotatedTileBox;
|
||||
import net.osmand.plus.views.layers.geometry.GeometryWay;
|
||||
import net.osmand.plus.views.layers.geometry.GpxGeometryWay;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -62,6 +66,8 @@ public class Renderable {
|
|||
protected AsynchronousResampler culler = null; // The currently active resampler
|
||||
protected Paint paint = null; // MUST be set by 'updateLocalPaint' before use
|
||||
|
||||
protected GpxGeometryWay geometryWay;
|
||||
|
||||
public RenderableSegment(List<WptPt> points, double segmentSize) {
|
||||
this.points = points;
|
||||
this.segmentSize = segmentSize;
|
||||
|
@ -73,12 +79,20 @@ public class Renderable {
|
|||
paint = new Paint(p);
|
||||
paint.setStrokeCap(Paint.Cap.ROUND);
|
||||
paint.setStrokeJoin(Paint.Join.ROUND);
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
}
|
||||
paint.setColor(p.getColor());
|
||||
paint.setStrokeWidth(p.getStrokeWidth());
|
||||
}
|
||||
|
||||
public GpxGeometryWay getGeometryWay() {
|
||||
return geometryWay;
|
||||
}
|
||||
|
||||
public void setGeometryWay(GpxGeometryWay geometryWay) {
|
||||
this.geometryWay = geometryWay;
|
||||
}
|
||||
|
||||
protected abstract void startCuller(double newZoom);
|
||||
|
||||
protected void drawSingleSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) {}
|
||||
|
@ -95,45 +109,54 @@ public class Renderable {
|
|||
culled = cull;
|
||||
}
|
||||
|
||||
public List<WptPt> getPointsForDrawing() {
|
||||
return culled.isEmpty() ? points : culled;
|
||||
}
|
||||
|
||||
public void drawGeometry(Canvas canvas, RotatedTileBox tileBox, QuadRect quadRect, int arrowColor, int trackColor, float trackWidth) {
|
||||
if (geometryWay != null) {
|
||||
List<WptPt> points = getPointsForDrawing();
|
||||
if (!Algorithms.isEmpty(points)) {
|
||||
geometryWay.setTrackStyleParams(arrowColor, trackColor, trackWidth);
|
||||
geometryWay.updatePoints(tileBox, points);
|
||||
geometryWay.drawSegments(tileBox, canvas, quadRect.top, quadRect.left, quadRect.bottom, quadRect.right, null, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void draw(List<WptPt> pts, Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
||||
|
||||
if (pts.size() > 1) {
|
||||
|
||||
updateLocalPaint(p);
|
||||
canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
|
||||
QuadRect tileBounds = tileBox.getLatLonBounds();
|
||||
|
||||
WptPt lastPt = pts.get(0);
|
||||
float lastx = 0;
|
||||
float lasty = 0;
|
||||
boolean reCalculateLastXY = true;
|
||||
|
||||
int size = pts.size();
|
||||
for (int i = 1; i < size; i++) {
|
||||
boolean recalculateLastXY = true;
|
||||
Path path = new Path();
|
||||
for (int i = 1; i < pts.size(); i++) {
|
||||
WptPt pt = pts.get(i);
|
||||
|
||||
if (Math.min(pt.lon, lastPt.lon) < tileBounds.right && Math.max(pt.lon, lastPt.lon) > tileBounds.left
|
||||
&& Math.min(pt.lat, lastPt.lat) < tileBounds.top && Math.max(pt.lat, lastPt.lat) > tileBounds.bottom) {
|
||||
|
||||
if (reCalculateLastXY) {
|
||||
lastx = tileBox.getPixXFromLatLon(lastPt.lat, lastPt.lon);
|
||||
lasty = tileBox.getPixYFromLatLon(lastPt.lat, lastPt.lon);
|
||||
reCalculateLastXY = false;
|
||||
if (recalculateLastXY) {
|
||||
recalculateLastXY = false;
|
||||
float lastX = tileBox.getPixXFromLatLon(lastPt.lat, lastPt.lon);
|
||||
float lastY = tileBox.getPixYFromLatLon(lastPt.lat, lastPt.lon);
|
||||
if (!path.isEmpty()) {
|
||||
canvas.drawPath(path, paint);
|
||||
}
|
||||
path.reset();
|
||||
path.moveTo(lastX, lastY);
|
||||
}
|
||||
|
||||
float x = tileBox.getPixXFromLatLon(pt.lat, pt.lon);
|
||||
float y = tileBox.getPixYFromLatLon(pt.lat, pt.lon);
|
||||
|
||||
canvas.drawLine(lastx, lasty, x, y, paint);
|
||||
|
||||
lastx = x;
|
||||
lasty = y;
|
||||
|
||||
path.lineTo(x, y);
|
||||
} else {
|
||||
reCalculateLastXY = true;
|
||||
recalculateLastXY = true;
|
||||
}
|
||||
lastPt = pt;
|
||||
}
|
||||
if (!path.isEmpty()) {
|
||||
canvas.drawPath(path, paint);
|
||||
}
|
||||
canvas.rotate(tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,4 @@
|
|||
package net.osmand.plus.views;
|
||||
package net.osmand.plus.views.layers;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
|
@ -28,7 +28,9 @@ import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference;
|
|||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.mapcontextmenu.MapContextMenu;
|
||||
import net.osmand.plus.views.ContextMenuLayer.IContextMenuProvider;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer.IContextMenuProvider;
|
||||
import net.osmand.plus.widgets.tools.CropCircleTransformation;
|
||||
|
||||
import java.io.IOException;
|
|
@ -1,4 +1,4 @@
|
|||
package net.osmand.plus.views;
|
||||
package net.osmand.plus.views.layers;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
|
@ -66,7 +66,11 @@ import net.osmand.plus.render.MapRenderRepositories;
|
|||
import net.osmand.plus.render.NativeOsmandLibrary;
|
||||
import net.osmand.plus.routepreparationmenu.ChooseRouteFragment;
|
||||
import net.osmand.plus.routepreparationmenu.MapRouteInfoMenu;
|
||||
import net.osmand.plus.views.AddGpxPointBottomSheetHelper;
|
||||
import net.osmand.plus.views.AddGpxPointBottomSheetHelper.NewGpxPoint;
|
||||
import net.osmand.plus.views.MoveMarkerBottomSheetHelper;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.corenative.NativeCoreContext;
|
||||
import net.osmand.util.Algorithms;
|
||||
import net.osmand.util.MapUtils;
|
|
@ -1,4 +1,4 @@
|
|||
package net.osmand.plus.views;
|
||||
package net.osmand.plus.views.layers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
|
@ -34,8 +34,11 @@ import net.osmand.plus.download.ui.DownloadMapToolbarController;
|
|||
import net.osmand.plus.mapcontextmenu.MapContextMenu;
|
||||
import net.osmand.plus.mapcontextmenu.other.MapMultiSelectionMenu;
|
||||
import net.osmand.plus.resources.ResourceManager;
|
||||
import net.osmand.plus.views.ContextMenuLayer.IContextMenuProvider;
|
||||
import net.osmand.plus.views.ContextMenuLayer.IContextMenuProviderSelection;
|
||||
import net.osmand.plus.views.MapTileLayer;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer.IContextMenuProvider;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer.IContextMenuProviderSelection;
|
||||
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarController;
|
||||
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarControllerType;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
@ -200,12 +203,12 @@ public class DownloadedRegionsLayer extends OsmandMapLayer implements IContextMe
|
|||
return;
|
||||
}
|
||||
//make sure no maps are loaded for the location
|
||||
checkMapToDownload(tileBox, data.results);
|
||||
checkMapToDownload(tileBox, data.getResults());
|
||||
// draw objects
|
||||
if (osmandRegions.isInitialized() && zoom >= ZOOM_TO_SHOW_SELECTION_ST && zoom < ZOOM_TO_SHOW_SELECTION) {
|
||||
final List<BinaryMapDataObject> currentObjects = new LinkedList<>();
|
||||
if (data.results != null) {
|
||||
currentObjects.addAll(data.results);
|
||||
if (data.getResults() != null) {
|
||||
currentObjects.addAll(data.getResults());
|
||||
}
|
||||
final List<BinaryMapDataObject> selectedObjects = new LinkedList<>(this.selectedObjects);
|
||||
|
||||
|
@ -457,7 +460,7 @@ public class DownloadedRegionsLayer extends OsmandMapLayer implements IContextMe
|
|||
StringBuilder filter = new StringBuilder();
|
||||
int zoom = view.getZoom();
|
||||
RotatedTileBox queriedBox = data.getQueriedBox();
|
||||
final List<BinaryMapDataObject> currentObjects = data.results;
|
||||
final List<BinaryMapDataObject> currentObjects = data.getResults();
|
||||
if (osmandRegions.isInitialized() && queriedBox != null) {
|
||||
if(zoom >= ZOOM_TO_SHOW_MAP_NAMES && Math.abs(queriedBox.getZoom() - zoom) <= ZOOM_THRESHOLD &&
|
||||
currentObjects != null){
|
||||
|
@ -578,12 +581,12 @@ public class DownloadedRegionsLayer extends OsmandMapLayer implements IContextMe
|
|||
private void getWorldRegionFromPoint(RotatedTileBox tb, PointF point, List<? super DownloadMapObject> dataObjects) {
|
||||
int zoom = tb.getZoom();
|
||||
if (zoom >= ZOOM_TO_SHOW_SELECTION_ST && zoom < ZOOM_TO_SHOW_SELECTION
|
||||
&& data.results != null && osmandRegions.isInitialized()) {
|
||||
&& data.getResults() != null && osmandRegions.isInitialized()) {
|
||||
LatLon pointLatLon = tb.getLatLonFromPixel(point.x, point.y);
|
||||
int point31x = MapUtils.get31TileNumberX(pointLatLon.getLongitude());
|
||||
int point31y = MapUtils.get31TileNumberY(pointLatLon.getLatitude());
|
||||
|
||||
List<BinaryMapDataObject> result = new LinkedList<>(data.results);
|
||||
List<BinaryMapDataObject> result = new LinkedList<>(data.getResults());
|
||||
Iterator<BinaryMapDataObject> it = result.iterator();
|
||||
while (it.hasNext()) {
|
||||
BinaryMapDataObject o = it.next();
|
|
@ -1,4 +1,4 @@
|
|||
package net.osmand.plus.views;
|
||||
package net.osmand.plus.views.layers;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.PointF;
|
||||
|
@ -22,8 +22,10 @@ import net.osmand.plus.MapMarkersHelper.MapMarker;
|
|||
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.base.PointImageDrawable;
|
||||
import net.osmand.plus.views.ContextMenuLayer.ApplyMovedObjectCallback;
|
||||
import net.osmand.plus.views.MapTextLayer.MapTextProvider;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer.ApplyMovedObjectCallback;
|
||||
import net.osmand.plus.views.layers.MapTextLayer.MapTextProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
|
@ -1,11 +1,9 @@
|
|||
package net.osmand.plus.views;
|
||||
package net.osmand.plus.views.layers;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Align;
|
||||
import android.graphics.Paint.Cap;
|
||||
import android.graphics.Paint.Style;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
|
@ -50,13 +48,17 @@ import net.osmand.plus.mapcontextmenu.controllers.SelectedGpxMenuController.Sele
|
|||
import net.osmand.plus.mapcontextmenu.other.TrackChartPoints;
|
||||
import net.osmand.plus.render.OsmandRenderer;
|
||||
import net.osmand.plus.render.OsmandRenderer.RenderingContext;
|
||||
import net.osmand.plus.render.RenderingIcons;
|
||||
import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference;
|
||||
import net.osmand.plus.track.SaveGpxAsyncTask;
|
||||
import net.osmand.plus.track.TrackDrawInfo;
|
||||
import net.osmand.plus.views.ContextMenuLayer.IContextMenuProvider;
|
||||
import net.osmand.plus.views.ContextMenuLayer.IMoveObjectProvider;
|
||||
import net.osmand.plus.views.MapTextLayer.MapTextProvider;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.Renderable;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer.IContextMenuProvider;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer.IMoveObjectProvider;
|
||||
import net.osmand.plus.views.layers.MapTextLayer.MapTextProvider;
|
||||
import net.osmand.plus.views.layers.geometry.GpxGeometryWay;
|
||||
import net.osmand.plus.views.layers.geometry.GpxGeometryWayContext;
|
||||
import net.osmand.render.RenderingRuleProperty;
|
||||
import net.osmand.render.RenderingRuleSearchRequest;
|
||||
import net.osmand.render.RenderingRulesStorage;
|
||||
|
@ -79,8 +81,6 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
|
|||
private static final Log log = PlatformUtil.getLog(GPXLayer.class);
|
||||
|
||||
private static final double TOUCH_RADIUS_MULTIPLIER = 1.5;
|
||||
private static final double DIRECTION_ARROW_DISTANCE_MULTIPLIER = 10.0;
|
||||
private static final float DIRECTION_ARROW_CIRCLE_MULTIPLIER = 1.5f;
|
||||
private static final int DEFAULT_WIDTH_MULTIPLIER = 7;
|
||||
private static final int START_ZOOM = 7;
|
||||
|
||||
|
@ -117,8 +117,8 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
|
|||
|
||||
private Paint paintTextIcon;
|
||||
|
||||
private Bitmap arrowBitmap;
|
||||
private GeometryWayContext wayContext;
|
||||
private GpxGeometryWayContext wayContext;
|
||||
private GpxGeometryWay wayGeometry;
|
||||
|
||||
private OsmandRenderer osmandRenderer;
|
||||
|
||||
|
@ -214,12 +214,8 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
|
|||
defPointColor = ContextCompat.getColor(view.getApplication(), R.color.gpx_color_point);
|
||||
grayColor = ContextCompat.getColor(view.getApplication(), R.color.color_favorite_gray);
|
||||
|
||||
wayContext = new GeometryWayContext(view.getContext(), view.getDensity());
|
||||
|
||||
Paint paint = wayContext.getPaintIcon();
|
||||
paint.setStrokeCap(Cap.ROUND);
|
||||
|
||||
arrowBitmap = RenderingIcons.getBitmapFromVectorDrawable(view.getContext(), R.drawable.mm_special_arrow_up);
|
||||
wayContext = new GpxGeometryWayContext(view.getContext(), view.getDensity());
|
||||
wayGeometry = new GpxGeometryWay(wayContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -423,33 +419,12 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
|
|||
QuadRect correctedQuadRect = getCorrectedQuadRect(tileBox.getLatLonBounds());
|
||||
String width = getTrackWidthName(selectedGpxFile.getGpxFile(), "");
|
||||
float trackWidth = getTrackWidth(width, defaultTrackWidth);
|
||||
int color = getTrackColor(selectedGpxFile.getGpxFile(), cachedColor);
|
||||
int contrastColor = UiUtilities.getContrastColor(view.getApplication(), color, false);
|
||||
GeometryWayStyle arrowsWayStyle = new GeometryArrowsWayStyle(wayContext, arrowBitmap, contrastColor, color, trackWidth);
|
||||
int trackColor = getTrackColor(selectedGpxFile.getGpxFile(), cachedColor);
|
||||
int arrowColor = UiUtilities.getContrastColor(view.getApplication(), trackColor, false);
|
||||
for (TrkSegment segment : selectedGpxFile.getPointsToDisplay()) {
|
||||
List<Float> tx = new ArrayList<>();
|
||||
List<Float> ty = new ArrayList<>();
|
||||
List<Double> distances = new ArrayList<>();
|
||||
List<Double> angles = new ArrayList<>();
|
||||
List<GeometryWayStyle> styles = new ArrayList<>();
|
||||
boolean previousVisible = false;
|
||||
|
||||
List<WptPt> points = segment.points;
|
||||
if (points.size() > 1) {
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
WptPt pt = points.get(i);
|
||||
if (correctedQuadRect.left <= pt.getLongitude()
|
||||
&& pt.getLongitude() <= correctedQuadRect.right
|
||||
&& correctedQuadRect.bottom <= pt.getLatitude()
|
||||
&& pt.getLatitude() <= correctedQuadRect.top) {
|
||||
addLocation(tileBox, pt.getLatitude(), pt.getLongitude(), null, tx, ty, angles, distances, 0, styles);
|
||||
previousVisible = true;
|
||||
} else if (previousVisible) {
|
||||
addLocation(tileBox, pt.getLatitude(), pt.getLongitude(), null, tx, ty, angles, distances, 0, styles);
|
||||
previousVisible = false;
|
||||
}
|
||||
}
|
||||
drawArrowsOverPath(tx, ty, angles, distances, canvas, tileBox, arrowsWayStyle);
|
||||
if (segment.renderer instanceof Renderable.RenderableSegment) {
|
||||
((Renderable.RenderableSegment) segment.renderer)
|
||||
.drawGeometry(canvas, tileBox, correctedQuadRect, arrowColor, trackColor, trackWidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -457,122 +432,6 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
|
|||
}
|
||||
}
|
||||
|
||||
private void drawArrowsOverPath(List<Float> tx, List<Float> ty, List<Double> angles, List<Double> distances,
|
||||
Canvas canvas, RotatedTileBox tb, GeometryWayStyle wayStyle) {
|
||||
int pixHeight = tb.getPixHeight();
|
||||
int pixWidth = tb.getPixWidth();
|
||||
int left = -pixWidth / 4;
|
||||
int right = pixWidth + pixWidth / 4;
|
||||
int top = -pixHeight / 4;
|
||||
int bottom = pixHeight + pixHeight / 4;
|
||||
|
||||
double zoomCoef = tb.getZoomAnimation() > 0 ? (Math.pow(2, tb.getZoomAnimation() + tb.getZoomFloatPart())) : 1f;
|
||||
double pxStep = arrowBitmap.getHeight() * DIRECTION_ARROW_DISTANCE_MULTIPLIER * zoomCoef;
|
||||
double dist = 0;
|
||||
|
||||
List<ArrowPathPoint> arrows = new ArrayList<>();
|
||||
for (int i = tx.size() - 2; i >= 0; i--) {
|
||||
float px = tx.get(i);
|
||||
float py = ty.get(i);
|
||||
float x = tx.get(i + 1);
|
||||
float y = ty.get(i + 1);
|
||||
double angle = angles.get(i + 1);
|
||||
double distSegment = distances.get(i + 1);
|
||||
if (distSegment == 0) {
|
||||
continue;
|
||||
}
|
||||
if (dist >= pxStep) {
|
||||
dist = 0;
|
||||
}
|
||||
double percent = 1 - (pxStep - dist) / distSegment;
|
||||
dist += distSegment;
|
||||
while (dist >= pxStep) {
|
||||
double pdx = (x - px) * percent;
|
||||
double pdy = (y - py) * percent;
|
||||
float iconx = (float) (px + pdx);
|
||||
float icony = (float) (py + pdy);
|
||||
if (isIn(iconx, icony, left, top, right, bottom)) {
|
||||
arrows.add(new ArrowPathPoint(iconx, icony, angle, wayStyle));
|
||||
}
|
||||
dist -= pxStep;
|
||||
percent -= pxStep / distSegment;
|
||||
}
|
||||
}
|
||||
for (int i = arrows.size() - 1; i >= 0; i--) {
|
||||
PathPoint a = arrows.get(i);
|
||||
a.draw(canvas, wayContext);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ArrowPathPoint extends PathPoint {
|
||||
|
||||
ArrowPathPoint(float x, float y, double angle, GeometryWayStyle style) {
|
||||
super(x, y, angle, style);
|
||||
}
|
||||
|
||||
@Override
|
||||
void draw(Canvas canvas, GeometryWayContext context) {
|
||||
if (style instanceof GeometryArrowsWayStyle) {
|
||||
GeometryArrowsWayStyle arrowsWayStyle = (GeometryArrowsWayStyle) style;
|
||||
|
||||
float arrowWidth = style.getPointBitmap().getWidth();
|
||||
if (arrowWidth > arrowsWayStyle.getTrackWidth()) {
|
||||
Paint paint = context.getPaintIcon();
|
||||
paint.setColor(arrowsWayStyle.getTrackColor());
|
||||
paint.setStrokeWidth(arrowWidth * DIRECTION_ARROW_CIRCLE_MULTIPLIER);
|
||||
canvas.drawPoint(x, y, paint);
|
||||
}
|
||||
}
|
||||
super.draw(canvas, context);
|
||||
}
|
||||
}
|
||||
|
||||
private static class GeometryArrowsWayStyle extends GeometryWayStyle {
|
||||
|
||||
private Bitmap arrowBitmap;
|
||||
|
||||
protected int pointColor;
|
||||
protected int trackColor;
|
||||
protected float trackWidth;
|
||||
|
||||
GeometryArrowsWayStyle(GeometryWayContext context, Bitmap arrowBitmap, int arrowColor, int trackColor, float trackWidth) {
|
||||
super(context);
|
||||
this.arrowBitmap = arrowBitmap;
|
||||
this.pointColor = arrowColor;
|
||||
this.trackColor = trackColor;
|
||||
this.trackWidth = trackWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
return other instanceof GeometryArrowsWayStyle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bitmap getPointBitmap() {
|
||||
return arrowBitmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getPointColor() {
|
||||
return pointColor;
|
||||
}
|
||||
|
||||
public int getTrackColor() {
|
||||
return trackColor;
|
||||
}
|
||||
|
||||
public float getTrackWidth() {
|
||||
return trackWidth;
|
||||
}
|
||||
}
|
||||
|
||||
private void drawSelectedFilesStartEndPoints(Canvas canvas, RotatedTileBox tileBox, List<SelectedGpxFile> selectedGPXFiles) {
|
||||
if (tileBox.getZoom() >= START_ZOOM) {
|
||||
for (SelectedGpxFile selectedGpxFile : selectedGPXFiles) {
|
||||
|
@ -781,11 +640,14 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
|
|||
String width = getTrackWidthName(selectedGpxFile.getGpxFile(), "");
|
||||
int color = getTrackColor(selectedGpxFile.getGpxFile(), ts.getColor(cachedColor));
|
||||
if (ts.renderer == null && !ts.points.isEmpty()) {
|
||||
Renderable.RenderableSegment renderer;
|
||||
if (currentTrack) {
|
||||
ts.renderer = new Renderable.CurrentTrack(ts.points);
|
||||
renderer = new Renderable.CurrentTrack(ts.points);
|
||||
} else {
|
||||
ts.renderer = new Renderable.StandardTrack(ts.points, 17.2);
|
||||
renderer = new Renderable.StandardTrack(ts.points, 17.2);
|
||||
}
|
||||
ts.renderer = renderer;
|
||||
renderer.setGeometryWay(new GpxGeometryWay(wayContext));
|
||||
}
|
||||
updatePaints(color, width, selectedGpxFile.isRoutePoints(), currentTrack, settings, tileBox);
|
||||
if (ts.renderer instanceof Renderable.RenderableSegment) {
|
|
@ -1,4 +1,4 @@
|
|||
package net.osmand.plus.views;
|
||||
package net.osmand.plus.views.layers;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
|
@ -21,7 +21,9 @@ import net.osmand.plus.activities.MapActivity;
|
|||
import net.osmand.plus.helpers.AvoidSpecificRoads;
|
||||
import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidRoadInfo;
|
||||
import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidSpecificRoadsCallback;
|
||||
import net.osmand.plus.views.ContextMenuLayer.ApplyMovedObjectCallback;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer.ApplyMovedObjectCallback;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
|
@ -1,4 +1,4 @@
|
|||
package net.osmand.plus.views;
|
||||
package net.osmand.plus.views.layers;
|
||||
|
||||
import android.Manifest;
|
||||
import android.animation.Animator;
|
||||
|
@ -65,6 +65,8 @@ import net.osmand.plus.settings.backend.OsmAndAppCustomization;
|
|||
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||
import net.osmand.plus.settings.backend.OsmandSettings.CommonPreference;
|
||||
import net.osmand.plus.settings.backend.OsmandSettings.LayerTransparencySeekbarMode;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.corenative.NativeCoreContext;
|
||||
|
||||
import java.util.ArrayList;
|
|
@ -1,4 +1,4 @@
|
|||
package net.osmand.plus.views;
|
||||
package net.osmand.plus.views.layers;
|
||||
|
||||
|
||||
import android.graphics.Canvas;
|
||||
|
@ -18,6 +18,8 @@ import net.osmand.plus.activities.MapActivity;
|
|||
import net.osmand.plus.helpers.AndroidUiHelper;
|
||||
import net.osmand.plus.mapcontextmenu.other.TrackChartPoints;
|
||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.mapwidgets.LanesControl;
|
||||
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory;
|
||||
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopCoordinatesView;
|
|
@ -1,4 +1,4 @@
|
|||
package net.osmand.plus.views;
|
||||
package net.osmand.plus.views.layers;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
|
@ -41,9 +41,13 @@ import net.osmand.plus.R;
|
|||
import net.osmand.plus.TargetPointsHelper.TargetPoint;
|
||||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.base.MapViewTrackingUtilities;
|
||||
import net.osmand.plus.views.ContextMenuLayer.ApplyMovedObjectCallback;
|
||||
import net.osmand.plus.views.ContextMenuLayer.IContextMenuProvider;
|
||||
import net.osmand.plus.views.ContextMenuLayer.IContextMenuProviderSelection;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.Renderable;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer.ApplyMovedObjectCallback;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer.IContextMenuProvider;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer.IContextMenuProviderSelection;
|
||||
import net.osmand.plus.views.layers.geometry.GeometryWay;
|
||||
import net.osmand.plus.views.mapwidgets.MapMarkersWidgetsFactory;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
||||
|
@ -295,7 +299,7 @@ public class MapMarkersLayer extends OsmandMapLayer implements IContextMenuProvi
|
|||
tx.add(markerX);
|
||||
ty.add(markerY);
|
||||
|
||||
calculatePath(tileBox, tx, ty, linePath);
|
||||
GeometryWay.calculatePath(tileBox, tx, ty, linePath);
|
||||
PathMeasure pm = new PathMeasure(linePath, false);
|
||||
float[] pos = new float[2];
|
||||
pm.getPosTan(pm.getLength() / 2, pos, null);
|
|
@ -1,4 +1,4 @@
|
|||
package net.osmand.plus.views;
|
||||
package net.osmand.plus.views.layers;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
|
@ -41,12 +41,14 @@ import net.osmand.plus.quickaction.QuickActionRegistry;
|
|||
import net.osmand.plus.quickaction.QuickActionsWidget;
|
||||
import net.osmand.plus.routepreparationmenu.MapRouteInfoMenu;
|
||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static net.osmand.plus.views.ContextMenuLayer.VIBRATE_SHORT;
|
||||
import static net.osmand.plus.views.layers.ContextMenuLayer.VIBRATE_SHORT;
|
||||
|
||||
/**
|
||||
* Created by okorsun on 23.12.16.
|
|
@ -1,4 +1,4 @@
|
|||
package net.osmand.plus.views;
|
||||
package net.osmand.plus.views.layers;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
|
@ -19,6 +19,8 @@ import java.util.TreeMap;
|
|||
|
||||
import gnu.trove.set.hash.TIntHashSet;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
|
||||
public class MapTextLayer extends OsmandMapLayer {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package net.osmand.plus.views;
|
||||
package net.osmand.plus.views.layers;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
|
@ -38,7 +38,9 @@ import net.osmand.plus.poi.PoiUIFilter;
|
|||
import net.osmand.plus.render.RenderingIcons;
|
||||
import net.osmand.plus.routing.IRouteInformationListener;
|
||||
import net.osmand.plus.routing.RoutingHelper;
|
||||
import net.osmand.plus.views.MapTextLayer.MapTextProvider;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.layers.MapTextLayer.MapTextProvider;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
||||
import java.util.ArrayList;
|
|
@ -1,4 +1,4 @@
|
|||
package net.osmand.plus.views;
|
||||
package net.osmand.plus.views.layers;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
|
@ -26,6 +26,8 @@ import net.osmand.plus.R;
|
|||
import net.osmand.plus.UiUtilities;
|
||||
import net.osmand.plus.base.MapViewTrackingUtilities;
|
||||
import net.osmand.plus.profiles.ProfileIconColors;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package net.osmand.plus.views;
|
||||
package net.osmand.plus.views.layers;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
|
@ -20,7 +20,9 @@ import net.osmand.plus.R;
|
|||
import net.osmand.plus.TargetPointsHelper;
|
||||
import net.osmand.plus.TargetPointsHelper.TargetPoint;
|
||||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.views.ContextMenuLayer.IContextMenuProvider;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.layers.ContextMenuLayer.IContextMenuProvider;
|
||||
|
||||
import java.util.List;
|
||||
|
642
OsmAnd/src/net/osmand/plus/views/layers/RouteLayer.java
Normal file
642
OsmAnd/src/net/osmand/plus/views/layers/RouteLayer.java
Normal file
|
@ -0,0 +1,642 @@
|
|||
package net.osmand.plus.views.layers;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Cap;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.drawable.LayerDrawable;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
|
||||
import net.osmand.AndroidUtils;
|
||||
import net.osmand.Location;
|
||||
import net.osmand.data.LatLon;
|
||||
import net.osmand.data.PointDescription;
|
||||
import net.osmand.data.QuadRect;
|
||||
import net.osmand.data.RotatedTileBox;
|
||||
import net.osmand.data.TransportStop;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.mapcontextmenu.other.TrackChartPoints;
|
||||
import net.osmand.plus.profiles.LocationIcon;
|
||||
import net.osmand.plus.routing.RouteCalculationResult;
|
||||
import net.osmand.plus.routing.RouteDirectionInfo;
|
||||
import net.osmand.plus.routing.RouteProvider;
|
||||
import net.osmand.plus.routing.RoutingHelper;
|
||||
import net.osmand.plus.routing.TransportRoutingHelper;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.layers.geometry.PublicTransportGeometryWay;
|
||||
import net.osmand.plus.views.layers.geometry.PublicTransportGeometryWayContext;
|
||||
import net.osmand.plus.views.layers.geometry.RouteGeometryWay;
|
||||
import net.osmand.plus.views.layers.geometry.RouteGeometryWayContext;
|
||||
import net.osmand.router.TransportRouteResult;
|
||||
import net.osmand.util.Algorithms;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.IContextMenuProvider {
|
||||
|
||||
private OsmandMapTileView view;
|
||||
|
||||
private final RoutingHelper helper;
|
||||
private final TransportRoutingHelper transportHelper;
|
||||
// keep array lists created
|
||||
private List<Location> actionPoints = new ArrayList<Location>();
|
||||
|
||||
// cache
|
||||
private Bitmap actionArrow;
|
||||
|
||||
private Paint paintIconAction;
|
||||
private Paint paintGridOuterCircle;
|
||||
private Paint paintGridCircle;
|
||||
|
||||
private LayerDrawable selectedPoint;
|
||||
private TrackChartPoints trackChartPoints;
|
||||
|
||||
private RenderingLineAttributes attrs;
|
||||
private RenderingLineAttributes attrsPT;
|
||||
private RenderingLineAttributes attrsW;
|
||||
private boolean nightMode;
|
||||
|
||||
private RouteGeometryWayContext routeWayContext;
|
||||
private PublicTransportGeometryWayContext publicTransportWayContext;
|
||||
private RouteGeometryWay routeGeometry;
|
||||
private PublicTransportGeometryWay publicTransportRouteGeometry;
|
||||
|
||||
private LayerDrawable projectionIcon;
|
||||
|
||||
public RouteLayer(RoutingHelper helper) {
|
||||
this.helper = helper;
|
||||
this.transportHelper = helper.getTransportRoutingHelper();
|
||||
}
|
||||
|
||||
public RoutingHelper getHelper() {
|
||||
return helper;
|
||||
}
|
||||
|
||||
public void setTrackChartPoints(TrackChartPoints trackChartPoints) {
|
||||
this.trackChartPoints = trackChartPoints;
|
||||
}
|
||||
|
||||
private void initUI() {
|
||||
float density = view.getDensity();
|
||||
|
||||
actionArrow = BitmapFactory.decodeResource(view.getResources(), R.drawable.map_action_arrow, null);
|
||||
|
||||
paintIconAction = new Paint();
|
||||
paintIconAction.setFilterBitmap(true);
|
||||
paintIconAction.setAntiAlias(true);
|
||||
|
||||
attrs = new RenderingLineAttributes("route");
|
||||
attrs.defaultWidth = (int) (12 * density);
|
||||
attrs.defaultWidth3 = (int) (7 * density);
|
||||
attrs.defaultColor = view.getResources().getColor(R.color.nav_track);
|
||||
attrs.paint3.setStrokeCap(Cap.BUTT);
|
||||
attrs.paint3.setColor(Color.WHITE);
|
||||
attrs.paint2.setStrokeCap(Cap.BUTT);
|
||||
attrs.paint2.setColor(Color.BLACK);
|
||||
|
||||
attrsPT = new RenderingLineAttributes("publicTransportLine");
|
||||
attrsPT.defaultWidth = (int) (12 * density);
|
||||
attrsPT.defaultWidth3 = (int) (7 * density);
|
||||
attrsPT.defaultColor = view.getResources().getColor(R.color.nav_track);
|
||||
attrsPT.paint3.setStrokeCap(Cap.BUTT);
|
||||
attrsPT.paint3.setColor(Color.WHITE);
|
||||
attrsPT.paint2.setStrokeCap(Cap.BUTT);
|
||||
attrsPT.paint2.setColor(Color.BLACK);
|
||||
|
||||
attrsW = new RenderingLineAttributes("walkingRouteLine");
|
||||
attrsW.defaultWidth = (int) (12 * density);
|
||||
attrsW.defaultWidth3 = (int) (7 * density);
|
||||
attrsW.defaultColor = view.getResources().getColor(R.color.nav_track_walk_fill);
|
||||
attrsW.paint3.setStrokeCap(Cap.BUTT);
|
||||
attrsW.paint3.setColor(Color.WHITE);
|
||||
attrsW.paint2.setStrokeCap(Cap.BUTT);
|
||||
attrsW.paint2.setColor(Color.BLACK);
|
||||
|
||||
routeWayContext = new RouteGeometryWayContext(view.getContext(), density);
|
||||
routeWayContext.updatePaints(nightMode, attrs);
|
||||
routeGeometry = new RouteGeometryWay(routeWayContext);
|
||||
|
||||
publicTransportWayContext = new PublicTransportGeometryWayContext(view.getContext(), density);
|
||||
publicTransportWayContext.updatePaints(nightMode, attrs, attrsPT, attrsW);
|
||||
publicTransportRouteGeometry = new PublicTransportGeometryWay(publicTransportWayContext);
|
||||
|
||||
selectedPoint = (LayerDrawable) AppCompatResources.getDrawable(view.getContext(), R.drawable.map_location_default);
|
||||
|
||||
paintGridCircle = new Paint();
|
||||
paintGridCircle.setStyle(Paint.Style.FILL_AND_STROKE);
|
||||
paintGridCircle.setAntiAlias(true);
|
||||
paintGridCircle.setColor(attrs.defaultColor);
|
||||
paintGridCircle.setAlpha(255);
|
||||
paintGridOuterCircle = new Paint();
|
||||
paintGridOuterCircle.setStyle(Paint.Style.FILL_AND_STROKE);
|
||||
paintGridOuterCircle.setAntiAlias(true);
|
||||
paintGridOuterCircle.setColor(Color.WHITE);
|
||||
paintGridOuterCircle.setAlpha(204);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initLayer(OsmandMapTileView view) {
|
||||
this.view = view;
|
||||
initUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareBufferImage(Canvas canvas, RotatedTileBox tileBox, DrawSettings settings) {
|
||||
if ((helper.isPublicTransportMode() && transportHelper.getRoutes() != null) ||
|
||||
(helper.getFinalLocation() != null && helper.getRoute().isCalculated())) {
|
||||
|
||||
updateAttrs(settings, tileBox);
|
||||
|
||||
int w = tileBox.getPixWidth();
|
||||
int h = tileBox.getPixHeight();
|
||||
Location lastProjection = helper.getLastProjection();
|
||||
final RotatedTileBox cp ;
|
||||
if(lastProjection != null &&
|
||||
tileBox.containsLatLon(lastProjection.getLatitude(), lastProjection.getLongitude())){
|
||||
cp = tileBox.copy();
|
||||
cp.increasePixelDimensions(w /2, h);
|
||||
} else {
|
||||
cp = tileBox;
|
||||
}
|
||||
|
||||
final QuadRect latlonRect = cp.getLatLonBounds();
|
||||
final QuadRect correctedQuadRect = getCorrectedQuadRect(latlonRect);
|
||||
drawLocations(tileBox, canvas, correctedQuadRect.top, correctedQuadRect.left, correctedQuadRect.bottom, correctedQuadRect.right);
|
||||
|
||||
if (trackChartPoints != null) {
|
||||
canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
|
||||
|
||||
drawXAxisPoints(canvas, tileBox);
|
||||
LatLon highlightedPoint = trackChartPoints.getHighlightedPoint();
|
||||
if (highlightedPoint != null
|
||||
&& highlightedPoint.getLatitude() >= latlonRect.bottom
|
||||
&& highlightedPoint.getLatitude() <= latlonRect.top
|
||||
&& highlightedPoint.getLongitude() >= latlonRect.left
|
||||
&& highlightedPoint.getLongitude() <= latlonRect.right) {
|
||||
float x = tileBox.getPixXFromLatLon(highlightedPoint.getLatitude(), highlightedPoint.getLongitude());
|
||||
float y = tileBox.getPixYFromLatLon(highlightedPoint.getLatitude(), highlightedPoint.getLongitude());
|
||||
selectedPoint.setBounds((int) x - selectedPoint.getIntrinsicWidth() / 2,
|
||||
(int) y - selectedPoint.getIntrinsicHeight() / 2,
|
||||
(int) x + selectedPoint.getIntrinsicWidth() / 2,
|
||||
(int) y + selectedPoint.getIntrinsicHeight() / 2);
|
||||
selectedPoint.draw(canvas);
|
||||
}
|
||||
canvas.rotate(tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void updateAttrs(DrawSettings settings, RotatedTileBox tileBox) {
|
||||
boolean updatePaints = attrs.updatePaints(view.getApplication(), settings, tileBox);
|
||||
attrs.isPaint3 = false;
|
||||
attrs.isPaint2 = false;
|
||||
attrsPT.updatePaints(view.getApplication(), settings, tileBox);
|
||||
attrsPT.isPaint3 = false;
|
||||
attrsPT.isPaint2 = false;
|
||||
attrsW.updatePaints(view.getApplication(), settings, tileBox);
|
||||
attrsPT.isPaint3 = false;
|
||||
attrsPT.isPaint2 = false;
|
||||
|
||||
nightMode = settings != null && settings.isNightMode();
|
||||
|
||||
if (updatePaints) {
|
||||
paintIconAction.setColorFilter(new PorterDuffColorFilter(attrs.paint3.getColor(), Mode.MULTIPLY));
|
||||
routeWayContext.updatePaints(nightMode, attrs);
|
||||
publicTransportWayContext.updatePaints(nightMode, attrs, attrsPT, attrsW);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawXAxisPoints(Canvas canvas, RotatedTileBox tileBox) {
|
||||
QuadRect latLonBounds = tileBox.getLatLonBounds();
|
||||
List<LatLon> xAxisPoints = trackChartPoints.getXAxisPoints();
|
||||
if (xAxisPoints != null) {
|
||||
float r = 3 * tileBox.getDensity();
|
||||
float density = (float) Math.ceil(tileBox.getDensity());
|
||||
float outerRadius = r + 2 * density;
|
||||
float innerRadius = r + density;
|
||||
QuadRect prevPointRect = null;
|
||||
for (int i = 0; i < xAxisPoints.size(); i++) {
|
||||
LatLon axisPoint = xAxisPoints.get(i);
|
||||
if (axisPoint.getLatitude() >= latLonBounds.bottom
|
||||
&& axisPoint.getLatitude() <= latLonBounds.top
|
||||
&& axisPoint.getLongitude() >= latLonBounds.left
|
||||
&& axisPoint.getLongitude() <= latLonBounds.right) {
|
||||
float x = tileBox.getPixXFromLatLon(axisPoint.getLatitude(), axisPoint.getLongitude());
|
||||
float y = tileBox.getPixYFromLatLon(axisPoint.getLatitude(), axisPoint.getLongitude());
|
||||
QuadRect pointRect = new QuadRect(x - outerRadius, y - outerRadius, x + outerRadius, y + outerRadius);
|
||||
if (prevPointRect == null || !QuadRect.intersects(prevPointRect, pointRect)) {
|
||||
canvas.drawCircle(x, y, outerRadius, paintGridOuterCircle);
|
||||
canvas.drawCircle(x, y, innerRadius, paintGridCircle);
|
||||
prevPointRect = pointRect;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas canvas, RotatedTileBox tileBox, DrawSettings settings) {}
|
||||
|
||||
private void drawAction(RotatedTileBox tb, Canvas canvas, List<Location> actionPoints) {
|
||||
if (actionPoints.size() > 0) {
|
||||
canvas.rotate(-tb.getRotate(), tb.getCenterPixelX(), tb.getCenterPixelY());
|
||||
try {
|
||||
Path pth = new Path();
|
||||
Matrix matrix = new Matrix();
|
||||
boolean first = true;
|
||||
int x = 0, px = 0, py = 0, y = 0;
|
||||
for (int i = 0; i < actionPoints.size(); i++) {
|
||||
Location o = actionPoints.get(i);
|
||||
if (o == null) {
|
||||
first = true;
|
||||
canvas.drawPath(pth, attrs.paint3);
|
||||
double angleRad = Math.atan2(y - py, x - px);
|
||||
double angle = (angleRad * 180 / Math.PI) + 90f;
|
||||
double distSegment = Math.sqrt((y - py) * (y - py) + (x - px) * (x - px));
|
||||
if (distSegment == 0) {
|
||||
continue;
|
||||
}
|
||||
// int len = (int) (distSegment / pxStep);
|
||||
float pdx = x - px;
|
||||
float pdy = y - py;
|
||||
float scale = attrs.paint3.getStrokeWidth() / ( actionArrow.getWidth() / 2.25f);
|
||||
float scaledWidth = actionArrow.getWidth();
|
||||
matrix.reset();
|
||||
matrix.postTranslate(0, -actionArrow.getHeight() / 2f);
|
||||
matrix.postRotate((float) angle, actionArrow.getWidth() / 2f, 0);
|
||||
if (scale > 1.0f) {
|
||||
matrix.postScale(scale, scale);
|
||||
scaledWidth *= scale;
|
||||
}
|
||||
matrix.postTranslate(px + pdx - scaledWidth/ 2f, py + pdy);
|
||||
canvas.drawBitmap(actionArrow, matrix, paintIconAction);
|
||||
} else {
|
||||
px = x;
|
||||
py = y;
|
||||
x = (int) tb.getPixXFromLatLon(o.getLatitude(), o.getLongitude());
|
||||
y = (int) tb.getPixYFromLatLon(o.getLatitude(), o.getLongitude());
|
||||
if (first) {
|
||||
pth.reset();
|
||||
pth.moveTo(x, y);
|
||||
first = false;
|
||||
} else {
|
||||
pth.lineTo(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
canvas.rotate(tb.getRotate(), tb.getCenterPixelX(), tb.getCenterPixelY());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void drawProjectionPoint(Canvas canvas, double[] projectionXY) {
|
||||
if (projectionIcon == null) {
|
||||
helper.getSettings().getApplicationMode().getLocationIcon();
|
||||
projectionIcon = (LayerDrawable) AppCompatResources.getDrawable(view.getContext(), LocationIcon.DEFAULT.getIconId());
|
||||
}
|
||||
int locationX = (int) projectionXY[0];
|
||||
int locationY = (int) projectionXY[1];
|
||||
|
||||
projectionIcon.setBounds(locationX - projectionIcon.getIntrinsicWidth() / 2,
|
||||
locationY - projectionIcon.getIntrinsicHeight() / 2,
|
||||
locationX + projectionIcon.getIntrinsicWidth() / 2,
|
||||
locationY + projectionIcon.getIntrinsicHeight() / 2);
|
||||
projectionIcon.draw(canvas);
|
||||
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
public int getRouteLineColor(boolean night) {
|
||||
updateAttrs(new DrawSettings(night), view.getCurrentRotatedTileBox());
|
||||
return attrs.paint.getColor();
|
||||
}
|
||||
|
||||
public void drawLocations(RotatedTileBox tb, Canvas canvas, double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude) {
|
||||
if (helper.isPublicTransportMode()) {
|
||||
int currentRoute = transportHelper.getCurrentRoute();
|
||||
List<TransportRouteResult> routes = transportHelper.getRoutes();
|
||||
TransportRouteResult route = routes != null && routes.size() > currentRoute ? routes.get(currentRoute) : null;
|
||||
routeGeometry.clearRoute();
|
||||
publicTransportRouteGeometry.updateRoute(tb, route);
|
||||
if (route != null) {
|
||||
LatLon start = transportHelper.getStartLocation();
|
||||
Location startLocation = new Location("transport");
|
||||
startLocation.setLatitude(start.getLatitude());
|
||||
startLocation.setLongitude(start.getLongitude());
|
||||
publicTransportRouteGeometry.drawSegments(tb, canvas, topLatitude, leftLongitude, bottomLatitude, rightLongitude,
|
||||
startLocation, 0);
|
||||
}
|
||||
} else {
|
||||
RouteCalculationResult route = helper.getRoute();
|
||||
boolean directTo = route.getRouteService() == RouteProvider.RouteService.DIRECT_TO;
|
||||
boolean straight = route.getRouteService() == RouteProvider.RouteService.STRAIGHT;
|
||||
publicTransportRouteGeometry.clearRoute();
|
||||
routeGeometry.updateRoute(tb, route);
|
||||
if (directTo) {
|
||||
routeGeometry.drawSegments(tb, canvas, topLatitude, leftLongitude, bottomLatitude, rightLongitude,
|
||||
null, 0);
|
||||
} else if (straight) {
|
||||
routeGeometry.drawSegments(tb, canvas, topLatitude, leftLongitude, bottomLatitude, rightLongitude,
|
||||
helper.getLastFixedLocation(), route.getCurrentStraightAngleRoute());
|
||||
} else {
|
||||
routeGeometry.drawSegments(tb, canvas, topLatitude, leftLongitude, bottomLatitude, rightLongitude,
|
||||
helper.getLastProjection(), route.getCurrentStraightAngleRoute());
|
||||
}
|
||||
List<RouteDirectionInfo> rd = helper.getRouteDirections();
|
||||
Iterator<RouteDirectionInfo> it = rd.iterator();
|
||||
if (!directTo && tb.getZoom() >= 14) {
|
||||
List<Location> actionPoints = calculateActionPoints(topLatitude, leftLongitude, bottomLatitude, rightLongitude, helper.getLastProjection(),
|
||||
helper.getRoute().getRouteLocations(), helper.getRoute().getCurrentRoute(), it, tb.getZoom());
|
||||
drawAction(tb, canvas, actionPoints);
|
||||
}
|
||||
if (directTo) {
|
||||
//add projection point on original route
|
||||
double[] projectionOnRoute = calculateProjectionOnRoutePoint(
|
||||
helper.getRoute().getImmutableAllLocations(), helper, tb);
|
||||
if (projectionOnRoute != null) {
|
||||
drawProjectionPoint(canvas, projectionOnRoute);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double[] calculateProjectionOnRoutePoint(List<Location> routeNodes, RoutingHelper helper, RotatedTileBox box) {
|
||||
double[] projectionXY = null;
|
||||
Location ll = helper.getLastFixedLocation();
|
||||
RouteCalculationResult route = helper.getRoute();
|
||||
List<Location> locs = route.getImmutableAllLocations();
|
||||
int cr = route.getCurrentRoute();
|
||||
int locIndex = locs.size() - 1;
|
||||
if(route.getIntermediatePointsToPass() > 0) {
|
||||
locIndex = route.getIndexOfIntermediate(route.getIntermediatePointsToPass() - 1);
|
||||
}
|
||||
if(ll != null && cr > 0 && cr < locs.size() && locIndex >= 0 && locIndex < locs.size()) {
|
||||
Location loc1 = locs.get(cr - 1);
|
||||
Location loc2 = locs.get(cr);
|
||||
double distLeft = route.getDistanceFromPoint(cr) - route.getDistanceFromPoint(locIndex);
|
||||
double baDist = route.getDistanceFromPoint(cr - 1) - route.getDistanceFromPoint(cr);
|
||||
Location target = locs.get(locIndex);
|
||||
double dTarget = ll.distanceTo(target);
|
||||
final int aX = box.getPixXFromLonNoRot(loc1.getLongitude());
|
||||
final int aY = box.getPixYFromLatNoRot(loc1.getLatitude());
|
||||
final int bX = box.getPixXFromLonNoRot(loc2.getLongitude());
|
||||
final int bY = box.getPixYFromLatNoRot(loc2.getLatitude());
|
||||
if(baDist != 0) {
|
||||
double CF = (dTarget - distLeft) / baDist;
|
||||
double rX = bX - CF * (bX - aX);
|
||||
double rY = bY - CF * (bY - aY);
|
||||
projectionXY = new double[] {rX, rY};
|
||||
}
|
||||
}
|
||||
if(projectionXY != null) {
|
||||
|
||||
double distanceLoc2Proj = MapUtils.getSqrtDistance((int)projectionXY[0], (int) projectionXY[1],
|
||||
box.getPixXFromLonNoRot(ll.getLongitude()), box.getPixYFromLatNoRot(ll.getLatitude()));
|
||||
boolean visible = box.containsPoint((float) projectionXY[0], (float) projectionXY[1], 20.0f)
|
||||
&& distanceLoc2Proj > AndroidUtils.dpToPx(view.getContext(), 52) / 2.0;
|
||||
if (visible) {
|
||||
return projectionXY;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<Location> calculateActionPoints(double topLatitude, double leftLongitude, double bottomLatitude,
|
||||
double rightLongitude, Location lastProjection, List<Location> routeNodes, int cd,
|
||||
Iterator<RouteDirectionInfo> it, int zoom) {
|
||||
RouteDirectionInfo nf = null;
|
||||
|
||||
double DISTANCE_ACTION = 35;
|
||||
if(zoom >= 17) {
|
||||
DISTANCE_ACTION = 15;
|
||||
} else if (zoom == 15) {
|
||||
DISTANCE_ACTION = 70;
|
||||
} else if (zoom < 15) {
|
||||
DISTANCE_ACTION = 110;
|
||||
}
|
||||
double actionDist = 0;
|
||||
Location previousAction = null;
|
||||
List<Location> actionPoints = this.actionPoints;
|
||||
actionPoints.clear();
|
||||
int prevFinishPoint = -1;
|
||||
for (int routePoint = 0; routePoint < routeNodes.size(); routePoint++) {
|
||||
Location loc = routeNodes.get(routePoint);
|
||||
if(nf != null) {
|
||||
int pnt = nf.routeEndPointOffset == 0 ? nf.routePointOffset : nf.routeEndPointOffset;
|
||||
if(pnt < routePoint + cd ) {
|
||||
nf = null;
|
||||
}
|
||||
}
|
||||
while (nf == null && it.hasNext()) {
|
||||
nf = it.next();
|
||||
int pnt = nf.routeEndPointOffset == 0 ? nf.routePointOffset : nf.routeEndPointOffset;
|
||||
if (pnt < routePoint + cd) {
|
||||
nf = null;
|
||||
}
|
||||
}
|
||||
boolean action = nf != null && (nf.routePointOffset == routePoint + cd ||
|
||||
(nf.routePointOffset <= routePoint + cd && routePoint + cd <= nf.routeEndPointOffset));
|
||||
if(!action && previousAction == null) {
|
||||
// no need to check
|
||||
continue;
|
||||
}
|
||||
boolean visible = leftLongitude <= loc.getLongitude() && loc.getLongitude() <= rightLongitude && bottomLatitude <= loc.getLatitude()
|
||||
&& loc.getLatitude() <= topLatitude;
|
||||
if(action && !visible && previousAction == null) {
|
||||
continue;
|
||||
}
|
||||
if (!action) {
|
||||
// previousAction != null
|
||||
float dist = loc.distanceTo(previousAction);
|
||||
actionDist += dist;
|
||||
if (actionDist >= DISTANCE_ACTION) {
|
||||
actionPoints.add(calculateProjection(1 - (actionDist - DISTANCE_ACTION) / dist, previousAction, loc));
|
||||
actionPoints.add(null);
|
||||
prevFinishPoint = routePoint;
|
||||
previousAction = null;
|
||||
actionDist = 0;
|
||||
} else {
|
||||
actionPoints.add(loc);
|
||||
previousAction = loc;
|
||||
}
|
||||
} else {
|
||||
// action point
|
||||
if (previousAction == null) {
|
||||
addPreviousToActionPoints(actionPoints, lastProjection, routeNodes, DISTANCE_ACTION,
|
||||
prevFinishPoint, routePoint, loc);
|
||||
}
|
||||
actionPoints.add(loc);
|
||||
previousAction = loc;
|
||||
prevFinishPoint = -1;
|
||||
actionDist = 0;
|
||||
}
|
||||
}
|
||||
if(previousAction != null) {
|
||||
actionPoints.add(null);
|
||||
}
|
||||
return actionPoints;
|
||||
}
|
||||
|
||||
|
||||
private void addPreviousToActionPoints(List<Location> actionPoints, Location lastProjection, List<Location> routeNodes, double DISTANCE_ACTION,
|
||||
int prevFinishPoint, int routePoint, Location loc) {
|
||||
// put some points in front
|
||||
int ind = actionPoints.size();
|
||||
Location lprevious = loc;
|
||||
double dist = 0;
|
||||
for (int k = routePoint - 1; k >= -1; k--) {
|
||||
Location l = k == -1 ? lastProjection : routeNodes.get(k);
|
||||
float locDist = lprevious.distanceTo(l);
|
||||
dist += locDist;
|
||||
if (dist >= DISTANCE_ACTION) {
|
||||
if (locDist > 1) {
|
||||
actionPoints.add(ind,
|
||||
calculateProjection(1 - (dist - DISTANCE_ACTION) / locDist, lprevious, l));
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
actionPoints.add(ind, l);
|
||||
lprevious = l;
|
||||
}
|
||||
if (prevFinishPoint == k) {
|
||||
if (ind >= 2) {
|
||||
actionPoints.remove(ind - 2);
|
||||
actionPoints.remove(ind - 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Location calculateProjection(double part, Location lp, Location l) {
|
||||
Location p = new Location(l);
|
||||
p.setLatitude(lp.getLatitude() + part * (l.getLatitude() - lp.getLatitude()));
|
||||
p.setLongitude(lp.getLongitude() + part * (l.getLongitude() - lp.getLongitude()));
|
||||
return p;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyLayer() {
|
||||
|
||||
}
|
||||
@Override
|
||||
public boolean drawInScreenPixels() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongPressEvent(PointF point, RotatedTileBox tileBox) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSingleTap(PointF point, RotatedTileBox tileBox) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private int getRadiusPoi(RotatedTileBox tb){
|
||||
final double zoom = tb.getZoom();
|
||||
int r;
|
||||
if(zoom <= 15) {
|
||||
r = 8;
|
||||
} else if(zoom <= 16) {
|
||||
r = 10;
|
||||
} else if(zoom <= 17) {
|
||||
r = 14;
|
||||
} else {
|
||||
r = 18;
|
||||
}
|
||||
return (int) (r * tb.getDensity());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private List<TransportStop> getRouteTransportStops() {
|
||||
return helper.isPublicTransportMode() ? publicTransportRouteGeometry.getDrawer().getRouteTransportStops() : null;
|
||||
}
|
||||
|
||||
private void getFromPoint(RotatedTileBox tb, PointF point, List<? super TransportStop> res, @NonNull List<TransportStop> routeTransportStops) {
|
||||
int ex = (int) point.x;
|
||||
int ey = (int) point.y;
|
||||
final int rp = getRadiusPoi(tb);
|
||||
int radius = rp * 3 / 2;
|
||||
try {
|
||||
for (int i = 0; i < routeTransportStops.size(); i++) {
|
||||
TransportStop n = routeTransportStops.get(i);
|
||||
if (n.getLocation() == null) {
|
||||
continue;
|
||||
}
|
||||
int x = (int) tb.getPixXFromLatLon(n.getLocation().getLatitude(), n.getLocation().getLongitude());
|
||||
int y = (int) tb.getPixYFromLatLon(n.getLocation().getLatitude(), n.getLocation().getLongitude());
|
||||
if (Math.abs(x - ex) <= radius && Math.abs(y - ey) <= radius) {
|
||||
radius = rp;
|
||||
res.add(n);
|
||||
}
|
||||
}
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collectObjectsFromPoint(PointF point, RotatedTileBox tileBox, List<Object> res, boolean unknownLocation) {
|
||||
List<TransportStop> routeTransportStops = getRouteTransportStops();
|
||||
if (!Algorithms.isEmpty(routeTransportStops)) {
|
||||
getFromPoint(tileBox, point, res, routeTransportStops);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LatLon getObjectLocation(Object o) {
|
||||
if (o instanceof TransportStop){
|
||||
return ((TransportStop)o).getLocation();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PointDescription getObjectName(Object o) {
|
||||
if (o instanceof TransportStop){
|
||||
return new PointDescription(PointDescription.POINT_TYPE_TRANSPORT_STOP, view.getContext().getString(R.string.transport_Stop),
|
||||
((TransportStop)o).getName());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean disableSingleTap() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean disableLongPressOnMap() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isObjectClickable(Object o) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean runExclusiveAction(@Nullable Object o, boolean unknownLocation) {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package net.osmand.plus.views;
|
||||
package net.osmand.plus.views.layers;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
|
@ -34,6 +34,9 @@ import net.osmand.plus.settings.backend.OsmandSettings.AngularConstants;
|
|||
import net.osmand.plus.settings.backend.OsmandSettings.RulerMode;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.layers.geometry.GeometryWay;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -428,7 +431,7 @@ public class RulerControlLayer extends OsmandMapLayer {
|
|||
tx.add(currX);
|
||||
ty.add(currY);
|
||||
|
||||
calculatePath(tb, tx, ty, linePath);
|
||||
GeometryWay.calculatePath(tb, tx, ty, linePath);
|
||||
|
||||
float dist = (float) MapUtils.getDistance(touchPointLatLon, currLoc.getLatitude(), currLoc.getLongitude());
|
||||
String text = OsmAndFormatter.getFormattedDistance(dist, app);
|
|
@ -1,4 +1,4 @@
|
|||
package net.osmand.plus.views;
|
||||
package net.osmand.plus.views.layers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
|
@ -27,6 +27,9 @@ import net.osmand.plus.activities.MapActivity;
|
|||
import net.osmand.plus.render.RenderingIcons;
|
||||
import net.osmand.plus.transport.TransportStopRoute;
|
||||
import net.osmand.plus.transport.TransportStopType;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.layers.geometry.GeometryWay;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
@ -197,7 +200,7 @@ public class TransportStopsLayer extends OsmandMapLayer implements ContextMenuLa
|
|||
tx.add(x);
|
||||
ty.add(y);
|
||||
}
|
||||
calculatePath(tb, tx, ty, path);
|
||||
GeometryWay.calculatePath(tb, tx, ty, path);
|
||||
}
|
||||
}
|
||||
attrs.drawPath(canvas, path);
|
|
@ -0,0 +1,450 @@
|
|||
package net.osmand.plus.views.layers.geometry;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Path;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import net.osmand.Location;
|
||||
import net.osmand.data.RotatedTileBox;
|
||||
import net.osmand.util.MapAlgorithms;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import gnu.trove.list.array.TByteArrayList;
|
||||
|
||||
public abstract class GeometryWay<T extends GeometryWayContext, D extends GeometryWayDrawer<T>> {
|
||||
private double mapDensity;
|
||||
private T context;
|
||||
private D drawer;
|
||||
private TreeMap<Integer, PathGeometryZoom> zooms = new TreeMap<>();
|
||||
private GeometryWayProvider locationProvider;
|
||||
private Map<Integer, GeometryWayStyle<?>> styleMap = Collections.emptyMap();
|
||||
|
||||
// cache arrays
|
||||
private List<Float> tx = new ArrayList<>();
|
||||
private List<Float> ty = new ArrayList<>();
|
||||
private List<Double> angles = new ArrayList<>();
|
||||
private List<Double> distances = new ArrayList<>();
|
||||
private List<GeometryWayStyle<?>> styles = new ArrayList<>();
|
||||
|
||||
public interface GeometryWayProvider {
|
||||
double getLatitude(int index);
|
||||
|
||||
double getLongitude(int index);
|
||||
|
||||
int getSize();
|
||||
}
|
||||
|
||||
private static class GeometryWayLocationProvider implements GeometryWayProvider {
|
||||
private List<Location> locations;
|
||||
|
||||
public GeometryWayLocationProvider(@NonNull List<Location> locations) {
|
||||
this.locations = locations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLatitude(int index) {
|
||||
return locations.get(index).getLatitude();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLongitude(int index) {
|
||||
return locations.get(index).getLongitude();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return locations.size();
|
||||
}
|
||||
}
|
||||
|
||||
public GeometryWay(T context, D drawer) {
|
||||
this.context = context;
|
||||
this.drawer = drawer;
|
||||
}
|
||||
|
||||
private GeometryWayStyle<?> getStyle(int index, GeometryWayStyle<?> defaultWayStyle) {
|
||||
List<Integer> list = new ArrayList<>(styleMap.keySet());
|
||||
for (int i = list.size() - 1; i >= 0; i--) {
|
||||
int c = list.get(i);
|
||||
if (c <= index) {
|
||||
return styleMap.get(c);
|
||||
}
|
||||
}
|
||||
return defaultWayStyle;
|
||||
}
|
||||
|
||||
public T getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public D getDrawer() {
|
||||
return drawer;
|
||||
}
|
||||
|
||||
public double getMapDensity() {
|
||||
return mapDensity;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public GeometryWayProvider getLocationProvider() {
|
||||
return locationProvider;
|
||||
}
|
||||
|
||||
protected void updateWay(@NonNull GeometryWayProvider locationProvider, @NonNull RotatedTileBox tb) {
|
||||
updateWay(locationProvider, null, tb);
|
||||
}
|
||||
|
||||
protected void updateWay(@NonNull GeometryWayProvider locationProvider, @Nullable Map<Integer, GeometryWayStyle<?>> styleMap, @NonNull RotatedTileBox tb) {
|
||||
this.locationProvider = locationProvider;
|
||||
this.styleMap = styleMap == null ? Collections.<Integer, GeometryWayStyle<?>>emptyMap() : styleMap;
|
||||
this.mapDensity = tb.getMapDensity();
|
||||
this.zooms.clear();
|
||||
}
|
||||
|
||||
public void updateWay(@NonNull List<Location> locations, @NonNull RotatedTileBox tb) {
|
||||
updateWay(locations, null, tb);
|
||||
}
|
||||
|
||||
public void updateWay(@NonNull List<Location> locations, @Nullable Map<Integer, GeometryWayStyle<?>> styleMap, @NonNull RotatedTileBox tb) {
|
||||
this.locationProvider = new GeometryWayLocationProvider(locations);
|
||||
this.styleMap = styleMap == null ? Collections.<Integer, GeometryWayStyle<?>>emptyMap() : styleMap;
|
||||
this.mapDensity = tb.getMapDensity();
|
||||
this.zooms.clear();
|
||||
}
|
||||
|
||||
public void clearWay() {
|
||||
locationProvider = null;
|
||||
styleMap = Collections.emptyMap();
|
||||
zooms.clear();
|
||||
}
|
||||
|
||||
private PathGeometryZoom getGeometryZoom(RotatedTileBox tb) {
|
||||
int zoom = tb.getZoom();
|
||||
PathGeometryZoom zm = zooms.size() > zoom ? zooms.get(zoom) : null;
|
||||
if (zm == null) {
|
||||
zm = new PathGeometryZoom(locationProvider, tb, tb.getZoom() < context.getSimplificationZoom());
|
||||
zooms.put(zoom, zm);
|
||||
}
|
||||
return zm;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public abstract GeometryWayStyle<?> getDefaultWayStyle();
|
||||
|
||||
public Location getNextVisiblePoint() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void drawSegments(RotatedTileBox tb, Canvas canvas, double topLatitude, double leftLongitude,
|
||||
double bottomLatitude, double rightLongitude, Location lastProjection, int startLocationIndex) {
|
||||
if (locationProvider == null || locationProvider.getSize() == 0) {
|
||||
return;
|
||||
}
|
||||
PathGeometryZoom geometryZoom = getGeometryZoom(tb);
|
||||
TByteArrayList simplification = geometryZoom.getSimplifyPoints();
|
||||
List<Double> odistances = geometryZoom.getDistances();
|
||||
|
||||
clearArrays();
|
||||
|
||||
GeometryWayStyle<?> defaultWayStyle = getDefaultWayStyle();
|
||||
GeometryWayStyle<?> style = defaultWayStyle;
|
||||
boolean previousVisible = false;
|
||||
if (lastProjection != null) {
|
||||
previousVisible = addPoint(tb, topLatitude, leftLongitude, bottomLatitude, rightLongitude, style, previousVisible, lastProjection);
|
||||
}
|
||||
Location nextVisiblePoint = getNextVisiblePoint();
|
||||
if (nextVisiblePoint != null) {
|
||||
previousVisible = addPoint(tb, topLatitude, leftLongitude, bottomLatitude, rightLongitude, style, previousVisible, nextVisiblePoint);
|
||||
}
|
||||
GeometryWayProvider locationProvider = this.locationProvider;
|
||||
int previous = -1;
|
||||
for (int i = startLocationIndex; i < locationProvider.getSize(); i++) {
|
||||
style = getStyle(i, defaultWayStyle);
|
||||
if (simplification.getQuick(i) == 0 && !styleMap.containsKey(i)) {
|
||||
continue;
|
||||
}
|
||||
double lat = locationProvider.getLatitude(i);
|
||||
double lon = locationProvider.getLongitude(i);
|
||||
if (leftLongitude <= lon && lon <= rightLongitude && bottomLatitude <= lat
|
||||
&& lat <= topLatitude) {
|
||||
double dist = previous == -1 ? 0 : odistances.get(i);
|
||||
if (!previousVisible) {
|
||||
double prevLat = Double.NaN;
|
||||
double prevLon = Double.NaN;
|
||||
if (previous != -1) {
|
||||
prevLat = locationProvider.getLatitude(previous);
|
||||
prevLon = locationProvider.getLongitude(previous);
|
||||
} else if (lastProjection != null) {
|
||||
prevLat = lastProjection.getLatitude();
|
||||
prevLon = lastProjection.getLongitude();
|
||||
}
|
||||
if (!Double.isNaN(prevLat) && !Double.isNaN(prevLon)) {
|
||||
addLocation(tb, prevLat, prevLon, style, tx, ty, angles, distances, dist, styles); // first point
|
||||
}
|
||||
}
|
||||
addLocation(tb, lat, lon, style, tx, ty, angles, distances, dist, styles);
|
||||
previousVisible = true;
|
||||
} else if (previousVisible) {
|
||||
addLocation(tb, lat, lon, style, tx, ty, angles, distances, previous == -1 ? 0 : odistances.get(i), styles);
|
||||
double distToFinish = 0;
|
||||
for (int ki = i + 1; ki < odistances.size(); ki++) {
|
||||
distToFinish += odistances.get(ki);
|
||||
}
|
||||
drawRouteSegment(tb, canvas, tx, ty, angles, distances, distToFinish, styles);
|
||||
previousVisible = false;
|
||||
clearArrays();
|
||||
}
|
||||
previous = i;
|
||||
}
|
||||
drawRouteSegment(tb, canvas, tx, ty, angles, distances, 0, styles);
|
||||
}
|
||||
|
||||
private void addLocation(RotatedTileBox tb, double latitude, double longitude, GeometryWayStyle<?> style,
|
||||
List<Float> tx, List<Float> ty, List<Double> angles, List<Double> distances,
|
||||
double dist, List<GeometryWayStyle<?>> styles) {
|
||||
float x = tb.getPixXFromLatLon(latitude, longitude);
|
||||
float y = tb.getPixYFromLatLon(latitude, longitude);
|
||||
float px = x;
|
||||
float py = y;
|
||||
int previous = tx.size() - 1;
|
||||
if (previous >= 0) {
|
||||
px = tx.get(previous);
|
||||
py = ty.get(previous);
|
||||
}
|
||||
double angle = 0;
|
||||
if (px != x || py != y) {
|
||||
double angleRad = Math.atan2(y - py, x - px);
|
||||
angle = (angleRad * 180 / Math.PI) + 90f;
|
||||
}
|
||||
double distSegment = dist != 0 ? dist : Math.sqrt((y - py) * (y - py) + (x - px) * (x - px));
|
||||
tx.add(x);
|
||||
ty.add(y);
|
||||
angles.add(angle);
|
||||
distances.add(distSegment);
|
||||
styles.add(style);
|
||||
}
|
||||
|
||||
private boolean addPoint(RotatedTileBox tb, double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, GeometryWayStyle<?> style, boolean previousVisible, Location lastPoint) {
|
||||
if (leftLongitude <= lastPoint.getLongitude() && lastPoint.getLongitude() <= rightLongitude
|
||||
&& bottomLatitude <= lastPoint.getLatitude() && lastPoint.getLatitude() <= topLatitude) {
|
||||
addLocation(tb, lastPoint.getLatitude(), lastPoint.getLongitude(), style, tx, ty, angles, distances, 0, styles);
|
||||
previousVisible = true;
|
||||
}
|
||||
return previousVisible;
|
||||
}
|
||||
|
||||
private void clearArrays() {
|
||||
tx.clear();
|
||||
ty.clear();
|
||||
distances.clear();
|
||||
angles.clear();
|
||||
styles.clear();
|
||||
}
|
||||
|
||||
public static boolean isIn(float x, float y, int lx, int ty, int rx, int by) {
|
||||
return x >= lx && x <= rx && y >= ty && y <= by;
|
||||
}
|
||||
|
||||
public static int calculatePath(RotatedTileBox tb, List<Float> xs, List<Float> ys, Path path) {
|
||||
List<Pair<Path, GeometryWayStyle<?>>> paths = new ArrayList<>();
|
||||
int res = calculatePath(tb, xs, ys, null, paths);
|
||||
if (paths.size() > 0) {
|
||||
path.addPath(paths.get(0).first);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public static int calculatePath(RotatedTileBox tb, List<Float> xs, List<Float> ys, List<GeometryWayStyle<?>> styles, List<Pair<Path, GeometryWayStyle<?>>> paths) {
|
||||
boolean segmentStarted = false;
|
||||
float prevX = xs.get(0);
|
||||
float prevY = ys.get(0);
|
||||
int height = tb.getPixHeight();
|
||||
int width = tb.getPixWidth();
|
||||
int cnt = 0;
|
||||
boolean hasStyles = styles != null && styles.size() == xs.size();
|
||||
GeometryWayStyle<?> style = hasStyles ? styles.get(0) : null;
|
||||
Path path = new Path();
|
||||
boolean prevIn = isIn(prevX, prevY, 0, 0, width, height);
|
||||
for (int i = 1; i < xs.size(); i++) {
|
||||
float currX = xs.get(i);
|
||||
float currY = ys.get(i);
|
||||
boolean currIn = isIn(currX, currY, 0, 0, width, height);
|
||||
boolean draw = false;
|
||||
if (prevIn && currIn) {
|
||||
draw = true;
|
||||
} else {
|
||||
long intersection = MapAlgorithms.calculateIntersection((int) currX, (int) currY, (int) prevX, (int) prevY, 0, width, height, 0);
|
||||
if (intersection != -1) {
|
||||
if (prevIn && (i == 1)) {
|
||||
cnt++;
|
||||
path.moveTo(prevX, prevY);
|
||||
segmentStarted = true;
|
||||
}
|
||||
prevX = (int) (intersection >> 32);
|
||||
prevY = (int) (intersection & 0xffffffff);
|
||||
draw = true;
|
||||
}
|
||||
if (i == xs.size() - 1 && !currIn) {
|
||||
long inter = MapAlgorithms.calculateIntersection((int) prevX, (int) prevY, (int) currX, (int) currY, 0, width, height, 0);
|
||||
if (inter != -1) {
|
||||
currX = (int) (inter >> 32);
|
||||
currY = (int) (inter & 0xffffffff);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (draw) {
|
||||
if (!segmentStarted) {
|
||||
cnt++;
|
||||
path.moveTo(prevX, prevY);
|
||||
segmentStarted = true;
|
||||
}
|
||||
path.lineTo(currX, currY);
|
||||
} else {
|
||||
segmentStarted = false;
|
||||
}
|
||||
prevIn = currIn;
|
||||
prevX = currX;
|
||||
prevY = currY;
|
||||
|
||||
if (hasStyles) {
|
||||
GeometryWayStyle<?> newStyle = styles.get(i);
|
||||
if (!style.equals(newStyle)) {
|
||||
paths.add(new Pair<Path, GeometryWayStyle<?>>(path, style));
|
||||
path = new Path();
|
||||
if (segmentStarted) {
|
||||
path.moveTo(currX, currY);
|
||||
}
|
||||
style = newStyle;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!path.isEmpty()) {
|
||||
paths.add(new Pair<Path, GeometryWayStyle<?>>(path, style));
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
private void drawRouteSegment(RotatedTileBox tb, Canvas canvas, List<Float> tx, List<Float> ty,
|
||||
List<Double> angles, List<Double> distances, double distToFinish, List<GeometryWayStyle<?>> styles) {
|
||||
if (tx.size() < 2) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
boolean hasPathLine = false;
|
||||
for (GeometryWayStyle<?> style : styles) {
|
||||
if (style.hasPathLine()) {
|
||||
hasPathLine = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasPathLine) {
|
||||
List<Pair<Path, GeometryWayStyle<?>>> paths = new ArrayList<>();
|
||||
canvas.rotate(-tb.getRotate(), tb.getCenterPixelX(), tb.getCenterPixelY());
|
||||
calculatePath(tb, tx, ty, styles, paths);
|
||||
for (Pair<Path, GeometryWayStyle<?>> pc : paths) {
|
||||
GeometryWayStyle<?> style = pc.second;
|
||||
if (style.hasPathLine()) {
|
||||
drawer.drawPath(canvas, pc.first, style);
|
||||
}
|
||||
}
|
||||
context.clearCustomColor();
|
||||
}
|
||||
drawer.drawArrowsOverPath(canvas, tb, tx, ty, angles, distances, distToFinish, styles);
|
||||
} finally {
|
||||
canvas.rotate(tb.getRotate(), tb.getCenterPixelX(), tb.getCenterPixelY());
|
||||
}
|
||||
}
|
||||
|
||||
private static class PathGeometryZoom {
|
||||
|
||||
private static final float EPSILON_IN_DPI = 2;
|
||||
|
||||
private final TByteArrayList simplifyPoints;
|
||||
private List<Double> distances;
|
||||
private List<Double> angles;
|
||||
|
||||
public PathGeometryZoom(GeometryWayProvider locationProvider, RotatedTileBox tb, boolean simplify) {
|
||||
// this.locations = locations;
|
||||
tb = new RotatedTileBox(tb);
|
||||
tb.setZoomAndAnimation(tb.getZoom(), 0, tb.getZoomFloatPart());
|
||||
int size = locationProvider.getSize();
|
||||
simplifyPoints = new TByteArrayList(size);
|
||||
distances = new ArrayList<>(size);
|
||||
angles = new ArrayList<>(size);
|
||||
if (simplify) {
|
||||
simplifyPoints.fill(0, size, (byte) 0);
|
||||
if (size > 0) {
|
||||
simplifyPoints.set(0, (byte) 1);
|
||||
}
|
||||
double distInPix = (tb.getDistance(0, 0, tb.getPixWidth(), 0) / tb.getPixWidth());
|
||||
double cullDistance = (distInPix * (EPSILON_IN_DPI * Math.max(1, tb.getDensity())));
|
||||
cullRamerDouglasPeucker(simplifyPoints, locationProvider, 0, size - 1, cullDistance);
|
||||
} else {
|
||||
simplifyPoints.fill(0, size, (byte) 1);
|
||||
}
|
||||
int previousIndex = -1;
|
||||
for (int i = 0; i < size; i++) {
|
||||
double d = 0;
|
||||
double angle = 0;
|
||||
if (simplifyPoints.get(i) > 0) {
|
||||
if (previousIndex > -1) {
|
||||
float x = tb.getPixXFromLatLon(locationProvider.getLatitude(i), locationProvider.getLongitude(i));
|
||||
float y = tb.getPixYFromLatLon(locationProvider.getLatitude(i), locationProvider.getLongitude(i));
|
||||
float px = tb.getPixXFromLatLon(locationProvider.getLatitude(previousIndex), locationProvider.getLongitude(previousIndex));
|
||||
float py = tb.getPixYFromLatLon(locationProvider.getLatitude(previousIndex), locationProvider.getLongitude(previousIndex));
|
||||
d = Math.sqrt((y - py) * (y - py) + (x - px) * (x - px));
|
||||
if (px != x || py != y) {
|
||||
double angleRad = Math.atan2(y - py, x - px);
|
||||
angle = (angleRad * 180 / Math.PI) + 90f;
|
||||
}
|
||||
}
|
||||
previousIndex = i;
|
||||
}
|
||||
distances.add(d);
|
||||
angles.add(angle);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Double> getDistances() {
|
||||
return distances;
|
||||
}
|
||||
|
||||
public List<Double> getAngles() {
|
||||
return angles;
|
||||
}
|
||||
|
||||
private void cullRamerDouglasPeucker(TByteArrayList survivor, GeometryWayProvider locationProvider,
|
||||
int start, int end, double epsillon) {
|
||||
double dmax = Double.NEGATIVE_INFINITY;
|
||||
int index = -1;
|
||||
for (int i = start + 1; i < end; i++) {
|
||||
double d = MapUtils.getOrthogonalDistance(locationProvider.getLatitude(i), locationProvider.getLongitude(i),
|
||||
locationProvider.getLatitude(start), locationProvider.getLongitude(start),
|
||||
locationProvider.getLatitude(end), locationProvider.getLongitude(end));
|
||||
if (d > dmax) {
|
||||
dmax = d;
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
if (dmax > epsillon) {
|
||||
cullRamerDouglasPeucker(survivor, locationProvider, start, index, epsillon);
|
||||
cullRamerDouglasPeucker(survivor, locationProvider, index, end, epsillon);
|
||||
} else {
|
||||
survivor.set(end, (byte) 1);
|
||||
}
|
||||
}
|
||||
|
||||
public TByteArrayList getSimplifyPoints() {
|
||||
return simplifyPoints;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
package net.osmand.plus.views.layers.geometry;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
|
||||
import net.osmand.plus.OsmandApplication;
|
||||
import net.osmand.plus.render.RenderingIcons;
|
||||
import net.osmand.plus.views.OsmandMapLayer.RenderingLineAttributes;
|
||||
|
||||
public abstract class GeometryWayContext {
|
||||
|
||||
public static final int DEFAULT_SIMPLIFICATION_ZOOM = 6;
|
||||
|
||||
private Context ctx;
|
||||
private float density;
|
||||
private boolean nightMode;
|
||||
private int simplificationZoom = DEFAULT_SIMPLIFICATION_ZOOM;
|
||||
|
||||
private Paint paintIcon;
|
||||
private Paint paintIconCustom;
|
||||
|
||||
private RenderingLineAttributes attrs;
|
||||
|
||||
private Bitmap arrowBitmap;
|
||||
|
||||
public GeometryWayContext(Context ctx, float density) {
|
||||
this.ctx = ctx;
|
||||
this.density = density;
|
||||
|
||||
paintIcon = new Paint();
|
||||
paintIcon.setFilterBitmap(true);
|
||||
paintIcon.setAntiAlias(true);
|
||||
paintIcon.setColor(Color.BLACK);
|
||||
paintIcon.setStrokeWidth(1f * density);
|
||||
|
||||
paintIconCustom = new Paint();
|
||||
paintIconCustom.setFilterBitmap(true);
|
||||
paintIconCustom.setAntiAlias(true);
|
||||
paintIconCustom.setColor(Color.BLACK);
|
||||
paintIconCustom.setStrokeWidth(1f * density);
|
||||
|
||||
arrowBitmap = RenderingIcons.getBitmapFromVectorDrawable(ctx, getArrowBitmapResId());
|
||||
}
|
||||
|
||||
public OsmandApplication getApp() {
|
||||
return (OsmandApplication) ctx.getApplicationContext();
|
||||
}
|
||||
|
||||
public Context getCtx() {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
public RenderingLineAttributes getAttrs() {
|
||||
return attrs;
|
||||
}
|
||||
|
||||
public float getDensity() {
|
||||
return density;
|
||||
}
|
||||
|
||||
public boolean isNightMode() {
|
||||
return nightMode;
|
||||
}
|
||||
|
||||
public int getSimplificationZoom() {
|
||||
return simplificationZoom;
|
||||
}
|
||||
|
||||
public void setSimplificationZoom(int simplificationZoom) {
|
||||
this.simplificationZoom = simplificationZoom;
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
protected abstract int getArrowBitmapResId();
|
||||
|
||||
public void updatePaints(boolean nightMode, @NonNull RenderingLineAttributes attrs) {
|
||||
this.attrs = attrs;
|
||||
paintIcon.setColorFilter(new PorterDuffColorFilter(attrs.paint2.getColor(), PorterDuff.Mode.MULTIPLY));
|
||||
this.nightMode = nightMode;
|
||||
recreateBitmapsInternal();
|
||||
}
|
||||
|
||||
protected boolean hasAttrs() {
|
||||
return attrs != null;
|
||||
}
|
||||
|
||||
protected void recreateBitmaps() {
|
||||
}
|
||||
|
||||
private void recreateBitmapsInternal() {
|
||||
if (hasAttrs()) {
|
||||
recreateBitmaps();
|
||||
}
|
||||
}
|
||||
|
||||
public void clearCustomColor() {
|
||||
attrs.customColor = 0;
|
||||
}
|
||||
|
||||
public int getStrokeColor(int sourceColor) {
|
||||
return ColorUtils.blendARGB(sourceColor, Color.BLACK, 0.6f);
|
||||
}
|
||||
|
||||
public Paint getPaintIcon() {
|
||||
return paintIcon;
|
||||
}
|
||||
|
||||
public Paint getPaintIconCustom() {
|
||||
return paintIconCustom;
|
||||
}
|
||||
|
||||
public Bitmap getArrowBitmap() {
|
||||
return arrowBitmap;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
package net.osmand.plus.views.layers.geometry;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
|
||||
import net.osmand.data.RotatedTileBox;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GeometryWayDrawer<T extends GeometryWayContext> {
|
||||
|
||||
private T context;
|
||||
|
||||
public GeometryWayDrawer(T context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public T getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public void drawArrowsOverPath(Canvas canvas, RotatedTileBox tb, List<Float> tx, List<Float> ty,
|
||||
List<Double> angles, List<Double> distances, double distPixToFinish, List<GeometryWayStyle<?>> styles) {
|
||||
List<PathPoint> arrows = new ArrayList<>();
|
||||
|
||||
int h = tb.getPixHeight();
|
||||
int w = tb.getPixWidth();
|
||||
int left = -w / 4;
|
||||
int right = w + w / 4;
|
||||
int top = - h/4;
|
||||
int bottom = h + h/4;
|
||||
|
||||
boolean hasStyles = styles != null && styles.size() == tx.size();
|
||||
double zoomCoef = tb.getZoomAnimation() > 0 ? (Math.pow(2, tb.getZoomAnimation() + tb.getZoomFloatPart())) : 1f;
|
||||
Bitmap arrow = context.getArrowBitmap();
|
||||
int arrowHeight = arrow.getHeight();
|
||||
double pxStep = arrowHeight * 4f * zoomCoef;
|
||||
double pxStepRegular = arrowHeight * 4f * zoomCoef;
|
||||
double dist = 0;
|
||||
if (distPixToFinish != 0) {
|
||||
dist = distPixToFinish - pxStep * ((int) (distPixToFinish / pxStep)); // dist < 1
|
||||
}
|
||||
for (int i = tx.size() - 2; i >= 0; i --) {
|
||||
GeometryWayStyle<?> style = hasStyles ? styles.get(i) : null;
|
||||
float px = tx.get(i);
|
||||
float py = ty.get(i);
|
||||
float x = tx.get(i + 1);
|
||||
float y = ty.get(i + 1);
|
||||
double distSegment = distances.get(i + 1);
|
||||
double angle = angles.get(i + 1);
|
||||
if (distSegment == 0) {
|
||||
continue;
|
||||
}
|
||||
pxStep = style != null ? style.getPointStepPx(zoomCoef) : pxStepRegular;
|
||||
if (dist >= pxStep) {
|
||||
dist = 0;
|
||||
}
|
||||
double percent = 1 - (pxStep - dist) / distSegment;
|
||||
dist += distSegment;
|
||||
while (dist >= pxStep) {
|
||||
double pdx = (x - px) * percent;
|
||||
double pdy = (y - py) * percent;
|
||||
float iconx = (float) (px + pdx);
|
||||
float icony = (float) (py + pdy);
|
||||
if (GeometryWay.isIn(iconx, icony, left, top, right, bottom)) {
|
||||
arrows.add(getArrowPathPoint(iconx, icony, style, angle));
|
||||
}
|
||||
dist -= pxStep;
|
||||
percent -= pxStep / distSegment;
|
||||
}
|
||||
}
|
||||
for (int i = arrows.size() - 1; i >= 0; i--) {
|
||||
PathPoint a = arrows.get(i);
|
||||
if (!tb.isZoomAnimated() || a.style.isVisibleWhileZooming()) {
|
||||
a.draw(canvas, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected PathPoint getArrowPathPoint(float iconx, float icony, GeometryWayStyle<?> style, double angle) {
|
||||
return new PathPoint(iconx, icony, angle, style);
|
||||
}
|
||||
|
||||
public void drawPath(Canvas canvas, Path path, GeometryWayStyle<?> style) {
|
||||
context.getAttrs().customColor = style.getColor();
|
||||
context.getAttrs().drawPath(canvas, path);
|
||||
}
|
||||
|
||||
public static class PathPoint {
|
||||
float x;
|
||||
float y;
|
||||
double angle;
|
||||
GeometryWayStyle<?> style;
|
||||
|
||||
private Matrix matrix = new Matrix();
|
||||
|
||||
public PathPoint(float x, float y, double angle, GeometryWayStyle<?> style) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.angle = angle;
|
||||
this.style = style;
|
||||
}
|
||||
|
||||
protected Matrix getMatrix() {
|
||||
return matrix;
|
||||
}
|
||||
|
||||
void draw(Canvas canvas, GeometryWayContext context) {
|
||||
if (style != null && style.getPointBitmap() != null) {
|
||||
Bitmap bitmap = style.getPointBitmap();
|
||||
Integer pointColor = style.getPointColor();
|
||||
float paintH2 = bitmap.getHeight() / 2f;
|
||||
float paintW2 = bitmap.getWidth() / 2f;
|
||||
|
||||
matrix.reset();
|
||||
matrix.postRotate((float) angle, paintW2, paintH2);
|
||||
matrix.postTranslate(x - paintW2, y - paintH2);
|
||||
if (pointColor != null) {
|
||||
Paint paint = context.getPaintIconCustom();
|
||||
paint.setColorFilter(new PorterDuffColorFilter(pointColor, PorterDuff.Mode.SRC_IN));
|
||||
canvas.drawBitmap(bitmap, matrix, paint);
|
||||
} else {
|
||||
if (style.hasPaintedPointBitmap()) {
|
||||
Paint paint = context.getPaintIconCustom();
|
||||
paint.setColorFilter(null);
|
||||
canvas.drawBitmap(bitmap, matrix, paint);
|
||||
} else {
|
||||
canvas.drawBitmap(bitmap, matrix, context.getPaintIcon());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package net.osmand.plus.views.layers.geometry;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
public abstract class GeometryWayStyle<T extends GeometryWayContext> {
|
||||
|
||||
private T context;
|
||||
protected Integer color;
|
||||
|
||||
public GeometryWayStyle(T context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public GeometryWayStyle(T context, Integer color) {
|
||||
this.context = context;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public T getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public Context getCtx() {
|
||||
return context.getCtx();
|
||||
}
|
||||
|
||||
public Integer getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public Integer getStrokeColor() {
|
||||
return context.getStrokeColor(color);
|
||||
}
|
||||
|
||||
public Integer getPointColor() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isNightMode() {
|
||||
return context.isNightMode();
|
||||
}
|
||||
|
||||
public boolean hasPathLine() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isVisibleWhileZooming() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public double getPointStepPx(double zoomCoef) {
|
||||
Bitmap arrow = context.getArrowBitmap();
|
||||
int arrowHeight = arrow.getHeight();
|
||||
return arrowHeight * 4f * zoomCoef;
|
||||
}
|
||||
|
||||
public abstract Bitmap getPointBitmap();
|
||||
|
||||
public boolean hasPaintedPointBitmap() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (color != null ? color.hashCode() : 0) + (context.isNightMode() ? 1231 : 1237);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof GeometryWayStyle)) {
|
||||
return false;
|
||||
}
|
||||
GeometryWayStyle<?> o = (GeometryWayStyle<?>) other;
|
||||
if (color != null && o.color != null) {
|
||||
return color.equals(o.color);
|
||||
}
|
||||
return color == null && o.color == null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
package net.osmand.plus.views.layers.geometry;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import net.osmand.GPXUtilities.WptPt;
|
||||
import net.osmand.data.RotatedTileBox;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GpxGeometryWay extends GeometryWay<GpxGeometryWayContext, GeometryWayDrawer<GpxGeometryWayContext>> {
|
||||
|
||||
private List<WptPt> points;
|
||||
|
||||
private float trackWidth;
|
||||
private int trackColor;
|
||||
private int arrowColor;
|
||||
|
||||
private static class GeometryWayWptPtProvider implements GeometryWayProvider {
|
||||
private List<WptPt> points;
|
||||
|
||||
public GeometryWayWptPtProvider(@NonNull List<WptPt> points) {
|
||||
this.points = points;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLatitude(int index) {
|
||||
return points.get(index).getLatitude();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLongitude(int index) {
|
||||
return points.get(index).getLongitude();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return points.size();
|
||||
}
|
||||
}
|
||||
|
||||
public GpxGeometryWay(GpxGeometryWayContext context) {
|
||||
super(context, new GpxGeometryWayDrawer(context));
|
||||
}
|
||||
|
||||
public void setTrackStyleParams(int arrowColor, int trackColor, float trackWidth) {
|
||||
this.arrowColor = arrowColor;
|
||||
this.trackColor = trackColor;
|
||||
this.trackWidth = trackWidth;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public GeometryWayStyle<?> getDefaultWayStyle() {
|
||||
return new GeometryArrowsStyle(getContext(), arrowColor, trackColor, trackWidth);
|
||||
}
|
||||
|
||||
public void updatePoints(RotatedTileBox tb, List<WptPt> points) {
|
||||
if (tb.getMapDensity() != getMapDensity() || this.points != points) {
|
||||
this.points = points;
|
||||
if (points != null) {
|
||||
updateWay(new GeometryWayWptPtProvider(points), tb);
|
||||
} else {
|
||||
clearWay();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clearPoints() {
|
||||
if (points != null) {
|
||||
points = null;
|
||||
clearWay();
|
||||
}
|
||||
}
|
||||
|
||||
public static class GeometryArrowsStyle extends GeometryWayStyle<GpxGeometryWayContext> {
|
||||
|
||||
private static final double DIRECTION_ARROW_DISTANCE_MULTIPLIER = 10.0;
|
||||
|
||||
private Bitmap arrowBitmap;
|
||||
|
||||
protected int pointColor;
|
||||
protected int trackColor;
|
||||
protected float trackWidth;
|
||||
|
||||
GeometryArrowsStyle(GpxGeometryWayContext context, int arrowColor, int trackColor, float trackWidth) {
|
||||
this(context, null, arrowColor, trackColor, trackWidth);
|
||||
}
|
||||
|
||||
GeometryArrowsStyle(GpxGeometryWayContext context, Bitmap arrowBitmap, int arrowColor, int trackColor, float trackWidth) {
|
||||
super(context);
|
||||
this.arrowBitmap = arrowBitmap;
|
||||
this.pointColor = arrowColor;
|
||||
this.trackColor = trackColor;
|
||||
this.trackWidth = trackWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
return other instanceof GeometryArrowsStyle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPathLine() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bitmap getPointBitmap() {
|
||||
return arrowBitmap != null ? arrowBitmap : getContext().getArrowBitmap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getPointColor() {
|
||||
return pointColor;
|
||||
}
|
||||
|
||||
public int getTrackColor() {
|
||||
return trackColor;
|
||||
}
|
||||
|
||||
public float getTrackWidth() {
|
||||
return trackWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getPointStepPx(double zoomCoef) {
|
||||
return getPointBitmap().getHeight() * DIRECTION_ARROW_DISTANCE_MULTIPLIER * zoomCoef;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package net.osmand.plus.views.layers.geometry;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Paint;
|
||||
|
||||
import net.osmand.plus.R;
|
||||
|
||||
public class GpxGeometryWayContext extends GeometryWayContext {
|
||||
|
||||
public GpxGeometryWayContext(Context ctx, float density) {
|
||||
super(ctx, density);
|
||||
Paint paint = getPaintIcon();
|
||||
paint.setStrokeCap(Paint.Cap.ROUND);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getArrowBitmapResId() {
|
||||
return R.drawable.mm_special_arrow_up;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package net.osmand.plus.views.layers.geometry;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
|
||||
import net.osmand.data.RotatedTileBox;
|
||||
import net.osmand.plus.views.layers.geometry.GeometryWayDrawer.PathPoint;
|
||||
import net.osmand.plus.views.layers.geometry.GpxGeometryWay.GeometryArrowsStyle;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GpxGeometryWayDrawer extends GeometryWayDrawer<GpxGeometryWayContext> {
|
||||
|
||||
private static final float DIRECTION_ARROW_CIRCLE_MULTIPLIER = 1.5f;
|
||||
|
||||
public GpxGeometryWayDrawer(GpxGeometryWayContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PathPoint getArrowPathPoint(float iconx, float icony, GeometryWayStyle<?> style, double angle) {
|
||||
return new ArrowPathPoint(iconx, icony, angle, style);
|
||||
}
|
||||
|
||||
private static class ArrowPathPoint extends PathPoint {
|
||||
|
||||
ArrowPathPoint(float x, float y, double angle, GeometryWayStyle<?> style) {
|
||||
super(x, y, angle, style);
|
||||
}
|
||||
|
||||
@Override
|
||||
void draw(Canvas canvas, GeometryWayContext context) {
|
||||
if (style instanceof GeometryArrowsStyle) {
|
||||
GeometryArrowsStyle arrowsWayStyle = (GeometryArrowsStyle) style;
|
||||
|
||||
float arrowWidth = style.getPointBitmap().getWidth();
|
||||
if (arrowWidth > arrowsWayStyle.getTrackWidth()) {
|
||||
Paint paint = context.getPaintIcon();
|
||||
paint.setColor(arrowsWayStyle.getTrackColor());
|
||||
paint.setStrokeWidth(arrowWidth * DIRECTION_ARROW_CIRCLE_MULTIPLIER);
|
||||
canvas.drawPoint(x, y, paint);
|
||||
}
|
||||
}
|
||||
super.draw(canvas, context);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,302 @@
|
|||
package net.osmand.plus.views.layers.geometry;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import net.osmand.Location;
|
||||
import net.osmand.data.LatLon;
|
||||
import net.osmand.data.RotatedTileBox;
|
||||
import net.osmand.data.TransportRoute;
|
||||
import net.osmand.osm.edit.Node;
|
||||
import net.osmand.osm.edit.OSMSettings;
|
||||
import net.osmand.osm.edit.Way;
|
||||
import net.osmand.plus.OsmandApplication;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.UiUtilities;
|
||||
import net.osmand.plus.render.RenderingIcons;
|
||||
import net.osmand.plus.routing.RouteCalculationResult;
|
||||
import net.osmand.plus.routing.TransportRoutingHelper;
|
||||
import net.osmand.plus.transport.TransportStopRoute;
|
||||
import net.osmand.plus.transport.TransportStopType;
|
||||
import net.osmand.router.TransportRoutePlanner;
|
||||
import net.osmand.router.TransportRoutePlanner.TransportRouteResultSegment;
|
||||
import net.osmand.router.TransportRouteResult;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class PublicTransportGeometryWay extends GeometryWay<PublicTransportGeometryWayContext, PublicTransportGeometryWayDrawer> {
|
||||
|
||||
private TransportRoutingHelper transportHelper;
|
||||
private TransportRouteResult route;
|
||||
|
||||
public PublicTransportGeometryWay(PublicTransportGeometryWayContext context) {
|
||||
super(context, new PublicTransportGeometryWayDrawer(context));
|
||||
this.transportHelper = context.getApp().getTransportRoutingHelper();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public GeometryWayStyle<PublicTransportGeometryWayContext> getDefaultWayStyle() {
|
||||
return new GeometryWalkWayStyle(getContext());
|
||||
}
|
||||
|
||||
public void updateRoute(RotatedTileBox tb, TransportRouteResult route) {
|
||||
if (tb.getMapDensity() != getMapDensity() || this.route != route) {
|
||||
this.route = route;
|
||||
List<Location> locations;
|
||||
Map<Integer, GeometryWayStyle<?>> styleMap;
|
||||
if (route == null) {
|
||||
locations = Collections.emptyList();
|
||||
styleMap = Collections.emptyMap();
|
||||
} else {
|
||||
LatLon start = transportHelper.getStartLocation();
|
||||
LatLon end = transportHelper.getEndLocation();
|
||||
List<Way> list = new ArrayList<>();
|
||||
List<GeometryWayStyle<?>> styles = new ArrayList<>();
|
||||
calculateTransportResult(start, end, route, list, styles);
|
||||
List<Location> locs = new ArrayList<>();
|
||||
Map<Integer, GeometryWayStyle<?>> stlMap = new TreeMap<>();
|
||||
int i = 0;
|
||||
int k = 0;
|
||||
if (list.size() > 0) {
|
||||
for (Way w : list) {
|
||||
stlMap.put(k, styles.get(i++));
|
||||
for (Node n : w.getNodes()) {
|
||||
Location ln = new Location("");
|
||||
ln.setLatitude(n.getLatitude());
|
||||
ln.setLongitude(n.getLongitude());
|
||||
locs.add(ln);
|
||||
k++;
|
||||
}
|
||||
}
|
||||
}
|
||||
locations = locs;
|
||||
styleMap = stlMap;
|
||||
}
|
||||
updateWay(locations, styleMap, tb);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearRoute() {
|
||||
if (route != null) {
|
||||
route = null;
|
||||
clearWay();
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateTransportResult(LatLon start, LatLon end, TransportRouteResult r, List<Way> res, List<GeometryWayStyle<?>> styles) {
|
||||
if (r != null) {
|
||||
LatLon p = start;
|
||||
TransportRouteResultSegment prev = null;
|
||||
for (TransportRouteResultSegment s : r.getSegments()) {
|
||||
LatLon floc = s.getStart().getLocation();
|
||||
addRouteWalk(prev, s, p, floc, res, styles);
|
||||
List<Way> geometry = s.getGeometry();
|
||||
res.addAll(geometry);
|
||||
addStyle(s, geometry, styles);
|
||||
p = s.getEnd().getLocation();
|
||||
prev = s;
|
||||
}
|
||||
addRouteWalk(prev, null, p, end, res, styles);
|
||||
}
|
||||
}
|
||||
|
||||
private void addRouteWalk(TransportRouteResultSegment s1, TransportRouteResultSegment s2,
|
||||
LatLon start, LatLon end, List<Way> res, List<GeometryWayStyle<?>> styles) {
|
||||
final RouteCalculationResult walkingRouteSegment = transportHelper.getWalkingRouteSegment(s1, s2);
|
||||
if (walkingRouteSegment != null && walkingRouteSegment.getRouteLocations().size() > 0) {
|
||||
final List<Location> routeLocations = walkingRouteSegment.getRouteLocations();
|
||||
Way way = new Way(TransportRoutePlanner.GEOMETRY_WAY_ID);
|
||||
way.putTag(OSMSettings.OSMTagKey.NAME.getValue(), String.format(Locale.US, "Walk %d m", walkingRouteSegment.getWholeDistance()));
|
||||
for (Location l : routeLocations) {
|
||||
way.addNode(new Node(l.getLatitude(), l.getLongitude(), -1));
|
||||
}
|
||||
res.add(way);
|
||||
addStyle(null, Collections.singletonList(way), styles);
|
||||
} else {
|
||||
double dist = MapUtils.getDistance(start, end);
|
||||
Way way = new Way(TransportRoutePlanner.GEOMETRY_WAY_ID);
|
||||
way.putTag(OSMSettings.OSMTagKey.NAME.getValue(), String.format(Locale.US, "Walk %.1f m", dist));
|
||||
way.addNode(new Node(start.getLatitude(), start.getLongitude(), -1));
|
||||
way.addNode(new Node(end.getLatitude(), end.getLongitude(), -1));
|
||||
res.add(way);
|
||||
addStyle(null, Collections.singletonList(way), styles);
|
||||
}
|
||||
}
|
||||
|
||||
private void addStyle(TransportRouteResultSegment segment, List<Way> geometry, List<GeometryWayStyle<?>> styles) {
|
||||
PublicTransportGeometryWayContext context = getContext();
|
||||
GeometryWayStyle<?> style;
|
||||
Way w = geometry.get(0);
|
||||
if (segment == null || segment.route == null) {
|
||||
style = new GeometryWalkWayStyle(context);
|
||||
} else if (w.getId() == TransportRoutePlanner.GEOMETRY_WAY_ID) {
|
||||
style = new GeometryTransportWayStyle(context, segment);
|
||||
} else {
|
||||
style = new TransportStopsWayStyle(context, segment);
|
||||
}
|
||||
for (int i = 0; i < geometry.size(); i++) {
|
||||
styles.add(style);
|
||||
}
|
||||
}
|
||||
|
||||
public static class GeometryWalkWayStyle extends PublicTransportGeometryWayStyle {
|
||||
|
||||
GeometryWalkWayStyle(PublicTransportGeometryWayContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPathLine() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
return other instanceof GeometryWalkWayStyle;
|
||||
}
|
||||
|
||||
public Bitmap getPointBitmap() {
|
||||
return getContext().getWalkArrowBitmap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPaintedPointBitmap() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getPointStepPx(double zoomCoef) {
|
||||
return getPointBitmap().getHeight() * 1.2f * zoomCoef;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisibleWhileZooming() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class GeometryAnchorWayStyle extends GeometryWayStyle<PublicTransportGeometryWayContext> {
|
||||
|
||||
GeometryAnchorWayStyle(PublicTransportGeometryWayContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPathLine() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
return other instanceof GeometryAnchorWayStyle;
|
||||
}
|
||||
|
||||
public Bitmap getPointBitmap() {
|
||||
return getContext().getAnchorBitmap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPaintedPointBitmap() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TransportStopsWayStyle extends GeometryTransportWayStyle {
|
||||
TransportStopsWayStyle(PublicTransportGeometryWayContext context, TransportRouteResultSegment segment) {
|
||||
super(context, segment);
|
||||
OsmandApplication app = (OsmandApplication) getCtx().getApplicationContext();
|
||||
this.color = ContextCompat.getColor(app, R.color.icon_color_default_light);
|
||||
this.pointColor = UiUtilities.getContrastColor(app, color, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static class GeometryTransportWayStyle extends GeometryWayStyle<PublicTransportGeometryWayContext> {
|
||||
|
||||
private TransportRouteResultSegment segment;
|
||||
private Drawable stopDrawable;
|
||||
protected Integer pointColor;
|
||||
|
||||
GeometryTransportWayStyle(PublicTransportGeometryWayContext context, TransportRouteResultSegment segment) {
|
||||
super(context);
|
||||
this.segment = segment;
|
||||
|
||||
TransportStopRoute r = new TransportStopRoute();
|
||||
TransportRoute route = segment.route;
|
||||
r.type = TransportStopType.findType(route.getType());
|
||||
r.route = route;
|
||||
OsmandApplication app = (OsmandApplication) getCtx().getApplicationContext();
|
||||
this.color = r.getRouteColor(app, isNightMode());
|
||||
this.pointColor = UiUtilities.getContrastColor(app, color, true);
|
||||
|
||||
TransportStopType type = TransportStopType.findType(route.getType());
|
||||
if (type == null) {
|
||||
type = TransportStopType.findType("bus");
|
||||
}
|
||||
if (type != null) {
|
||||
stopDrawable = RenderingIcons.getDrawableIcon(getCtx(), type.getResName(), false);
|
||||
}
|
||||
}
|
||||
|
||||
public TransportRouteResultSegment getSegment() {
|
||||
return segment;
|
||||
}
|
||||
|
||||
public TransportRoute getRoute() {
|
||||
return segment.route;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bitmap getPointBitmap() {
|
||||
return getContext().getArrowBitmap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getPointColor() {
|
||||
return pointColor;
|
||||
}
|
||||
|
||||
public Bitmap getStopBitmap() {
|
||||
return getContext().getStopShieldBitmap(color, stopDrawable);
|
||||
}
|
||||
|
||||
public Bitmap getStopSmallBitmap() {
|
||||
return getContext().getStopSmallShieldBitmap(color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
if (!(other instanceof GeometryTransportWayStyle)) {
|
||||
return false;
|
||||
}
|
||||
return getRoute() == ((GeometryTransportWayStyle) other).getRoute();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,207 @@
|
|||
package net.osmand.plus.views.layers.geometry;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.UiUtilities;
|
||||
import net.osmand.plus.views.OsmandMapLayer.RenderingLineAttributes;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class PublicTransportGeometryWayContext extends GeometryWayContext {
|
||||
|
||||
private RenderingLineAttributes attrsPT;
|
||||
private RenderingLineAttributes attrsW;
|
||||
|
||||
private Bitmap walkArrowBitmap;
|
||||
private Bitmap anchorBitmap;
|
||||
private Map<Pair<Integer, Drawable>, Bitmap> stopBitmapsCache = new HashMap<>();
|
||||
private Map<Integer, Bitmap> stopSmallBitmapsCache = new HashMap<>();
|
||||
|
||||
public PublicTransportGeometryWayContext(Context ctx, float density) {
|
||||
super(ctx, density);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getArrowBitmapResId() {
|
||||
return R.drawable.map_route_direction_arrow;
|
||||
}
|
||||
|
||||
public RenderingLineAttributes getAttrsPT() {
|
||||
return attrsPT;
|
||||
}
|
||||
|
||||
public RenderingLineAttributes getAttrsW() {
|
||||
return attrsW;
|
||||
}
|
||||
|
||||
public void updatePaints(boolean nightMode, @NonNull RenderingLineAttributes attrs,
|
||||
@NonNull RenderingLineAttributes attrsPT, @NonNull RenderingLineAttributes attrsW) {
|
||||
this.attrsPT = attrsPT;
|
||||
this.attrsW = attrsW;
|
||||
updatePaints(nightMode, attrs);
|
||||
}
|
||||
|
||||
public Bitmap getWalkArrowBitmap() {
|
||||
return walkArrowBitmap;
|
||||
}
|
||||
|
||||
public Bitmap getAnchorBitmap() {
|
||||
return anchorBitmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasAttrs() {
|
||||
return super.hasAttrs() && attrsPT != null && attrsW != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void recreateBitmaps() {
|
||||
super.recreateBitmaps();
|
||||
float walkCircleH = attrsW.paint.getStrokeWidth() * 1.33f;
|
||||
float walkCircleW = attrsW.paint.getStrokeWidth();
|
||||
float walkCircleRadius = attrsW.paint.getStrokeWidth() / 2f;
|
||||
float routeShieldRadius = attrsPT.paint3.getStrokeWidth() / 2f;
|
||||
|
||||
// create anchor bitmap
|
||||
float density = getDensity();
|
||||
float margin = 2f * density;
|
||||
float width = routeShieldRadius * 2 + margin * 2;
|
||||
float height = routeShieldRadius * 2 + margin * 2;
|
||||
Bitmap bitmap = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888);
|
||||
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
Paint paint = new Paint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStrokeWidth(1f * density);
|
||||
|
||||
float x = width / 2;
|
||||
float y = height / 2;
|
||||
paint.setColor(Color.WHITE);
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
canvas.drawCircle(x, y, routeShieldRadius, paint);
|
||||
paint.setColor(Color.BLACK);
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
canvas.drawCircle(x, y, routeShieldRadius, paint);
|
||||
|
||||
anchorBitmap = bitmap;
|
||||
|
||||
// create walk arrow bitmap
|
||||
width = walkCircleW + margin * 2;
|
||||
height = walkCircleH + margin * 2;
|
||||
bitmap = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888);
|
||||
|
||||
canvas = new Canvas(bitmap);
|
||||
paint = new Paint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStrokeWidth(1f * density);
|
||||
|
||||
RectF rect = new RectF(margin, margin, width - margin, height - margin);
|
||||
paint.setColor(attrsW.paint.getColor());
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
canvas.drawRoundRect(rect, walkCircleRadius, walkCircleRadius, paint);
|
||||
paint.setColor(getStrokeColor(paint.getColor()));
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
canvas.drawRoundRect(rect, walkCircleRadius, walkCircleRadius, paint);
|
||||
|
||||
Bitmap arrowBitmap = getArrowBitmap();
|
||||
paint = new Paint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setColor(Color.WHITE);
|
||||
paint.setStrokeWidth(1f * density);
|
||||
paint.setAlpha(200);
|
||||
canvas.drawBitmap(arrowBitmap, width / 2 - arrowBitmap.getWidth() / 2f, height / 2 - arrowBitmap.getHeight() / 2f, paint);
|
||||
|
||||
walkArrowBitmap = bitmap;
|
||||
stopBitmapsCache = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCustomColor() {
|
||||
super.clearCustomColor();
|
||||
attrsPT.customColor = 0;
|
||||
}
|
||||
|
||||
public Bitmap getStopShieldBitmap(int color, Drawable stopDrawable) {
|
||||
Bitmap bmp = stopBitmapsCache.get(new Pair<>(color, stopDrawable));
|
||||
if (bmp == null) {
|
||||
int fillColor = UiUtilities.getContrastColor(getApp(), color, true);
|
||||
int strokeColor = getStrokeColor(color);
|
||||
|
||||
float density = getDensity();
|
||||
float routeShieldRadius = attrsPT.paint3.getStrokeWidth() / 2;
|
||||
float routeShieldCornerRadius = 3 * density;
|
||||
|
||||
float margin = 2f * density;
|
||||
float width = routeShieldRadius * 2 + margin * 2;
|
||||
float height = routeShieldRadius * 2 + margin * 2;
|
||||
bmp = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888);
|
||||
|
||||
Canvas canvas = new Canvas(bmp);
|
||||
Paint paint = new Paint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStrokeWidth(1f * density);
|
||||
|
||||
RectF rect = new RectF(margin, margin, width - margin, height - margin);
|
||||
paint.setColor(fillColor);
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
canvas.drawRoundRect(rect, routeShieldCornerRadius, routeShieldCornerRadius, paint);
|
||||
paint.setColor(strokeColor);
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
canvas.drawRoundRect(rect, routeShieldCornerRadius, routeShieldCornerRadius, paint);
|
||||
|
||||
if (stopDrawable != null) {
|
||||
stopDrawable.setColorFilter(new PorterDuffColorFilter(strokeColor, PorterDuff.Mode.SRC_IN));
|
||||
float marginBitmap = 1f * density;
|
||||
rect.inset(marginBitmap, marginBitmap);
|
||||
stopDrawable.setBounds(0, 0, (int) rect.width(), (int) rect.height());
|
||||
canvas.translate(rect.left, rect.top);
|
||||
stopDrawable.draw(canvas);
|
||||
}
|
||||
stopBitmapsCache.put(new Pair<>(color, stopDrawable), bmp);
|
||||
}
|
||||
return bmp;
|
||||
}
|
||||
|
||||
public Bitmap getStopSmallShieldBitmap(int color) {
|
||||
Bitmap bmp = stopSmallBitmapsCache.get(color);
|
||||
if (bmp == null) {
|
||||
int fillColor = UiUtilities.getContrastColor(getApp(), color, true);
|
||||
int strokeColor = getStrokeColor(color);
|
||||
|
||||
float routeShieldRadius = attrsPT.paint3.getStrokeWidth() / 4;
|
||||
|
||||
float density = getDensity();
|
||||
float margin = 3f * density;
|
||||
float width = routeShieldRadius * 2 + margin * 2;
|
||||
float height = routeShieldRadius * 2 + margin * 2;
|
||||
bmp = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888);
|
||||
|
||||
Canvas canvas = new Canvas(bmp);
|
||||
Paint paint = new Paint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStrokeWidth(1f * density);
|
||||
paint.setColor(fillColor);
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
canvas.drawCircle(width / 2, height / 2, routeShieldRadius, paint);
|
||||
paint.setColor(strokeColor);
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
canvas.drawCircle(width / 2, height / 2, routeShieldRadius, paint);
|
||||
|
||||
stopSmallBitmapsCache.put(color, bmp);
|
||||
}
|
||||
return bmp;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,222 @@
|
|||
package net.osmand.plus.views.layers.geometry;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
|
||||
import net.osmand.data.QuadRect;
|
||||
import net.osmand.data.QuadTree;
|
||||
import net.osmand.data.RotatedTileBox;
|
||||
import net.osmand.data.TransportStop;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
import net.osmand.plus.views.OsmandMapLayer.RenderingLineAttributes;
|
||||
import net.osmand.plus.views.layers.geometry.PublicTransportGeometryWay.GeometryAnchorWayStyle;
|
||||
import net.osmand.plus.views.layers.geometry.PublicTransportGeometryWay.GeometryTransportWayStyle;
|
||||
import net.osmand.router.TransportRoutePlanner.TransportRouteResultSegment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class PublicTransportGeometryWayDrawer extends GeometryWayDrawer<PublicTransportGeometryWayContext> {
|
||||
|
||||
private List<TransportStop> routeTransportStops = new ArrayList<>();
|
||||
|
||||
public PublicTransportGeometryWayDrawer(PublicTransportGeometryWayContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public List<TransportStop> getRouteTransportStops() {
|
||||
return routeTransportStops;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPath(Canvas canvas, Path path, GeometryWayStyle<?> style) {
|
||||
if (style instanceof GeometryTransportWayStyle) {
|
||||
RenderingLineAttributes attrsPT = getContext().getAttrsPT();
|
||||
attrsPT.customColor = style.getStrokeColor();
|
||||
attrsPT.customColorPaint.setStrokeWidth(attrsPT.paint2.getStrokeWidth());
|
||||
attrsPT.drawPath(canvas, path);
|
||||
attrsPT.customColorPaint.setStrokeWidth(attrsPT.paint.getStrokeWidth());
|
||||
attrsPT.customColor = style.getColor();
|
||||
attrsPT.drawPath(canvas, path);
|
||||
} else {
|
||||
super.drawPath(canvas, path, style);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawArrowsOverPath(Canvas canvas, RotatedTileBox tb, List<Float> tx, List<Float> ty,
|
||||
List<Double> angles, List<Double> distances, double distPixToFinish, List<GeometryWayStyle<?>> styles) {
|
||||
PublicTransportGeometryWayContext context = getContext();
|
||||
|
||||
List<PathPoint> arrows = new ArrayList<>();
|
||||
List<PathAnchor> anchors = new ArrayList<>();
|
||||
List<PathTransportStop> stops = new ArrayList<>();
|
||||
Set<GeometryTransportWayStyle> transportWaysStyles = new HashSet<>();
|
||||
GeometryAnchorWayStyle anchorWayStyle = new GeometryAnchorWayStyle(context);
|
||||
|
||||
int h = tb.getPixHeight();
|
||||
int w = tb.getPixWidth();
|
||||
int left = -w / 4;
|
||||
int right = w + w / 4;
|
||||
int top = - h/4;
|
||||
int bottom = h + h/4;
|
||||
|
||||
boolean hasStyles = styles != null && styles.size() == tx.size();
|
||||
double zoomCoef = tb.getZoomAnimation() > 0 ? (Math.pow(2, tb.getZoomAnimation() + tb.getZoomFloatPart())) : 1f;
|
||||
|
||||
Bitmap arrow = context.getArrowBitmap();
|
||||
int arrowHeight = arrow.getHeight();
|
||||
double pxStep = arrowHeight * 4f * zoomCoef;
|
||||
double pxStepRegular = arrowHeight * 4f * zoomCoef;
|
||||
double dist = 0;
|
||||
if (distPixToFinish != 0) {
|
||||
dist = distPixToFinish - pxStep * ((int) (distPixToFinish / pxStep)); // dist < 1
|
||||
}
|
||||
|
||||
GeometryWayStyle<?> prevStyle = null;
|
||||
for (int i = tx.size() - 2; i >= 0; i --) {
|
||||
GeometryWayStyle<?> style = hasStyles ? styles.get(i) : null;
|
||||
float px = tx.get(i);
|
||||
float py = ty.get(i);
|
||||
float x = tx.get(i + 1);
|
||||
float y = ty.get(i + 1);
|
||||
double distSegment = distances.get(i + 1);
|
||||
double angle = angles.get(i + 1);
|
||||
if (distSegment == 0) {
|
||||
continue;
|
||||
}
|
||||
pxStep = style != null ? style.getPointStepPx(zoomCoef) : pxStepRegular;
|
||||
boolean transportStyle = style instanceof GeometryTransportWayStyle;
|
||||
if (style != null && !style.equals(prevStyle) && (prevStyle != null || transportStyle)) {
|
||||
prevStyle = style;
|
||||
anchors.add(new PathAnchor(x, y, anchorWayStyle));
|
||||
dist = 0;
|
||||
}
|
||||
if (transportStyle) {
|
||||
transportWaysStyles.add((GeometryTransportWayStyle) style);
|
||||
}
|
||||
if (dist >= pxStep) {
|
||||
dist = 0;
|
||||
}
|
||||
double percent = 1 - (pxStep - dist) / distSegment;
|
||||
dist += distSegment;
|
||||
while (dist >= pxStep) {
|
||||
double pdx = (x - px) * percent;
|
||||
double pdy = (y - py) * percent;
|
||||
float iconx = (float) (px + pdx);
|
||||
float icony = (float) (py + pdy);
|
||||
if (GeometryWay.isIn(iconx, icony, left, top, right, bottom)) {
|
||||
arrows.add(new PathPoint(iconx, icony, angle, style));
|
||||
}
|
||||
dist -= pxStep;
|
||||
percent -= pxStep / distSegment;
|
||||
}
|
||||
}
|
||||
List<TransportStop> routeTransportStops = new ArrayList<>();
|
||||
for (GeometryTransportWayStyle style : transportWaysStyles) {
|
||||
List<TransportStop> transportStops = style.getRoute().getForwardStops();
|
||||
TransportRouteResultSegment segment = style.getSegment();
|
||||
int start = segment.start;
|
||||
int end = segment.end;
|
||||
for (int i = start; i <= end; i++) {
|
||||
TransportStop stop = transportStops.get(i);
|
||||
double lat = stop.getLocation().getLatitude();
|
||||
double lon = stop.getLocation().getLongitude();
|
||||
float x = tb.getPixXFromLatLon(lat, lon);
|
||||
float y = tb.getPixYFromLatLon(lat, lon);
|
||||
if (GeometryWay.isIn(x, y, left, top, right, bottom)) {
|
||||
if (i != start && i != end) {
|
||||
stops.add(new PathTransportStop(x, y, style));
|
||||
}
|
||||
routeTransportStops.add(transportStops.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
this.routeTransportStops = routeTransportStops;
|
||||
|
||||
for (int i = arrows.size() - 1; i >= 0; i--) {
|
||||
PathPoint a = arrows.get(i);
|
||||
if (!tb.isZoomAnimated() || a.style.isVisibleWhileZooming()) {
|
||||
a.draw(canvas, context);
|
||||
}
|
||||
}
|
||||
for (int i = anchors.size() - 1; i >= 0; i--) {
|
||||
PathAnchor anchor = anchors.get(i);
|
||||
anchor.draw(canvas, context);
|
||||
}
|
||||
if (stops.size() > 0) {
|
||||
QuadTree<QuadRect> boundIntersections = OsmandMapLayer.initBoundIntersections(tb);
|
||||
List<PathTransportStop> fullObjects = new ArrayList<>();
|
||||
Bitmap stopBitmap = null;
|
||||
float iconSize = 1f;
|
||||
for (int i = stops.size() - 1; i >= 0; i--) {
|
||||
PathTransportStop stop = stops.get(i);
|
||||
if (stopBitmap == null) {
|
||||
stopBitmap = stop.getTransportWayStyle().getStopBitmap();
|
||||
iconSize = stopBitmap.getWidth() * 3 / 2.5f;
|
||||
}
|
||||
float x = stop.x;
|
||||
float y = stop.y;
|
||||
if (OsmandMapLayer.intersects(boundIntersections, x, y, iconSize, iconSize)) {
|
||||
stop.setSmallPoint(true);
|
||||
stop.draw(canvas, context);
|
||||
} else {
|
||||
stop.setSmallPoint(false);
|
||||
fullObjects.add(stop);
|
||||
}
|
||||
}
|
||||
for (PathTransportStop stop : fullObjects) {
|
||||
stop.draw(canvas, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class PathAnchor extends PathPoint {
|
||||
PathAnchor(float x, float y, GeometryAnchorWayStyle style) {
|
||||
super(x, y, 0, style);
|
||||
}
|
||||
}
|
||||
|
||||
public static class PathTransportStop extends PathPoint {
|
||||
|
||||
private boolean smallPoint;
|
||||
|
||||
public boolean isSmallPoint() {
|
||||
return smallPoint;
|
||||
}
|
||||
|
||||
public void setSmallPoint(boolean smallPoint) {
|
||||
this.smallPoint = smallPoint;
|
||||
}
|
||||
|
||||
PathTransportStop(float x, float y, GeometryTransportWayStyle style) {
|
||||
super(x, y, 0, style);
|
||||
}
|
||||
|
||||
GeometryTransportWayStyle getTransportWayStyle() {
|
||||
return (GeometryTransportWayStyle) style;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
void draw(Canvas canvas, GeometryWayContext context) {
|
||||
Bitmap stopBitmap = smallPoint ?
|
||||
getTransportWayStyle().getStopSmallBitmap() : getTransportWayStyle().getStopBitmap();
|
||||
float paintH2 = stopBitmap.getHeight() / 2f;
|
||||
float paintW2 = stopBitmap.getWidth() / 2f;
|
||||
|
||||
Matrix matrix = getMatrix();
|
||||
matrix.reset();
|
||||
matrix.postRotate(0f, paintW2, paintH2);
|
||||
matrix.postTranslate(x - paintW2, y - paintH2);
|
||||
Paint paint = context.getPaintIconCustom();
|
||||
paint.setColorFilter(null);
|
||||
canvas.drawBitmap(stopBitmap, matrix, paint);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package net.osmand.plus.views.layers.geometry;
|
||||
|
||||
public abstract class PublicTransportGeometryWayStyle extends GeometryWayStyle<PublicTransportGeometryWayContext> {
|
||||
|
||||
public PublicTransportGeometryWayStyle(PublicTransportGeometryWayContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public PublicTransportGeometryWayStyle(PublicTransportGeometryWayContext context, Integer color) {
|
||||
super(context, color);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package net.osmand.plus.views.layers.geometry;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import net.osmand.Location;
|
||||
import net.osmand.data.RotatedTileBox;
|
||||
import net.osmand.plus.routing.RouteCalculationResult;
|
||||
import net.osmand.plus.routing.RoutingHelper;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class RouteGeometryWay extends GeometryWay<RouteGeometryWayContext, GeometryWayDrawer<RouteGeometryWayContext>> {
|
||||
|
||||
private RoutingHelper helper;
|
||||
private RouteCalculationResult route;
|
||||
|
||||
public RouteGeometryWay(RouteGeometryWayContext context) {
|
||||
super(context, new GeometryWayDrawer<>(context));
|
||||
this.helper = context.getApp().getRoutingHelper();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public GeometryWayStyle<RouteGeometryWayContext> getDefaultWayStyle() {
|
||||
return new GeometrySolidWayStyle(getContext(), getContext().getAttrs().paint.getColor());
|
||||
}
|
||||
|
||||
public void updateRoute(RotatedTileBox tb, RouteCalculationResult route) {
|
||||
if (tb.getMapDensity() != getMapDensity() || this.route != route) {
|
||||
this.route = route;
|
||||
List<Location> locations = route != null ? route.getImmutableAllLocations() : Collections.<Location>emptyList();
|
||||
updateWay(locations, tb);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearRoute() {
|
||||
if (route != null) {
|
||||
route = null;
|
||||
clearWay();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getNextVisiblePoint() {
|
||||
return helper.getRoute().getCurrentStraightAnglePoint();
|
||||
}
|
||||
|
||||
private static class GeometrySolidWayStyle extends GeometryWayStyle<RouteGeometryWayContext> {
|
||||
|
||||
GeometrySolidWayStyle(RouteGeometryWayContext context, Integer color) {
|
||||
super(context, color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bitmap getPointBitmap() {
|
||||
return getContext().getArrowBitmap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
return other instanceof GeometrySolidWayStyle;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package net.osmand.plus.views.layers.geometry;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import net.osmand.plus.R;
|
||||
|
||||
public class RouteGeometryWayContext extends GeometryWayContext {
|
||||
|
||||
public RouteGeometryWayContext(Context ctx, float density) {
|
||||
super(ctx, density);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getArrowBitmapResId() {
|
||||
return R.drawable.map_route_direction_arrow;
|
||||
}
|
||||
}
|
|
@ -67,7 +67,7 @@ import net.osmand.plus.settings.backend.OsmandSettings;
|
|||
import net.osmand.plus.settings.backend.OsmandSettings.RulerMode;
|
||||
import net.osmand.plus.views.OsmandMapLayer.DrawSettings;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.views.RulerControlLayer;
|
||||
import net.osmand.plus.views.layers.RulerControlLayer;
|
||||
import net.osmand.plus.views.mapwidgets.widgets.TextInfoWidget;
|
||||
import net.osmand.render.RenderingRuleSearchRequest;
|
||||
import net.osmand.render.RenderingRulesStorage;
|
||||
|
|
|
@ -24,8 +24,8 @@ import net.osmand.plus.activities.MapActivity;
|
|||
import net.osmand.plus.dialogs.ConfigureMapMenu;
|
||||
import net.osmand.plus.mapmarkers.DirectionIndicationDialogFragment;
|
||||
import net.osmand.plus.quickaction.QuickActionListFragment;
|
||||
import net.osmand.plus.views.MapInfoLayer;
|
||||
import net.osmand.plus.views.MapQuickActionLayer;
|
||||
import net.osmand.plus.views.layers.MapInfoLayer;
|
||||
import net.osmand.plus.views.layers.MapQuickActionLayer;
|
||||
import net.osmand.plus.views.OsmandMapLayer.DrawSettings;
|
||||
import net.osmand.plus.views.mapwidgets.widgets.TextInfoWidget;
|
||||
import net.osmand.plus.views.mapwidgets.widgetstates.WidgetState;
|
||||
|
|
|
@ -31,7 +31,7 @@ import net.osmand.plus.search.QuickSearchListAdapter;
|
|||
import net.osmand.plus.search.listitems.QuickSearchBannerListItem;
|
||||
import net.osmand.plus.search.listitems.QuickSearchFreeBannerListItem;
|
||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||
import net.osmand.plus.views.DownloadedRegionsLayer;
|
||||
import net.osmand.plus.views.layers.DownloadedRegionsLayer;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.search.core.ObjectType;
|
||||
import net.osmand.search.core.SearchPhrase;
|
||||
|
|
Loading…
Reference in a new issue