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
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());
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
public static int metersToRadius(float meters, MapView map, double latitude) {
return (int) (map.getProjection().metersToEquatorPixels(meters) * (1/ Math.cos(Math.toRadians(latitude))));
}
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);
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;
}