Merge pull request #11335 from osmandapp/track_coloring_fixes

Track coloring fixes
This commit is contained in:
Vitaliy 2021-04-05 12:45:42 +03:00 committed by GitHub
commit 5f244aa249
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 85 additions and 37 deletions

View file

@ -1,6 +1,10 @@
package net.osmand.router; package net.osmand.router;
import net.osmand.GPXUtilities; import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.GPXTrackAnalysis;
import net.osmand.GPXUtilities.Track;
import net.osmand.GPXUtilities.TrkSegment;
import net.osmand.GPXUtilities.WptPt;
import net.osmand.PlatformUtil; import net.osmand.PlatformUtil;
import net.osmand.osm.edit.Node; import net.osmand.osm.edit.Node;
import net.osmand.osm.edit.OsmMapUtils; import net.osmand.osm.edit.OsmMapUtils;
@ -30,6 +34,8 @@ public class RouteColorize {
public static final int RED = rgbaToDecimal(243, 55, 77, 255); public static final int RED = rgbaToDecimal(243, 55, 77, 255);
public static final int[] colors = new int[] {GREEN, YELLOW, RED}; public static final int[] colors = new int[] {GREEN, YELLOW, RED};
private static final int MAX_SLOPE_VALUE = 25;
public enum ColorizationType { public enum ColorizationType {
ELEVATION, ELEVATION,
SPEED, SPEED,
@ -75,7 +81,7 @@ public class RouteColorize {
/** /**
* @param type ELEVATION, SPEED, SLOPE * @param type ELEVATION, SPEED, SLOPE
*/ */
public RouteColorize(int zoom, GPXUtilities.GPXFile gpxFile, ColorizationType type) { public RouteColorize(int zoom, GPXFile gpxFile, GPXTrackAnalysis analysis, ColorizationType type, float maxProfileSpeed) {
if (!gpxFile.hasTrkPt()) { if (!gpxFile.hasTrkPt()) {
LOG.warn("GPX file is not consist of track points"); LOG.warn("GPX file is not consist of track points");
@ -85,21 +91,25 @@ public class RouteColorize {
List<Double> latList = new ArrayList<>(); List<Double> latList = new ArrayList<>();
List<Double> lonList = new ArrayList<>(); List<Double> lonList = new ArrayList<>();
List<Double> valList = new ArrayList<>(); List<Double> valList = new ArrayList<>();
for (GPXUtilities.Track t : gpxFile.tracks) {
for (GPXUtilities.TrkSegment ts : t.segments) { int wptIdx = 0;
for (GPXUtilities.WptPt p : ts.points) { for (Track t : gpxFile.tracks) {
for (TrkSegment ts : t.segments) {
for (WptPt p : ts.points) {
latList.add(p.lat); latList.add(p.lat);
lonList.add(p.lon); lonList.add(p.lon);
if (type == ColorizationType.SPEED) { if (type == ColorizationType.SPEED) {
valList.add(p.speed); valList.add((double) analysis.speedData.get(wptIdx).speed);
} else { } else {
valList.add(p.ele); valList.add((double) analysis.elevationData.get(wptIdx).elevation);
} }
wptIdx++;
} }
} }
} }
this.zoom = zoom; this.zoom = zoom;
colorizationType = type;
latitudes = listToArray(latList); latitudes = listToArray(latList);
longitudes = listToArray(lonList); longitudes = listToArray(lonList);
@ -108,9 +118,8 @@ public class RouteColorize {
} else { } else {
values = listToArray(valList); values = listToArray(valList);
} }
calculateMinMaxValue(); calculateMinMaxValue();
colorizationType = type; maxValue = getMaxValue(colorizationType, analysis, minValue, maxProfileSpeed);
checkPalette(); checkPalette();
sortPalette(); sortPalette();
} }
@ -194,7 +203,7 @@ public class RouteColorize {
return rgbaToDecimal((int) resultRed, (int) resultGreen, (int) resultBlue, (int) resultAlpha); return rgbaToDecimal((int) resultRed, (int) resultGreen, (int) resultBlue, (int) resultAlpha);
} }
} }
return getDefaultColor(); return getTransparentColor();
} }
public void setPalette(double[][] palette) { public void setPalette(double[][] palette) {
@ -209,12 +218,12 @@ public class RouteColorize {
} }
setPalette(new double[][] { setPalette(new double[][] {
{minValue, gradientPalette[0]}, {minValue, gradientPalette[0]},
{colorizationType == ColorizationType.SLOPE ? 0 : (minValue + maxValue) / 2, gradientPalette[1]}, {(minValue + maxValue) / 2, gradientPalette[1]},
{maxValue, gradientPalette[2]} {maxValue, gradientPalette[2]}
}); });
} }
private int getDefaultColor() { private int getTransparentColor() {
return rgbaToDecimal(0, 0, 0, 0); return rgbaToDecimal(0, 0, 0, 0);
} }
@ -295,7 +304,7 @@ public class RouteColorize {
double[][] defaultPalette = { double[][] defaultPalette = {
{minValue, GREEN}, {minValue, GREEN},
{colorizationType == ColorizationType.SLOPE ? 0 : (minValue + maxValue) / 2, YELLOW}, {(minValue + maxValue) / 2, YELLOW},
{maxValue, RED} {maxValue, RED}
}; };
palette = defaultPalette; palette = defaultPalette;
@ -397,6 +406,20 @@ public class RouteColorize {
return result; return result;
} }
public static double getMinValue(ColorizationType type, GPXTrackAnalysis analysis) {
return type == ColorizationType.ELEVATION ? analysis.minElevation : 0.0;
}
public static double getMaxValue(ColorizationType type, GPXTrackAnalysis analysis, double minValue, double maxProfileSpeed) {
if (type == ColorizationType.SPEED) {
return Math.max(analysis.maxSpeed, maxProfileSpeed);
} else if (type == ColorizationType.ELEVATION) {
return Math.max(analysis.maxElevation, minValue + 50);
} else {
return MAX_SLOPE_VALUE;
}
}
private void calculateMinMaxValue() { private void calculateMinMaxValue() {
if (values.length == 0) if (values.length == 0)
return; return;
@ -457,5 +480,4 @@ public class RouteColorize {
this.val = val; this.val = val;
} }
} }
}
}

View file

@ -12,6 +12,7 @@ import net.osmand.plus.R;
import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.routepreparationmenu.cards.BaseCard; import net.osmand.plus.routepreparationmenu.cards.BaseCard;
import net.osmand.router.RouteColorize;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -42,8 +43,9 @@ public class GradientCard extends BaseCard {
AndroidUiHelper.updateVisibility(view, true); AndroidUiHelper.updateVisibility(view, true);
TextView minValue = view.findViewById(R.id.min_value); TextView minValue = view.findViewById(R.id.min_value);
TextView maxValue = view.findViewById(R.id.max_value); TextView maxValue = view.findViewById(R.id.max_value);
float min = getMinValue(); double min = RouteColorize.getMinValue(selectedScaleType.toColorizationType(), gpxTrackAnalysis);
float max = getMaxValue(min); double max = RouteColorize.getMaxValue(selectedScaleType.toColorizationType(),
gpxTrackAnalysis, min, app.getSettings().getApplicationMode().getMaxSpeed());
minValue.setText(formatValue(min)); minValue.setText(formatValue(min));
maxValue.setText(formatValue(max)); maxValue.setText(formatValue(max));
} }
@ -53,27 +55,13 @@ public class GradientCard extends BaseCard {
updateContent(); updateContent();
} }
private float getMinValue() { private CharSequence formatValue(double value) {
return (float) (selectedScaleType == GradientScaleType.ALTITUDE ? gpxTrackAnalysis.minElevation : 0.0);
}
private float getMaxValue(float minValue) {
if (selectedScaleType == GradientScaleType.SPEED) {
return (Math.max(gpxTrackAnalysis.maxSpeed, app.getSettings().getApplicationMode().getMaxSpeed()));
} else if (selectedScaleType == GradientScaleType.ALTITUDE) {
return (float) Math.max(gpxTrackAnalysis.maxElevation, minValue + 50);
} else {
return 25;
}
}
private CharSequence formatValue(float value) {
if (selectedScaleType == GradientScaleType.ALTITUDE) { if (selectedScaleType == GradientScaleType.ALTITUDE) {
return OsmAndFormatter.getFormattedAlt(value, app); return OsmAndFormatter.getFormattedAlt(value, app);
} else if (selectedScaleType == GradientScaleType.SLOPE) { } else if (selectedScaleType == GradientScaleType.SLOPE) {
return (int) value + " %"; return (int) value + " %";
} }
String speed = OsmAndFormatter.getFormattedSpeed(value, app); String speed = OsmAndFormatter.getFormattedSpeed((float) value, app);
String speedUnit = app.getSettings().SPEED_SYSTEM.get().toShortString(app); String speedUnit = app.getSettings().SPEED_SYSTEM.get().toShortString(app);
Spannable formattedSpeed = new SpannableString(speed); Spannable formattedSpeed = new SpannableString(speed);
formattedSpeed.setSpan( formattedSpeed.setSpan(

View file

@ -61,6 +61,7 @@ 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;
@ -116,7 +117,7 @@ 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(getPointsForDrawing(), p, canvas, tileBox); drawGradient(getPointsForDrawingWithBorder(), p, canvas, tileBox);
} else { } else {
drawSolid(getPointsForDrawing(), p, canvas, tileBox); drawSolid(getPointsForDrawing(), p, canvas, tileBox);
} }
@ -126,6 +127,9 @@ public class Renderable {
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) {
oldCulled = new ArrayList<>(culled);
}
startCuller(zoom); startCuller(zoom);
drawSingleSegment(zoom, p, canvas, tileBox); drawSingleSegment(zoom, p, canvas, tileBox);
} }
@ -139,6 +143,16 @@ 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();
@ -290,7 +304,8 @@ public class Renderable {
super(pt, 0); super(pt, 0);
} }
@Override public void drawSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) { @Override
public void drawSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) {
if (points.size() != pointSize) { if (points.size() != pointSize) {
int prevSize = pointSize; int prevSize = pointSize;
pointSize = points.size(); pointSize = points.size();

View file

@ -680,15 +680,19 @@ 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) {
OsmandApplication app = view.getApplication();
GPXFile gpxFile = selectedGpxFile.getGpxFile(); GPXFile gpxFile = selectedGpxFile.getGpxFile();
List<TrkSegment> segments = selectedGpxFile.getPointsToDisplay(); List<TrkSegment> segments = selectedGpxFile.getPointsToDisplay();
GradientScaleType scaleType = getGradientScaleType(gpxFile); GradientScaleType scaleType = getGradientScaleType(gpxFile);
List<RouteColorize.RouteColorizationPoint> colorsOfPoints = null; List<RouteColorize.RouteColorizationPoint> colorsOfPoints = null;
if (scaleType != null) {
RouteColorize colorize = new RouteColorize(view.getZoom(), gpxFile, scaleType.toColorizationType()); if (needCalculatePointsColors(segments, scaleType)) {
RouteColorize colorize = new RouteColorize(view.getZoom(), gpxFile, selectedGpxFile.getTrackAnalysis(app),
scaleType.toColorizationType(), app.getSettings().getApplicationMode().getMaxSpeed());
colorize.setPalette(getColorizationPalette(gpxFile, scaleType)); colorize.setPalette(getColorizationPalette(gpxFile, scaleType));
colorsOfPoints = colorize.getResult(false); colorsOfPoints = colorize.getResult(false);
} }
int startIdx = 0; int startIdx = 0;
for (TrkSegment ts : segments) { for (TrkSegment ts : segments) {
String width = getTrackWidthName(gpxFile, defaultTrackWidthPref.get()); String width = getTrackWidthName(gpxFile, defaultTrackWidthPref.get());
@ -716,6 +720,25 @@ 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;
}
}
}
}
return false;
}
private int setColorsToPoints(TrkSegment segment, List<RouteColorize.RouteColorizationPoint> colors, GradientScaleType scaleType, int startIdx) { private int setColorsToPoints(TrkSegment segment, List<RouteColorize.RouteColorizationPoint> colors, GradientScaleType scaleType, int startIdx) {
int pointsSize = segment.points.size(); int pointsSize = segment.points.size();
RouteColorize.RouteColorizationPoint startColor = colors.get(startIdx); RouteColorize.RouteColorizationPoint startColor = colors.get(startIdx);