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?
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:
Any corrections are very welcome.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With