From 3da32a65cfc59b98f68d7db1b32166716ab1a0e7 Mon Sep 17 00:00:00 2001 From: cepprice Date: Wed, 7 Apr 2021 23:49:13 +0500 Subject: [PATCH] Proper implementation of route colorization algorithm --- .../main/java/net/osmand/GPXUtilities.java | 19 ++++ .../java/net/osmand/router/RouteColorize.java | 2 +- .../src/net/osmand/plus/views/Renderable.java | 25 ++---- .../osmand/plus/views/layers/GPXLayer.java | 86 ++++++++----------- 4 files changed, 63 insertions(+), 69 deletions(-) diff --git a/OsmAnd-java/src/main/java/net/osmand/GPXUtilities.java b/OsmAnd-java/src/main/java/net/osmand/GPXUtilities.java index ae4080b367..06ee6689be 100644 --- a/OsmAnd-java/src/main/java/net/osmand/GPXUtilities.java +++ b/OsmAnd-java/src/main/java/net/osmand/GPXUtilities.java @@ -328,6 +328,16 @@ public class GPXUtilities { } } + public void setColor(ColorizationType type, int color) { + if (type == ColorizationType.SPEED) { + speedColor = color; + } else if (type == ColorizationType.ELEVATION) { + altitudeColor = color; + } else if (type == ColorizationType.SLOPE) { + slopeColor = color; + } + } + public String getBackgroundType() { return getExtensionsToRead().get(BACKGROUND_TYPE_EXTENSION); } @@ -1103,6 +1113,15 @@ public class GPXUtilities { return trackBounds; } + public static QuadRect calculateTrackBounds(List segments) { + QuadRect trackBounds = new QuadRect(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, + Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + for (TrkSegment segment : segments) { + updateBounds(trackBounds, segment.points, 0); + } + return trackBounds; + } + public static void updateBounds(QuadRect trackBounds, List pts, int startIndex) { for (int i = startIndex; i < pts.size(); i++) { WptPt pt = pts.get(i); diff --git a/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java b/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java index 0a39d57e25..88b6555e19 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java @@ -477,7 +477,7 @@ public class RouteColorize { } public static class RouteColorizationPoint { - int id; + public int id; public double lat; public double lon; public double val; diff --git a/OsmAnd/src/net/osmand/plus/views/Renderable.java b/OsmAnd/src/net/osmand/plus/views/Renderable.java index 657cf3856a..e02a9d13f8 100644 --- a/OsmAnd/src/net/osmand/plus/views/Renderable.java +++ b/OsmAnd/src/net/osmand/plus/views/Renderable.java @@ -28,7 +28,6 @@ import java.util.concurrent.atomic.AtomicInteger; import androidx.annotation.NonNull; - public class Renderable { private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); @@ -61,7 +60,6 @@ public class Renderable { public List points = null; // Original list of points protected List culled = new ArrayList<>(); // Reduced/resampled list of points - protected List oldCulled = new ArrayList<>(); protected int pointSize; protected double segmentSize; @@ -89,6 +87,9 @@ public class Renderable { } paint.setColor(p.getColor()); paint.setStrokeWidth(p.getStrokeWidth()); + if (scaleType != null) { + paint.setAlpha(0xFF); + } } public void setBorderPaint(@NonNull Paint paint) { @@ -117,20 +118,18 @@ public class Renderable { updateLocalPaint(p); canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY()); if (scaleType != null) { - drawGradient(getPointsForDrawingWithBorder(), p, canvas, tileBox); + drawGradient(points, p, canvas, tileBox); } else { drawSolid(getPointsForDrawing(), p, canvas, tileBox); } canvas.rotate(tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY()); } - public void drawSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) { if (QuadRect.trivialOverlap(tileBox.getLatLonBounds(), trackBounds)) { // is visible? - if (tileBox.getZoomAnimation() > 0 && !Algorithms.isEmpty(culled) && scaleType != null) { - oldCulled = new ArrayList<>(culled); + if (scaleType == null) { + startCuller(zoom); } - startCuller(zoom); drawSingleSegment(zoom, p, canvas, tileBox); } } @@ -143,16 +142,6 @@ public class Renderable { return culled.isEmpty() ? points : culled; } - public List getPointsForDrawingWithBorder() { - if (!culled.isEmpty()) { - return culled; - } else if (!oldCulled.isEmpty()) { - return oldCulled; - } else { - return points; - } - } - public void drawGeometry(Canvas canvas, RotatedTileBox tileBox, QuadRect quadRect, int arrowColor, int trackColor, float trackWidth) { if (geometryWay != null) { List points = getPointsForDrawing(); @@ -316,4 +305,4 @@ public class Renderable { @Override protected void startCuller(double newZoom) {} } -} +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java index 69266746a9..7948e7fd8d 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java @@ -68,6 +68,8 @@ import net.osmand.render.RenderingRuleProperty; import net.osmand.render.RenderingRuleSearchRequest; import net.osmand.render.RenderingRulesStorage; import net.osmand.router.RouteColorize; +import net.osmand.router.RouteColorize.ColorizationType; +import net.osmand.router.RouteColorize.RouteColorizationPoint; import net.osmand.util.Algorithms; import net.osmand.util.MapUtils; @@ -680,26 +682,29 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM private void drawSelectedFileSegments(SelectedGpxFile selectedGpxFile, boolean currentTrack, Canvas canvas, RotatedTileBox tileBox, DrawSettings settings) { + boolean visible = QuadRect.trivialOverlap(tileBox.getLatLonBounds(), GPXUtilities.calculateTrackBounds(selectedGpxFile.getModifiablePointsToDisplay())); + if (!selectedGpxFile.getGpxFile().hasTrkPt() || !visible) { + return; + } + OsmandApplication app = view.getApplication(); GPXFile gpxFile = selectedGpxFile.getGpxFile(); - List segments = selectedGpxFile.getPointsToDisplay(); GradientScaleType scaleType = getGradientScaleType(gpxFile); - List colorsOfPoints = null; + List segments = new ArrayList<>(); - if (needCalculatePointsColors(segments, scaleType)) { + if (scaleType == null) { + segments.addAll(selectedGpxFile.getModifiablePointsToDisplay()); + } else { RouteColorize colorize = new RouteColorize(view.getZoom(), gpxFile, selectedGpxFile.getTrackAnalysis(app), scaleType.toColorizationType(), app.getSettings().getApplicationMode().getMaxSpeed()); colorize.setPalette(getColorizationPalette(gpxFile, scaleType)); - colorsOfPoints = colorize.getResult(false); + List colorsOfPoints = colorize.getResult(true); + segments.addAll(createSimplifiedSegmentsFromColorizedPoints(gpxFile, colorsOfPoints, scaleType)); } - int startIdx = 0; for (TrkSegment ts : segments) { String width = getTrackWidthName(gpxFile, defaultTrackWidthPref.get()); int color = getTrackColor(gpxFile, ts.getColor(cachedColor)); - if (colorsOfPoints != null) { - startIdx = setColorsToPoints(ts, colorsOfPoints, scaleType, startIdx); - } if (ts.renderer == null && !ts.points.isEmpty()) { Renderable.RenderableSegment renderer; if (currentTrack) { @@ -720,53 +725,34 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM } } - private boolean needCalculatePointsColors(List segments, GradientScaleType scaleType) { - if (scaleType == null) { - return false; - } - RouteColorize.ColorizationType colorizationType = scaleType.toColorizationType(); - for (int segIdx = segments.size() - 1; segIdx >= 0; segIdx--) { - List pts = segments.get(segIdx).points; - if (!Algorithms.isEmpty(pts)) { - for (int wptIdx = pts.size() - 1; wptIdx >= 0; wptIdx--) { - WptPt pt = pts.get(wptIdx); - if (pt.getColor(colorizationType) == 0) { - return true; + private List createSimplifiedSegmentsFromColorizedPoints(GPXFile gpxFile, + List colorizationPoints, + GradientScaleType scaleType) { + List simplifiedSegments = new ArrayList<>(); + ColorizationType colorizationType = scaleType.toColorizationType(); + int id = 0; + int colorPointIdx = 0; + + for (GPXUtilities.Track track : gpxFile.tracks) { + for (TrkSegment segment : track.segments) { + TrkSegment simplifiedSegment = new TrkSegment(); + simplifiedSegments.add(simplifiedSegment); + for (WptPt pt : segment.points) { + if (colorPointIdx >= colorizationPoints.size()) { + return simplifiedSegments; } + RouteColorizationPoint colorPoint = colorizationPoints.get(colorPointIdx); + if (colorPoint.id == id) { + simplifiedSegment.points.add(pt); + pt.setColor(colorizationType, colorPoint.color); + colorPointIdx++; + } + id++; } } } - return false; - } - private int setColorsToPoints(TrkSegment segment, List colors, GradientScaleType scaleType, int startIdx) { - int pointsSize = segment.points.size(); - RouteColorize.RouteColorizationPoint startColor = colors.get(startIdx); - RouteColorize.RouteColorizationPoint endColor = colors.get(startIdx + pointsSize - 1); - WptPt firstPoint = segment.points.get(0); - WptPt lastPoint = segment.points.get(pointsSize - 1); - while (!compareCoordinates(firstPoint, startColor) && compareCoordinates(lastPoint, endColor)) { - startIdx++; - startColor = colors.get(startIdx); - endColor = colors.get(startIdx + pointsSize - 1); - } - - for (int i = startIdx; i < startIdx + pointsSize; i++) { - WptPt currentPoint = segment.points.get(i - startIdx); - int currentColor = colors.get(i).color; - if (scaleType == GradientScaleType.SPEED) { - currentPoint.speedColor = currentColor; - } else if (scaleType == GradientScaleType.ALTITUDE) { - currentPoint.altitudeColor = currentColor; - } else { - currentPoint.slopeColor = currentColor; - } - } - return startIdx; - } - - private boolean compareCoordinates(WptPt left, RouteColorize.RouteColorizationPoint right) { - return left.lat == right.lat && left.lon == right.lon; + return simplifiedSegments; } private float getTrackWidth(String width, float defaultTrackWidth) {