diff --git a/OsmAnd/src/net/osmand/AndroidUtils.java b/OsmAnd/src/net/osmand/AndroidUtils.java index 42b4a60919..4f0729d355 100644 --- a/OsmAnd/src/net/osmand/AndroidUtils.java +++ b/OsmAnd/src/net/osmand/AndroidUtils.java @@ -151,6 +151,10 @@ public class AndroidUtils { return resizedBitmap; } + public static Bitmap createScaledBitmap(Drawable drawable, int width, int height) { + return scaleBitmap(drawableToBitmap(drawable), width, height, false); + } + public static ColorStateList createBottomNavColorStateList(Context ctx, boolean nightMode) { return AndroidUtils.createCheckedColorStateList(ctx, nightMode, R.color.icon_color_default_light, R.color.wikivoyage_active_light, diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolLayer.java b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolLayer.java index 7ab66f44b2..4ed8aa719a 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolLayer.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolLayer.java @@ -82,7 +82,8 @@ public class MeasurementToolLayer extends OsmandMapLayer implements ContextMenuL multiProfileLineAttrs.isPaint3 = false; multiProfileLineAttrs.paint3.setStrokeWidth(density * 2); - multiProfileGeometryWayContext = new MultiProfileGeometryWayContext(view.getContext(), density); + multiProfileGeometryWayContext = new MultiProfileGeometryWayContext( + view.getContext(), view.getApplication().getUIUtilities(), density); multiProfileGeometry = new MultiProfileGeometryWay(multiProfileGeometryWayContext); bitmapPaint = new Paint(); @@ -227,6 +228,7 @@ public class MeasurementToolLayer extends OsmandMapLayer implements ContextMenuL multiProfileGeometry.drawSegments(canvas, tb); } } else { + multiProfileGeometry.clearWay(); List before = editingCtx.getBeforeTrkSegmentLine(); for (TrkSegment segment : before) { new Renderable.StandardTrack(new ArrayList<>(segment.points), 17.2). diff --git a/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWay.java b/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWay.java index cc1633839e..9489cd9620 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWay.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWay.java @@ -174,7 +174,7 @@ public abstract class GeometryWay= lx && x <= rx && y >= ty && y <= by; } + public static boolean isIn(float x, float y, int lx, int ty, int rx, int by, float outMargin) { + return x >= lx - outMargin && x <= rx + outMargin && y >= ty - outMargin && y <= by + outMargin; + } + public static int calculatePath(RotatedTileBox tb, List xs, List ys, Path path) { List>> paths = new ArrayList<>(); - int res = calculatePath(tb, xs, ys, null, paths); + int res = calculatePath(tb, xs, ys, 0, 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) { + public static int calculatePath(RotatedTileBox tb, List xs, List ys, float outMargin, List> styles, List>> paths) { boolean segmentStarted = false; float prevX = xs.get(0); float prevY = ys.get(0); @@ -280,11 +284,11 @@ public abstract class GeometryWay style = hasStyles ? styles.get(0) : null; Path path = new Path(); - boolean prevIn = isIn(prevX, prevY, 0, 0, width, height); + boolean prevIn = isIn(prevX, prevY, 0, 0, width, height, outMargin); 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 currIn = isIn(currX, currY, 0, 0, width, height, outMargin); boolean draw = false; if (prevIn && currIn) { draw = true; @@ -356,7 +360,7 @@ public abstract class GeometryWay>> paths = new ArrayList<>(); canvas.rotate(-tb.getRotate(), tb.getCenterPixelX(), tb.getCenterPixelY()); - calculatePath(tb, tx, ty, styles, paths); + calculatePath(tb, tx, ty, 0, styles, paths); for (Pair> pc : paths) { GeometryWayStyle style = pc.second; if (style.hasPathLine()) { diff --git a/OsmAnd/src/net/osmand/plus/views/layers/geometry/MultiProfileGeometryWay.java b/OsmAnd/src/net/osmand/plus/views/layers/geometry/MultiProfileGeometryWay.java index e378c4edbd..865e56e60b 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/geometry/MultiProfileGeometryWay.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/geometry/MultiProfileGeometryWay.java @@ -4,6 +4,7 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Path; +import android.graphics.PathMeasure; import android.util.Pair; import net.osmand.GPXUtilities.TrkSegment; @@ -55,14 +56,14 @@ public class MultiProfileGeometryWay extends GeometryWay>> pathStyles = new ArrayList<>(); canvas.rotate(-tb.getRotate(), tb.getCenterPixelX(), tb.getCenterPixelY()); - calculatePath(tb, tx, ty, styles, pathStyles); + calculatePath(tb, tx, ty, getContext().circleSize, styles, pathStyles); for (int i = 0; i < pathStyles.size(); i++) { Pair> currPathStyle = pathStyles.get(i); getDrawer().drawPathBorder(canvas, currPathStyle.first, currPathStyle.second); getDrawer().drawPath(canvas, currPathStyle.first, currPathStyle.second); } -// drawer.drawArrowsOverPath(canvas, tb, tx, ty, angles, distances, distToFinish, styles); + getDrawer().drawArrowsOverPath(canvas, tb, tx, ty, angles, distances, distToFinish, styles); } finally { canvas.rotate(tb.getRotate(), tb.getCenterPixelX(), tb.getCenterPixelY()); } @@ -71,8 +72,8 @@ public class MultiProfileGeometryWay extends GeometryWay, RoadSegmentData> segmentData, boolean before, List segments, int segmentIdx) { boolean shouldUpdateRoute = tileBox.getMapDensity() != getMapDensity() || segmentDataChanged(segmentData) - || getSegments(before) != segments || true; - if (shouldUpdateRoute && segments.get(segmentIdx).points.size() >= 2) { + || getSegments(before) != segments || getLocationProvider() == null; + if (shouldUpdateRoute) { this.segmentData = segmentData; setSegments(before, segments); List userPoints = segments.get(segmentIdx).points; @@ -81,7 +82,7 @@ public class MultiProfileGeometryWay extends GeometryWay ways = new ArrayList<>(); List> styles = new ArrayList<>(); - setStyles(userPoints, ways, styles); + setStyles(tileBox, userPoints, ways, styles); locations = new ArrayList<>(); styleMap = new TreeMap<>(); @@ -104,45 +105,68 @@ public class MultiProfileGeometryWay extends GeometryWay userPoints, List ways, List> styles) { - String prevProfileKey = ""; - Way way = new Way(-2); + private void setStyles(RotatedTileBox tileBox, List userPoints, List ways, List> styles) { + MultiProfileGeometryWayContext context = getContext(); + Path path = new Path(); + PathMeasure pathMeasure = new PathMeasure(); for (int i = 0; i < userPoints.size() - 1; i++) { WptPt leftPt = userPoints.get(i); Pair userLine = new Pair<>(leftPt, userPoints.get(i + 1)); RoadSegmentData routeBetweenPoints = segmentData.get(userLine); - if (!prevProfileKey.equals(getProfileKey(leftPt)) && !leftPt.isGap()) { - way = new Way(-2); - String currProfileKey = getProfileKey(leftPt); - Pair profileData = getProfileData(currProfileKey); - styles.add(new GeometryMultiProfileWayStyle(getContext(), profileData.first, profileData.second)); - ways.add(way); - prevProfileKey = currProfileKey; - } + Way way = new Way(-1); + String currProfileKey = getProfileKey(leftPt); + Pair profileData = getProfileData(currProfileKey); + GeometryMultiProfileWayStyle style = new GeometryMultiProfileWayStyle( + getContext(), currProfileKey, profileData.first, profileData.second); + styles.add(style); + ways.add(way); + path.reset(); boolean isSecondToLast = i + 2 == userPoints.size(); if (routeBetweenPoints == null || Algorithms.isEmpty(routeBetweenPoints.getPoints())) { way.addNode(new Node(userLine.first.lat, userLine.first.lon, -1)); if (isSecondToLast) { way.addNode(new Node(userLine.second.lat, userLine.second.lon, -1)); } + movePathToWpt(path, tileBox, userLine.first); + pathLineToWpt(path, tileBox, userLine.second); } else { + movePathToWpt(path, tileBox, routeBetweenPoints.getPoints().get(0)); for (WptPt pt : routeBetweenPoints.getPoints()) { if (pt.lat != userLine.second.lat && pt.lon != userLine.second.lon || isSecondToLast) { way.addNode(new Node(pt.lat, pt.lon, -1)); } + pathLineToWpt(path, tileBox, pt); } } + + float[] xy = new float[2]; + pathMeasure.setPath(path, false); + float routeLength = pathMeasure.getLength(); + if ((routeLength - context.circleSize) / 2 >= context.minIconMargin) { + pathMeasure.getPosTan(pathMeasure.getLength() * 0.5f, xy, null); + style.setIconLat(tileBox.getLatFromPixel(xy[0], xy[1])); + style.setIconLon(tileBox.getLonFromPixel(xy[0], xy[1])); + } } } @Override - protected boolean shouldAddLocation(double leftLon, double rightLon, double bottomLat, double topLat, GeometryWayProvider provider, int currLocationIdx) { - return super.shouldAddLocation(leftLon, rightLon, bottomLat, topLat, provider, currLocationIdx) - || currLocationIdx + 1 < provider.getSize() - && super.shouldAddLocation(leftLon, rightLon, bottomLat, topLat, provider, currLocationIdx + 1); + protected boolean shouldAddLocation(RotatedTileBox tileBox, double leftLon, double rightLon, + double bottomLat, double topLat, GeometryWayProvider provider, + int currLocationIdx) { + float currX = tileBox.getPixXFromLatLon(provider.getLatitude(currLocationIdx), provider.getLongitude(currLocationIdx)); + float currY = tileBox.getPixYFromLatLon(provider.getLatitude(currLocationIdx), provider.getLongitude(currLocationIdx)); + if (tileBox.containsPoint(currX, currY, getContext().circleSize)) { + return true; + } else if (currLocationIdx + 1 >= provider.getSize()) { + return false; + } + float nextX = tileBox.getPixXFromLatLon(provider.getLatitude(currLocationIdx + 1), provider.getLongitude(currLocationIdx + 1)); + float nextY = tileBox.getPixXFromLatLon(provider.getLatitude(currLocationIdx + 1), provider.getLongitude(currLocationIdx + 1)); + return tileBox.containsPoint(nextX, nextY, getContext().circleSize); } private boolean segmentDataChanged(Map, RoadSegmentData> other) { @@ -169,6 +193,14 @@ public class MultiProfileGeometryWay extends GeometryWay { + private final String profileKey; @ColorInt private final int lineColor; @ColorInt @@ -198,9 +231,13 @@ public class MultiProfileGeometryWay extends GeometryWay profileIconsBitmapCache; - public MultiProfileGeometryWayContext(Context ctx, float density) { + public MultiProfileGeometryWayContext(Context ctx, UiUtilities iconsCache, float density) { super(ctx, density); + this.iconsCache = iconsCache; profileIconsBitmapCache = new HashMap<>(); + minIconMargin = density * 30; + circleSize = density * 70; } public void updatePaints(boolean nightMode, @NonNull RenderingLineAttributes multiProfileAttrs) { @@ -62,18 +73,24 @@ public class MultiProfileGeometryWayContext extends GeometryWayContext { } @NonNull - public Bitmap getProfileIconBitmap(@NonNull String profileKey, int profileColor) { - String key = profileKey + "_" + profileColor; + public Bitmap getProfileIconBitmap(String profileKey, @DrawableRes int iconRes, @ColorInt int color) { + String key = profileKey + "_" + iconRes + "_" + color; Bitmap bitmap = profileIconsBitmapCache.get(key); if (bitmap == null) { - float density = getDensity(); - float diameter = density * 18; - bitmap = Bitmap.createBitmap((int) diameter, (int) diameter, Bitmap.Config.ARGB_8888); - + bitmap = Bitmap.createBitmap((int) circleSize, (int) circleSize, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); - canvas.drawCircle(diameter / 2, diameter / 2, diameter / 2, multiProfileAttrs.paint_1); - multiProfileAttrs.paint3.setColor(profileColor); - canvas.drawCircle(diameter / 2, diameter / 2, diameter / 2, multiProfileAttrs.paint3); + float center = bitmap.getWidth() / 2f; + + canvas.drawCircle(center, center, center / 2, multiProfileAttrs.paint_1); + multiProfileAttrs.paint3.setColor(color); + canvas.drawCircle(center, center, center / 2, multiProfileAttrs.paint3); + + float iconSize = center - getDensity() * 10; + Bitmap profileIconBitmap = AndroidUtils.createScaledBitmap( + iconsCache.getPaintedIcon(iconRes, color), (int) iconSize, (int) iconSize); + canvas.drawBitmap(profileIconBitmap, center - iconSize / 2, center - iconSize / 2, multiProfileAttrs.paint3); + + profileIconsBitmapCache.put(key, bitmap); } return bitmap; } diff --git a/OsmAnd/src/net/osmand/plus/views/layers/geometry/MultiProfileGeometryWayDrawer.java b/OsmAnd/src/net/osmand/plus/views/layers/geometry/MultiProfileGeometryWayDrawer.java index 5d8120f1c2..6e947a26a3 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/geometry/MultiProfileGeometryWayDrawer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/geometry/MultiProfileGeometryWayDrawer.java @@ -3,9 +3,11 @@ package net.osmand.plus.views.layers.geometry; import android.graphics.Canvas; import android.graphics.Path; +import net.osmand.data.RotatedTileBox; +import net.osmand.plus.views.OsmandMapLayer.RenderingLineAttributes; import net.osmand.plus.views.layers.geometry.MultiProfileGeometryWay.GeometryMultiProfileWayStyle; -import net.osmand.plus.views.OsmandMapLayer.RenderingLineAttributes; +import java.util.List; public class MultiProfileGeometryWayDrawer extends GeometryWayDrawer { @@ -22,6 +24,23 @@ public class MultiProfileGeometryWayDrawer extends GeometryWayDrawer tx, List ty, List angles, List distances, double distPixToFinish, List> styles) { + MultiProfileGeometryWayContext context = getContext(); + GeometryMultiProfileWayStyle style = null; + + for (int i = 0; i < styles.size(); i++) { + if (styles.get(i) != null && !styles.get(i).equals(style)) { + style = (GeometryMultiProfileWayStyle) styles.get(i); + double lat = style.getIconLat(); + double lon = style.getIconLon(); + float x = tb.getPixXFromLatLon(lat, lon) - context.circleSize / 2; + float y = tb.getPixYFromLatLon(lat, lon) - context.circleSize / 2; + canvas.drawBitmap(style.getPointBitmap(), x, y, null); + } + } + } + public void drawPathBorder(Canvas canvas, Path path, GeometryWayStyle style) { if (style instanceof GeometryMultiProfileWayStyle) { RenderingLineAttributes attrs = getContext().getAttrs();