Proper implementation of route colorization algorithm

This commit is contained in:
cepprice 2021-04-07 23:49:13 +05:00
parent acff47bf8a
commit 3da32a65cf
4 changed files with 63 additions and 69 deletions

View file

@ -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() { public String getBackgroundType() {
return getExtensionsToRead().get(BACKGROUND_TYPE_EXTENSION); return getExtensionsToRead().get(BACKGROUND_TYPE_EXTENSION);
} }
@ -1103,6 +1113,15 @@ public class GPXUtilities {
return trackBounds; return trackBounds;
} }
public static QuadRect calculateTrackBounds(List<TrkSegment> 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<WptPt> pts, int startIndex) { public static void updateBounds(QuadRect trackBounds, List<WptPt> pts, int startIndex) {
for (int i = startIndex; i < pts.size(); i++) { for (int i = startIndex; i < pts.size(); i++) {
WptPt pt = pts.get(i); WptPt pt = pts.get(i);

View file

@ -477,7 +477,7 @@ public class RouteColorize {
} }
public static class RouteColorizationPoint { public static class RouteColorizationPoint {
int id; public int id;
public double lat; public double lat;
public double lon; public double lon;
public double val; public double val;

View file

@ -28,7 +28,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
public class Renderable { public class Renderable {
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
@ -61,7 +60,6 @@ public class Renderable {
public List<WptPt> points = null; // Original list of points public List<WptPt> points = null; // Original list of points
protected List<WptPt> culled = new ArrayList<>(); // Reduced/resampled list of points protected List<WptPt> culled = new ArrayList<>(); // Reduced/resampled list of points
protected List<WptPt> oldCulled = new ArrayList<>();
protected int pointSize; protected int pointSize;
protected double segmentSize; protected double segmentSize;
@ -89,6 +87,9 @@ public class Renderable {
} }
paint.setColor(p.getColor()); paint.setColor(p.getColor());
paint.setStrokeWidth(p.getStrokeWidth()); paint.setStrokeWidth(p.getStrokeWidth());
if (scaleType != null) {
paint.setAlpha(0xFF);
}
} }
public void setBorderPaint(@NonNull Paint paint) { public void setBorderPaint(@NonNull Paint paint) {
@ -117,20 +118,18 @@ public class Renderable {
updateLocalPaint(p); updateLocalPaint(p);
canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY()); canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
if (scaleType != null) { if (scaleType != null) {
drawGradient(getPointsForDrawingWithBorder(), p, canvas, tileBox); drawGradient(points, p, canvas, tileBox);
} else { } else {
drawSolid(getPointsForDrawing(), p, canvas, tileBox); drawSolid(getPointsForDrawing(), p, canvas, tileBox);
} }
canvas.rotate(tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY()); canvas.rotate(tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
} }
public void drawSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) { public void drawSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) {
if (QuadRect.trivialOverlap(tileBox.getLatLonBounds(), trackBounds)) { // is visible? if (QuadRect.trivialOverlap(tileBox.getLatLonBounds(), trackBounds)) { // is visible?
if (tileBox.getZoomAnimation() > 0 && !Algorithms.isEmpty(culled) && scaleType != null) { if (scaleType == null) {
oldCulled = new ArrayList<>(culled); startCuller(zoom);
} }
startCuller(zoom);
drawSingleSegment(zoom, p, canvas, tileBox); drawSingleSegment(zoom, p, canvas, tileBox);
} }
} }
@ -143,16 +142,6 @@ public class Renderable {
return culled.isEmpty() ? points : culled; return culled.isEmpty() ? points : culled;
} }
public List<WptPt> 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) { public void drawGeometry(Canvas canvas, RotatedTileBox tileBox, QuadRect quadRect, int arrowColor, int trackColor, float trackWidth) {
if (geometryWay != null) { if (geometryWay != null) {
List<WptPt> points = getPointsForDrawing(); List<WptPt> points = getPointsForDrawing();

View file

@ -68,6 +68,8 @@ import net.osmand.render.RenderingRuleProperty;
import net.osmand.render.RenderingRuleSearchRequest; import net.osmand.render.RenderingRuleSearchRequest;
import net.osmand.render.RenderingRulesStorage; import net.osmand.render.RenderingRulesStorage;
import net.osmand.router.RouteColorize; 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.Algorithms;
import net.osmand.util.MapUtils; 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, private void drawSelectedFileSegments(SelectedGpxFile selectedGpxFile, boolean currentTrack, Canvas canvas,
RotatedTileBox tileBox, DrawSettings settings) { RotatedTileBox tileBox, DrawSettings settings) {
boolean visible = QuadRect.trivialOverlap(tileBox.getLatLonBounds(), GPXUtilities.calculateTrackBounds(selectedGpxFile.getModifiablePointsToDisplay()));
if (!selectedGpxFile.getGpxFile().hasTrkPt() || !visible) {
return;
}
OsmandApplication app = view.getApplication(); OsmandApplication app = view.getApplication();
GPXFile gpxFile = selectedGpxFile.getGpxFile(); GPXFile gpxFile = selectedGpxFile.getGpxFile();
List<TrkSegment> segments = selectedGpxFile.getPointsToDisplay();
GradientScaleType scaleType = getGradientScaleType(gpxFile); GradientScaleType scaleType = getGradientScaleType(gpxFile);
List<RouteColorize.RouteColorizationPoint> colorsOfPoints = null; List<TrkSegment> 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), RouteColorize colorize = new RouteColorize(view.getZoom(), gpxFile, selectedGpxFile.getTrackAnalysis(app),
scaleType.toColorizationType(), app.getSettings().getApplicationMode().getMaxSpeed()); scaleType.toColorizationType(), app.getSettings().getApplicationMode().getMaxSpeed());
colorize.setPalette(getColorizationPalette(gpxFile, scaleType)); colorize.setPalette(getColorizationPalette(gpxFile, scaleType));
colorsOfPoints = colorize.getResult(false); List<RouteColorizationPoint> colorsOfPoints = colorize.getResult(true);
segments.addAll(createSimplifiedSegmentsFromColorizedPoints(gpxFile, colorsOfPoints, scaleType));
} }
int startIdx = 0;
for (TrkSegment ts : segments) { for (TrkSegment ts : segments) {
String width = getTrackWidthName(gpxFile, defaultTrackWidthPref.get()); String width = getTrackWidthName(gpxFile, defaultTrackWidthPref.get());
int color = getTrackColor(gpxFile, ts.getColor(cachedColor)); int color = getTrackColor(gpxFile, ts.getColor(cachedColor));
if (colorsOfPoints != null) {
startIdx = setColorsToPoints(ts, colorsOfPoints, scaleType, startIdx);
}
if (ts.renderer == null && !ts.points.isEmpty()) { if (ts.renderer == null && !ts.points.isEmpty()) {
Renderable.RenderableSegment renderer; Renderable.RenderableSegment renderer;
if (currentTrack) { if (currentTrack) {
@ -720,53 +725,34 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
} }
} }
private boolean needCalculatePointsColors(List<TrkSegment> segments, GradientScaleType scaleType) { private List<TrkSegment> createSimplifiedSegmentsFromColorizedPoints(GPXFile gpxFile,
if (scaleType == null) { List<RouteColorizationPoint> colorizationPoints,
return false; GradientScaleType scaleType) {
} List<TrkSegment> simplifiedSegments = new ArrayList<>();
RouteColorize.ColorizationType colorizationType = scaleType.toColorizationType(); ColorizationType colorizationType = scaleType.toColorizationType();
for (int segIdx = segments.size() - 1; segIdx >= 0; segIdx--) { int id = 0;
List<WptPt> pts = segments.get(segIdx).points; int colorPointIdx = 0;
if (!Algorithms.isEmpty(pts)) {
for (int wptIdx = pts.size() - 1; wptIdx >= 0; wptIdx--) { for (GPXUtilities.Track track : gpxFile.tracks) {
WptPt pt = pts.get(wptIdx); for (TrkSegment segment : track.segments) {
if (pt.getColor(colorizationType) == 0) { TrkSegment simplifiedSegment = new TrkSegment();
return true; 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<RouteColorize.RouteColorizationPoint> colors, GradientScaleType scaleType, int startIdx) { return simplifiedSegments;
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;
} }
private float getTrackWidth(String width, float defaultTrackWidth) { private float getTrackWidth(String width, float defaultTrackWidth) {