Merge pull request #11403 from osmandapp/track_line_fixes
Track line fixes
This commit is contained in:
commit
e6a4be79f2
7 changed files with 258 additions and 143 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);
|
||||||
|
|
|
@ -340,6 +340,30 @@ public class OsmMapUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void simplifyDouglasPeucker(List<Node> nodes, int start, int end, List<Node> survivedNodes, double epsilon) {
|
||||||
|
double dmax = Double.NEGATIVE_INFINITY;
|
||||||
|
int index = -1;
|
||||||
|
|
||||||
|
Node startPt = nodes.get(start);
|
||||||
|
Node endPt = nodes.get(end);
|
||||||
|
|
||||||
|
for (int i = start + 1; i < end; i++) {
|
||||||
|
Node pt = nodes.get(i);
|
||||||
|
double d = MapUtils.getOrthogonalDistance(pt.getLatitude(), pt.getLongitude(),
|
||||||
|
startPt.getLatitude(), startPt.getLongitude(), endPt.getLatitude(), endPt.getLongitude());
|
||||||
|
if (d > dmax) {
|
||||||
|
dmax = d;
|
||||||
|
index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dmax > epsilon) {
|
||||||
|
simplifyDouglasPeucker(nodes, start, index, survivedNodes, epsilon);
|
||||||
|
simplifyDouglasPeucker(nodes, index, end, survivedNodes, epsilon);
|
||||||
|
} else {
|
||||||
|
survivedNodes.add(nodes.get(end));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static double orthogonalDistance(int zoom, Node nodeLineStart, Node nodeLineEnd, Node node) {
|
private static double orthogonalDistance(int zoom, Node nodeLineStart, Node nodeLineEnd, Node node) {
|
||||||
LatLon p = MapUtils.getProjection(node.getLatitude(), node.getLongitude(), nodeLineStart.getLatitude(),
|
LatLon p = MapUtils.getProjection(node.getLatitude(), node.getLongitude(), nodeLineStart.getLatitude(),
|
||||||
nodeLineStart.getLongitude(), nodeLineEnd.getLatitude(), nodeLineEnd.getLongitude());
|
nodeLineStart.getLongitude(), nodeLineEnd.getLatitude(), nodeLineEnd.getLongitude());
|
||||||
|
|
|
@ -35,6 +35,7 @@ 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 float DEFAULT_BASE = 17.2f;
|
||||||
private static final int MAX_SLOPE_VALUE = 25;
|
private static final int MAX_SLOPE_VALUE = 25;
|
||||||
|
|
||||||
public enum ColorizationType {
|
public enum ColorizationType {
|
||||||
|
@ -241,7 +242,6 @@ public class RouteColorize {
|
||||||
if (dataList == null) {
|
if (dataList == null) {
|
||||||
dataList = new ArrayList<>();
|
dataList = new ArrayList<>();
|
||||||
for (int i = 0; i < latitudes.length; i++) {
|
for (int i = 0; i < latitudes.length; i++) {
|
||||||
//System.out.println(latitudes[i] + " " + longitudes[i] + " " + values[i]);
|
|
||||||
dataList.add(new RouteColorizationPoint(i, latitudes[i], longitudes[i], values[i]));
|
dataList.add(new RouteColorizationPoint(i, latitudes[i], longitudes[i], values[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -250,11 +250,13 @@ public class RouteColorize {
|
||||||
for (RouteColorizationPoint data : dataList) {
|
for (RouteColorizationPoint data : dataList) {
|
||||||
nodes.add(new net.osmand.osm.edit.Node(data.lat, data.lon, data.id));
|
nodes.add(new net.osmand.osm.edit.Node(data.lat, data.lon, data.id));
|
||||||
}
|
}
|
||||||
OsmMapUtils.simplifyDouglasPeucker(nodes, zoom + 5, 1, result, true);
|
|
||||||
|
double epsilon = Math.pow(2.0, DEFAULT_BASE - zoom);
|
||||||
|
result.add(nodes.get(0));
|
||||||
|
OsmMapUtils.simplifyDouglasPeucker(nodes, 0, nodes.size() - 1, result, epsilon);
|
||||||
|
|
||||||
List<RouteColorizationPoint> simplified = new ArrayList<>();
|
List<RouteColorizationPoint> simplified = new ArrayList<>();
|
||||||
|
for (int i = 1; i < result.size(); i++) {
|
||||||
for (int i = 1; i < result.size() - 1; i++) {
|
|
||||||
int prevId = (int) result.get(i - 1).getId();
|
int prevId = (int) result.get(i - 1).getId();
|
||||||
int currentId = (int) result.get(i).getId();
|
int currentId = (int) result.get(i).getId();
|
||||||
List<RouteColorizationPoint> sublist = dataList.subList(prevId, currentId);
|
List<RouteColorizationPoint> sublist = dataList.subList(prevId, currentId);
|
||||||
|
@ -477,7 +479,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;
|
||||||
|
|
|
@ -747,7 +747,9 @@ public class MapUtils {
|
||||||
return Math.sqrt((endX - startX) * (endX - startX) + (endY - startY) * (endY - startY));
|
return Math.sqrt((endX - startX) * (endX - startX) + (endY - startY) * (endY - startY));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static double getSqrtDistance(float startX, float startY, float endX, float endY) {
|
||||||
|
return Math.sqrt((endX - startX) * (endX - startX) + (endY - startY) * (endY - startY));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import android.widget.TextView;
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
|
||||||
import net.osmand.AndroidUtils;
|
import net.osmand.AndroidUtils;
|
||||||
|
import net.osmand.GPXUtilities.Elevation;
|
||||||
import net.osmand.GPXUtilities.GPXTrackAnalysis;
|
import net.osmand.GPXUtilities.GPXTrackAnalysis;
|
||||||
import net.osmand.PlatformUtil;
|
import net.osmand.PlatformUtil;
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
|
@ -92,7 +93,15 @@ public class TrackColoringCard extends BaseCard {
|
||||||
if (scaleType == GradientScaleType.SPEED) {
|
if (scaleType == GradientScaleType.SPEED) {
|
||||||
return gpxTrackAnalysis.isSpeedSpecified();
|
return gpxTrackAnalysis.isSpeedSpecified();
|
||||||
} else {
|
} else {
|
||||||
return gpxTrackAnalysis.isElevationSpecified();
|
if (!gpxTrackAnalysis.isElevationSpecified()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (Elevation elevation : gpxTrackAnalysis.elevationData) {
|
||||||
|
if (Float.isNaN(elevation.elevation)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import net.osmand.data.RotatedTileBox;
|
||||||
import net.osmand.plus.track.GradientScaleType;
|
import net.osmand.plus.track.GradientScaleType;
|
||||||
import net.osmand.plus.views.layers.geometry.GpxGeometryWay;
|
import net.osmand.plus.views.layers.geometry.GpxGeometryWay;
|
||||||
import net.osmand.util.Algorithms;
|
import net.osmand.util.Algorithms;
|
||||||
|
import net.osmand.util.MapUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -28,7 +29,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 +61,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 +88,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 +119,19 @@ 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);
|
drawSolid(points, borderPaint, canvas, tileBox);
|
||||||
|
drawGradient(points, paint, canvas, tileBox);
|
||||||
} else {
|
} else {
|
||||||
drawSolid(getPointsForDrawing(), p, canvas, tileBox);
|
drawSolid(getPointsForDrawing(), paint, 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 +144,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();
|
||||||
|
@ -177,7 +168,7 @@ public class Renderable {
|
||||||
float lastX = tileBox.getPixXFromLatLon(lastPt.lat, lastPt.lon);
|
float lastX = tileBox.getPixXFromLatLon(lastPt.lat, lastPt.lon);
|
||||||
float lastY = tileBox.getPixYFromLatLon(lastPt.lat, lastPt.lon);
|
float lastY = tileBox.getPixYFromLatLon(lastPt.lat, lastPt.lon);
|
||||||
if (!path.isEmpty()) {
|
if (!path.isEmpty()) {
|
||||||
canvas.drawPath(path, paint);
|
canvas.drawPath(path, p);
|
||||||
}
|
}
|
||||||
path.reset();
|
path.reset();
|
||||||
path.moveTo(lastX, lastY);
|
path.moveTo(lastX, lastY);
|
||||||
|
@ -191,58 +182,99 @@ public class Renderable {
|
||||||
lastPt = pt;
|
lastPt = pt;
|
||||||
}
|
}
|
||||||
if (!path.isEmpty()) {
|
if (!path.isEmpty()) {
|
||||||
canvas.drawPath(path, paint);
|
canvas.drawPath(path, p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void drawGradient(List<WptPt> pts, Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
protected void drawGradient(List<WptPt> pts, Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
||||||
QuadRect tileBounds = tileBox.getLatLonBounds();
|
QuadRect tileBounds = tileBox.getLatLonBounds();
|
||||||
Path currentPath = new Path();
|
Path path = new Path();
|
||||||
Path nextPath = new Path();
|
boolean recalculateLastXY = true;
|
||||||
Paint paint = new Paint(this.paint);
|
WptPt lastPt = pts.get(0);
|
||||||
|
|
||||||
WptPt prevWpt = pts.get(0);
|
List<PointF> gradientPoints = new ArrayList<>();
|
||||||
WptPt currWpt = pts.get(1);
|
List<Integer> gradientColors = new ArrayList<>();
|
||||||
|
float gradientAngle = 0;
|
||||||
PointF prevXY = new PointF();
|
|
||||||
PointF currXY = new PointF();
|
|
||||||
PointF nextXY = new PointF();
|
|
||||||
|
|
||||||
boolean currLineVisible = arePointsInsideTile(prevWpt, currWpt, tileBounds);
|
|
||||||
boolean nextLineVisible;
|
|
||||||
|
|
||||||
if (currLineVisible) {
|
|
||||||
pixXYFromWptPt(tileBox, prevXY, prevWpt);
|
|
||||||
pixXYFromWptPt(tileBox, currXY, currWpt);
|
|
||||||
canvas.drawPath(pathFromStartEnd(currentPath, prevXY, currXY), borderPaint);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 1; i < pts.size(); i++) {
|
for (int i = 1; i < pts.size(); i++) {
|
||||||
currWpt = pts.get(i);
|
WptPt pt = pts.get(i);
|
||||||
WptPt nextWpt = i + 1 == pts.size() ? null : pts.get(i + 1);
|
WptPt nextPt = i + 1 < pts.size() ? pts.get(i + 1) : null;
|
||||||
|
float nextX = nextPt == null ? 0 : tileBox.getPixXFromLatLon(nextPt.lat, nextPt.lon);
|
||||||
|
float nextY = nextPt == null ? 0 : tileBox.getPixYFromLatLon(nextPt.lat, nextPt.lon);
|
||||||
|
float lastX = 0;
|
||||||
|
float lastY = 0;
|
||||||
|
if (arePointsInsideTile(pt, lastPt, tileBounds)) {
|
||||||
|
if (recalculateLastXY) {
|
||||||
|
recalculateLastXY = false;
|
||||||
|
lastX = tileBox.getPixXFromLatLon(lastPt.lat, lastPt.lon);
|
||||||
|
lastY = tileBox.getPixYFromLatLon(lastPt.lat, lastPt.lon);
|
||||||
|
if (!path.isEmpty()) {
|
||||||
|
p.setShader(createGradient(gradientPoints, gradientColors));
|
||||||
|
canvas.drawPath(path, p);
|
||||||
|
}
|
||||||
|
path.reset();
|
||||||
|
path.moveTo(lastX, lastY);
|
||||||
|
|
||||||
nextLineVisible = arePointsInsideTile(currWpt, nextWpt, tileBounds);
|
gradientPoints.clear();
|
||||||
if (nextWpt != null && nextLineVisible) {
|
gradientColors.clear();
|
||||||
pixXYFromWptPt(tileBox, currXY, currWpt);
|
gradientPoints.add(new PointF(lastX, lastY));
|
||||||
pixXYFromWptPt(tileBox, nextXY, nextWpt);
|
gradientColors.add(lastPt.getColor(scaleType.toColorizationType()));
|
||||||
canvas.drawPath(pathFromStartEnd(nextPath, currXY, nextXY), borderPaint);
|
}
|
||||||
|
float x = tileBox.getPixXFromLatLon(pt.lat, pt.lon);
|
||||||
|
float y = tileBox.getPixYFromLatLon(pt.lat, pt.lon);
|
||||||
|
path.lineTo(x, y);
|
||||||
|
gradientPoints.add(new PointF(x, y));
|
||||||
|
gradientColors.add(pt.getColor(scaleType.toColorizationType()));
|
||||||
|
|
||||||
|
if (gradientColors.size() == 2) {
|
||||||
|
gradientAngle = calculateAngle(lastX, lastY, x, y);
|
||||||
|
}
|
||||||
|
if (nextPt != null) {
|
||||||
|
float nextAngle = calculateAngle(x, y, nextX, nextY);
|
||||||
|
if (Math.abs(nextAngle - gradientAngle) > 20) {
|
||||||
|
recalculateLastXY = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
recalculateLastXY = true;
|
||||||
}
|
}
|
||||||
|
lastPt = pt;
|
||||||
if (currLineVisible) {
|
|
||||||
int prevColor = prevWpt.getColor(scaleType.toColorizationType());
|
|
||||||
int currentColor = currWpt.getColor(scaleType.toColorizationType());
|
|
||||||
LinearGradient gradient = new LinearGradient(prevXY.x, prevXY.y, currXY.x, currXY.y,
|
|
||||||
prevColor, currentColor, Shader.TileMode.CLAMP);
|
|
||||||
paint.setShader(gradient);
|
|
||||||
canvas.drawPath(currentPath, paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
prevWpt = currWpt;
|
|
||||||
currentPath.set(nextPath);
|
|
||||||
prevXY.set(currXY);
|
|
||||||
currXY.set(nextXY);
|
|
||||||
currLineVisible = nextLineVisible;
|
|
||||||
}
|
}
|
||||||
|
if (!path.isEmpty()) {
|
||||||
|
p.setShader(createGradient(gradientPoints, gradientColors));
|
||||||
|
canvas.drawPath(path, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinearGradient createGradient(List<PointF> gradientPoints, List<Integer> gradientColors) {
|
||||||
|
float gradientLength = 0;
|
||||||
|
List<Float> pointsLength = new ArrayList<>(gradientPoints.size() - 1);
|
||||||
|
for (int i = 1; i < gradientPoints.size(); i++) {
|
||||||
|
PointF start = gradientPoints.get(i - 1);
|
||||||
|
PointF end = gradientPoints.get(i);
|
||||||
|
pointsLength.add((float) MapUtils.getSqrtDistance(start.x, start.y, end.x, end.y));
|
||||||
|
gradientLength += pointsLength.get(i - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
float[] positions = new float[gradientPoints.size()];
|
||||||
|
positions[0] = 0;
|
||||||
|
for (int i = 1; i < gradientPoints.size(); i++) {
|
||||||
|
positions[i] = positions[i - 1] + pointsLength.get(i - 1) / gradientLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] colors = new int[gradientColors.size()];
|
||||||
|
for (int i = 0; i < gradientColors.size(); i++) {
|
||||||
|
colors[i] = gradientColors.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
PointF gradientStart = gradientPoints.get(0);
|
||||||
|
PointF gradientEnd = gradientPoints.get(gradientPoints.size() - 1);
|
||||||
|
return new LinearGradient(gradientStart.x, gradientStart.y, gradientEnd.x, gradientEnd.y,
|
||||||
|
colors, positions, Shader.TileMode.CLAMP);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float calculateAngle(float startX, float startY, float endX, float endY) {
|
||||||
|
return (float) Math.abs(Math.toDegrees(Math.atan2(endY - startY, endX - startX)));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean arePointsInsideTile(WptPt first, WptPt second, QuadRect tileBounds) {
|
protected boolean arePointsInsideTile(WptPt first, WptPt second, QuadRect tileBounds) {
|
||||||
|
@ -252,19 +284,6 @@ public class Renderable {
|
||||||
return Math.min(first.lon, second.lon) < tileBounds.right && Math.max(first.lon, second.lon) > tileBounds.left
|
return Math.min(first.lon, second.lon) < tileBounds.right && Math.max(first.lon, second.lon) > tileBounds.left
|
||||||
&& Math.min(first.lat, second.lat) < tileBounds.top && Math.max(first.lat, second.lat) > tileBounds.bottom;
|
&& Math.min(first.lat, second.lat) < tileBounds.top && Math.max(first.lat, second.lat) > tileBounds.bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected PointF pixXYFromWptPt(RotatedTileBox tileBox, PointF pointF, WptPt wptPt) {
|
|
||||||
pointF.x = tileBox.getPixXFromLatLon(wptPt.lat, wptPt.lon);
|
|
||||||
pointF.y = tileBox.getPixYFromLatLon(wptPt.lat, wptPt.lon);
|
|
||||||
return pointF;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Path pathFromStartEnd(Path path, PointF start, PointF end) {
|
|
||||||
path.reset();
|
|
||||||
path.moveTo(start.x, start.y);
|
|
||||||
path.lineTo(end.x, end.y);
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class StandardTrack extends RenderableSegment {
|
public static class StandardTrack extends RenderableSegment {
|
||||||
|
|
|
@ -69,6 +69,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;
|
||||||
|
|
||||||
|
@ -117,6 +119,8 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
|
||||||
private MapMarkersHelper mapMarkersHelper;
|
private MapMarkersHelper mapMarkersHelper;
|
||||||
private GpxSelectionHelper selectedGpxHelper;
|
private GpxSelectionHelper selectedGpxHelper;
|
||||||
|
|
||||||
|
private final Map<String, CachedTrack> segmentsCache = new HashMap<>();
|
||||||
|
|
||||||
private List<WptPt> cache = new ArrayList<>();
|
private List<WptPt> cache = new ArrayList<>();
|
||||||
private Map<WptPt, SelectedGpxFile> pointFileMap = new HashMap<>();
|
private Map<WptPt, SelectedGpxFile> pointFileMap = new HashMap<>();
|
||||||
private MapTextLayer textLayer;
|
private MapTextLayer textLayer;
|
||||||
|
@ -459,7 +463,10 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
|
||||||
float trackWidth = getTrackWidth(width, defaultTrackWidth);
|
float trackWidth = getTrackWidth(width, defaultTrackWidth);
|
||||||
int trackColor = getTrackColor(selectedGpxFile.getGpxFile(), cachedColor);
|
int trackColor = getTrackColor(selectedGpxFile.getGpxFile(), cachedColor);
|
||||||
int arrowColor = UiUtilities.getContrastColor(view.getApplication(), trackColor, false);
|
int arrowColor = UiUtilities.getContrastColor(view.getApplication(), trackColor, false);
|
||||||
for (TrkSegment segment : selectedGpxFile.getPointsToDisplay()) {
|
GradientScaleType scaleType = getGradientScaleType(selectedGpxFile.getGpxFile());
|
||||||
|
List<TrkSegment> segments = scaleType != null ?
|
||||||
|
getCachedSegments(selectedGpxFile, scaleType) : selectedGpxFile.getPointsToDisplay();
|
||||||
|
for (TrkSegment segment : segments) {
|
||||||
if (segment.renderer instanceof Renderable.RenderableSegment) {
|
if (segment.renderer instanceof Renderable.RenderableSegment) {
|
||||||
((Renderable.RenderableSegment) segment.renderer)
|
((Renderable.RenderableSegment) segment.renderer)
|
||||||
.drawGeometry(canvas, tileBox, correctedQuadRect, arrowColor, trackColor, trackWidth);
|
.drawGeometry(canvas, tileBox, correctedQuadRect, arrowColor, trackColor, trackWidth);
|
||||||
|
@ -681,26 +688,24 @@ 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();
|
boolean visible = QuadRect.trivialOverlap(tileBox.getLatLonBounds(), GPXUtilities.calculateTrackBounds(selectedGpxFile.getPointsToDisplay()));
|
||||||
GPXFile gpxFile = selectedGpxFile.getGpxFile();
|
if (!selectedGpxFile.getGpxFile().hasTrkPt() || !visible) {
|
||||||
List<TrkSegment> segments = selectedGpxFile.getPointsToDisplay();
|
return;
|
||||||
GradientScaleType scaleType = getGradientScaleType(gpxFile);
|
}
|
||||||
List<RouteColorize.RouteColorizationPoint> colorsOfPoints = null;
|
|
||||||
|
GPXFile gpxFile = selectedGpxFile.getGpxFile();
|
||||||
if (needCalculatePointsColors(segments, scaleType)) {
|
GradientScaleType scaleType = getGradientScaleType(gpxFile);
|
||||||
RouteColorize colorize = new RouteColorize(view.getZoom(), gpxFile, selectedGpxFile.getTrackAnalysis(app),
|
List<TrkSegment> segments = new ArrayList<>();
|
||||||
scaleType.toColorizationType(), app.getSettings().getApplicationMode().getMaxSpeed());
|
|
||||||
colorize.setPalette(getColorizationPalette(gpxFile, scaleType));
|
if (scaleType == null) {
|
||||||
colorsOfPoints = colorize.getResult(false);
|
segments.addAll(selectedGpxFile.getPointsToDisplay());
|
||||||
|
} else {
|
||||||
|
segments.addAll(getCachedSegments(selectedGpxFile, 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) {
|
||||||
|
@ -721,53 +726,16 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean needCalculatePointsColors(List<TrkSegment> segments, GradientScaleType scaleType) {
|
private List<TrkSegment> getCachedSegments(SelectedGpxFile selectedGpxFile, GradientScaleType scaleType) {
|
||||||
if (scaleType == null) {
|
GPXFile gpxFile = selectedGpxFile.getGpxFile();
|
||||||
return false;
|
String path = gpxFile.path;
|
||||||
|
long modifiedTime = gpxFile.modifiedTime;
|
||||||
|
CachedTrack cachedTrack = segmentsCache.get(path);
|
||||||
|
if (cachedTrack == null) {
|
||||||
|
cachedTrack = new CachedTrack(view.getApplication(), modifiedTime);
|
||||||
|
segmentsCache.put(path, cachedTrack);
|
||||||
}
|
}
|
||||||
RouteColorize.ColorizationType colorizationType = scaleType.toColorizationType();
|
return cachedTrack.getCachedSegments(selectedGpxFile, view.getZoom(), scaleType, getColorizationPalette(gpxFile, scaleType));
|
||||||
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) {
|
|
||||||
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) {
|
||||||
|
@ -1247,4 +1215,76 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
|
||||||
view.getApplication().getItineraryHelper().runSynchronization(group);
|
view.getApplication().getItineraryHelper().runSynchronization(group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class CachedTrack {
|
||||||
|
|
||||||
|
private OsmandApplication app;
|
||||||
|
|
||||||
|
private long modifiedTime;
|
||||||
|
private final Map<String, List<TrkSegment>> cache = new HashMap<>();
|
||||||
|
|
||||||
|
public CachedTrack(@NonNull OsmandApplication app, long modifiedTime) {
|
||||||
|
this.app = app;
|
||||||
|
this.modifiedTime = modifiedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TrkSegment> getCachedSegments(@NonNull SelectedGpxFile selectedGpxFile, int zoom,
|
||||||
|
@NonNull GradientScaleType scaleType,
|
||||||
|
int[] gradientPalette) {
|
||||||
|
GPXFile gpxFile = selectedGpxFile.getGpxFile();
|
||||||
|
String trackId = zoom + "_" + scaleType.toString();
|
||||||
|
if (modifiedTime == gpxFile.modifiedTime) {
|
||||||
|
List<TrkSegment> segments = cache.get(trackId);
|
||||||
|
if (segments == null) {
|
||||||
|
segments = calculateGradientTrack(selectedGpxFile, zoom, scaleType, gradientPalette);
|
||||||
|
cache.put(trackId, segments);
|
||||||
|
}
|
||||||
|
return segments;
|
||||||
|
} else {
|
||||||
|
cache.clear();
|
||||||
|
modifiedTime = gpxFile.modifiedTime;
|
||||||
|
List<TrkSegment> segments = calculateGradientTrack(selectedGpxFile, zoom, scaleType, gradientPalette);
|
||||||
|
cache.put(trackId, segments);
|
||||||
|
return segments;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TrkSegment> calculateGradientTrack(SelectedGpxFile selectedGpxFile, int zoom,
|
||||||
|
GradientScaleType scaleType, int[] gradientPalette) {
|
||||||
|
GPXFile gpxFile = selectedGpxFile.getGpxFile();
|
||||||
|
RouteColorize colorize = new RouteColorize(zoom, gpxFile, selectedGpxFile.getTrackAnalysis(app),
|
||||||
|
scaleType.toColorizationType(), app.getSettings().getApplicationMode().getMaxSpeed());
|
||||||
|
colorize.setPalette(gradientPalette);
|
||||||
|
List<RouteColorizationPoint> colorsOfPoints = colorize.getResult(true);
|
||||||
|
return createSimplifiedSegments(selectedGpxFile.getGpxFile(), colorsOfPoints, scaleType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TrkSegment> createSimplifiedSegments(GPXFile gpxFile,
|
||||||
|
List<RouteColorizationPoint> colorizationPoints,
|
||||||
|
GradientScaleType scaleType) {
|
||||||
|
List<TrkSegment> simplifiedSegments = new ArrayList<>();
|
||||||
|
ColorizationType colorizationType = scaleType.toColorizationType();
|
||||||
|
int id = 0;
|
||||||
|
int colorPointIdx = 0;
|
||||||
|
|
||||||
|
for (TrkSegment segment : gpxFile.getNonEmptyTrkSegments(false)) {
|
||||||
|
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 simplifiedSegments;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue