从点和填充创建 CameraUpdate 以很好地显示标记

Create CameraUpdate from points and padding to show markers nicely

我有一个 LatLng 点列表,我想在 GoogleMap 上显示为标记。 我目前正在通过以下方式计算适合我所有观点的相机视图:

  1. 首先我用我所有的点计算一个 LatLngBounds。

    Iterator<LatLng> i = items.iterator();
    LatLngBounds.Builder builder = LatLngBounds.builder();
    while (i.hasNext()) {
        builder.include(i.next());
    }
    LatLngBounds bounds = builder.build();
    
  2. 那我就用合适的方法:

    CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngBounds(bounds, padding);
    

此更新提供了对我所有观点的良好看法,并有一些漂亮的填充。

问题

我想为 paddingTop、paddingBottom、paddingLeft 和 paddingRight 应用不同的值。即,我两边什么都没有,然后我在顶部有一个浮动工具栏(大约50dp),在底部有一个浮动卡片(大约200dp)。

如果我不添加填充,一些点将被顶部工具栏或底部卡片覆盖。

如果我加上最大内边距(200dp),那么顶部、左侧和右侧就会有巨大的空隙。有什么想法吗?

__________________________
| ___________o__________ |
| |                    | |
| |____________________|o|
|         o              |
|                  o     |
|    o                   |
|          MAP           |
|     o                  |
|                   o    |
| ______________________ |
| |                    | |
|o|                    | |
| |                    |o|
| |____________________| |
|____________________o___|

我现在唯一的想法是,以像素为单位获取地图宽度,以 Lng 为单位获取地图宽度(定义不明确),找到 pixels/lng 比率,将所需的填充从像素转换为 lng,然后重新计算LatLngBounds 添加一个与我的原始边界相距很远的假点(每侧一个)。

这似乎可行,但如果有更好的解决方案,我会等待。

您需要传递持有地图的视图的宽度和高度(以像素为单位),以便我们可以计算像素到投影坐标的系数。 然后在 Rect 对象中传递所需的填充。

如果您有很多积分,这可能最适合 运行 在后台任务中。

public static CameraUpdate computeCameraView(final List<LatLng> items,
                                             final GoogleMap map, final Rect padding,
                                             final int viewWidth, final int viewHeight) {

    // Compute bounds without padding
    Iterator<LatLng> i = items.iterator();
    LatLngBounds.Builder builder = LatLngBounds.builder();
    while (i.hasNext()) {
        builder.include(i.next());
    }
    LatLngBounds bounds = builder.build();

    // Create a first CameraUpdate and apply
    CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngBounds(bounds, 0);
    map.moveCamera(cameraUpdate);

    // Convert padding to lat/lng based on current projection
    bounds = map.getProjection().getVisibleRegion().latLngBounds;
    double mapWidth = bounds.northeast.longitude - bounds.southwest.longitude;
    double mapHeight = bounds.northeast.latitude - bounds.southwest.latitude;
    double pixelToLng = mapWidth / viewWidth;
    double pixelToLat = mapHeight / viewHeight;
    padding.top = (int) (padding.top * pixelToLat);
    padding.bottom = (int) (padding.bottom * pixelToLat);
    padding.left = (int) (padding.left * pixelToLng);
    padding.right = (int) (padding.right * pixelToLng);

    // Now padding holds insets in lat/lng values.
    // Let's create two fake points and bound
    LatLng northEast = new LatLng(bounds.northeast.latitude + padding.top,
            bounds.northeast.longitude + padding.right);
    LatLng southWest = new LatLng(bounds.southwest.latitude - padding.bottom,
            bounds.southwest.longitude - padding.left);
    LatLngBounds newBounds = new LatLngBounds.Builder()
            .include(northEast).include(southWest).build();

    return CameraUpdateFactory.newLatLngBounds(newBounds, 0);
}

这里的弱点是double mapWidth = bounds.northeast.longitude - bounds.southwest.longitude。你不能那样计算经度,因为它是一个循环变量(即,如果可见区域穿过 180 度子午线,就会出现问题)。 我会调查一下。

这应该足够了:

public static double computeLngDistance(LatLng east, LatLng west) {
    if (east.longitude <= 0 && west.longitude >= 0) {
        // We are crossing the 180 line.
        return (east.longitude + 180d) + (180d - west.longitude);
    } else {
        return east.longitude - west.longitude;
    }
}

实际上有一个更简单、更短的解决方案,只需要三行代码。 感谢 的解决方案。

首先,将工具栏和底部卡片大小转换为像素。

然后,添加这三行代码:

map.setPadding(left, top, right, bottom);
map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0));
map.setPadding(0,0,0,0);

第一行:顶部和底部填充将是您的 toolbar and bottom card size plus some extra,左侧和右侧可以是任何填充,只要它看起来不错。

第二行:move/animate你的相机到边界并且remember to set the padding to 0因为你不需要任何额外的填充,因为你已经在第一行中设置了它。

第三行:设置地图填充back to original padding(我的情况为0)因为第一行中设置的地图填充是永久性的。

到此为止!希望对您有所帮助!

您可以使用
public static CameraUpdate newLatLngBounds (LatLngBounds bounds, int width, int height, int padding)

Returns a CameraUpdate that transforms the camera such that the specified latitude/longitude bounds are centered on screen within a bounding box of specified dimensions at the greatest possible zoom level. You can specify additional padding, to further restrict the size of the bounding box.

see ref

您将定义一个框,其宽度和高度将在地图的可用区域内居中。然后边界将在该框内尽可能居中和缩放。

您可以通过该框“模拟”填充,而无需对地图应用填充(然后无需移动水印等地图覆盖)。 如果你想要 16dp 的水平填充,你将你的框的宽度设置为:mapWidth - dpToPx(2 * 16)

  1. 2 * 是必需的,因为您的框将在地图内居中。因此,如果您仅删除 16dp,则每边将有 8dp。
  2. 我跳过了转换 dpToPx 的方法,但您可以在 SO 上轻松找到它。

该方法的局限性在于,由于框居中,因此左右填充必须相同。 (顶部和底部相同)。

最后的想法: 当地图有填充时,会发生一些奇怪的事情。要么将地图的填充应用于您正在定义的框,要么发生其他情况。
当我将框的高度定义为 mapHeight - verticalPaddingOfTheMap 时,结果与预期不符。我终于放了 mapHeight 一切都很好。
如果你操作有填充的地图,请记住这一点