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() {
return getExtensionsToRead().get(BACKGROUND_TYPE_EXTENSION);
}
@ -1103,6 +1113,15 @@ public class GPXUtilities {
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) {
for (int i = startIndex; i < pts.size(); i++) {
WptPt pt = pts.get(i);

View file

@ -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;

View file

@ -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<WptPt> points = null; // Original list of points
protected List<WptPt> culled = new ArrayList<>(); // Reduced/resampled list of points
protected List<WptPt> 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<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) {
if (geometryWay != null) {
List<WptPt> points = getPointsForDrawing();
@ -316,4 +305,4 @@ public class Renderable {
@Override protected void startCuller(double newZoom) {}
}
}
}

View file

@ -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<TrkSegment> segments = selectedGpxFile.getPointsToDisplay();
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),
scaleType.toColorizationType(), app.getSettings().getApplicationMode().getMaxSpeed());
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) {
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<TrkSegment> segments, GradientScaleType scaleType) {
if (scaleType == null) {
return false;
}
RouteColorize.ColorizationType colorizationType = scaleType.toColorizationType();
for (int segIdx = segments.size() - 1; segIdx >= 0; segIdx--) {
List<WptPt> 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<TrkSegment> createSimplifiedSegmentsFromColorizedPoints(GPXFile gpxFile,
List<RouteColorizationPoint> colorizationPoints,
GradientScaleType scaleType) {
List<TrkSegment> 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<RouteColorize.RouteColorizationPoint> 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) {