Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine a reasonable zoom level for Google Maps given location accuracy

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?

like image 889
Didac Perez Parera Avatar asked Aug 22 '13 14:08

Didac Perez Parera


2 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.

Formula

This can be simplified (might not seem so) to this:

Simplified/Scarified formula

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.

like image 172
Simas Avatar answered Oct 28 '22 14:10

Simas


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.

like image 43
bendytree Avatar answered Oct 28 '22 15:10

bendytree