繁体   English   中英

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

[英]How does this Google Maps zoom level calculation work?

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

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

我使用下面的简单公式:

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

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

一些数字可以很容易地解释

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

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

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

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

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

57.2958是180 / pi

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

我需要相反的是:给定某个缩放级别的特定半径(即缩放级别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的中心放置了一个视图,只是画了一个小圆圈。

经过多次尝试,我找到了解决方案。 我假设你在半径外有一个填充(例如,如果你有半径= 10000米,那么它将是左右2500米)。 你也应该有米的准确度。 您可以使用recoursion设置合适的缩放(二分搜索)。 如果将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) {
    val zoom = getCircleZoomValue(latitude, longitude, radius, googleMap!!.minZoomLevel, 
        googleMap!!.maxZoomLevel)
}

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

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

在此输入图像描述

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM