11

When I develop an Android map application, I want to draw a circle on the map whose radius is 1 meter. As you known, I can't draw 1 meter directly, I should convert 1 meter to the distance of two pixels depend on the zoom level. How to I convert it, is there anything API I can use.

Canvas.draw(x, y, radius), what value should I put to this method ?

Chris
  • 6,431
  • 10
  • 44
  • 59
  • 1
    if you want to convert 1 meter to two pixels, and you want a 1 meter radius, then you want to pass `2` to the `radius` parameter. Now, you say *depend on the zoom level*, so I guess you'll have to pass something like `2 * (zoomLevel * zoomLevelCoefficient)` – David Hedlund Aug 02 '10 at 05:06
  • This was an interesting question, did you find a solution that worked for you? – Idistic Jul 14 '11 at 13:10

5 Answers5

11

Assuming that your map is Google Maps, they use the Mercator projection, so you'd need to use that for the conversion. Under the Mercator projection, the distance that a pixel represents in meters varies with latitude, so while a meter is a very small distance compared to the Earth radius, latitude is important.

All the examples below are javascript, so you might need to translate them.

Here is a general explanation of the coordinate system:

http://code.google.com/apis/maps/documentation/javascript/maptypes.html#WorldCoordinates

This example contains a MercatorProjection object, which includes the methods fromLatLngToPoint() and fromPointToLatLng():

http://code.google.com/apis/maps/documentation/javascript/examples/map-coordinates.html

Once you have converted your (x,y) to (lat,lon), this is how you draw a circle:

// Pseudo code
var d = radius/6378800; // 6378800 is Earth radius in meters
var lat1 = (PI/180)* centerLat;
var lng1 = (PI/180)* centerLng;

// Go around a circle from 0 to 360 degrees, every 10 degrees
for (var a = 0 ; a < 361 ; a+=10 ) {
    var tc = (PI/180)*a;
    var y = asin(sin(lat1)*cos(d)+cos(lat1)*sin(d)*cos(tc));
    var dlng = atan2(sin(tc)*sin(d)*cos(lat1),cos(d)-sin(lat1)*sin(y));
    var x = ((lng1-dlng+PI) % (2*PI)) - PI ;
    var lat = y*(180/PI);
    var lon = x*(180/PI);

    // Convert the lat and lon to pixel (x,y) 
}

These two mashups draw a circle of a given radius on the surface of the Earth:

http://maps.forum.nu/gm_sensitive_circle2.html

http://maps.forum.nu/gm_drag_polygon.html

If you choose to ignore the projection then you'd use cartesian coordinates and simply draw the circle using Pythagoras Theorem:

http://en.wikipedia.org/wiki/Circle#Cartesian_coordinates

Community
  • 1
  • 1
Marcelo
  • 9,387
  • 3
  • 35
  • 40
8

Take a look at the Projection object in the api. It has a method on it called metersToEquatorPixels(). Given the description in the api, it might only be accurate along the equator, but I thought it was worth mentioning in case accuracy wasn't an issue for you.

Here's the way to use this inside the draw method of your overlay, given the radius in meters and the latitude and longitude of where you want to draw the circle:

Projection projection = mapView.getProjection();
Point center = projection.toPixels(new GeoPoint(yourLat * E6, yourLong * E6), null);
float radius = projection.metersToEquatorPixels(radiusInMeters);
canvas.draw(center.x, center.y, radius, new Paint());
SilithCrowe
  • 1,448
  • 1
  • 15
  • 20
  • You might be interested in this topic, which gives a simple formula to compensate for the Mercator distortion: http://stackoverflow.com/questions/2077054/how-to-compute-a-radius-around-a-point-in-an-android-mapview – Romain Jan 11 '11 at 11:05
3

Three questions you got to ask 1- How big is your map 2- What is your zoom level 3- How big is your screen

Let's make the assumption that the map has the same aspect ratio as your screen (if not then you need to worry about which way to crop (verically vs horizontally) or which way to stretch and then change y our answer to 1)

Once you have the answer 1 and 3 you can work out the ratio between meters and pixels in the 100% zoom case, so you will have a pixels per meter

Next you need to maintain a zoom factor (eg: zoom in double size is 200%)

your call to draw the circle will look like this

 Canvas.draw(x,y, radius_in_meters * pixels_per_meter * zoom_factor/100);
hhafez
  • 38,949
  • 39
  • 113
  • 143
  • Finding pixels per meter is the crux of this question. How big is your map is likely to be easily answered in a lat x lon manner, not pixels squared. How do you go from latxlon to square pixels? – jamesh Feb 22 '11 at 11:12
3
public static int metersToRadius(float meters, MapView map, double latitude) {
    return (int) (map.getProjection().metersToEquatorPixels(meters) * (1/ Math.cos(Math.toRadians(latitude))));         
}
Jérôme Verstrynge
  • 57,710
  • 92
  • 283
  • 453
criss
  • 31
  • 1
1

You can calculate the zoom level for the radius you want:

First we need to calculate the screen width of the phone.At zoom level 1 the equator of Earth is 256 pixels long and every subsequent zoom level doubles the number of pixels needed to represent earths equator. The following function returns the zoom level where the screen will show an area of 1Km width.

private int calculateZoomLevel() {

    int ht, screenWidth;
    DisplayMetrics displaymetrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
    ht = displaymetrics.heightPixels;
    screenWidth = displaymetrics.widthPixels;

    double equatorLength = 40075004; // in meters
    double widthInPixels = screenWidth;
    double metersPerPixel = equatorLength / 256;
    int zoomLevel = 1;
    while ((metersPerPixel * widthInPixels) > 1000) {
        metersPerPixel /= 2;
        ++zoomLevel;
    }

    Log.i(tag, "zoom level = " + zoomLevel);
    return zoomLevel;
}
derekerdmann
  • 17,696
  • 11
  • 76
  • 110
Parth mehta
  • 1,468
  • 2
  • 23
  • 33