java 这个谷歌地图缩放级别计算是如何工作的?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5939983/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 13:34:31  来源:igfitidea点击:

How does this Google Maps zoom level calculation work?

javaalgorithmgoogle-mapslatitude-longitude

提问by Alex Beardsley

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

此代码用于,给定包含一组点的最小和最大经度/纬度(一个正方形),确定仍将显示所有这些点的 Google 地图上的最大缩放级别。原作者已经不在了,所以我不确定其中一些数字是什么(即 6371 和 8)。把它当作一个谜题 =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;
}

采纳答案by Howard

Some numbers can be explained easily

有些数字很容易解释

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.

从数字我们发现缩放级别 8 对应的距离为 276.89km。

回答by Ken

I use a simple formula below:

我使用下面的简单公式:

public int getZoomLevel(Circle circle) {
    if (circle != null){
        double radius = circle.getRadius();
        double scale = radius / 500;
        zoomLevel =(int) (16 - Math.log(scale) / Math.log(2));
    }
    return zoomLevel;
}

You can also replace circle with its specific radius.

您还可以用特定的半径替换圆。

回答by CoolMind

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

经过多次尝试,我提出了解决方案。我假设你有一个半径外的填充(例如,如果你有半径 = 10000 m,那么它的左右都是 2500 m)。你也应该有一个以米为单位的精度。您可以使用递归(二进制搜索)设置合适的缩放比例。如果更改moveCameraanimateCamera,您将获得有趣的搜索动画。半径越大,您获得的缩放值就越准确。这是通常的二分查找。

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.

您应该在不早于 的第一个事件内调用此方法googleMap?.setOnCameraIdleListener,请参阅animateCamera 工作,而 moveCamera 不适用于 GoogleMap - Android。如果您在 之后立即调用它onMapReady,则会得到错误的距离,因为那时地图不会自行绘制。

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

警告!缩放级别取决于位置(纬度)。因此,根据距赤道的距离,圆将具有相同缩放级别的不同大小(请参阅确定给定位置精度的 Google 地图的合理缩放级别)。

enter image description here

在此处输入图片说明

回答by Dave

This pageis extremely helpful for explaining all this stuff (distance between two lat-lng pairs, etc).

这个页面对于解释所有这些东西(两个纬度-经度对之间的距离等)非常有帮助。

6371 is the approximate radius of the earth in kilometers.

6371是以公里为单位的地球近似半径。

57.2958 is 180/pi

57.2958 是 180/pi

also, check out these Mercator projection calculations for converting between latitude-longitude and X-Y: http://wiki.openstreetmap.org/wiki/Mercator

另外,请查看这些用于在纬度-经度和 XY 之间转换的墨卡托投影计算:http: //wiki.openstreetmap.org/wiki/Mercator

回答by Danny Daglas

I needed the opposite: given a particular radius at a certain zoom level (i.e., 40 meters at zoom level 15), I needed the radii at other zoom levels that showed the same circle size (graphically) in the map. To do this:

我需要相反的情况:给定特定缩放级别(即缩放级别 15 时的 40 米)的特定半径,我需要其他缩放级别的半径,在地图中显示相同的圆大小(以图形方式)。去做这个:

// after retrieving the googleMap from either getMap() or getMapAsync()...

// we want a circle with r=40 meters at zoom level 15
double base = 40 / zoomToDistFactor(15);

final Circle circle = googleMap.addCircle(new CircleOptions()
        .center(center)
        .radius(40)
        .fillColor(Color.LTGRAY)
);

googleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() {
    @Override
    public void onCameraMove() {
        CameraPosition cameraPosition = googleMap.getCameraPosition();
        LatLng center = cameraPosition.target;
        float z2 = cameraPosition.zoom;
        double newR = base * zoomToDistFactor(z2);
        circle.setRadius(newR);
    }
});

// ...

private double zoomToDistFactor(double z) {
    return Math.pow(2,8-z) / 1.6446;
}

I figure I'd put it here to save anyone else the effort to get this conversion.

我想我把它放在这里是为了省去其他人获得这种转换的努力。

As a side note, moving the circle around like this in a cameramovelistener made the circle motion very choppy. I ended up putting a view in the center of the enclosing MapView that simply drew a small circle.

作为旁注,在相机移动监听器中像这样移动圆圈会使圆圈运动非常不稳定。我最终在封闭的 MapView 的中心放置了一个视图,它只是画了一个小圆圈。

回答by Lucas Moreira

I think he gets this function:

我认为他得到了这个功能:

 function calculateZoom(WidthPixel,Ratio,Lat,Length){
    // from a segment Length (km), 
    // with size ratio of the of the segment expected on a map (70%),
    // with a map widthpixel size (100px), and a latitude (45°) we can ge the best Zoom
    // earth radius : 6,378,137m, earth is a perfect ball; perimeter at the equator = 40,075,016.7 m
    // the full world on googlemap is available in a box of 256 px; It has a ratio of 156543.03392 (px/m)
    // for Z = 0; 
    // pixel scale at the Lat_level is ( 156543,03392 * cos ( PI * (Lat/180) ))
    // map scale increase at the rate of square root of Z
    //
    Length = Length *1000;                     //Length is in Km
    var k = WidthPixel * 156543.03392 * Math.cos(Lat * Math.PI / 180);        //k = perimeter of the world at the Lat_level, for Z=0 
    var myZoom = Math.round( Math.log( (Ratio * k)/(Length*100) )/Math.LN2 );
    myZoom =  myZoom -1;                   // z start from 0 instead of 1
    //console.log("calculateZoom: width "+WidthPixel+" Ratio "+Ratio+" Lat "+Lat+" length "+Length+" (m) calculated zoom "+ myZoom);

    // not used but it could be usefull for some: Part of the world size a the Lat 
    MapDim = k /Math.pow(2,myZoom);
    //console.log("calculateZoom: size of the map at the Lat: "+MapDim + " meters.");
    //console.log("calculateZoom: world perimeter at the Lat: " +k+ " meters.");
return(myZoom);
}