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

时间:2016-07-02 15:40:11

标签: android google-maps google-maps-markers google-maps-android-api-2 android-maps-utils

我有一个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);
    
  3. 这个更新提供了一个很好的视图,我的所有点,一些不错的填充。

    问题

    我想为paddingTop,paddingBottom,paddingLeft和paddingRight应用不同的值。也就是说,我两边都没有任何东西,然后我在顶部有一个浮动工具栏(约50dp),底部有一张浮动卡片(约200dp)。

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

    如果我添加最大填充(200dp),那么顶部,左侧和右侧都会有巨大的间隙。有什么想法吗?

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

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

3 个答案:

答案 0 :(得分:1)

这似乎有效,但我会等待更好的解决方案。

您需要传递包含地图的视图的宽度和高度(以像素为单位),以便我们可以计算像素到投影坐标系数。 然后在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;
    }
}

答案 1 :(得分:0)

实际上有一个更简单,更短的解决方案,只需要三行代码。 感谢https://stackoverflow.com/a/27937256/10035225提供解决方案。

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

然后,添加以下三行代码:

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,左侧和右侧可以是任何填充,只要看起来不错即可。

第二行:将相机移动/设置动画到remember to set the padding to 0的边界,因为您不需要任何额外的填充,因为您已经在第一行进行了设置。

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

就是这样,然后您就可以了!希望对您有帮助!

答案 2 :(得分:0)

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

<块引用>

返回一个 CameraUpdate 来转换相机,使得 指定的纬度/经度边界以屏幕为中心 以最大可能缩放指定尺寸的边界框 等级。您可以指定额外的填充,以进一步限制 边界框的大小。

see ref

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

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

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

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

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