【问题标题】:Determine a reasonable zoom level for Google Maps given location accuracy在给定位置准确性的情况下确定 Google 地图的合理缩放级别
【发布时间】:2013-08-25 08:21:51
【问题描述】:

我正在尝试将 Google 地图以用户位置为中心,同时考虑到该位置的准确性,提供合理的缩放级别。谁能描述我应该如何计算它?涉及到哪些变量,你是怎么做到的?

【问题讨论】:

  • This post 应该有助于解决您关于查找位置准确性的问题的第一部分。一旦你知道你有多少准确度,那么弄清楚要使用的缩放级别将很简单(并且实际上取决于应用程序)。此外,由于该 API 的 V2 已被弃用,this 展示了如何使用最新的 API。
  • 我也想知道。我什至在 math.SE 上发布了这个:math.stackexchange.com/questions/887868/…

标签: java android google-maps


【解决方案1】:

您正在寻找的是根据位置精度计算缩放级别的公式。

我设法想出了这个公式(在我的测试中)效果很好。

这可以简化(可能看起来不是这样):

这个看起来很吓人的东西就是你想要的。

EquatorLength 是 40,075,004 米。而Meters/Pixel 可以通过将精度圆的直径除以设备屏幕的长度(以像素为单位)来计算。

这是我用来测试这个公式的示例程序:

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

注意事项:

  • 此答案基于this 并实现它以检查生成的值
  • 准确度是用户位置的半径,根据文档,它可以正确率高达 68%。

欢迎大家指正。

【讨论】:

  • 你为什么要getWindowManager().getDefaultDisplay().getMetrics(metrics);
  • @Supuhstar 获取设备屏幕测量值并将其存储在变量 metrics 中。
  • 为什么不在OnMyLocationChangeListener 中使用calculateZoomLevel 方法?
  • @Supuhstar 我的答案中有一个链接,它指向calculateZoomLevel 的答案。但是,该函数会计算近似的缩放级别,这可以从该应用吐出的日志消息中清楚地看出。
  • 好吧...为什么equatorlong 而不是int?抱歉,这里只是想了解您的选择。 ^^;
【解决方案2】:

如果您正在寻找简单的东西:

var zoom = Math.min(20, Math.max(1, Math.log2(591657550/accuracy)-2));

调整 -2 以获得所需的缩放。

结帐this answer 以获取相应的精确缩放图表。

【讨论】:

    【解决方案3】:

    感谢@Simas!考虑到 CLLocation 的准确性,我选择了您的算法来对 GMSMapView 进行此扩展,以计算理想的缩放级别。

    我不得不进行调整以考虑配备 Retina 显示屏的设备,因为每个像素与屏幕上的 1 个点并不完全相同:

    extension GMSMapView {
    
        func getIdealZoomLevel(usingLocation location:CLLocation)->Float {
    
            let retinaScale = Double(UIScreen.mainScreen().scale)
    
            let equatorLength : Double = 40075004 // in meters
            var metersPerPixel = equatorLength / 256
            let accuracy = location.horizontalAccuracy
            // I used height because I'm on landscape, but moving forward I'll have to get the Min of the width and height.
            // I also took only 75% of the height to give it some margin
            let screenWidth : Double = Double( self.frame.size.height) * 0.75
    
            var display = metersPerPixel * (screenWidth / retinaScale)
    
            var zoomLevel : Float = 0.0
    
            while (display > accuracy) {
                metersPerPixel /= 2
                display = metersPerPixel * (screenWidth / retinaScale)
                zoomLevel += 1
            }
    
            return zoomLevel
        }
    }
    

    它适用于我正在处理的 Swift 项目,现在,我能够在给定的 CLLocation 内显示包围半径的良好接近度。

    希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-01-05
      • 1970-01-01
      • 1970-01-01
      • 2023-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-14
      相关资源
      最近更新 更多