diff --git a/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutePlanner.java b/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutePlanner.java index c94831d8f4..79fd6501ac 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutePlanner.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutePlanner.java @@ -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 prepareResults(TransportRoutingContext ctx, List results) { Collections.sort(results, new SegmentsComparator(ctx)); List lst = new ArrayList(); - 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; @@ -486,157 +470,13 @@ public class TransportRoutePlanner { return route.getForwardStops().get(i); } } - - public static class TransportRouteResult { - - List segments = new ArrayList(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 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; @@ -677,8 +513,7 @@ public class TransportRoutePlanner { this.segStart = c.segStart; 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,165 +568,10 @@ 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 visitedSegments = new TLongObjectHashMap(); - public TransportRoutingConfiguration cfg; - public TLongObjectHashMap combinedRoutesCache = new TLongObjectHashMap(); - public Map> missingStopsCache = new HashMap>(); - - public TLongObjectHashMap> 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>(); - this.library = library; - transportStopsReader = new TransportStopsRouteReader(Arrays.asList(readers)); - } - - public List getTransportStops(LatLon loc) throws IOException { - int y = MapUtils.get31TileNumberY(loc.getLatitude()); - int x = MapUtils.get31TileNumberX(loc.getLongitude()); - return getTransportStops(x, y, false, new ArrayList()); - } - - public List getTransportStops(int x, int y, boolean change, List res) throws IOException { - return loadNativeTransportStops(x, y, change, res); - } - - private List loadNativeTransportStops(int sx, int sy, boolean change, List 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 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 loadTile(int x, int y) throws IOException { - long nanoTime = System.nanoTime(); - List lst = new ArrayList(); - int pz = (31 - cfg.ZOOM_TO_LOAD_TILES); - SearchRequest sr = BinaryMapIndexReader.buildSearchTransportRequest(x << pz, (x + 1) << pz, - y << pz, (y + 1) << pz, -1, null); - Collection stops = transportStopsReader.readMergedTransportStops(sr); - loadTransportSegments(stops, lst); - readTime += System.nanoTime() - nanoTime; - return lst; - } - - private void loadTransportSegments(Collection stops, List 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 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) ); } } - + public static List convertToTransportRoutingResult(NativeTransportRoutingResult[] res, TransportRoutingConfiguration cfg) { // cache for converted TransportRoutes: diff --git a/OsmAnd-java/src/main/java/net/osmand/router/TransportRouteResult.java b/OsmAnd-java/src/main/java/net/osmand/router/TransportRouteResult.java new file mode 100644 index 0000000000..9b45637970 --- /dev/null +++ b/OsmAnd-java/src/main/java/net/osmand/router/TransportRouteResult.java @@ -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 segments = new ArrayList(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 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(); + } +} diff --git a/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutingContext.java b/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutingContext.java new file mode 100644 index 0000000000..94039af146 --- /dev/null +++ b/OsmAnd-java/src/main/java/net/osmand/router/TransportRoutingContext.java @@ -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 visitedSegments = new TLongObjectHashMap(); + public TransportRoutingConfiguration cfg; + public TLongObjectHashMap combinedRoutesCache = new TLongObjectHashMap(); + public Map> missingStopsCache = new HashMap>(); + + public TLongObjectHashMap> 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>(); + this.library = library; + transportStopsReader = new TransportStopsRouteReader(Arrays.asList(readers)); + } + + public List getTransportStops(LatLon loc) throws IOException { + int y = MapUtils.get31TileNumberY(loc.getLatitude()); + int x = MapUtils.get31TileNumberX(loc.getLongitude()); + return getTransportStops(x, y, false, new ArrayList()); + } + + public List getTransportStops(int x, int y, boolean change, List res) throws IOException { + return loadNativeTransportStops(x, y, change, res); + } + + private List loadNativeTransportStops(int sx, int sy, boolean change, List 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 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 loadTile(int x, int y) throws IOException { + long nanoTime = System.nanoTime(); + List lst = new ArrayList(); + int pz = (31 - cfg.ZOOM_TO_LOAD_TILES); + BinaryMapIndexReader.SearchRequest sr = BinaryMapIndexReader.buildSearchTransportRequest(x << pz, (x + 1) << pz, + y << pz, (y + 1) << pz, -1, null); + Collection stops = transportStopsReader.readMergedTransportStops(sr); + loadTransportSegments(stops, lst); + readTime += System.nanoTime() - nanoTime; + return lst; + } + + private void loadTransportSegments(Collection stops, List 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 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); + } + } + } + } +} diff --git a/OsmAnd/src/net/osmand/aidl/ConnectedApp.java b/OsmAnd/src/net/osmand/aidl/ConnectedApp.java index 753a5972d5..7b4c0d1e79 100644 --- a/OsmAnd/src/net/osmand/aidl/ConnectedApp.java +++ b/OsmAnd/src/net/osmand/aidl/ConnectedApp.java @@ -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; diff --git a/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java b/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java index e1a97413ac..3f8ccb9447 100644 --- a/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java +++ b/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java index 50868c95a2..87fb588e9d 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java b/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java index 360c13e781..57c499407f 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivityLayers.java b/OsmAnd/src/net/osmand/plus/activities/MapActivityLayers.java index 4c43d58a75..64f3ad8b9f 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivityLayers.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivityLayers.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/audionotes/AudioNotesLayer.java b/OsmAnd/src/net/osmand/plus/audionotes/AudioNotesLayer.java index b5f0aed71d..20b9ac2d93 100644 --- a/OsmAnd/src/net/osmand/plus/audionotes/AudioNotesLayer.java +++ b/OsmAnd/src/net/osmand/plus/audionotes/AudioNotesLayer.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java index a96b9b7134..b62e1152af 100644 --- a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java +++ b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/base/ContextMenuScrollFragment.java b/OsmAnd/src/net/osmand/plus/base/ContextMenuScrollFragment.java index 6393c76cf5..4282a93ed7 100644 --- a/OsmAnd/src/net/osmand/plus/base/ContextMenuScrollFragment.java +++ b/OsmAnd/src/net/osmand/plus/base/ContextMenuScrollFragment.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/dashboard/DashboardOnMap.java b/OsmAnd/src/net/osmand/plus/dashboard/DashboardOnMap.java index ff0051a451..25683f23ce 100644 --- a/OsmAnd/src/net/osmand/plus/dashboard/DashboardOnMap.java +++ b/OsmAnd/src/net/osmand/plus/dashboard/DashboardOnMap.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/development/OsmandDevelopmentPlugin.java b/OsmAnd/src/net/osmand/plus/development/OsmandDevelopmentPlugin.java index 2f0eee097e..cbd2fcba13 100644 --- a/OsmAnd/src/net/osmand/plus/development/OsmandDevelopmentPlugin.java +++ b/OsmAnd/src/net/osmand/plus/development/OsmandDevelopmentPlugin.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/helpers/AvoidSpecificRoads.java b/OsmAnd/src/net/osmand/plus/helpers/AvoidSpecificRoads.java index ddf15618b5..ccd166818a 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/AvoidSpecificRoads.java +++ b/OsmAnd/src/net/osmand/plus/helpers/AvoidSpecificRoads.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenu.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenu.java index e28559d055..bfc8838ef0 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenu.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenu.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenuFragment.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenuFragment.java index 194f07552a..4abe2f71df 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenuFragment.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenuFragment.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/MenuBuilder.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/MenuBuilder.java index fd025205e6..5d3a560ddc 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/MenuBuilder.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/MenuBuilder.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/MenuController.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/MenuController.java index d31d538677..23f9393f22 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/MenuController.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/MenuController.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/AmenityMenuBuilder.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/AmenityMenuBuilder.java index d627b4e6e6..fa1d0e7e6e 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/AmenityMenuBuilder.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/AmenityMenuBuilder.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/WptPtMenuBuilder.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/WptPtMenuBuilder.java index 341ef3ecfe..2a4912f232 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/WptPtMenuBuilder.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/WptPtMenuBuilder.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/MapDataMenuController.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/MapDataMenuController.java index 9bfde65368..272975b987 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/MapDataMenuController.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/MapDataMenuController.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/TransportRouteController.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/TransportRouteController.java index 7d9fc87d32..44465be5e7 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/TransportRouteController.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/TransportRouteController.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/MapMultiSelectionMenu.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/MapMultiSelectionMenu.java index 1e54f3bd88..dc523b9ee2 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/MapMultiSelectionMenu.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/MapMultiSelectionMenu.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/TrackDetailsMenu.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/TrackDetailsMenu.java index dd8e64825c..f9d8c11b9b 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/TrackDetailsMenu.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/TrackDetailsMenu.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/mapillary/MapillaryPlugin.java b/OsmAnd/src/net/osmand/plus/mapillary/MapillaryPlugin.java index 879e3e0184..4c95326e00 100644 --- a/OsmAnd/src/net/osmand/plus/mapillary/MapillaryPlugin.java +++ b/OsmAnd/src/net/osmand/plus/mapillary/MapillaryPlugin.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/mapillary/MapillaryVectorLayer.java b/OsmAnd/src/net/osmand/plus/mapillary/MapillaryVectorLayer.java index 3c2f4d8c33..512281a079 100644 --- a/OsmAnd/src/net/osmand/plus/mapillary/MapillaryVectorLayer.java +++ b/OsmAnd/src/net/osmand/plus/mapillary/MapillaryVectorLayer.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/mapmarkers/PlanRouteFragment.java b/OsmAnd/src/net/osmand/plus/mapmarkers/PlanRouteFragment.java index 8cdee993c1..89a1016ea0 100644 --- a/OsmAnd/src/net/osmand/plus/mapmarkers/PlanRouteFragment.java +++ b/OsmAnd/src/net/osmand/plus/mapmarkers/PlanRouteFragment.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java index ffd26ec83d..2e42b16db2 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolLayer.java b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolLayer.java index 0d1666af86..3533da5ea4 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolLayer.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolLayer.java @@ -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); } diff --git a/OsmAnd/src/net/osmand/plus/monitoring/OsmandMonitoringPlugin.java b/OsmAnd/src/net/osmand/plus/monitoring/OsmandMonitoringPlugin.java index 22e18c0262..24785b9dbd 100644 --- a/OsmAnd/src/net/osmand/plus/monitoring/OsmandMonitoringPlugin.java +++ b/OsmAnd/src/net/osmand/plus/monitoring/OsmandMonitoringPlugin.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/osmedit/OsmBugsLayer.java b/OsmAnd/src/net/osmand/plus/osmedit/OsmBugsLayer.java index 5c76f40431..8c3b9be15f 100644 --- a/OsmAnd/src/net/osmand/plus/osmedit/OsmBugsLayer.java +++ b/OsmAnd/src/net/osmand/plus/osmedit/OsmBugsLayer.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/osmedit/OsmEditsLayer.java b/OsmAnd/src/net/osmand/plus/osmedit/OsmEditsLayer.java index d67ae86b83..99947c7de2 100644 --- a/OsmAnd/src/net/osmand/plus/osmedit/OsmEditsLayer.java +++ b/OsmAnd/src/net/osmand/plus/osmedit/OsmEditsLayer.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/parkingpoint/ParkingPositionPlugin.java b/OsmAnd/src/net/osmand/plus/parkingpoint/ParkingPositionPlugin.java index b910ca75e0..8bcd323f3e 100644 --- a/OsmAnd/src/net/osmand/plus/parkingpoint/ParkingPositionPlugin.java +++ b/OsmAnd/src/net/osmand/plus/parkingpoint/ParkingPositionPlugin.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/quickaction/QuickActionListFragment.java b/OsmAnd/src/net/osmand/plus/quickaction/QuickActionListFragment.java index b32addf136..3e21c21dca 100644 --- a/OsmAnd/src/net/osmand/plus/quickaction/QuickActionListFragment.java +++ b/OsmAnd/src/net/osmand/plus/quickaction/QuickActionListFragment.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/ChooseRouteFragment.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/ChooseRouteFragment.java index e9161ec96c..8e46e3aa03 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/ChooseRouteFragment.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/ChooseRouteFragment.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenu.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenu.java index f340b6caf6..c61e132794 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenu.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenu.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenuFragment.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenuFragment.java index e8b7100c0a..0f7f812f48 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenuFragment.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenuFragment.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/RouteDetailsFragment.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/RouteDetailsFragment.java index fced15daa2..fcab4d6578 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/RouteDetailsFragment.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/RouteDetailsFragment.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/PublicTransportCard.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/PublicTransportCard.java index c75c522219..a47920b29c 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/PublicTransportCard.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/PublicTransportCard.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/routing/TransportRoutingHelper.java b/OsmAnd/src/net/osmand/plus/routing/TransportRoutingHelper.java index 1cd8547156..b883a56bd1 100644 --- a/OsmAnd/src/net/osmand/plus/routing/TransportRoutingHelper.java +++ b/OsmAnd/src/net/osmand/plus/routing/TransportRoutingHelper.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/views/AddGpxPointBottomSheetHelper.java b/OsmAnd/src/net/osmand/plus/views/AddGpxPointBottomSheetHelper.java index 1fba9d5544..b43cab4492 100644 --- a/OsmAnd/src/net/osmand/plus/views/AddGpxPointBottomSheetHelper.java +++ b/OsmAnd/src/net/osmand/plus/views/AddGpxPointBottomSheetHelper.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/views/MoveMarkerBottomSheetHelper.java b/OsmAnd/src/net/osmand/plus/views/MoveMarkerBottomSheetHelper.java index 4e8c0b498d..7dff5a7e2a 100644 --- a/OsmAnd/src/net/osmand/plus/views/MoveMarkerBottomSheetHelper.java +++ b/OsmAnd/src/net/osmand/plus/views/MoveMarkerBottomSheetHelper.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/views/OsmandMapLayer.java b/OsmAnd/src/net/osmand/plus/views/OsmandMapLayer.java index 462927e0cd..2b00b9f498 100644 --- a/OsmAnd/src/net/osmand/plus/views/OsmandMapLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/OsmandMapLayer.java @@ -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 xs, List ys, Path path) { - List> 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, Bitmap> stopBitmapsCache = new HashMap<>(); - private Map 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 tx, List ty, List angles, List distances, - double dist, List 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 xs, List ys, List styles, List> 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 initBoundIntersections(RotatedTileBox tileBox) { + public static QuadTree 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 boundIntersections, float x, float y, float width, float height) { + public static boolean intersects(QuadTree boundIntersections, float x, float y, float width, float height) { List 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; diff --git a/OsmAnd/src/net/osmand/plus/views/OsmandMapTileView.java b/OsmAnd/src/net/osmand/plus/views/OsmandMapTileView.java index c6576a1027..260f30fa50 100644 --- a/OsmAnd/src/net/osmand/plus/views/OsmandMapTileView.java +++ b/OsmAnd/src/net/osmand/plus/views/OsmandMapTileView.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/views/Renderable.java b/OsmAnd/src/net/osmand/plus/views/Renderable.java index e8bd0e75be..ec8d302dab 100644 --- a/OsmAnd/src/net/osmand/plus/views/Renderable.java +++ b/OsmAnd/src/net/osmand/plus/views/Renderable.java @@ -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 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 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 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 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()); } } diff --git a/OsmAnd/src/net/osmand/plus/views/RouteLayer.java b/OsmAnd/src/net/osmand/plus/views/RouteLayer.java deleted file mode 100644 index 299d9d01a1..0000000000 --- a/OsmAnd/src/net/osmand/plus/views/RouteLayer.java +++ /dev/null @@ -1,1393 +0,0 @@ -package net.osmand.plus.views; - -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.Drawable; -import android.graphics.drawable.LayerDrawable; -import android.util.Pair; - -import androidx.annotation.ColorInt; -import androidx.annotation.Nullable; -import androidx.appcompat.content.res.AppCompatResources; -import androidx.core.content.ContextCompat; - -import net.osmand.AndroidUtils; -import net.osmand.Location; -import net.osmand.PlatformUtil; -import net.osmand.data.LatLon; -import net.osmand.data.PointDescription; -import net.osmand.data.QuadRect; -import net.osmand.data.QuadTree; -import net.osmand.data.RotatedTileBox; -import net.osmand.data.TransportRoute; -import net.osmand.data.TransportStop; -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.mapcontextmenu.other.TrackChartPoints; -import net.osmand.plus.profiles.LocationIcon; -import net.osmand.plus.render.RenderingIcons; -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.transport.TransportStopRoute; -import net.osmand.plus.transport.TransportStopType; -import net.osmand.router.TransportRoutePlanner; -import net.osmand.router.TransportRoutePlanner.TransportRouteResult; -import net.osmand.router.TransportRoutePlanner.TransportRouteResultSegment; -import net.osmand.util.MapUtils; - -import org.apache.commons.logging.Log; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; - -import gnu.trove.list.array.TByteArrayList; - -public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.IContextMenuProvider { - - private static final float EPSILON_IN_DPI = 2; - - private OsmandMapTileView view; - - private final RoutingHelper helper; - private final TransportRoutingHelper transportHelper; - // keep array lists created - private List actionPoints = new ArrayList(); - private List routeTransportStops = new ArrayList<>(); - private double[] lastProjectionOnPathPoint; - - // cache - private Bitmap actionArrow; - - private Paint paintIconAction; - private Paint paintGridOuterCircle; - private Paint paintGridCircle; - - private Paint paintIconSelected; - private LayerDrawable selectedPoint; - private TrackChartPoints trackChartPoints; - - private RenderingLineAttributes attrs; - private RenderingLineAttributes attrsPT; - private RenderingLineAttributes attrsW; - private boolean nightMode; - - private GeometryWayContext wayContext; - - private LayerDrawable projectionIcon; - private final static Log log = PlatformUtil.getLog(RouteLayer.class); - public RouteLayer(RoutingHelper helper) { - this.helper = helper; - this.transportHelper = helper.getTransportRoutingHelper(); - } - - 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); - - wayContext = new GeometryWayContext(view.getContext(), density); - - paintIconSelected = new Paint(); - 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)); - wayContext.updatePaints(nightMode, attrs, attrsPT, attrsW); - } - } - - private void drawXAxisPoints(Canvas canvas, RotatedTileBox tileBox) { - QuadRect latLonBounds = tileBox.getLatLonBounds(); - List 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 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(); - } - - private void cullRamerDouglasPeucker(TByteArrayList survivor, List points, - int start, int end, double epsillon) { - double dmax = Double.NEGATIVE_INFINITY; - int index = -1; - Location startPt = points.get(start); - Location endPt = points.get(end); - - for (int i = start + 1; i < end; i++) { - Location pt = points.get(i); - double d = MapUtils.getOrthogonalDistance(pt.getLatitude(), pt.getLongitude(), - startPt.getLatitude(), startPt.getLongitude(), endPt.getLatitude(), endPt.getLongitude()); - if (d > dmax) { - dmax = d; - index = i; - } - } - if (dmax > epsillon) { - cullRamerDouglasPeucker(survivor, points, start, index, epsillon); - cullRamerDouglasPeucker(survivor, points, index, end, epsillon); - } else { - survivor.set(end, (byte) 1); - } - } - - private static class PathAnchor extends PathPoint { - PathAnchor(float x, float y, GeometryAnchorWayStyle style) { - super(x, y, 0, style); - } - } - - private 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); - } - } - - private static class GeometryWalkWayStyle extends GeometryWayStyle { - - GeometryWalkWayStyle(GeometryWayContext 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 isWalkLine() { - return true; - } - - @Override - public boolean hasPaintedPointBitmap() { - return true; - } - } - - private static class GeometryAnchorWayStyle extends GeometryWayStyle { - - GeometryAnchorWayStyle(GeometryWayContext 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; - } - } - - private static class GeometrySolidWayStyle extends GeometryWayStyle { - - GeometrySolidWayStyle(GeometryWayContext 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; - } - } - - private static class TransportStopsWayStyle extends GeometryTransportWayStyle { - TransportStopsWayStyle(GeometryWayContext 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); - } - } - - private static class GeometryTransportWayStyle extends GeometryWayStyle { - - private TransportRouteResultSegment segment; - private Drawable stopDrawable; - protected Integer pointColor; - - GeometryTransportWayStyle(GeometryWayContext 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 boolean hasAnchors() { - return true; - } - - @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 isTransportLine() { - return true; - } - - @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(); - } - } - - private void drawArrowsOverPath(Canvas canvas, RotatedTileBox tb, List tx, List ty, - List angles, List distances, double distPixToFinish, List styles) { - 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 = wayContext.getArrowBitmap(); - int arrowHeight = arrow.getHeight(); - double pxStep = arrowHeight * 4f * zoomCoef; - double dist = 0; - if (distPixToFinish != 0) { - dist = distPixToFinish - pxStep * ((int) (distPixToFinish / pxStep)); // dist < 1 - } - - List arrows = new ArrayList<>(); - List anchors = new ArrayList<>(); - List stops = new ArrayList<>(); - Set transportWaysStyles = new HashSet<>(); - - GeometryAnchorWayStyle anchorWayStyle = new GeometryAnchorWayStyle(wayContext); - - GeometryWalkWayStyle walkWayStyle = new GeometryWalkWayStyle(wayContext); - Bitmap walkArrow = walkWayStyle.getPointBitmap(); - int walkArrowHeight = walkArrow.getHeight(); - double pxStepWalk = walkArrowHeight * 1.2f * zoomCoef; - double pxStepRegular = arrowHeight * 4f * zoomCoef; - - 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; - } - if (style != null && style.isWalkLine()) { - pxStep = pxStepWalk; - } else { - pxStep = pxStepRegular; - } - if (style != null && !style.equals(prevStyle) && (prevStyle != null || style.hasAnchors())) { - prevStyle = style; - anchors.add(new PathAnchor(x, y, anchorWayStyle)); - dist = 0; - } - if (style != null && style.isTransportLine()) { - 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 (isIn(iconx, icony, left, top, right, bottom)) { - arrows.add(new PathPoint(iconx, icony, angle, style)); - } - dist -= pxStep; - percent -= pxStep / distSegment; - } - } - List routeTransportStops = new ArrayList<>(); - for (GeometryTransportWayStyle style : transportWaysStyles) { - List 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 (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.isWalkLine()) { - a.draw(canvas, wayContext); - } - } - for (int i = anchors.size() - 1; i >= 0; i--) { - PathAnchor anchor = anchors.get(i); - anchor.draw(canvas, wayContext); - } - if (stops.size() > 0) { - QuadTree boundIntersections = initBoundIntersections(tb); - List 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 (intersects(boundIntersections, x, y, iconSize, iconSize)) { - stop.setSmallPoint(true); - stop.draw(canvas, wayContext); - } else { - stop.setSmallPoint(false); - fullObjects.add(stop); - } - } - for (PathTransportStop stop : fullObjects) { - stop.draw(canvas, wayContext); - } - } - } - - private static class RouteGeometryZoom { - final TByteArrayList simplifyPoints; - List distances; - List angles; - - public RouteGeometryZoom(List locations, RotatedTileBox tb) { - // this.locations = locations; - tb = new RotatedTileBox(tb); - tb.setZoomAndAnimation(tb.getZoom(), 0, tb.getZoomFloatPart()); - simplifyPoints = new TByteArrayList(locations.size()); - distances = new ArrayList<>(locations.size()); - angles = new ArrayList<>(locations.size()); - simplifyPoints.fill(0, locations.size(), (byte)0); - if(locations.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, locations, 0, locations.size() - 1, cullDistance); - - int previousIndex = -1; - for(int i = 0; i < locations.size(); i++) { - double d = 0; - double angle = 0; - if(simplifyPoints.get(i) > 0) { - if(previousIndex > -1) { - Location loc = locations.get(i); - Location pr = locations.get(previousIndex); - float x = tb.getPixXFromLatLon(loc.getLatitude(), loc.getLongitude()); - float y = tb.getPixYFromLatLon(loc.getLatitude(), loc.getLongitude()); - float px = tb.getPixXFromLatLon(pr.getLatitude(), pr.getLongitude()); - float py = tb.getPixYFromLatLon(pr.getLatitude(), pr.getLongitude()); - 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 getDistances() { - return distances; - } - - private void cullRamerDouglasPeucker(TByteArrayList survivor, List points, - int start, int end, double epsillon) { - double dmax = Double.NEGATIVE_INFINITY; - int index = -1; - Location startPt = points.get(start); - Location endPt = points.get(end); - - for (int i = start + 1; i < end; i++) { - Location pt = points.get(i); - double d = MapUtils.getOrthogonalDistance(pt.getLatitude(), pt.getLongitude(), - startPt.getLatitude(), startPt.getLongitude(), endPt.getLatitude(), endPt.getLongitude()); - if (d > dmax) { - dmax = d; - index = i; - } - } - if (dmax > epsillon) { - cullRamerDouglasPeucker(survivor, points, start, index, epsillon); - cullRamerDouglasPeucker(survivor, points, index, end, epsillon); - } else { - survivor.set(end, (byte) 1); - } - } - - public TByteArrayList getSimplifyPoints() { - return simplifyPoints; - } - } - - private class RouteSimplificationGeometry { - RouteCalculationResult route; - TransportRouteResult transportRoute; - double mapDensity; - TreeMap zooms = new TreeMap<>(); - List locations = Collections.emptyList(); - Map styleMap = Collections.emptyMap(); - - // cache arrays - List tx = new ArrayList<>(); - List ty = new ArrayList<>(); - List angles = new ArrayList<>(); - List distances = new ArrayList<>(); - List styles = new ArrayList<>(); - - private GeometryWayStyle getStyle(int index, GeometryWayStyle defaultWayStyle) { - List 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; - } - - private boolean isTransportRoute() { - return transportRoute != null; - } - - public void updateRoute(RotatedTileBox tb, RouteCalculationResult route) { - if(tb.getMapDensity() != mapDensity || this.route != route) { - this.route = route; - if (route == null) { - locations = Collections.emptyList(); - } else { - locations = route.getImmutableAllLocations(); - } - styleMap = Collections.emptyMap(); - this.mapDensity = tb.getMapDensity(); - zooms.clear(); - } - } - - public void clearRoute() { - if (route != null) { - route = null; - locations = Collections.emptyList(); - styleMap = Collections.emptyMap(); - zooms.clear(); - } - } - - public void updateTransportRoute(RotatedTileBox tb, TransportRouteResult route) { - if (tb.getMapDensity() != mapDensity || this.transportRoute != route) { - this.transportRoute = route; - if (route == null) { - locations = Collections.emptyList(); - styleMap = Collections.emptyMap(); - } else { - LatLon start = transportHelper.getStartLocation(); - LatLon end = transportHelper.getEndLocation(); - List list = new ArrayList<>(); - List styles = new ArrayList<>(); - calculateTransportResult(start, end, route, list, styles); - List locs = new ArrayList<>(); - Map 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; - } - this.mapDensity = tb.getMapDensity(); - zooms.clear(); - } - } - - public void clearTransportRoute() { - if (transportRoute != null) { - transportRoute = null; - locations = Collections.emptyList(); - styleMap = Collections.emptyMap(); - zooms.clear(); - } - } - - private RouteGeometryZoom getGeometryZoom(RotatedTileBox tb) { - int zoom = tb.getZoom(); - RouteGeometryZoom zm = zooms.size() > zoom ? zooms.get(zoom) : null; - if (zm == null) { - zm = new RouteGeometryZoom(locations, tb); - zooms.put(zoom, zm); - } - return zm; - } - - private void drawSegments(RotatedTileBox tb, Canvas canvas, double topLatitude, double leftLongitude, - double bottomLatitude, double rightLongitude, Location lastProjection, int currentRoute) { - if (locations.size() == 0) { - return; - } - RouteGeometryZoom geometryZoom = getGeometryZoom(tb); - TByteArrayList simplification = geometryZoom.getSimplifyPoints(); - List odistances = geometryZoom.getDistances(); - - clearArrays(); - GeometryWayStyle defaultWayStyle = isTransportRoute() ? - new GeometryWalkWayStyle(wayContext) : - new GeometrySolidWayStyle(wayContext, attrs.paint.getColor()); - GeometryWayStyle style = defaultWayStyle; - boolean previousVisible = false; - - Location lastPoint = lastProjection; - if (lastPoint != null) { - previousVisible = addPoint(tb, topLatitude, leftLongitude, bottomLatitude, rightLongitude, style, previousVisible, lastPoint); - } - Location sp = helper.getRoute().getCurrentStraightAnglePoint(); - if (sp != null) { - lastPoint = sp; - previousVisible = addPoint(tb, topLatitude, leftLongitude, bottomLatitude, rightLongitude, style, previousVisible, sp); - } - List routeNodes = locations; - int previous = -1; - for (int i = currentRoute; i < routeNodes.size(); i++) { - Location ls = routeNodes.get(i); - style = getStyle(i, defaultWayStyle); - if (leftLongitude <= ls.getLongitude() && ls.getLongitude() <= rightLongitude && bottomLatitude <= ls.getLatitude() - && ls.getLatitude() <= topLatitude) { - double dist = 0; - if (!previousVisible) { - Location lt = null; - if (previous != -1) { - lt = routeNodes.get(previous); - dist = odistances.get(i); - } else if (lastProjection != null) { - lt = lastProjection; - } - if (lt != null) { - addLocation(tb, lt.getLatitude(), lt.getLongitude(), style, tx, ty, angles, distances, 0, styles); // first point - } - } - addLocation(tb, ls.getLatitude(), ls.getLongitude(), style, tx, ty, angles, distances, dist, styles); - previousVisible = true; - } else if (previousVisible) { - addLocation(tb, ls.getLatitude(), ls.getLongitude(), 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 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(); - } - } - - private RouteSimplificationGeometry routeGeometry = new RouteSimplificationGeometry(); - - private void drawRouteSegment(RotatedTileBox tb, Canvas canvas, List tx, List ty, - List angles, List distances, double distToFinish, List styles) { - if (tx.size() < 2) { - return; - } - try { - List> paths = new ArrayList<>(); - canvas.rotate(-tb.getRotate(), tb.getCenterPixelX(), tb.getCenterPixelY()); - calculatePath(tb, tx, ty, styles, paths); - for (Pair pc : paths) { - GeometryWayStyle style = pc.second; - if (style.hasPathLine()) { - if (style.isTransportLine()) { - attrsPT.customColor = style.getStrokeColor(); - attrsPT.customColorPaint.setStrokeWidth(attrsPT.paint2.getStrokeWidth()); - attrsPT.drawPath(canvas, pc.first); - attrsPT.customColorPaint.setStrokeWidth(attrsPT.paint.getStrokeWidth()); - attrsPT.customColor = style.getColor(); - attrsPT.drawPath(canvas, pc.first); - } else { - attrs.customColor = style.getColor(); - attrs.drawPath(canvas, pc.first); - } - } - } - attrs.customColor = 0; - attrsPT.customColor = 0; - drawArrowsOverPath(canvas, tb, tx, ty, angles, distances, distToFinish, styles); - } finally { - canvas.rotate(tb.getRotate(), tb.getCenterPixelX(), tb.getCenterPixelY()); - } - } - - public void drawLocations(RotatedTileBox tb, Canvas canvas, double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude) { - if (helper.isPublicTransportMode()) { - int currentRoute = transportHelper.getCurrentRoute(); - List routes = transportHelper.getRoutes(); - TransportRouteResult route = routes != null && routes.size() > currentRoute ? routes.get(currentRoute) : null; - routeGeometry.clearRoute(); - routeGeometry.updateTransportRoute(tb, route); - if (route != null) { - LatLon start = transportHelper.getStartLocation(); - Location startLocation = new Location("transport"); - startLocation.setLatitude(start.getLatitude()); - startLocation.setLongitude(start.getLongitude()); - routeGeometry.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; - routeGeometry.clearTransportRoute(); - routeGeometry.updateRoute(tb, route); - RouteProvider.RouteService rs = helper.getRoute().getRouteService(); - 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 rd = helper.getRouteDirections(); - Iterator it = rd.iterator(); - if (!directTo && tb.getZoom() >= 14) { - List 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 routeNodes, RoutingHelper helper, RotatedTileBox box) { - double[] projectionXY = null; - Location ll = helper.getLastFixedLocation(); - RouteCalculationResult route = helper.getRoute(); - List 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 calculateActionPoints(double topLatitude, double leftLongitude, double bottomLatitude, - double rightLongitude, Location lastProjection, List routeNodes, int cd, - Iterator 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 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 actionPoints, Location lastProjection, List 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; - } - - - public RoutingHelper getHelper() { - return helper; - } - - - - @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 void calculateTransportResult(LatLon start, LatLon end, TransportRouteResult r, List res, List 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 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 res, List styles) { - final RouteCalculationResult walkingRouteSegment = transportHelper.getWalkingRouteSegment(s1, s2); - if (walkingRouteSegment != null && walkingRouteSegment.getRouteLocations().size() > 0) { - final List 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 geometry, List styles) { - GeometryWayStyle style; - Way w = geometry.get(0); - if (segment == null || segment.route == null) { - style = new GeometryWalkWayStyle(wayContext); - } else if (w.getId() == TransportRoutePlanner.GEOMETRY_WAY_ID) { - style = new GeometryTransportWayStyle(wayContext, segment); - } else { - style = new TransportStopsWayStyle(wayContext, segment); - } - for (int i = 0; i < geometry.size(); i++) { - styles.add(style); - } - } - - 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()); - } - - private void getFromPoint(RotatedTileBox tb, PointF point, List res) { - 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 res, boolean unknownLocation) { - if (routeTransportStops.size() > 0) { - getFromPoint(tileBox, point, res); - } - } - - @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; - } -} diff --git a/OsmAnd/src/net/osmand/plus/views/AidlMapLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/AidlMapLayer.java similarity index 98% rename from OsmAnd/src/net/osmand/plus/views/AidlMapLayer.java rename to OsmAnd/src/net/osmand/plus/views/layers/AidlMapLayer.java index 248c911834..d1c32ee4a9 100644 --- a/OsmAnd/src/net/osmand/plus/views/AidlMapLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/AidlMapLayer.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/views/ContextMenuLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/ContextMenuLayer.java similarity index 99% rename from OsmAnd/src/net/osmand/plus/views/ContextMenuLayer.java rename to OsmAnd/src/net/osmand/plus/views/layers/ContextMenuLayer.java index 848ad6ec3e..ad8f68ee83 100644 --- a/OsmAnd/src/net/osmand/plus/views/ContextMenuLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/ContextMenuLayer.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/views/DownloadedRegionsLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/DownloadedRegionsLayer.java similarity index 97% rename from OsmAnd/src/net/osmand/plus/views/DownloadedRegionsLayer.java rename to OsmAnd/src/net/osmand/plus/views/layers/DownloadedRegionsLayer.java index b3b5528c2e..cd6ba10dd4 100644 --- a/OsmAnd/src/net/osmand/plus/views/DownloadedRegionsLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/DownloadedRegionsLayer.java @@ -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 currentObjects = new LinkedList<>(); - if (data.results != null) { - currentObjects.addAll(data.results); + if (data.getResults() != null) { + currentObjects.addAll(data.getResults()); } final List 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 currentObjects = data.results; + final List 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 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 result = new LinkedList<>(data.results); + List result = new LinkedList<>(data.getResults()); Iterator it = result.iterator(); while (it.hasNext()) { BinaryMapDataObject o = it.next(); diff --git a/OsmAnd/src/net/osmand/plus/views/FavouritesLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/FavouritesLayer.java similarity index 97% rename from OsmAnd/src/net/osmand/plus/views/FavouritesLayer.java rename to OsmAnd/src/net/osmand/plus/views/layers/FavouritesLayer.java index 2426dc213a..d0a1b91d8b 100644 --- a/OsmAnd/src/net/osmand/plus/views/FavouritesLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/FavouritesLayer.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/views/GPXLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java similarity index 86% rename from OsmAnd/src/net/osmand/plus/views/GPXLayer.java rename to OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java index 108c646776..2f7cf6cb5f 100644 --- a/OsmAnd/src/net/osmand/plus/views/GPXLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java @@ -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 tx = new ArrayList<>(); - List ty = new ArrayList<>(); - List distances = new ArrayList<>(); - List angles = new ArrayList<>(); - List styles = new ArrayList<>(); - boolean previousVisible = false; - - List 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 tx, List ty, List angles, List 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 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 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) { diff --git a/OsmAnd/src/net/osmand/plus/views/ImpassableRoadsLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/ImpassableRoadsLayer.java similarity index 96% rename from OsmAnd/src/net/osmand/plus/views/ImpassableRoadsLayer.java rename to OsmAnd/src/net/osmand/plus/views/layers/ImpassableRoadsLayer.java index e2e715a029..98a6bcb0dd 100644 --- a/OsmAnd/src/net/osmand/plus/views/ImpassableRoadsLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/ImpassableRoadsLayer.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/views/MapControlsLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java similarity index 99% rename from OsmAnd/src/net/osmand/plus/views/MapControlsLayer.java rename to OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java index 3d3cd27f2a..4300046334 100644 --- a/OsmAnd/src/net/osmand/plus/views/MapControlsLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/views/MapInfoLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/MapInfoLayer.java similarity index 99% rename from OsmAnd/src/net/osmand/plus/views/MapInfoLayer.java rename to OsmAnd/src/net/osmand/plus/views/layers/MapInfoLayer.java index 9a4ad3b74c..2189c2b26e 100644 --- a/OsmAnd/src/net/osmand/plus/views/MapInfoLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/MapInfoLayer.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/views/MapMarkersLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/MapMarkersLayer.java similarity index 97% rename from OsmAnd/src/net/osmand/plus/views/MapMarkersLayer.java rename to OsmAnd/src/net/osmand/plus/views/layers/MapMarkersLayer.java index afb6d3fc19..244a9efc17 100644 --- a/OsmAnd/src/net/osmand/plus/views/MapMarkersLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/MapMarkersLayer.java @@ -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); diff --git a/OsmAnd/src/net/osmand/plus/views/MapQuickActionLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/MapQuickActionLayer.java similarity index 99% rename from OsmAnd/src/net/osmand/plus/views/MapQuickActionLayer.java rename to OsmAnd/src/net/osmand/plus/views/layers/MapQuickActionLayer.java index 97e9f64f2d..5527a32989 100644 --- a/OsmAnd/src/net/osmand/plus/views/MapQuickActionLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/MapQuickActionLayer.java @@ -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. diff --git a/OsmAnd/src/net/osmand/plus/views/MapTextLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/MapTextLayer.java similarity index 97% rename from OsmAnd/src/net/osmand/plus/views/MapTextLayer.java rename to OsmAnd/src/net/osmand/plus/views/layers/MapTextLayer.java index 9f0c1ea45a..991bed0d55 100644 --- a/OsmAnd/src/net/osmand/plus/views/MapTextLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/MapTextLayer.java @@ -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 { diff --git a/OsmAnd/src/net/osmand/plus/views/POIMapLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/POIMapLayer.java similarity index 98% rename from OsmAnd/src/net/osmand/plus/views/POIMapLayer.java rename to OsmAnd/src/net/osmand/plus/views/layers/POIMapLayer.java index 18b23fd8e8..ed0f2cc45d 100644 --- a/OsmAnd/src/net/osmand/plus/views/POIMapLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/POIMapLayer.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/views/PointLocationLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/PointLocationLayer.java similarity index 98% rename from OsmAnd/src/net/osmand/plus/views/PointLocationLayer.java rename to OsmAnd/src/net/osmand/plus/views/layers/PointLocationLayer.java index 44e092b5e5..21dbbcc7bd 100644 --- a/OsmAnd/src/net/osmand/plus/views/PointLocationLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/PointLocationLayer.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/views/PointNavigationLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/PointNavigationLayer.java similarity index 97% rename from OsmAnd/src/net/osmand/plus/views/PointNavigationLayer.java rename to OsmAnd/src/net/osmand/plus/views/layers/PointNavigationLayer.java index 87a48227d1..e4398eab33 100644 --- a/OsmAnd/src/net/osmand/plus/views/PointNavigationLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/PointNavigationLayer.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/views/layers/RouteLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/RouteLayer.java new file mode 100644 index 0000000000..8491f24a67 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/views/layers/RouteLayer.java @@ -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 actionPoints = new ArrayList(); + + // 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 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 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 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 rd = helper.getRouteDirections(); + Iterator it = rd.iterator(); + if (!directTo && tb.getZoom() >= 14) { + List 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 routeNodes, RoutingHelper helper, RotatedTileBox box) { + double[] projectionXY = null; + Location ll = helper.getLastFixedLocation(); + RouteCalculationResult route = helper.getRoute(); + List 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 calculateActionPoints(double topLatitude, double leftLongitude, double bottomLatitude, + double rightLongitude, Location lastProjection, List routeNodes, int cd, + Iterator 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 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 actionPoints, Location lastProjection, List 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 getRouteTransportStops() { + return helper.isPublicTransportMode() ? publicTransportRouteGeometry.getDrawer().getRouteTransportStops() : null; + } + + private void getFromPoint(RotatedTileBox tb, PointF point, List res, @NonNull List 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 res, boolean unknownLocation) { + List 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; + } +} diff --git a/OsmAnd/src/net/osmand/plus/views/RulerControlLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/RulerControlLayer.java similarity index 99% rename from OsmAnd/src/net/osmand/plus/views/RulerControlLayer.java rename to OsmAnd/src/net/osmand/plus/views/layers/RulerControlLayer.java index 43131febe2..5bd719b11b 100644 --- a/OsmAnd/src/net/osmand/plus/views/RulerControlLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/RulerControlLayer.java @@ -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); diff --git a/OsmAnd/src/net/osmand/plus/views/TransportStopsLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/TransportStopsLayer.java similarity index 97% rename from OsmAnd/src/net/osmand/plus/views/TransportStopsLayer.java rename to OsmAnd/src/net/osmand/plus/views/layers/TransportStopsLayer.java index a5e1489ee1..ce20aa066e 100644 --- a/OsmAnd/src/net/osmand/plus/views/TransportStopsLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/TransportStopsLayer.java @@ -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); diff --git a/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWay.java b/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWay.java new file mode 100644 index 0000000000..02671e2207 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWay.java @@ -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> { + private double mapDensity; + private T context; + private D drawer; + private TreeMap zooms = new TreeMap<>(); + private GeometryWayProvider locationProvider; + private Map> styleMap = Collections.emptyMap(); + + // cache arrays + private List tx = new ArrayList<>(); + private List ty = new ArrayList<>(); + private List angles = new ArrayList<>(); + private List distances = new ArrayList<>(); + private List> styles = new ArrayList<>(); + + public interface GeometryWayProvider { + double getLatitude(int index); + + double getLongitude(int index); + + int getSize(); + } + + private static class GeometryWayLocationProvider implements GeometryWayProvider { + private List locations; + + public GeometryWayLocationProvider(@NonNull List 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 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> styleMap, @NonNull RotatedTileBox tb) { + this.locationProvider = locationProvider; + this.styleMap = styleMap == null ? Collections.>emptyMap() : styleMap; + this.mapDensity = tb.getMapDensity(); + this.zooms.clear(); + } + + public void updateWay(@NonNull List locations, @NonNull RotatedTileBox tb) { + updateWay(locations, null, tb); + } + + public void updateWay(@NonNull List locations, @Nullable Map> styleMap, @NonNull RotatedTileBox tb) { + this.locationProvider = new GeometryWayLocationProvider(locations); + this.styleMap = styleMap == null ? Collections.>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 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 tx, List ty, List angles, List distances, + double dist, List> 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 xs, List ys, Path path) { + List>> 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 xs, List ys, List> styles, List>> 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; + } + + private void drawRouteSegment(RotatedTileBox tb, Canvas canvas, List tx, List ty, + List angles, List distances, double distToFinish, List> styles) { + if (tx.size() < 2) { + return; + } + try { + boolean hasPathLine = false; + for (GeometryWayStyle style : styles) { + if (style.hasPathLine()) { + hasPathLine = true; + break; + } + } + if (hasPathLine) { + List>> paths = new ArrayList<>(); + canvas.rotate(-tb.getRotate(), tb.getCenterPixelX(), tb.getCenterPixelY()); + calculatePath(tb, tx, ty, styles, paths); + for (Pair> 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 distances; + private List 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 getDistances() { + return distances; + } + + public List 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; + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWayContext.java b/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWayContext.java new file mode 100644 index 0000000000..1b52aeb322 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWayContext.java @@ -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; + } + +} diff --git a/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWayDrawer.java b/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWayDrawer.java new file mode 100644 index 0000000000..db45a9b55c --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWayDrawer.java @@ -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 { + + private T context; + + public GeometryWayDrawer(T context) { + this.context = context; + } + + public T getContext() { + return context; + } + + public void drawArrowsOverPath(Canvas canvas, RotatedTileBox tb, List tx, List ty, + List angles, List distances, double distPixToFinish, List> styles) { + List 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()); + } + } + } + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWayStyle.java b/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWayStyle.java new file mode 100644 index 0000000000..27e37132db --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWayStyle.java @@ -0,0 +1,83 @@ +package net.osmand.plus.views.layers.geometry; + +import android.content.Context; +import android.graphics.Bitmap; + +public abstract class GeometryWayStyle { + + 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; + } +} diff --git a/OsmAnd/src/net/osmand/plus/views/layers/geometry/GpxGeometryWay.java b/OsmAnd/src/net/osmand/plus/views/layers/geometry/GpxGeometryWay.java new file mode 100644 index 0000000000..76bee7cdf2 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/views/layers/geometry/GpxGeometryWay.java @@ -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> { + + private List points; + + private float trackWidth; + private int trackColor; + private int arrowColor; + + private static class GeometryWayWptPtProvider implements GeometryWayProvider { + private List points; + + public GeometryWayWptPtProvider(@NonNull List 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 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 { + + 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; + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/views/layers/geometry/GpxGeometryWayContext.java b/OsmAnd/src/net/osmand/plus/views/layers/geometry/GpxGeometryWayContext.java new file mode 100644 index 0000000000..2a7fc21fed --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/views/layers/geometry/GpxGeometryWayContext.java @@ -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; + } +} diff --git a/OsmAnd/src/net/osmand/plus/views/layers/geometry/GpxGeometryWayDrawer.java b/OsmAnd/src/net/osmand/plus/views/layers/geometry/GpxGeometryWayDrawer.java new file mode 100644 index 0000000000..02c6ccedff --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/views/layers/geometry/GpxGeometryWayDrawer.java @@ -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 { + + 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); + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/views/layers/geometry/PublicTransportGeometryWay.java b/OsmAnd/src/net/osmand/plus/views/layers/geometry/PublicTransportGeometryWay.java new file mode 100644 index 0000000000..14186eef5a --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/views/layers/geometry/PublicTransportGeometryWay.java @@ -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 { + + private TransportRoutingHelper transportHelper; + private TransportRouteResult route; + + public PublicTransportGeometryWay(PublicTransportGeometryWayContext context) { + super(context, new PublicTransportGeometryWayDrawer(context)); + this.transportHelper = context.getApp().getTransportRoutingHelper(); + } + + @NonNull + @Override + public GeometryWayStyle getDefaultWayStyle() { + return new GeometryWalkWayStyle(getContext()); + } + + public void updateRoute(RotatedTileBox tb, TransportRouteResult route) { + if (tb.getMapDensity() != getMapDensity() || this.route != route) { + this.route = route; + List locations; + Map> styleMap; + if (route == null) { + locations = Collections.emptyList(); + styleMap = Collections.emptyMap(); + } else { + LatLon start = transportHelper.getStartLocation(); + LatLon end = transportHelper.getEndLocation(); + List list = new ArrayList<>(); + List> styles = new ArrayList<>(); + calculateTransportResult(start, end, route, list, styles); + List locs = new ArrayList<>(); + Map> 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 res, List> 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 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 res, List> styles) { + final RouteCalculationResult walkingRouteSegment = transportHelper.getWalkingRouteSegment(s1, s2); + if (walkingRouteSegment != null && walkingRouteSegment.getRouteLocations().size() > 0) { + final List 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 geometry, List> 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 { + + 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 { + + 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(); + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/views/layers/geometry/PublicTransportGeometryWayContext.java b/OsmAnd/src/net/osmand/plus/views/layers/geometry/PublicTransportGeometryWayContext.java new file mode 100644 index 0000000000..440b3c5872 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/views/layers/geometry/PublicTransportGeometryWayContext.java @@ -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, Bitmap> stopBitmapsCache = new HashMap<>(); + private Map 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; + } +} diff --git a/OsmAnd/src/net/osmand/plus/views/layers/geometry/PublicTransportGeometryWayDrawer.java b/OsmAnd/src/net/osmand/plus/views/layers/geometry/PublicTransportGeometryWayDrawer.java new file mode 100644 index 0000000000..c53fbe558f --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/views/layers/geometry/PublicTransportGeometryWayDrawer.java @@ -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 { + + private List routeTransportStops = new ArrayList<>(); + + public PublicTransportGeometryWayDrawer(PublicTransportGeometryWayContext context) { + super(context); + } + + public List 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 tx, List ty, + List angles, List distances, double distPixToFinish, List> styles) { + PublicTransportGeometryWayContext context = getContext(); + + List arrows = new ArrayList<>(); + List anchors = new ArrayList<>(); + List stops = new ArrayList<>(); + Set 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 routeTransportStops = new ArrayList<>(); + for (GeometryTransportWayStyle style : transportWaysStyles) { + List 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 boundIntersections = OsmandMapLayer.initBoundIntersections(tb); + List 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); + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/views/layers/geometry/PublicTransportGeometryWayStyle.java b/OsmAnd/src/net/osmand/plus/views/layers/geometry/PublicTransportGeometryWayStyle.java new file mode 100644 index 0000000000..a76614a757 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/views/layers/geometry/PublicTransportGeometryWayStyle.java @@ -0,0 +1,12 @@ +package net.osmand.plus.views.layers.geometry; + +public abstract class PublicTransportGeometryWayStyle extends GeometryWayStyle { + + public PublicTransportGeometryWayStyle(PublicTransportGeometryWayContext context) { + super(context); + } + + public PublicTransportGeometryWayStyle(PublicTransportGeometryWayContext context, Integer color) { + super(context, color); + } +} diff --git a/OsmAnd/src/net/osmand/plus/views/layers/geometry/RouteGeometryWay.java b/OsmAnd/src/net/osmand/plus/views/layers/geometry/RouteGeometryWay.java new file mode 100644 index 0000000000..610ea41454 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/views/layers/geometry/RouteGeometryWay.java @@ -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> { + + private RoutingHelper helper; + private RouteCalculationResult route; + + public RouteGeometryWay(RouteGeometryWayContext context) { + super(context, new GeometryWayDrawer<>(context)); + this.helper = context.getApp().getRoutingHelper(); + } + + @NonNull + @Override + public GeometryWayStyle 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 locations = route != null ? route.getImmutableAllLocations() : Collections.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 { + + 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; + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/views/layers/geometry/RouteGeometryWayContext.java b/OsmAnd/src/net/osmand/plus/views/layers/geometry/RouteGeometryWayContext.java new file mode 100644 index 0000000000..c39c74f752 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/views/layers/geometry/RouteGeometryWayContext.java @@ -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; + } +} diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java index 816798ad80..0597f54397 100644 --- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapWidgetRegistry.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapWidgetRegistry.java index d9f1b50edc..1e968e4ae6 100644 --- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapWidgetRegistry.java +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapWidgetRegistry.java @@ -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; diff --git a/OsmAnd/src/net/osmand/plus/wikipedia/WikipediaPlugin.java b/OsmAnd/src/net/osmand/plus/wikipedia/WikipediaPlugin.java index 614d3301e2..16684fbe25 100644 --- a/OsmAnd/src/net/osmand/plus/wikipedia/WikipediaPlugin.java +++ b/OsmAnd/src/net/osmand/plus/wikipedia/WikipediaPlugin.java @@ -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;