Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does this Google Maps zoom level calculation work?

I know what the input and outputs are, but I'm just not sure how or why it works.

This code is being used to, given a min and max longitude/latitude (a square) that contains a set of points, determine the maximum zoom level on Google Maps that will still display all of those points. The original author is gone, so I'm not sure what some of these numbers are even for (i.e. 6371 and 8). Consider it a puzzle =D

int mapdisplay = 322; //min of height and width of element which contains the map
double dist = (6371 * Math.acos(Math.sin(min_lat / 57.2958) * Math.sin(max_lat / 57.2958) + 
            (Math.cos(min_lat / 57.2958) * Math.cos(max_lat / 57.2958) * Math.cos((max_lon / 57.2958) - (min_lon / 57.2958)))));

double zoom = Math.floor(8 - Math.log(1.6446 * dist / Math.sqrt(2 * (mapdisplay * mapdisplay))) / Math.log (2));

if(numPoints == 1 || ((min_lat == max_lat)&&(min_lon == max_lon))){
    zoom = 11;
}
like image 889
Alex Beardsley Avatar asked May 09 '11 16:05

Alex Beardsley


People also ask

How is zoom level calculated?

Distance per pixel math As tiles are 256-pixels wide, the horizontal distance represented by one pixel is: Spixel = Stile / 256 = C ∙ cos(latitude) / 2. For example on the equator and at zoom level 0, we get 40 075 016.686 / 256 ≈ 156 543.03 (in meters per pixel).

What is zoom level in Google map?

The Google Maps API provides map tiles at various zoom levels for map type imagery. Most roadmap imagery is available from zoom levels 0 to 18, for example. Satellite imagery varies more widely as this imagery is not generated, but directly photographed.


2 Answers

Some numbers can be explained easily

  • MeanRadiusEarthInKm = 6371 (according to IUGG)

  • DegToRadDivisor = 180/PI = 57.2958

And again the zoom level doubles the size with each step, i.e. increase the zoomlevel by one halfs the size on the screen.

zoom = 8 - log(factor * dist) / log(2) = 8 - log_2(factor * dist)
=> dist = 2^(8-zoom) / factor

From the numbers we find that zoom level eight corresponds to a distance of 276.89km.

like image 61
Howard Avatar answered Oct 13 '22 15:10

Howard


After many attempts I made a solution. I assume that you have a padding outside a radius (for instance, if you have radius = 10000 m, then it will be 2500 m left and right). Also you should have an accuracy in meters. You can set suitable zoom with recoursion (binary search). If you change moveCamera to animateCamera, you will get an interesting animation of search. The larger radius, the more accurate zoom value you will receive. This is a usual binary search.

private fun getCircleZoomValue(latitude: Double, longitude: Double, radius: Double,
                               minZoom: Float, maxZoom: Float): Float {
    val position = LatLng(latitude, longitude)
    val currZoom = (minZoom + maxZoom) / 2
    val camera = CameraUpdateFactory.newLatLngZoom(position, currZoom)
    googleMap!!.moveCamera(camera)
    val results = FloatArray(1)
    val topLeft = googleMap!!.projection.visibleRegion.farLeft
    val topRight = googleMap!!.projection.visibleRegion.farRight
    Location.distanceBetween(topLeft.latitude, topLeft.longitude, topRight.latitude,
        topRight.longitude, results)
    // Difference between visible width in meters and 2.5 * radius.
    val delta = results[0] - 2.5 * radius
    val accuracy = 10 // 10 meters.
    return when {
        delta < -accuracy -> getCircleZoomValue(latitude, longitude, radius, minZoom,
            currZoom)
        delta > accuracy -> getCircleZoomValue(latitude, longitude, radius, currZoom,
            maxZoom)
        else -> currZoom
    }
}

Usage:

if (googleMap != null) {
    val zoom = getCircleZoomValue(latitude, longitude, radius, googleMap!!.minZoomLevel, 
        googleMap!!.maxZoomLevel)
}

You should call this method not earlier than inside first event of googleMap?.setOnCameraIdleListener, see animateCamera works and moveCamera doesn't for GoogleMap - Android. If you call it right after onMapReady, you will have a wrong distance, because the map will not draw itself that time.

Warning! Zoom level depends on location (latitude). So that the circle will have different sizes with the same zoom level depending on distance from equator (see Determine a reasonable zoom level for Google Maps given location accuracy).

enter image description here

like image 21
CoolMind Avatar answered Oct 13 '22 15:10

CoolMind