Reputation: 44228
I have a path()
which is created from a json object. I pass the JSON object to my overlay object in my constructor.
Objects that extend Overlay
always call draw()
any time the map is moved, rendered, touched, anything. Even if your overlay isn't visible or nearby or anything.
I want to generate my path object in a for loop, actually a nested loop as the json object contains nested json arrays. This has a high theoretical computation time, so I don't want to do it in the draw()
method. Instead I tried to do the logic in my constructor, to make the path()
file, and then call that path file only once in the draw()
method where required here canvas.drawPath(path, mPaint);
Unfortunately, when I create my path in the constructor, it does not pan with the map. But when I create it, the exact same code, in the draw()
method, it does have the desired functionality: a path that fences a portion of the map.
The problem is that the draw()
method will then call my double for loop over and over again, and the performance hit is obvious and debilitating. Even putting the loop in new Thread()
within draw() does not help performance. Running it in the constructor would be ideal, but then the path does not pan with the map.
similarly putting a private boolean flip within the draw()
method, to make the desired code only run, does not work either. the path will not appear on the map unless it is being constantly redrawn, which is too arduous of a task.
The problem with other answers on this site regarding this issue is that people are making squares, circles and images, which only require one call within draw()
, not a loop to generate the path.
suggestions? something about the draw()
method helps overlays stick to the map, how can I run my for loop only once
Upvotes: 1
Views: 567
Reputation: 12058
Although you have already accept an answer, here goes the approach I use for this. In the example code bellow, I´m using the old MapView
, but the concept should work in any version. I'm also using it with mapforges
(with minimum adjustments).
Concept
path
on first draw()
call, and record position of point(0)draw()
if point(0) position changed, map has moved. Offset ´path` by same value before draw it.path
object.Performance
With a 10.000 points path in a medium range device it takes about 2 ms
per cached draw()
call. When path needs to be rebuild (when zoom changed) it takes about 80 ms
.
Of cource you can also cache the different path zoom levels, trading a litle more performance by some more memory.
Example Code
The draw()method only checks if there is a zoom change (if so ask the path to be rebuild) and if map has moved (if so offset path) and finally draws the path.
@Override
public void draw(Canvas canvas, MapView mapview, boolean shadow) {
super.draw(canvas, mapview, shadow);
if(shadow) return;
if(mp.getPoints() == null || mp.getPoints().size() < 2) return;
Projection projection = mapview.getProjection();
int lonSpanNew = projection.fromPixels(0,mapview.getHeight()/2).getLongitudeE6() -
projection.fromPixels(mapview.getWidth(),mapview.getHeight()/2).getLongitudeE6();
if(lonSpanNew != pathInitialLonSpan)
pathBuild();
else{ //check if path need to be offset
projection.toPixels(mp.getPoints().get(0), p1);
if(p1.x != pathInitialPoint.x || p1.y != pathInitialPoint.y){
path.offset(p1.x - pathInitialPoint.x, p1.y - pathInitialPoint.y);
pathInitialPoint.x = p1.x;
pathInitialPoint.y = p1.y;
}
}
canvas.drawPath(path, paint);
}
The path has to be built every time zoom changes. The zoom change detection is done using pathInitialLonSpan as getZoomLevel() is not shyncronous with map zoom animation.
private void pathBuild(){
path.rewind();
if(mp.getPoints() == null || mp.getPoints().size() < 2) return;
Projection projection = mapView.getProjection();
pathInitialLonSpan = projection.fromPixels(0,mapView.getHeight()/2).getLongitudeE6() -
projection.fromPixels(mapView.getWidth(),mapView.getHeight()/2).getLongitudeE6();
projection.toPixels(mp.getPoints().get(0), pathInitialPoint);
path.moveTo(pathInitialPoint.x,pathInitialPoint.y);
for(int i=1; i<mp.getPoints().size(); i++){
projection.toPixels(mp.getPoints().get(i), p1);
int distance2 = (pPrev.x - p1.x) * (pPrev.x - p1.x) + (pPrev.y - p1.y) * (pPrev.y - p1.y);
if(distance2 > 9){
path.lineTo(p1.x,p1.y);
pPrev.set(p1.x, p1.y);
}
}
Regards.
Upvotes: 0
Reputation: 39836
A few considerations for you:
you should not be overriding the draw() method. The Overlay class already takes care of the whole panning operation for you. Read the docs that you'll find it.
the new API is a few thousand times easier to use. With a simple code such as:
PolylineOptions p = new new PolylineOptions().width(3).color(0xFFEE8888);
for( //... first for .... ){
for( // .... how many you'll need to nest... ){
// compute your JSON and get lat/long pair
p.add(new LatLng(lat, lon));
}
}
getMap().addPolyline(p);
and let the map class deal with the panning and zooming.
Upvotes: 2