Minimised calls to getPixXFromLatLon and getPixYFromLatLon during main draw loop

In the drawing loop, culling was performed on pixel boundaries based on x/y positions compared to the tileBox x/y position,
however calculating these from lat/lon was quite a slow process involving calls to getPixXFromLatLon and getPixYFromLatLon
which in turn had a horrendous amount of calculations happening. But since the tileBox has lat/lon and the pixel/lines being
drawn also have lat/lon it seems reasonable - despite potential spherical issues on lat/lon coordinates (?) to just use the
lat/lon as a visibility cull. So, that's what I've done and it does seem to work OK.  Heaps faster; aside from the actual
line draw we're talking a couple orders of magnitude improvement in speed.  Getting really zippy now, even when drawing
groups super big tracks, and I mean big - hundredsof thousands of points big.

The video at https://youtu.be/J1ppW3_hWds shows well over a thousand kilometers of tracks, 230 thousand points.
This commit is contained in:
Andrew Davie 2016-04-03 23:31:15 +10:00
parent 209203acd3
commit ceddb50e60
3 changed files with 88 additions and 66 deletions

View file

@ -193,7 +193,7 @@ public abstract class AsynchronousResampler extends AsyncTask<String,Integer,Str
boolean survivor[] = new boolean[nsize];
cullRamerDouglasPeucer(survivor, 0, nsize - 1);
if (!isCancelled()) {
culled = new ArrayList();
culled = new ArrayList<>();
survivor[0] = true;
for (int i = 0; i < nsize; i++) {
if (survivor[i]) {

View file

@ -323,6 +323,7 @@ public class GPXLayer extends OsmandMapLayer implements ContextMenuLayer.IContex
private void drawSelectedFilesSegments(Canvas canvas, RotatedTileBox tileBox,
List<SelectedGpxFile> selectedGPXFiles, DrawSettings settings) {
for (SelectedGpxFile g : selectedGPXFiles) {
List<TrkSegment> segments = g.getPointsToDisplay();
for (TrkSegment ts : segments) {
@ -333,11 +334,11 @@ public class GPXLayer extends OsmandMapLayer implements ContextMenuLayer.IContex
if (g.isShowCurrentTrack()) {
ts.renders.add(new Renderable.CurrentTrack(ts.points));
} 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.2));
ts.renders.add(new Renderable.Conveyor(ts.points, view, 5, 250));
ts.renders.add(new Renderable.Arrows(ts.points, view, 10, 250));
ts.renders.add(new Renderable.DistanceMarker(ts.points, 1000));
//ts.renders.add(new Renderable.Conveyor(ts.points, view, 5, 250));
//ts.renders.add(new Renderable.Arrows(ts.points, view, 10, 250));
//ts.renders.add(new Renderable.DistanceMarker(ts.points, 1000));
//ts.renders.add(new Renderable.Speed(ts.points, 50));
}
}

View file

@ -49,14 +49,14 @@ public class Renderable {
public static abstract class RenderableSegment {
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 double segmentSize;
protected QuadRect trackBounds;
double zoom = -1;
AsynchronousResampler culler = null; // The currently active resampler
protected Paint paint = null; // MUST be set by 'updateLocalPaint' before use
protected double zoom = -1;
protected AsynchronousResampler culler = null; // The currently active resampler
protected Paint paint = null; // MUST be set by 'updateLocalPaint' before use
public RenderableSegment(List <WptPt> points, double segmentSize) {
this.points = points;
@ -130,31 +130,38 @@ public class Renderable {
updateLocalPaint(p);
canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
QuadRect tileBounds = tileBox.getLatLonBounds();
float stroke = paint.getStrokeWidth() / 2;
float clipL = -stroke;
float clipB = -stroke;
float clipT = canvas.getHeight() + stroke;
float clipR = canvas.getWidth() + stroke;
WptPt pt = pts.get(0);
float lastx = tileBox.getPixXFromLatLon(pt.lat, pt.lon);
float lasty = tileBox.getPixYFromLatLon(pt.lat, pt.lon);
WptPt lastPt = pts.get(0);
float lastx = tileBox.getPixXFromLatLon(lastPt.lat, lastPt.lon);
float lasty = tileBox.getPixYFromLatLon(lastPt.lat, lastPt.lon);
boolean last = false;
int size = pts.size();
for (int i = 1; i < size; i++) {
pt = pts.get(i);
WptPt pt = pts.get(i);
float x = tileBox.getPixXFromLatLon(pt.lat, pt.lon);
float y = tileBox.getPixYFromLatLon(pt.lat, pt.lon);
if (Math.min(pt.lon, lastPt.lon) < tileBounds.right && Math.max(pt.lon, lastPt.lon) > tileBounds.left
&& Math.min(pt.lat, lastPt.lat) < tileBounds.top && Math.max(pt.lat, lastPt.lat) > tileBounds.bottom) {
if (!last) {
lastx = tileBox.getPixXFromLatLon(lastPt.lat, lastPt.lon);
lasty = tileBox.getPixYFromLatLon(lastPt.lat, lastPt.lon);
last = true;
}
float x = tileBox.getPixXFromLatLon(pt.lat, pt.lon);
float y = tileBox.getPixYFromLatLon(pt.lat, pt.lon);
if (Math.min(x, lastx) < clipR && Math.max(x, lastx) > clipL
&& Math.min(y, lasty) < clipT && Math.max(y, lasty) > clipB) {
canvas.drawLine(lastx, lasty, x, y, paint);
lastx = x;
lasty = y;
} else {
last = false;
}
lastx = x;
lasty = y;
lastPt = pt;
}
canvas.rotate(tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
}
@ -212,29 +219,39 @@ public class Renderable {
float bandWidth = paint.getStrokeWidth() * 3;
paint.setStrokeWidth(bandWidth);
float clipL = -bandWidth / 2;
float clipB = -bandWidth / 2;
float clipT = canvas.getHeight() + bandWidth / 2;
float clipR = canvas.getWidth() + bandWidth / 2;
QuadRect tileBounds = tileBox.getLatLonBounds();
WptPt pt = culled.get(0);
float lastx = tileBox.getPixXFromLatLon(pt.lat, pt.lon);
float lasty = tileBox.getPixYFromLatLon(pt.lat, pt.lon);
WptPt lastPt = culled.get(0);
float lastx = tileBox.getPixXFromLatLon(lastPt.lat, lastPt.lon);
float lasty = tileBox.getPixYFromLatLon(lastPt.lat, lastPt.lon);
boolean last = false;
int size = culled.size();
for (int i = 1; i < size; i++) {
pt = culled.get(i);
WptPt pt = culled.get(i);
float x = tileBox.getPixXFromLatLon(pt.lat, pt.lon);
float y = tileBox.getPixYFromLatLon(pt.lat, pt.lon);
if (Math.min(pt.lon, lastPt.lon) < tileBounds.right && Math.max(pt.lon, lastPt.lon) > tileBounds.left
&& Math.min(pt.lat, lastPt.lat) < tileBounds.top && Math.max(pt.lat, lastPt.lat) > tileBounds.bottom) {
if (!last) {
lastx = tileBox.getPixXFromLatLon(lastPt.lat, lastPt.lon);
lasty = tileBox.getPixYFromLatLon(lastPt.lat, lastPt.lon);
last = true;
}
float x = tileBox.getPixXFromLatLon(pt.lat, pt.lon);
float y = tileBox.getPixYFromLatLon(pt.lat, pt.lon);
if (Math.min(x, lastx) < clipR && Math.max(x, lastx) > clipL
&& Math.min(y, lasty) < clipT && Math.max(y, lasty) > clipB) {
paint.setColor(pt.colourARGB);
canvas.drawLine(lastx, lasty, x, y, paint);
lastx = x;
lasty = y;
} else {
last = false;
}
lastx = x;
lasty = y;
lastPt = pt;
}
canvas.rotate(tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
}
@ -278,34 +295,41 @@ public class Renderable {
paint.setColor(getComplementaryColor(p.getColor()));
float strokeRadius = paint.getStrokeWidth() / 2;
QuadRect tileBounds = tileBox.getLatLonBounds();
float clipL = -strokeRadius;
float clipB = -strokeRadius;
float clipT = canvas.getHeight() + strokeRadius;
float clipR = canvas.getWidth() + strokeRadius;
WptPt pt = culled.get(0);
float lastx = tileBox.getPixXFromLatLon(pt.lat, pt.lon);
float lasty = tileBox.getPixYFromLatLon(pt.lat, pt.lon);
WptPt lastPt = culled.get(0);
float lastx = tileBox.getPixXFromLatLon(lastPt.lat, lastPt.lon);
float lasty = tileBox.getPixYFromLatLon(lastPt.lat, lastPt.lon);
boolean last = false;
int intp = conveyor;
int size = culled.size();
for (int i = 1; i < size; i++, intp--) {
pt = culled.get(i);
WptPt pt = culled.get(i);
float x = tileBox.getPixXFromLatLon(pt.lat, pt.lon);
float y = tileBox.getPixYFromLatLon(pt.lat, pt.lon);
if ((intp & 7) < 3
&& Math.min(pt.lon, lastPt.lon) < tileBounds.right && Math.max(pt.lon, lastPt.lon) > tileBounds.left
&& Math.min(pt.lat, lastPt.lat) < tileBounds.top && Math.max(pt.lat, lastPt.lat) > tileBounds.bottom) {
if ((intp & 7) < 3) {
if (Math.min(x, lastx) < clipR && Math.max(x, lastx) > clipL
&& Math.min(y, lasty) < clipT && Math.max(y, lasty) > clipB) {
canvas.drawLine(lastx, lasty, x, y, paint);
if (!last) {
lastx = tileBox.getPixXFromLatLon(lastPt.lat, lastPt.lon);
lasty = tileBox.getPixYFromLatLon(lastPt.lat, lastPt.lon);
last = true;
}
float x = tileBox.getPixXFromLatLon(pt.lat, pt.lon);
float y = tileBox.getPixYFromLatLon(pt.lat, pt.lon);
canvas.drawLine(lastx, lasty, x, y, paint);
lastx = x;
lasty = y;
} else {
last = false;
}
lastx = x;
lasty = y;
lastPt = pt;
}
canvas.rotate(tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
}
@ -382,7 +406,7 @@ public class Renderable {
Renderable.startScreenRefresh(view, refreshRate);
}
private void drawArrows(int cachedC, double zoom, Canvas canvas, RotatedTileBox tileBox, boolean internal) {
private void drawArrows(int cachedC, Canvas canvas, RotatedTileBox tileBox, boolean internal) {
float scale = internal ? 0.6f : 1.0f;
@ -400,16 +424,13 @@ public class Renderable {
float lasty = Float.NEGATIVE_INFINITY;
int size = culled.size();
for (int i = 0; i < size; i++) {
for (int i = 0; i < size; i++, intp--) {
WptPt pt = culled.get(i);
intp--; // increment to go the other way!
float x = tileBox.getPixXFromLatLon(pt.lat, pt.lon);
float y = tileBox.getPixYFromLatLon(pt.lat, pt.lon);
boolean nextBroken = true;
if (Math.min(x, lastx) < clipR && Math.max(x, lastx) > clipL
&& Math.min(y, lasty) < clipT && Math.max(y, lasty) > clipB) {
@ -430,7 +451,7 @@ public class Renderable {
nextBroken = false;
// arrowhead...
if (segment == 0 && lasty != -Float.NEGATIVE_INFINITY) {
if (segment == 0 && lasty != Float.NEGATIVE_INFINITY) {
float sw = stroke * segpiece * scale;
paint.setStrokeWidth(sw);
double angle = Math.atan2(lasty - y, lastx - x);
@ -461,8 +482,8 @@ public class Renderable {
updateLocalPaint(p);
canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
int cachedC = conveyor;
drawArrows(cachedC, zoom, canvas, tileBox, false);
drawArrows(cachedC, zoom, canvas, tileBox, true);
drawArrows(cachedC, canvas, tileBox, false);
drawArrows(cachedC, canvas, tileBox, true);
canvas.rotate(tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
}
}