Merge pull request #11335 from osmandapp/track_coloring_fixes
Track coloring fixes
This commit is contained in:
commit
5f244aa249
4 changed files with 85 additions and 37 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
|
@ -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(
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue