Overlay behavior when zooming

岁酱吖の 提交于 2019-12-10 12:16:34

问题


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);
    }
}

}


回答1:


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.



来源:https://stackoverflow.com/questions/13051864/overlay-behavior-when-zooming

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!