I am trying to center a Google Map to the user location while giving a reasonable zoom level taking into account the accuracy of that location. Could anybody describe how should I compute it? Which variables are involved, how do you achieve this?
-
1[This post](http://stackoverflow.com/questions/2859705/google-maps-api-geocoding-accuracy-chart) should help with the first part of your question on finding location accuracy. Once you know approximately how much accuracy you have, then figuring out what zoom level to use will be simple (and really depends a bit on the application). Also, since V2 of that API is already deprecated, [this](http://stackoverflow.com/questions/3015370/how-to-get-the-equivalent-of-the-accuracy-in-google-map-geocoder-v3) shows how to work it in with the newest API. – Question Marks Aug 22 '13 at 17:41
-
I'd like to know, too. I even posted this on math.SE: https://math.stackexchange.com/questions/887868/how-can-i-find-a-hyperbolic-function-denoting-zoom-levels – Ky - Aug 05 '14 at 07:40
3 Answers
What you are looking for is the formula that calculates the zoom level based on the accuracy of the location.
I managed to come up with this formula which (in my tests) worked pretty well.
This can be simplified (might not seem so) to this:
This scary looking thing is what you want.
EquatorLength
is 40,075,004 meters. While the Meters/Pixel
can be calculated by diving the diameter of the accuracy circle by the length of the device screen (in pixels).
Here's a sample program that I used to test this formula:
GoogleMap mMap;
@Override
protected void onStart() {
super.onStart();
mMap = ((MapFragment)getFragmentManager().findFragmentById(R.id.map)).getMap();
// Enable user's location layer
mMap.setMyLocationEnabled(true);
mMap.setOnMyLocationChangeListener(new GoogleMap.OnMyLocationChangeListener() {
@Override
public void onMyLocationChange(Location location) {
// Location lat-lng
LatLng loc = new LatLng(location.getLatitude(), location.getLongitude());
// Location accuracy diameter (in meters)
float accuracy = location.getAccuracy() * 2;
// Screen measurements
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
// Use min(width, height) (to properly fit the screen
int screenSize = Math.min(metrics.widthPixels, metrics.heightPixels);
// Equators length
long equator = 40075004;
// The meters per pixel required to show the whole area the user might be located in
double requiredMpp = accuracy/screenSize;
// Calculate the zoom level
double zoomLevel = ((Math.log(equator / (256 * requiredMpp))) / Math.log(2)) + 1;
Log.e(TAG, String.format("Accuracy: %f. Screen Width: %d, Height: %d",
accuracy, metrics.widthPixels, metrics.heightPixels));
Log.e(TAG, String.format("Required M/Px: %f Zoom Level: %f Approx Zoom Level: %d",
requiredMpp, zoomLevel, calculateZoomLevel(screenSize, accuracy)));
// Center to user's position
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(loc, (float) zoomLevel));
// Prevent the camera centering on the user again
mMap.setOnMyLocationChangeListener(null);
}
});
}
private int calculateZoomLevel(int screenWidth, float accuracy) {
double equatorLength = 40075004; // in meters
double metersPerPixel = equatorLength / 256;
int zoomLevel = 1;
while ((metersPerPixel * (double) screenWidth) > accuracy) {
metersPerPixel /= 2;
zoomLevel++;
}
return zoomLevel;
}
Few things to note:
- This answer is based on this and implements it to check the values generated
- Accuracy is the radius of user's location and according to the docs it can be up to 68% correct.
Any corrections are very welcome.
-
why do you do `getWindowManager().getDefaultDisplay().getMetrics(metrics);`? – Ky - Aug 05 '14 at 16:45
-
@Supuhstar to get the device screen measurements and store them inside variable `metrics`. – Simas Aug 05 '14 at 16:48
-
Why don't you use your `calculateZoomLevel` method in the `OnMyLocationChangeListener`? – Ky - Aug 05 '14 at 16:49
-
1@Supuhstar There's a link in my answer where it points to the answer with `calculateZoomLevel`. However that function calculates approximate zoomLevel, which can be clearly seen from the log messages this app spits out. – Simas Aug 05 '14 at 16:53
-
Alright... why is `equator` a `long` instead of an `int`? Sorry, just trying to understand your choices, here. ^^; – Ky - Aug 05 '14 at 17:03
-
@Supuhstar hmm that's a typo :-). However it doesn't affect the workflow. – Simas Aug 05 '14 at 17:06
-
ah, alright. I was thinking you might've been doing some calculations that go over `2^31-1` – Ky - Aug 05 '14 at 17:11
-
Great answer. All i changed was this, and i get a better functionality double zoomLevel = ((Math.log(equator / (256 * accuracy))) / Math.log(2)) + 1; – sdelvalle57 Aug 04 '16 at 21:47
-
This seems to over zoom for map views with smaller heights. IE if I have a mapView that is only 200dp in height. Is it possible to constrain the zoom based on the height of the mapView such that the accuracy circle fits within the map? – lostintranslation May 04 '20 at 15:49
If you're looking for something simple:
var zoom = Math.min(20, Math.max(1, Math.log2(591657550/accuracy)-2));
Tweak -2
to get the desired zoom.
Checkout this answer for a chart corresponding zoom with accuracy.

- 13,095
- 11
- 75
- 91
Thanks @Simas! I picked up your algo to make this extension to GMSMapView to calculate for the ideal zoomLevel given the accuracy of a CLLocation.
I had to make adjustments to consider devices with Retina displays since each pixel is not exactly the same as 1 point on the screen:
extension GMSMapView {
func getIdealZoomLevel(usingLocation location:CLLocation)->Float {
let retinaScale = Double(UIScreen.mainScreen().scale)
let equatorLength : Double = 40075004 // in meters
var metersPerPixel = equatorLength / 256
let accuracy = location.horizontalAccuracy
// I used height because I'm on landscape, but moving forward I'll have to get the Min of the width and height.
// I also took only 75% of the height to give it some margin
let screenWidth : Double = Double( self.frame.size.height) * 0.75
var display = metersPerPixel * (screenWidth / retinaScale)
var zoomLevel : Float = 0.0
while (display > accuracy) {
metersPerPixel /= 2
display = metersPerPixel * (screenWidth / retinaScale)
zoomLevel += 1
}
return zoomLevel
}
}
It's for a Swift project I'm working on and right now, I'm able to display a good proximity enclosing the radius within the given CLLocation.
Hope this helps.

- 433
- 5
- 4