I am having trouble with overlays. I have drawn a polygon overlay on the map, however, when I zoom in or out, the edges no longer align with where I want them to. How can I fix this?
Here is what it looks like when I start the app (it covers the whole parking lot perfectly):
Correct
Here is what it looks like when I zoom out (edges no longer line up with parking lot. The overlay looks a bit bigger than the parking lot):
Zoomed out
Also, it doesn't align well when I zoom in. In this case the overlay is a bit smaller that the parking lot. (Sorry, stackoverflow won't let me post more that 2 links)
Any suggestions on how to fix this?
Here is the code:
private Projection projection;
private List<Overlay> mapOverlays;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MapView mapView = (MapView) findViewById(R.id.mapView);
mapView.setBuiltInZoomControls(true);
MapController mc = mapView.getController();
mc.setZoom(17);
mc.animateTo(new GeoPoint((int)(32.734248*1E6), (int)(-97.113448*1E6)));
mapOverlays = mapView.getOverlays();
projection = mapView.getProjection();
mapOverlays.add(new MyOverlay());
mapView.postInvalidate();
}
@Override
protected boolean isRouteDisplayed() {
return false;
}
class MyOverlay extends Overlay{
public MyOverlay(){
}
public void draw(Canvas canvas, MapView mapv, boolean shadow){
super.draw(canvas, mapv, shadow);
Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setAlpha(100);
GeoPoint gP1 = new GeoPoint(32733839,-97112976);
GeoPoint gP2 = new GeoPoint(32733875, -97113448);
GeoPoint gP3 = new GeoPoint(32734961,-97113455);
GeoPoint gP4 = new GeoPoint(32734953, -97112962);
Point p1 = new Point();
Point p2 = new Point();
Point p3 = new Point();
Point p4 = new Point();
Path path = new Path();
projection.toPixels(gP1, p1);
projection.toPixels(gP2, p2);
projection.toPixels(gP3, p3);
projection.toPixels(gP4, p4);
path.moveTo(p1.x, p1.y);
path.lineTo(p2.x,p2.y);
path.lineTo(p3.x,p3.y);
path.lineTo(p4.x,p4.y);
canvas.drawPath(path, mPaint);
}
}
}
Everytime the zoom changes, Projection
also changes. Even when you make a large move in the map without changing zoom, projection may change.
To correct your code you need to add the line as bellow:
public void draw(Canvas canvas, MapView mapv, boolean shadow){
super.draw(canvas, mapv, shadow);
Projection projection = mapv.getProjection(); //Add this line
Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setAlpha(100);
GeoPoint gP1 = new GeoPoint(32733839,-97112976);
GeoPoint gP2 = new GeoPoint(32733875, -97113448);
GeoPoint gP3 = new GeoPoint(32734961,-97113455);
GeoPoint gP4 = new GeoPoint(32734953, -97112962);
Point p1 = new Point();
Point p2 = new Point();
Point p3 = new Point();
Point p4 = new Point();
Path path = new Path();
projection.toPixels(gP1, p1);
projection.toPixels(gP2, p2);
projection.toPixels(gP3, p3);
projection.toPixels(gP4, p4);
path.moveTo(p1.x, p1.y);
path.lineTo(p2.x,p2.y);
path.lineTo(p3.x,p3.y);
path.lineTo(p4.x,p4.y);
canvas.drawPath(path, mPaint);
}
And you can remove the projection created in onCreate()
.
--EDITED--
Improvements - Quick wins
onDraw()
is called twice eveytime you move the map or change zoom level, so it's important to make it as possible. Bellow you can find some suggestions:
private List<Overlay> mapOverlays;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MapView mapView = (MapView) findViewById(R.id.mapView);
mapView.setBuiltInZoomControls(true);
MapController mc = mapView.getController();
mc.setZoom(17);
mc.animateTo(new GeoPoint((int)(32.734248*1E6), (int)(-97.113448*1E6)));
mapOverlays = mapView.getOverlays();
mapOverlays.add(new MyOverlay());
mapView.postInvalidate();
}
@Override
protected boolean isRouteDisplayed() {
return false;
}
class MyOverlay extends Overlay{
//Moved objects that need only one instance to the initialization, to avoid creating a new copy of each every time draw() runs
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private GeoPoint gP1 = new GeoPoint(32733839,-97112976);
private GeoPoint gP2 = new GeoPoint(32733875, -97113448);
private GeoPoint gP3 = new GeoPoint(32734961,-97113455);
private GeoPoint gP4 = new GeoPoint(32734953, -97112962);
private Point p1 = new Point();
private Point p2 = new Point();
private Point p3 = new Point();
private Point p4 = new Point();
private Path path = new Path();
public MyOverlay(){
//mPaint settings done on class creation, to avoid repeating them on draw() call
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setAlpha(100);
}
public void draw(Canvas canvas, MapView mapv, boolean shadow){
super.draw(canvas, mapv, shadow);
//draw is always called twice, one with shadow equal to true and one to false.
//This can be used to draw the same image with shadow
//But you are not using shadows, so you can immediately return half of the calls, and reduce the draw() effort by half
if(shadow) return;
Projection projection = mapv.getProjection();
projection.toPixels(gP1, p1);
projection.toPixels(gP2, p2);
projection.toPixels(gP3, p3);
projection.toPixels(gP4, p4);
path.rewind();
path.moveTo(p1.x,p1.y);
path.lineTo(p2.x,p2.y);
path.lineTo(p3.x,p3.y);
path.lineTo(p4.x,p4.y);
canvas.drawPath(path, mPaint);
}
}
Improvements - a more complex one
The improvement I'm describing bellow is a overkill for you current overlay. It only makes sense when you have realy huge paths to draw.
When you move the map, without zomming it, with the code above the path will be recreated and redrawn in a different place to be aligned with the map. But the path you are creating is the same size, same shape as the previous one, just in a different position.
You can achive the same result using:
path.offset(dx, dy);
where dx, dy
is the number of pixels you need to move the path to keep it aligned with the map. Of course, you need to keep track where was the map last time you offset the path, so you can offset the path to the new map position.
If you have a path with several thousands of points, offseting the path will be about 100 times faster then redrawing it.
You also need to keep track on zoom level, as you still need to recreate the path when zoom changes. You can't use getCurrentZoomLevel()
to do it, as it´s not synchronized with zoom animation. You need to test longitudeSpan()
value changes, to be able to have a synchronized path creation with zoom change animation.
I hope this will help.
Regards.