Proper implementation of route colorization algorithm
This commit is contained in:
parent
acff47bf8a
commit
3da32a65cf
4 changed files with 63 additions and 69 deletions
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
@ -316,4 +305,4 @@ public class Renderable {
|
||||||
|
|
||||||
@Override protected void startCuller(double newZoom) {}
|
@Override protected void startCuller(double newZoom) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in a new issue