Add gradient coloring of track
This commit is contained in:
parent
a6cedd2767
commit
b0b5528cc6
5 changed files with 180 additions and 10 deletions
|
@ -6,6 +6,7 @@ import net.osmand.binary.StringBundle;
|
|||
import net.osmand.binary.StringBundleWriter;
|
||||
import net.osmand.binary.StringBundleXmlWriter;
|
||||
import net.osmand.data.QuadRect;
|
||||
import net.osmand.router.RouteColorize.ColorizationType;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
@ -227,6 +228,9 @@ public class GPXUtilities {
|
|||
public double hdop = Double.NaN;
|
||||
public float heading = Float.NaN;
|
||||
public boolean deleted = false;
|
||||
public int speedColor = 0;
|
||||
public int altitudeColor = 0;
|
||||
public int slopeColor = 0;
|
||||
public int colourARGB = 0; // point colour (used for altitude/speed colouring)
|
||||
public double distance = 0.0; // cumulative distance, if in a track
|
||||
|
||||
|
@ -249,6 +253,9 @@ public class GPXUtilities {
|
|||
this.hdop = wptPt.hdop;
|
||||
this.heading = wptPt.heading;
|
||||
this.deleted = wptPt.deleted;
|
||||
this.speedColor = wptPt.speedColor;
|
||||
this.altitudeColor = wptPt.altitudeColor;
|
||||
this.slopeColor = wptPt.slopeColor;
|
||||
this.colourARGB = wptPt.colourARGB;
|
||||
this.distance = wptPt.distance;
|
||||
}
|
||||
|
@ -311,6 +318,16 @@ public class GPXUtilities {
|
|||
getExtensionsToWrite().put(ICON_NAME_EXTENSION, iconName);
|
||||
}
|
||||
|
||||
public int getColor(ColorizationType type) {
|
||||
if (type == ColorizationType.SPEED) {
|
||||
return speedColor;
|
||||
} else if (type == ColorizationType.ELEVATION) {
|
||||
return altitudeColor;
|
||||
} else {
|
||||
return slopeColor;
|
||||
}
|
||||
}
|
||||
|
||||
public String getBackgroundType() {
|
||||
return getExtensionsToRead().get(BACKGROUND_TYPE_EXTENSION);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import net.osmand.PlatformUtil;
|
|||
import net.osmand.osm.edit.Node;
|
||||
import net.osmand.osm.edit.OsmMapUtils;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -24,9 +25,9 @@ public class RouteColorize {
|
|||
|
||||
public static final int DARK_GREY = rgbaToDecimal(92, 92, 92, 255);
|
||||
public static final int LIGHT_GREY = rgbaToDecimal(200, 200, 200, 255);
|
||||
public static final int GREEN = rgbaToDecimal(90, 220, 95, 1);
|
||||
public static final int YELLOW = rgbaToDecimal(212, 239, 50, 1);
|
||||
public static final int RED = rgbaToDecimal(243, 55, 77, 1);
|
||||
public static final int GREEN = rgbaToDecimal(90, 220, 95, 255);
|
||||
public static final int YELLOW = rgbaToDecimal(212, 239, 50, 255);
|
||||
public static final int RED = rgbaToDecimal(243, 55, 77, 255);
|
||||
public static final int[] colors = new int[] {GREEN, YELLOW, RED};
|
||||
|
||||
public enum ColorizationType {
|
||||
|
@ -202,6 +203,17 @@ public class RouteColorize {
|
|||
sortPalette();
|
||||
}
|
||||
|
||||
public void setPalette(int[] gradientPalette) {
|
||||
if (gradientPalette.length != 3) {
|
||||
return;
|
||||
}
|
||||
setPalette(new double[][] {
|
||||
{minValue, gradientPalette[0]},
|
||||
{colorizationType == ColorizationType.SLOPE ? 12.5 : (minValue + maxValue) / 2},
|
||||
{maxValue, gradientPalette[0]}
|
||||
});
|
||||
}
|
||||
|
||||
private int getDefaultColor() {
|
||||
return rgbaToDecimal(0, 0, 0, 0);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,9 @@ public class TrackDrawInfo {
|
|||
private String width;
|
||||
private GradientScaleType gradientScaleType;
|
||||
private int color;
|
||||
private int[] speedGradientPalette;
|
||||
private int[] altitudeGradientPalette;
|
||||
private int[] slopeGradientPalette;
|
||||
private int splitType;
|
||||
private double splitInterval;
|
||||
private boolean joinSegments;
|
||||
|
@ -46,6 +49,9 @@ public class TrackDrawInfo {
|
|||
width = gpxDataItem.getWidth();
|
||||
gradientScaleType = gpxDataItem.getGradientScaleType();
|
||||
color = gpxDataItem.getColor();
|
||||
speedGradientPalette = gpxDataItem.getGradientSpeedPalette();
|
||||
altitudeGradientPalette = gpxDataItem.getGradientAltitudePalette();
|
||||
slopeGradientPalette = gpxDataItem.getGradientSlopePalette();
|
||||
splitType = gpxDataItem.getSplitType();
|
||||
splitInterval = gpxDataItem.getSplitInterval();
|
||||
joinSegments = gpxDataItem.isJoinSegments();
|
||||
|
@ -82,6 +88,16 @@ public class TrackDrawInfo {
|
|||
this.color = color;
|
||||
}
|
||||
|
||||
public int[] getGradientPalette(@NonNull GradientScaleType scaleType) {
|
||||
if (scaleType == GradientScaleType.SPEED) {
|
||||
return speedGradientPalette;
|
||||
} else if (scaleType == GradientScaleType.ALTITUDE) {
|
||||
return altitudeGradientPalette;
|
||||
} else {
|
||||
return slopeGradientPalette;
|
||||
}
|
||||
}
|
||||
|
||||
public int getSplitType() {
|
||||
return splitType;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package net.osmand.plus.views;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Shader;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
|
@ -10,7 +12,7 @@ import net.osmand.GPXUtilities;
|
|||
import net.osmand.GPXUtilities.WptPt;
|
||||
import net.osmand.data.QuadRect;
|
||||
import net.osmand.data.RotatedTileBox;
|
||||
import net.osmand.plus.views.layers.geometry.GeometryWay;
|
||||
import net.osmand.plus.track.GradientScaleType;
|
||||
import net.osmand.plus.views.layers.geometry.GpxGeometryWay;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
||||
|
@ -65,6 +67,7 @@ public class Renderable {
|
|||
protected double zoom = -1;
|
||||
protected AsynchronousResampler culler = null; // The currently active resampler
|
||||
protected Paint paint = null; // MUST be set by 'updateLocalPaint' before use
|
||||
protected GradientScaleType scaleType = null;
|
||||
|
||||
protected GpxGeometryWay geometryWay;
|
||||
|
||||
|
@ -85,6 +88,10 @@ public class Renderable {
|
|||
paint.setStrokeWidth(p.getStrokeWidth());
|
||||
}
|
||||
|
||||
public void setGradientScaleType(GradientScaleType type) {
|
||||
this.scaleType = type;
|
||||
}
|
||||
|
||||
public GpxGeometryWay getGeometryWay() {
|
||||
return geometryWay;
|
||||
}
|
||||
|
@ -124,7 +131,7 @@ public class Renderable {
|
|||
}
|
||||
}
|
||||
|
||||
protected void draw(List<WptPt> pts, Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
||||
protected void drawSolid(List<WptPt> pts, Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
||||
if (pts.size() > 1) {
|
||||
updateLocalPaint(p);
|
||||
canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
|
||||
|
@ -160,6 +167,38 @@ public class Renderable {
|
|||
canvas.rotate(tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
|
||||
}
|
||||
}
|
||||
|
||||
protected void drawGradient(List<WptPt> pts, Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
||||
if (pts.size() < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateLocalPaint(p);
|
||||
canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
|
||||
QuadRect tileBounds = tileBox.getLatLonBounds();
|
||||
WptPt prevPt = pts.get(0);
|
||||
for (int i = 1; i < pts.size(); i++) {
|
||||
WptPt currentPt = pts.get(i);
|
||||
if (Math.min(currentPt.lon, prevPt.lon) < tileBounds.right && Math.max(currentPt.lon, prevPt.lon) > tileBounds.left
|
||||
&& Math.min(currentPt.lat, prevPt.lat) < tileBounds.top && Math.max(currentPt.lat, prevPt.lat) > tileBounds.bottom) {
|
||||
float startX = tileBox.getPixXFromLatLon(prevPt.lat, prevPt.lon);
|
||||
float startY = tileBox.getPixYFromLatLon(prevPt.lat, prevPt.lon);
|
||||
float endX = tileBox.getPixXFromLatLon(currentPt.lat, currentPt.lon);
|
||||
float endY = tileBox.getPixYFromLatLon(currentPt.lat, currentPt.lon);
|
||||
int prevColor = prevPt.getColor(scaleType.toColorizationType());
|
||||
int currentColor = currentPt.getColor(scaleType.toColorizationType());
|
||||
LinearGradient gradient = new LinearGradient(startX, startY, endX, endY, prevColor, currentColor, Shader.TileMode.CLAMP);
|
||||
Paint paint = new Paint(this.paint);
|
||||
paint.setShader(gradient);
|
||||
Path path = new Path();
|
||||
path.moveTo(startX, startY);
|
||||
path.lineTo(endX, endY);
|
||||
canvas.drawPath(path, paint);
|
||||
}
|
||||
prevPt = currentPt;
|
||||
}
|
||||
canvas.rotate(tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
|
||||
}
|
||||
}
|
||||
|
||||
public static class StandardTrack extends RenderableSegment {
|
||||
|
@ -193,7 +232,12 @@ public class Renderable {
|
|||
}
|
||||
|
||||
@Override public void drawSingleSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
||||
draw(culled.isEmpty() ? points : culled, p, canvas, tileBox);
|
||||
if (scaleType != null) {
|
||||
drawGradient(getPointsForDrawing(), p, canvas, tileBox);
|
||||
scaleType = null;
|
||||
} else {
|
||||
drawSolid(getPointsForDrawing(), p, canvas, tileBox);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,7 +259,7 @@ public class Renderable {
|
|||
@Override protected void startCuller(double newZoom) {}
|
||||
|
||||
@Override public void drawSingleSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
||||
draw(points, p, canvas, tileBox);
|
||||
drawSolid(points, p, canvas, tileBox);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ import net.osmand.plus.render.OsmandRenderer;
|
|||
import net.osmand.plus.render.OsmandRenderer.RenderingContext;
|
||||
import net.osmand.plus.routepreparationmenu.MapRouteInfoMenu;
|
||||
import net.osmand.plus.settings.backend.CommonPreference;
|
||||
import net.osmand.plus.track.GradientScaleType;
|
||||
import net.osmand.plus.track.SaveGpxAsyncTask;
|
||||
import net.osmand.plus.track.TrackDrawInfo;
|
||||
import net.osmand.plus.views.OsmandMapLayer;
|
||||
|
@ -66,6 +67,7 @@ import net.osmand.plus.views.layers.geometry.GpxGeometryWayContext;
|
|||
import net.osmand.render.RenderingRuleProperty;
|
||||
import net.osmand.render.RenderingRuleSearchRequest;
|
||||
import net.osmand.render.RenderingRulesStorage;
|
||||
import net.osmand.router.RouteColorize;
|
||||
import net.osmand.util.Algorithms;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
||||
|
@ -142,6 +144,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
|
|||
private CommonPreference<String> defaultTrackWidthPref;
|
||||
|
||||
private CommonPreference<Integer> currentTrackColorPref;
|
||||
private CommonPreference<GradientScaleType> currentTrackScaleType;
|
||||
private CommonPreference<String> currentTrackWidthPref;
|
||||
private CommonPreference<Boolean> currentTrackShowArrowsPref;
|
||||
private CommonPreference<Boolean> currentTrackShowStartFinishPref;
|
||||
|
@ -155,6 +158,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
|
|||
osmandRenderer = view.getApplication().getResourceManager().getRenderer().getRenderer();
|
||||
|
||||
currentTrackColorPref = view.getSettings().CURRENT_TRACK_COLOR;
|
||||
currentTrackScaleType = view.getSettings().CURRENT_TRACK_COLORIZATION;
|
||||
currentTrackWidthPref = view.getSettings().CURRENT_TRACK_WIDTH;
|
||||
currentTrackShowArrowsPref = view.getSettings().CURRENT_TRACK_SHOW_ARROWS;
|
||||
currentTrackShowStartFinishPref = view.getSettings().CURRENT_TRACK_SHOW_START_FINISH;
|
||||
|
@ -661,10 +665,22 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
|
|||
|
||||
private void drawSelectedFileSegments(SelectedGpxFile selectedGpxFile, boolean currentTrack, Canvas canvas,
|
||||
RotatedTileBox tileBox, DrawSettings settings) {
|
||||
GPXFile gpxFile = selectedGpxFile.getGpxFile();
|
||||
List<TrkSegment> segments = selectedGpxFile.getPointsToDisplay();
|
||||
GradientScaleType scaleType = getGradientScaleType(gpxFile);
|
||||
List<RouteColorize.RouteColorizationPoint> colorsOfPoints = null;
|
||||
if (scaleType != null) {
|
||||
RouteColorize colorize = new RouteColorize(view.getZoom(), gpxFile, scaleType.toColorizationType());
|
||||
colorize.setPalette(getColorizationPalette(gpxFile, scaleType));
|
||||
colorsOfPoints = colorize.getResult(false);
|
||||
}
|
||||
int startIdx = 0;
|
||||
for (TrkSegment ts : segments) {
|
||||
String width = getTrackWidthName(selectedGpxFile.getGpxFile(), defaultTrackWidthPref.get());
|
||||
int color = getTrackColor(selectedGpxFile.getGpxFile(), ts.getColor(cachedColor));
|
||||
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) {
|
||||
|
@ -677,11 +693,45 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
|
|||
}
|
||||
updatePaints(color, width, selectedGpxFile.isRoutePoints(), currentTrack, settings, tileBox);
|
||||
if (ts.renderer instanceof Renderable.RenderableSegment) {
|
||||
((Renderable.RenderableSegment) ts.renderer).drawSegment(view.getZoom(), paint, canvas, tileBox);
|
||||
Renderable.RenderableSegment renderableSegment = (Renderable.RenderableSegment) ts.renderer;
|
||||
if (scaleType != null) {
|
||||
renderableSegment.setGradientScaleType(scaleType);
|
||||
}
|
||||
renderableSegment.drawSegment(view.getZoom(), paint, canvas, tileBox);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private float getTrackWidth(String width, float defaultTrackWidth) {
|
||||
Float trackWidth = cachedTrackWidth.get(width);
|
||||
return trackWidth != null ? trackWidth : defaultTrackWidth;
|
||||
|
@ -702,6 +752,37 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
|
|||
return color != 0 ? color : defaultColor;
|
||||
}
|
||||
|
||||
private GradientScaleType getGradientScaleType(GPXFile gpxFile) {
|
||||
if (hasTrackDrawInfoForTrack(gpxFile)) {
|
||||
return trackDrawInfo.getGradientScaleType();
|
||||
} else if (gpxFile.showCurrentTrack) {
|
||||
return currentTrackScaleType.get();
|
||||
} else {
|
||||
GpxDataItem dataItem = gpxDbHelper.getItem(new File(gpxFile.path));
|
||||
if (dataItem != null && dataItem.getGradientScaleType() != null) {
|
||||
return dataItem.getGradientScaleType();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private int[] getColorizationPalette(GPXFile gpxFile, GradientScaleType scaleType) {
|
||||
if (hasTrackDrawInfoForTrack(gpxFile)) {
|
||||
return trackDrawInfo.getGradientPalette(scaleType);
|
||||
}
|
||||
GpxDataItem dataItem = gpxDbHelper.getItem(new File(gpxFile.path));
|
||||
if (dataItem == null) {
|
||||
return RouteColorize.colors;
|
||||
}
|
||||
if (scaleType == GradientScaleType.SPEED) {
|
||||
return dataItem.getGradientSpeedPalette();
|
||||
} else if (scaleType == GradientScaleType.ALTITUDE) {
|
||||
return dataItem.getGradientAltitudePalette();
|
||||
} else {
|
||||
return dataItem.getGradientSlopePalette();
|
||||
}
|
||||
}
|
||||
|
||||
private String getTrackWidthName(GPXFile gpxFile, String defaultWidth) {
|
||||
String width = null;
|
||||
if (hasTrackDrawInfoForTrack(gpxFile)) {
|
||||
|
|
Loading…
Reference in a new issue