"Massive" optimisation - on-demand culling for GPX tracks
A bit of inspiration; rather than trigger the culling for tracks when they are loaded and every time you zoom (i.e, creating the resampled or simplified tracks such as standard track (Ramer Douglas Peucer line reduction), arrows, speed, markers (all resample)... now the system uses the bounding rectangle of the original track to trigger the cull(s) only when the track is onscreen and viewable. This now allows dozens of tracks to be loaded without initial slowdown caused by all those resamplers and cullers running in the background. It's an on-demand system, in other words. So, whereas previously if you had 20 GPX tracks loaded, and you zoomed in/out, then all 20 of those tracks would start a Ramer-Douglas-Peucer resize, which would significantly slow down the whole thing (even though they are asynchronous they do take a toll on performance). Now, when you zoom in/out, only visible tracks (or ones which become visible) will start the RDP resize.
This commit is contained in:
parent
66128c8c66
commit
3e13d309f6
3 changed files with 57 additions and 67 deletions
|
@ -220,15 +220,9 @@ public class GPXUtilities {
|
||||||
return convert(splitSegments);
|
return convert(splitSegments);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void recalculateRenderScales(double zoom) {
|
public void drawRenderers(double zoom, Paint p, Canvas c, RotatedTileBox tb) {
|
||||||
for (Renderable.RenderableSegment rs : renders) {
|
for (Renderable.RenderableSegment rs : renders) {
|
||||||
rs.recalculateRenderScale(zoom);
|
rs.drawSegment(zoom, p, c, tb);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void drawRenderers(Paint p, Canvas c, RotatedTileBox tb) {
|
|
||||||
for (Renderable.RenderableSegment rs : renders) {
|
|
||||||
rs.drawSingleSegment(p, c, tb);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -205,7 +205,7 @@ public class GPXLayer extends OsmandMapLayer implements ContextMenuLayer.IContex
|
||||||
if(points != null) {
|
if(points != null) {
|
||||||
updatePaints(0, false, false, settings, tileBox);
|
updatePaints(0, false, false, settings, tileBox);
|
||||||
for (TrkSegment ts : points)
|
for (TrkSegment ts : points)
|
||||||
ts.drawRenderers(paint, canvas,tileBox);
|
ts.drawRenderers(view.getZoom(), paint, canvas, tileBox);
|
||||||
} else {
|
} else {
|
||||||
List<SelectedGpxFile> selectedGPXFiles = selectedGpxHelper.getSelectedGPXFiles();
|
List<SelectedGpxFile> selectedGPXFiles = selectedGpxHelper.getSelectedGPXFiles();
|
||||||
cache.clear();
|
cache.clear();
|
||||||
|
@ -334,17 +334,17 @@ public class GPXLayer extends OsmandMapLayer implements ContextMenuLayer.IContex
|
||||||
ts.renders.add(new Renderable.CurrentTrack(ts.points));
|
ts.renders.add(new Renderable.CurrentTrack(ts.points));
|
||||||
} else {
|
} else {
|
||||||
ts.renders.add(new Renderable.Altitude(ts.points, 10));
|
ts.renders.add(new Renderable.Altitude(ts.points, 10));
|
||||||
ts.renders.add(new Renderable.StandardTrack(ts.points, 17.5));
|
ts.renders.add(new Renderable.StandardTrack(ts.points, 17.2));
|
||||||
ts.renders.add(new Renderable.DistanceMarker(ts.points, 1000));
|
ts.renders.add(new Renderable.Conveyor(ts.points, view, 20, 250));
|
||||||
//ts.renders.add(new Renderable.Conveyor(ts.points, view, 20, 250));
|
|
||||||
//ts.renders.add(new Renderable.Speed(ts.points, 50));
|
|
||||||
ts.renders.add(new Renderable.Arrows(ts.points, view, 40, 250));
|
ts.renders.add(new Renderable.Arrows(ts.points, view, 40, 250));
|
||||||
|
ts.renders.add(new Renderable.DistanceMarker(ts.points, 1000));
|
||||||
|
//ts.renders.add(new Renderable.Speed(ts.points, 50));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ts.recalculateRenderScales(view.getZoom());
|
//ts.recalculateRenderScales(view.getZoom());
|
||||||
updatePaints(ts.getColor(cachedColor), g.isRoutePoints(), g.isShowCurrentTrack(), settings, tileBox);
|
updatePaints(ts.getColor(cachedColor), g.isRoutePoints(), g.isShowCurrentTrack(), settings, tileBox);
|
||||||
ts.drawRenderers(paint, canvas, tileBox);
|
ts.drawRenderers(view.getZoom(), paint, canvas, tileBox);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,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 int pointSize;
|
||||||
|
|
||||||
protected QuadRect trackBounds;
|
protected QuadRect trackBounds;
|
||||||
double zoom = -1;
|
double zoom = -1;
|
||||||
|
@ -72,8 +73,15 @@ public class Renderable {
|
||||||
paint.setStrokeWidth(p.getStrokeWidth());
|
paint.setStrokeWidth(p.getStrokeWidth());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void recalculateRenderScale(double zoom) {}
|
public void startCuller(double zoom) {}
|
||||||
public void drawSingleSegment(Paint p, Canvas canvas, RotatedTileBox tileBox) {}
|
protected void drawSingleSegment(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?
|
||||||
|
startCuller(zoom);
|
||||||
|
drawSingleSegment(zoom, p, canvas, tileBox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void calculateBounds(List<WptPt> pts) {
|
private void calculateBounds(List<WptPt> pts) {
|
||||||
trackBounds = new QuadRect(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY,
|
trackBounds = new QuadRect(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY,
|
||||||
|
@ -81,9 +89,9 @@ public class Renderable {
|
||||||
updateBounds(pts, 0);
|
updateBounds(pts, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateBounds(List<WptPt> pts, int startIndex) {
|
protected void updateBounds(List<WptPt> pts, int startIndex) {
|
||||||
int size = pts.size();
|
pointSize = pts.size();
|
||||||
for (int i = startIndex; i < size; i++) {
|
for (int i = startIndex; i < pointSize; i++) {
|
||||||
WptPt pt = pts.get(i);
|
WptPt pt = pts.get(i);
|
||||||
trackBounds.right = Math.max(trackBounds.right, pt.lon);
|
trackBounds.right = Math.max(trackBounds.right, pt.lon);
|
||||||
trackBounds.left = Math.min(trackBounds.left, pt.lon);
|
trackBounds.left = Math.min(trackBounds.left, pt.lon);
|
||||||
|
@ -95,12 +103,11 @@ public class Renderable {
|
||||||
// When the asynchronous task has finished, it calls this function to set the 'culled' list
|
// When the asynchronous task has finished, it calls this function to set the 'culled' list
|
||||||
public void setRDP(List<WptPt> cull) {
|
public void setRDP(List<WptPt> cull) {
|
||||||
culled = cull;
|
culled = cull;
|
||||||
//calculateBounds(culled);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void draw(List<WptPt> pts, Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
protected void draw(List<WptPt> pts, Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
||||||
|
|
||||||
if (pts.size() > 1 && QuadRect.trivialOverlap(tileBox.getLatLonBounds(), trackBounds)) {
|
if (pts.size() > 1) {
|
||||||
|
|
||||||
updateLocalPaint(p);
|
updateLocalPaint(p);
|
||||||
canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
|
canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
|
||||||
|
@ -133,7 +140,6 @@ public class Renderable {
|
||||||
canvas.rotate(tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
|
canvas.rotate(tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------
|
||||||
|
@ -147,7 +153,7 @@ public class Renderable {
|
||||||
this.base = base;
|
this.base = base;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void recalculateRenderScale(double newZoom) {
|
@Override public void startCuller(double newZoom) {
|
||||||
|
|
||||||
// Here we create the 'shadow' resampled/culled points list, based on the asynchronous call.
|
// Here we create the 'shadow' resampled/culled points list, based on the asynchronous call.
|
||||||
// The asynchronous callback will set the variable 'culled', and that is preferentially used for rendering
|
// The asynchronous callback will set the variable 'culled', and that is preferentially used for rendering
|
||||||
|
@ -170,7 +176,7 @@ public class Renderable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void drawSingleSegment(Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
@Override public void drawSingleSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
||||||
draw(culled.isEmpty() ? points : culled, p, canvas, tileBox);
|
draw(culled.isEmpty() ? points : culled, p, canvas, tileBox);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,18 +192,16 @@ public class Renderable {
|
||||||
this.segmentSize = segmentSize;
|
this.segmentSize = segmentSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void recalculateRenderScale(double zoom) {
|
@Override public void startCuller(double zoom) {
|
||||||
if (culler == null) {
|
if (culler == null) {
|
||||||
culler = new AsynchronousResampler.ResampleAltitude(this, segmentSize); // once only!
|
culler = new AsynchronousResampler.ResampleAltitude(this, segmentSize); // once only!
|
||||||
culler.execute("");
|
culler.execute("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void drawSingleSegment(Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
@Override public void drawSingleSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
||||||
|
|
||||||
if (culled.size() > 1
|
|
||||||
&& QuadRect.trivialOverlap(tileBox.getLatLonBounds(), trackBounds)) {
|
|
||||||
|
|
||||||
|
if (culled.size() > 1) {
|
||||||
updateLocalPaint(p);
|
updateLocalPaint(p);
|
||||||
canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
|
canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
|
||||||
|
|
||||||
|
@ -241,7 +245,7 @@ public class Renderable {
|
||||||
super(pt, segmentSize);
|
super(pt, segmentSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void recalculateRenderScale(double zoom) {
|
@Override public void startCuller(double zoom) {
|
||||||
if (culler == null) {
|
if (culler == null) {
|
||||||
culler = new AsynchronousResampler.ResampleSpeed(this, segmentSize); // once only!
|
culler = new AsynchronousResampler.ResampleSpeed(this, segmentSize); // once only!
|
||||||
culler.execute("");
|
culler.execute("");
|
||||||
|
@ -262,7 +266,7 @@ public class Renderable {
|
||||||
Renderable.startScreenRefresh(view, refreshRate);
|
Renderable.startScreenRefresh(view, refreshRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void recalculateRenderScale(double zoom) {
|
@Override public void startCuller(double zoom) {
|
||||||
if (culler == null) {
|
if (culler == null) {
|
||||||
culler = new AsynchronousResampler.GenericResampler(this, segmentSize); // once only!
|
culler = new AsynchronousResampler.GenericResampler(this, segmentSize); // once only!
|
||||||
culler.execute("");
|
culler.execute("");
|
||||||
|
@ -276,11 +280,9 @@ public class Renderable {
|
||||||
return Color.HSVToColor(hsv);
|
return Color.HSVToColor(hsv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void drawSingleSegment(Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
@Override public void drawSingleSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
||||||
|
|
||||||
if (culled.size() > 1
|
|
||||||
&& QuadRect.trivialOverlap(tileBox.getLatLonBounds(), trackBounds)) {
|
|
||||||
|
|
||||||
|
if (culled.size() > 1) {
|
||||||
updateLocalPaint(p);
|
updateLocalPaint(p);
|
||||||
canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
|
canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
|
||||||
|
|
||||||
|
@ -331,8 +333,7 @@ public class Renderable {
|
||||||
this.segmentSize = segmentSize;
|
this.segmentSize = segmentSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void recalculateRenderScale(double zoom) {
|
@Override public void startCuller(double zoom) {
|
||||||
this.zoom = zoom;
|
|
||||||
if (culler == null) {
|
if (culler == null) {
|
||||||
culler = new AsynchronousResampler.GenericResampler(this, segmentSize); // once only
|
culler = new AsynchronousResampler.GenericResampler(this, segmentSize); // once only
|
||||||
culler.execute("");
|
culler.execute("");
|
||||||
|
@ -345,11 +346,9 @@ public class Renderable {
|
||||||
return lab;
|
return lab;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void drawSingleSegment(Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
@Override public void drawSingleSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
||||||
|
|
||||||
if (!culled.isEmpty() && zoom > 12
|
|
||||||
&& QuadRect.trivialOverlap(tileBox.getLatLonBounds(), trackBounds)) {
|
|
||||||
|
|
||||||
|
if (zoom > 12 && !culled.isEmpty()) {
|
||||||
updateLocalPaint(p);
|
updateLocalPaint(p);
|
||||||
canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
|
canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
|
||||||
|
|
||||||
|
@ -401,17 +400,16 @@ public class Renderable {
|
||||||
Renderable.startScreenRefresh(view, refreshRate);
|
Renderable.startScreenRefresh(view, refreshRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void recalculateRenderScale(double zoom) {
|
@Override public void startCuller(double zoom) {
|
||||||
this.zoom = zoom;
|
|
||||||
if (culler == null) {
|
if (culler == null) {
|
||||||
culler = new AsynchronousResampler.GenericResampler(this, segmentSize); // once
|
culler = new AsynchronousResampler.GenericResampler(this, segmentSize); // once
|
||||||
culler.execute("");
|
culler.execute("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawArrows(Canvas canvas, RotatedTileBox tileBox, boolean internal) {
|
private void drawArrows(double zoom, Canvas canvas, RotatedTileBox tileBox, boolean internal) {
|
||||||
|
|
||||||
float scale = internal ? 0.8f : 1.0f;
|
float scale = internal ? 0.6f : 1.0f;
|
||||||
|
|
||||||
float stroke = paint.getStrokeWidth();
|
float stroke = paint.getStrokeWidth();
|
||||||
double zoomlimit = zoom > 15 ? 15f : zoom;
|
double zoomlimit = zoom > 15 ? 15f : zoom;
|
||||||
|
@ -482,16 +480,14 @@ public class Renderable {
|
||||||
paint.setStrokeWidth(stroke);
|
paint.setStrokeWidth(stroke);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void drawSingleSegment(Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
@Override public void drawSingleSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
||||||
|
|
||||||
if (!culled.isEmpty() && zoom > 13
|
|
||||||
&& QuadRect.trivialOverlap(tileBox.getLatLonBounds(), trackBounds)) {
|
|
||||||
|
|
||||||
|
if (zoom > 13 && !culled.isEmpty()) {
|
||||||
updateLocalPaint(p);
|
updateLocalPaint(p);
|
||||||
canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
|
canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
|
||||||
cachedC = conveyor;
|
cachedC = conveyor;
|
||||||
drawArrows(canvas, tileBox, false);
|
drawArrows(zoom, canvas, tileBox, false);
|
||||||
drawArrows(canvas, tileBox, true);
|
drawArrows(zoom, canvas, tileBox, true);
|
||||||
canvas.rotate(tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
|
canvas.rotate(tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -501,18 +497,18 @@ public class Renderable {
|
||||||
|
|
||||||
public static class CurrentTrack extends RenderableSegment {
|
public static class CurrentTrack extends RenderableSegment {
|
||||||
|
|
||||||
private int size;
|
|
||||||
|
|
||||||
public CurrentTrack(List<WptPt> pt) {
|
public CurrentTrack(List<WptPt> pt) {
|
||||||
super(pt);
|
super(pt);
|
||||||
size = pt.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void drawSingleSegment(Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
@Override public void drawSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
||||||
if (points.size() != size) {
|
if (points.size() != pointSize) {
|
||||||
updateBounds(points, size); // use newly added points to recalculate bounding box
|
updateBounds(points, pointSize);
|
||||||
size = points.size();
|
|
||||||
}
|
}
|
||||||
|
drawSingleSegment(zoom, p, canvas, tileBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void drawSingleSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) {
|
||||||
draw(points, p, canvas, tileBox);
|
draw(points, p, canvas, tileBox);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue