【问题标题】:Set a max zoom level on LatLngBounds builder在 LatLngBounds 构建器上设置最大缩放级别
【发布时间】:2014-01-01 13:02:38
【问题描述】:

我在搜索中没有找到答案,有一些关于 SO 的答案,但它们对我不起作用。

我在地图上有 2 个标记,我正在使用 LatLngBounds 构建器,以便让相机缩放到正确的缩放级别以包含它们。除了一件事之外,一切都按预期工作,当 2 个标记彼此非常接近时,地图非常非常缩放,而且,这种缩放级别没有任何意义。

LatLngBounds.Builder builder = new LatLngBounds.Builder();
builder.include(firstMarker.getPosition());
builder.include(secondMarker.getPosition());
LatLngBounds bounds = builder.build();

CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, markerPadding);

有没有办法强制一定程度的变焦,之后相机就不会变焦?这将避免地图过于放大/缩小。基本上我使用 15.0f 作为缩放级别。如果这些点太远,我希望缩放适合它们。如果点越来越近,我不希望缩放级别超过 15.0f。

【问题讨论】:

  • 最后我采用了stackoverflow.com/questions/15700808/… 的方法,而且效果很好
  • Alin,我下面的建议与您所指的答案相似,但有一个主要区别:相同的经度差异转化为非常不同的距离(以公里为单位),具体取决于测量的纬度。使用我的方法,您没有这种变化。最小缩放级别将始终由以公里为单位测量的相同地图对角线定义,与您在赤道或两极上显示标记无关(好吧,我知道,您无法在 Google 地图中真正显示两极)。
  • 你能在你的答案中添加代码示例以便我接受吗?
  • 好的,我已经添加了一个例子。

标签: android android-maps-v2 zooming


【解决方案1】:

我有一个类似的情况,我想要至少 2 公里的地图对角线。 因此,我只是在构建器中添加了两个附加点:第一个位于原始边界中心西北 1 公里处,第二个位于中心东南 1 公里处。如果这些在界限内,它们不会改变任何东西。如果它们超出原始边界,它们会将边界增加到有意义的大小。

这是一个完整的代码示例:

public LatLngBounds createBoundsWithMinDiagonal(MarkerOptions firstMarker, MarkerOptions secondMarker) {
    LatLngBounds.Builder builder = new LatLngBounds.Builder();
    builder.include(firstMarker.getPosition());
    builder.include(secondMarker.getPosition());
    
    LatLngBounds tmpBounds = builder.build();
    /** Add 2 points 1000m northEast and southWest of the center.
     * They increase the bounds only, if they are not already larger
     * than this. 
     * 1000m on the diagonal translates into about 709m to each direction. */
    LatLng center = tmpBounds.getCenter();
    LatLng northEast = move(center, 709, 709);
    LatLng southWest = move(center, -709, -709);
    builder.include(southWest);
    builder.include(northEast);
    return builder.build();     
}

private static final double EARTHRADIUS = 6366198;
/**
 * Create a new LatLng which lies toNorth meters north and toEast meters
 * east of startLL
 */
private static LatLng move(LatLng startLL, double toNorth, double toEast) {
    double lonDiff = meterToLongitude(toEast, startLL.latitude);
    double latDiff = meterToLatitude(toNorth);
    return new LatLng(startLL.latitude + latDiff, startLL.longitude
            + lonDiff);
}

private static double meterToLongitude(double meterToEast, double latitude) {
    double latArc = Math.toRadians(latitude);
    double radius = Math.cos(latArc) * EARTHRADIUS;
    double rad = meterToEast / radius;
    return Math.toDegrees(rad);
}


private static double meterToLatitude(double meterToNorth) {
    double rad = meterToNorth / EARTHRADIUS;
    return Math.toDegrees(rad);
}

编辑,因为这似乎仍然很受欢迎:

请参阅下面的https://stackoverflow.com/a/23092644/2808624:现在人们会使用 SphericalUtil 来移动 LatLng 坐标,而不是自己编码。

【讨论】:

  • 请同时添加meterToLatitude函数,以便我测试代码。 double latDiff = meterToLatitude(toNorth); 需要它,谢谢
  • 抱歉,错过了。我稍后再补充。
  • 非常感谢。它确实比以前的解决方案更好。我唯一注意到的是基于熨平板 dpi 的距离可能太大,所以最后我为不同的密度设置了不同的距离值以满足我的需要。
【解决方案2】:

我根据user2808624的回答实现了这个解决方案,但是使用Android Maps Utility Library中的SphericalUtil进行计算。

final double HEADING_NORTH_EAST = 45;
final double HEADING_SOUTH_WEST = 225;
LatLng center = tmpBounds.getCenter();
LatLng northEast = SphericalUtil.computeOffset(center, 709, HEADING_NORTH_EAST);
LatLng southWest = SphericalUtil.computeOffset(center, 709, HEADING_SOUTH_WEST);

【讨论】:

  • 如果你想要 2000 米的对角线,我想你必须在两个方向上使用 1000 米而不是 709 米。仅当您首先向北直行 709 米,然后向东直行 709 米(导致对角线约 1000 米)时,才需要您所指的答案中的 709
【解决方案3】:

来晚了,但也许有人遇到了与我相同的问题:我收到了来自 Place 的边界,而不是来自 LatLngBounds.Builder:

我使用 Location.distanceBetween(...) 来确定边界是否太小,然后使用边界中心的最小缩放级别:

if (areBoundsTooSmall(bounds, 300)) {
    mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(bounds.getCenter(), 17));
} else {
    mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 20));
}

检查方法是:

private boolean areBoundsTooSmall(LatLngBounds bounds, int minDistanceInMeter) {
    float[] result = new float[1];
    Location.distanceBetween(bounds.southwest.latitude, bounds.southwest.longitude, bounds.northeast.latitude, bounds.northeast.longitude, result);
    return result[0] < minDistanceInMeter;
}

对于太小的边界,我使用了最小缩放级别 17 和填充 20,太小在这里定义为以米为 300 的最小距离。您可以根据需要调整数字。

【讨论】:

  • 尝试了不止一种解决方案...这个解决方案做到了。谢谢
  • 这是最好的
【解决方案4】:

这是基于user2808624answer 的更短版本。

        LatLngBounds bounds = builder.build();
        LatLng center = bounds.getCenter();
        builder.include(new LatLng(center.latitude-0.001f,center.longitude-0.001f));
        builder.include(new LatLng(center.latitude+0.001f,center.longitude+0.001f));
        bounds = builder.build();

有点草率,但是你可以用 0.001 缩放不小于一个街区,0.01 不小于一个城市视图。唯一的优点是它只有 4 行额外的代码。

【讨论】:

    【解决方案5】:

    很简单:

    LatLngBounds.Builder latlngBuilder = new LatLngBounds.Builder();
    
    LatLng sydney1 = new LatLng(57.149651, -2.099075);
    
    LatLng sydney2 = new LatLng(51.621441, -3.943646);
    
    latlngBuilder.include(sydney1);
    latlngBuilder.include(sydney2);
    
    googlemap.animateCamera(CameraUpdateFactory.newLatLngBounds(latlngBuilder.build(), 100));
    

    【讨论】:

      【解决方案6】:

      Google 添加了一个用于限制最大/最小缩放级别的 API。

      GoogleMap.setMaxZoomPreference()
      GoogleMap.setMinZoomPreference()
      

      【讨论】:

      • 注意:这也将限制用户缩放超出这些限制。
      【解决方案7】:

      快速解决方法是:

      gMap.setOnMapLoadedCallback(this);
      CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, padding);
      gMap.setMaxZoomPreference(10);
      gMap.animateCamera(cu);
      
      
      @Override
      public void onMapLoaded() {
          gMap.resetMinMaxZoomPreference();
      }
      

      请注意,它可能会在地图完全重新加载之前停止缩放。但作为一个快速修复它的工作原理。

      【讨论】:

        【解决方案8】:

        您是否考虑过制作一个隐藏的地图片段,并使用它来确定您的缩放/范围?这显然不是最好的计算方法,但它可能是最简单/最准确的解决方案。例如,制作一个在两极附近和 0 度经度范围内工作的数学解决方案是很复杂的(如果你关心的话)。如果你使用地图,那一切都已经内置了。您显然也可以在现有地图上使用此解决方案,但用户会看到弹跳。

        你会这样做:

        1. 转到上面定义的边界
        2. 使用 map.getCameraPosition().zoom 获取缩放。
        3. 如果缩放 > 15 使用 CameraUpdateFactory.zoomTo(15) 缩小到 15

        【讨论】:

          【解决方案9】:

          根据 user2808624 和 dvdrlee 的回答,我在 Kotlin 中编写了一个扩展:

          fun LatLngBounds.Builder.createBoundsWithZoomMaintained(bounds: LatLngBounds) : LatLngBounds  {
              val HEADING_NORTH_EAST = 45.0   //NorthEast angle
              val HEADING_SOUTH_WEST = 215.0  //SouthWest angle
              val center = bounds.center
              val northEast = SphericalUtil.computeOffset(center, 709.0, HEADING_NORTH_EAST)  //1000 metres on the diagonal translates to 709 metres on either side.
              val southWest = SphericalUtil.computeOffset(center, 709.0, HEADING_SOUTH_WEST)
              this.include(northEast).include(southWest)
              return this.build()
          }
          

          这通常在您的 Utils 或 Extensions 文件中,然后您可以像这样在任何地方使用它:

          var bounds = builder.build()  //To include all your existing points.
          bounds = builder.createBoundsWithZoomMaintained(bounds)  //Updated bounds.
          mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 10)) 
          

          【讨论】:

            【解决方案10】:

            自 4.0.30 起无法限制缩放级别,但您可以跟踪 this issue

            简单的方法是强制缩放级别,如此视频:http://www.youtube.com/watch?v=-nCZ37HdheY
            基本上,当达到某个缩放级别时,他们会调用animateCamera

            这看起来有点奇怪,因为您有 2 个动画不是由用户启动的。

            困难的方法是自己做数学。尝试使用CameraUpdateFactory.newLatLngZoomLatLng 作为这些点之间的中心,并在检测到点彼此靠近时使用最大缩放。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-10-10
              • 2016-12-21
              • 1970-01-01
              • 1970-01-01
              • 2011-10-27
              • 1970-01-01
              • 2011-05-07
              相关资源
              最近更新 更多