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.
Related
public PNGOverlay(Bitmap original, GeoPoint topLeftGeoPoint, GeoPoint bottomRightGeoPoint) {
this.original = Bitmap.createScaledBitmap(original, original.getWidth(), original.getHeight(), true);
...
topGeoPoint = topLeftGeoPoint;
bottomGeoPoint = bottomRightGeoPoint;
}
#Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
super.draw(canvas, mapView, false);
Projection projection = mapView.getProjection();
Point leftTop = new Point();
Point rightTop = new Point();
Point rightBottom = new Point();
Point leftBottom = new Point();
projection.toPixels(topGeoPoint, leftTop);
projection.toPixels(new GeoPoint(topGeoPoint.getLatitudeE6(), bottomGeoPoint.getLongitudeE6()), rightTop);
projection.toPixels(bottomGeoPoint, rightBottom);
projection.toPixels(new GeoPoint(bottomGeoPoint.getLatitudeE6(), topGeoPoint.getLongitudeE6()), leftBottom);
....
Paint paint = new Paint();
paint.setFilterBitmap(true);
paint.setAntiAlias(true);
canvas.drawBitmap(original, null, new Rect(leftTop.x, leftTop.y, rightBottom.x, rightBottom.y), paint);
....
}
I have a problem with this code. When I draw the png on the mapView, the png image is showed over the compass and over the GPS position indicatior? Any idea?
From your question is difficult to guess what is exactly your problem, but if you mean that the PNG is being drawn over the other overlays and you want it to be under them, you can solve it by adding the PNG overlay first to the mapview.
MapView calls the draw method from each overlay by the order that you add them:
mapview.getoverlays().add(overlay1);
mapview.getoverlays().add(overlay2);
This results in overlay2 being drawn over overlay1.
mapview.getoverlays().add(overlay2);
mapview.getoverlays().add(overlay1);
This results in overlay1 being drawn over overlay2.
good luck.
I'm working on a project to display a route in a MapView based on current location from GPS Provider. I'm able to draw the path between two points but the problem starts when the location changes to a new point causing the draw path to erase.
Basically my Location Listener saves the current GeoPoint and sets the new GeoPoint location and after that it starts the overlay to draw a path based on those two points.I think an option could be saving the coordinates to a database and then pulling the information from there to draw the path.
Here is my code so far:
private class MyLocationListener implements LocationListener {
public void onLocationChanged(Location location) {
Toast.makeText(getBaseContext(), "Tracking device..",
Toast.LENGTH_SHORT).show();
gp2 = new GeoPoint(
gp1.getLatitudeE6(),
gp1.getLongitudeE6());
gp1 = new GeoPoint(
(int) (location.getLatitude() * 1000000),
(int) (location.getLongitude() * 1000000));
myOverlay = new MapOverlay();
mapOverlays_route.add(myOverlay);
myMapView.invalidate();
}
}
class MapOverlay extends com.google.android.maps.Overlay{
public MapOverlay(){
}
public void draw(Canvas canvas, MapView mapv, boolean shadow){
super.draw(canvas, mapv, shadow);
//Configuring the paint brush
Paint mPaint = new Paint();
mPaint.setDither(true);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(4);
Point p1 = new Point();
Point p2 = new Point();
Path path1 = new Path();
Path path2 = new Path();
projection.toPixels(gp1, p1);
projection.toPixels(gp2, p2);
path1.moveTo(p2.x, p2.y);//current location
path1.lineTo(p1.x,p1.y);//new location
canvas.drawPath(path1, mPaint);//drawing the path
}
}
Like you said, you need to have these coordinates stored somewhere. These can be stored in a database or a more temporary local list. The draw method must also iterate between all of the coordinates you store.
I am implementing an app using Google Maps. When the app comes into the foreground I am getting the current position lat,long and push pin on the map.
But I require that when I am moving, I want to draw a root path based on my movement.
If any one knows the solution, please help me.
Thanks in advance.
Have you tried to make a class which exends Overlay?
class MyOverlay extends Overlay{
public MyOverlay(){
}
public void draw(Canvas canvas, MapView mapv, boolean shadow){
super.draw(canvas, mapv, shadow);
Paint mPaint = new Paint();
mPaint.setDither(true);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(2);
GeoPoint gP1 = new GeoPoint(19240000,-99120000);
GeoPoint gP2 = new GeoPoint(37423157, -122085008);
Point p1 = new Point();
Point p2 = new Point();
Path path = new Path();
projection.toPixels(gP1, p1);
projection.toPixels(gP2, p2);
path.moveTo(p2.x, p2.y);
path.lineTo(p1.x,p1.y);
canvas.drawPath(path, mPaint);
}
Use it this way:
private List<Overlay> mapOverlays;
private Projection projection;
mapOverlays = mapView.getOverlays();
projection = mapView.getProjection();
mapOverlays.add(new MyOverlay());
Hope this helps!
i have done this:
public class LongTravlOverlay extends Overlay{
private Projection projection;
private List<GeoPoint> glist;
GeoPoint gP1;
GeoPoint gP2;
public LongTravlOverlay(Projection a,List<GeoPoint> b){
projection=a;
this.glist=b;
}
public void draw(Canvas canvas, MapView mapv, boolean shadow){
super.draw(canvas, mapv, shadow);
Paint mPaint = new Paint();
mPaint.setDither(true);
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(3);
for(GeoPoint g1:glist)
{
if(gP1!=null)
{
gP1=gP2;
gP2=new GeoPoint(g1.getLatitudeE6(),g1.getLongitudeE6());
}
else
{
gP1=new GeoPoint(g1.getLatitudeE6(),g1.getLongitudeE6());
gP2=gP1;
continue;
}
Point p1 = new Point();
Point p2 = new Point();
Path path = new Path();
projection.toPixels(gP1, p1);
projection.toPixels(gP2, p2);
path.moveTo(p2.x, p2.y);
path.lineTo(p1.x,p1.y);
canvas.drawPath(path, mPaint);
}
}
}
It works and draws the line between all the points.
The only problem is between the first point and the last point there is also a line that ends up crossing on the path of the other points.
How do i remove it?
You have your geopoints gP1 and gP2 as instance variables. draw will be called multiple times.
At the end of your iterations on the first call to draw, gP2 is the last point. At the first iteration of the second and subsequent calls to draw, gP1 is assigned to the last point and gP2 is assigned to your first point. This draws a line between the first and last point. To rectify this, just declare those variables in draw or initialize it to null before iterating through your geopoints.
gP1 = null;
gP2 = null;
for(GeoPoint g1:glist) {
//Drawing logic
}
I want to draw Driving direction Route from source geopoints to Destination geopoints. I tried this with the below code but its draw a straight line between the Location not proper shortest route.
#Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
// TODO Auto-generated method stub
super.draw(canvas, mapView, shadow);
Projection projection = classMapView.getProjection();
Paint mPaint = new Paint();
mPaint.setDither(true);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(2);
GeoPoint gP1 = new GeoPoint(22716221,75896816);
GeoPoint gP2 = new GeoPoint(22715212, 75895806);
Point p1 = new Point();
Point p2 = new Point();
Path path = new Path();
projection.toPixels(gP1, p1);
projection.toPixels(gP2, p2);
path.moveTo(p2.x, p2.y);
path.lineTo(p1.x,p1.y);
canvas.drawPath(path, mPaint);
}
Please help and also tell me is it possible to get text of direction as Google Maps provide.
Please go through Google Policies
This states that turn-by-
turn navigation is not allowed using android MapView.
instead you can use intent to do that as follows
Intent intent = new Intent(android.content.Intent.ACTION_VIEW,
Uri.parse("http://maps.google.com/maps?saddr=<start lat>,<start lon>&daddr=<dest lat>,<dest lon>"));
startActivity(intent);
Here is a complete source code at https://github.com/frogermcs/RoutePathExample for how to draw path between two geolocation.