此Google地图缩放级别计算如何运作?

时间:2011-05-09 16:49:50

标签: java algorithm google-maps latitude-longitude

我知道输入和输出是什么,但我不确定它是如何工作或为什么工作。

此代码用于给定包含一组点的最小和最大经度/纬度(正方形),确定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;
}

6 个答案:

答案 0 :(得分:11)

可以轻松解释一些数字

再次,缩放级别使每一步的尺寸加倍,即将缩放级别增加屏幕尺寸的一半。

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

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

答案 1 :(得分:11)

我使用下面的简单公式:

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;
}

您也可以用特定半径替换圆圈。

答案 2 :(得分:2)

This page非常有助于解释所有这些内容(两个lat-lng对之间的距离等)。

6371是以千米为单位的近似地球半径。

57.2958是180 / pi

另外,请查看这些墨卡托投影计算,以便在纬度 - 经度和X-Y之间进行转换:http://wiki.openstreetmap.org/wiki/Mercator

答案 3 :(得分:0)

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

 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);
}

答案 4 :(得分:0)

我需要相反:给定某个缩放级别的特定半径(即缩放级别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;
}

我想我会把它放在这里,以拯救其他人获得这种转换的努力。

作为旁注,在cameramovelistener中像这样移动圆圈会使圆圈运动变得非常不稳定。我最终将一个视图放在封闭的MapView的中心,只是画了一个小圆圈。

答案 5 :(得分:0)

经过多次尝试,我提出了解决方案。我假设您在半径之外有填充(例如,如果您的半径= 10000 m,那么左右将为2500 m)。另外,您还应该以米为单位。您可以设置带有反向缩放的合适缩放(二进制搜索)。如果将moveCamera更改为animateCamera,将获得有趣的搜索动画。半径越大,您收到的缩放值越准确。

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
    }
}

用法:

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

您应在googleMap?.setOnCameraIdleListener的第一个事件内部调用此方法,请参阅animateCamera works and moveCamera doesn't for GoogleMap - Android。如果您在onMapReady之后立即调用它,则距离会错误,因为该时间该地图不会自行绘制。

警告!缩放级别取决于位置(纬度)。这样,根据距赤道的距离,圆具有相同的缩放级别将具有不同的大小(请参见Determine a reasonable zoom level for Google Maps given location accuracy)。

enter image description here