在 google 地图上仅呈现可见集群项目的最佳方式

Best way to render only the visible cluster items on a google map

我有一个由 google 的集群管理器处理的地图片段,我使用我的自定义集群渲染器 extends DefaultClusterRenderer 配置了该集群管理器。

我已经重写了函数 onBeforeClusterItemRenderedonBeforeClusterRendered 以便能够显示我的图片:

现在,如果用户放大,渲染不在可见区域中的项目就没有意义了。 很容易找出项目是否在可见区域:

private Boolean isInBounds(LatLng position) {
    return map.getProjection().getVisibleRegion().latLngBounds.contains(position);
}

但是如果项目当前不可见我跳过渲染,当用户在地图上滚动时它将是空的。

那么谁知道在用户滚动时如何获取事件以及如何重新呈现不在可见范围内的项目? (从可见切换到不可见,反之亦然)?

(抱歉我的英语不好)

我有类似的问题。我跟踪用户的初始位置并在他周围显示地图标记。如果用户选择在初始缩放之外查看,我会下载其他标记。实施:

map.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {

            @Override
            public void onCameraChange(CameraPosition cameraPosition) {
                setCurrentRadius();
                viewCenter = cameraPosition.target;
                if (isNewGrounds(viewCenter, viewRadius)) {
                    downloadExtraPrinters(viewCenter,viewRadius);
                }
                myClusterManager.onCameraChange(cameraPosition);
            }
        });

   private void setCurrentRadius() {
        Projection projection = map.getProjection();
        VisibleRegion currentView = projection.getVisibleRegion();
        LatLng cameraCenter = map.getCameraPosition().target;
        float[] projectionRadius = new float[1];
        Location.distanceBetween(currentView.farLeft.latitude, currentView.farLeft.longitude, cameraCenter.latitude, cameraCenter.longitude, projectionRadius);
        viewRadius = Math.round((projectionRadius[0] / 1000.0f) + 0.5f);
    }

 /** Method checks if camera goes outside bounds of previously downloaded area **/
    protected boolean isNewGrounds(LatLng center, int radius) {
        //Check if it is the first time to update
        if (coveredGroundsCenter== null) {

            coveredGroundsCenter=center;
            coveredGroundsRadius= (int)(radius * EXTRA_RADIUS);

            return true;
        }else {
            float[] centerDistance = new float[1];
            Location.distanceBetween(coveredGroundsCenter.latitude, coveredGroundsCenter.longitude, center.latitude, center.longitude, centerDistance);

            int criticalDistance = (int) Math.round((centerDistance[0] / 1000.0f) + 0.5f) + radius;

            if (coveredGroundsRadius >= criticalDistance) {
                return false;
            } else {
                coveredGroundsCenter = center;
                coveredGroundsRadius = (int) (radius * EXTRA_RADIUS);
                return true;
            }
        }
    }

EXTRA_RADIUS 是一个常量,我用它在可见地图区域外留有小边距,因此最小的相机移动不会导致我的地图下载和重新渲染。我选择这个边距为当前半径的 50%:

 private static final double EXTRA_RADIUS=1.5;
 private LatLng coveredGroundsCenter;
 private int coveredGroundsRadius;
 private LatLng viewCenter;

这是我的解决方案。它工作得很好,只渲染可见的项目。 我使用相机更改的侦听器重新渲染现在可见的项目:

private void onBeforeClusterOrClusterItemRendered(final Cluster<MediaItem> cluster, final MediaItem mediaItem, final MarkerOptions markerOption
    if(!isAdded())
        return;

    // In visible area?
    Marker marker = cluster == null ? getMarker(mediaItem) : getMarker(cluster);
    Boolean isInBounds = isInBounds(marker != null ? marker.getPosition() : mediaItem.getPosition(), null);

    if(isInBounds) {
        // ...
    }
}

private Boolean isInBounds(LatLng position, LatLngBounds latLngBounds) {
    return (latLngBounds == null ? mMap.getProjection().getVisibleRegion().latLngBounds : latLngBounds).contains(position);
}

@Override
protected void onBeforeClusterItemRendered(final MediaItem mediaItem, final MarkerOptions markerOptions) {
    onBeforeClusterOrClusterItemRendered(null, mediaItem, markerOptions);
}

@Override
protected void onBeforeClusterRendered(final Cluster<MediaItem> cluster, final MarkerOptions markerOptions) {
    final MediaItem mediaItem = MediaPicker.getBestRated(new ArrayList<>(cluster.getItems()));
    onBeforeClusterOrClusterItemRendered(cluster, mediaItem, markerOptions);
}

...

// Re render
mMap.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
    @Override
    public void onCameraChange(CameraPosition cameraPosition) {
        mClusterManager.onCameraChange(cameraPosition);
        final LatLngBounds latLngBounds = mMap.getProjection().getVisibleRegion().latLngBounds;

        // Cluster only
        Collection<Marker> clusters = mClusterManager.getClusterMarkerCollection().getMarkers();
        for(Marker marker : clusters) {
            if(isInBounds(marker.getPosition(), latLngBounds))
                onBeforeClusterRendered(getCluster(marker), new MarkerOptions());
        }

        // Cluster item only
        Collection<Marker> markers = mClusterManager.getMarkerCollection().getMarkers();
        for(Marker marker : markers) {
            if(isInBounds(marker.getPosition(), latLngBounds))
                onBeforeClusterItemRendered(getClusterItem(marker), new MarkerOptions());
        }
    }
});

也许为时已晚,但我希望我能帮助到别人。我试图找到解决方案,但没有成功,所以我开始通过集群classes寻找解决方案。我找到了一个名为 NonHierarchicalViewBasedAlgorithm 的 class,但它无法正常工作或我使用不当。并基于此 class,我使用 LatLngBounds 编写了自己的代码。它叫做 VisibleBoundsAlgorithm。

注意:每次聚类前必须调用updateBounds()方法。在 onCameraIdleListener

中使用它

代码如下:

VisibleBoundsAlgorithm.java

package com.example.demo.app;

import android.util.Log;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.maps.android.clustering.ClusterItem;
import com.google.maps.android.clustering.algo.NonHierarchicalDistanceBasedAlgorithm;
import com.google.maps.android.clustering.algo.ScreenBasedAlgorithm;
import com.google.maps.android.geometry.Bounds;
import com.google.maps.android.quadtree.PointQuadTree;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class VisibleBoundsAlgorithm<T extends ClusterItem>
    extends NonHierarchicalDistanceBasedAlgorithm<T> implements ScreenBasedAlgorithm<T> {
private final GoogleMap map;
private LatLngBounds bounds;

public VisibleBoundsAlgorithm(GoogleMap map) {
    this.map = map;
    bounds = map.getProjection().getVisibleRegion().latLngBounds;
}

@Override
protected Collection<QuadItem<T>> getClusteringItems(PointQuadTree<QuadItem<T>> quadTree, float mapZoom) {
    long oldMillis = System.currentTimeMillis(); //Getting time before operations

    //Getting all items from QuadTree
    List<QuadItem<T>> items = new ArrayList<>(quadTree.search(new Bounds(0, 1, 0, 1)));

    for (int i = 0; i < items.size(); i++) {
        QuadItem<T> item = items.get(i);
        if (!bounds.contains(item.getPosition())) { //if map do not contain item on this position remove him
            items.remove(item);
            i--;
        }
    }

    long newMillis = System.currentTimeMillis();
    Log.println(Log.ERROR, "Clustering items", "end in: " + (newMillis - oldMillis) + " (" + TimeUnit.SECONDS.convert(newMillis - oldMillis, TimeUnit.MILLISECONDS) + " seconds)");
    return items; //Returning new massive of items
}

/**
 * Call this before clustering.
 */
public void updateBounds() {
    bounds = map.getProjection().getVisibleRegion().latLngBounds;
}


//When map is moving or zooming it should re-cluster
@Override
public boolean shouldReclusterOnMapMovement() {
    return true;
}

@Override
public void onCameraChange(CameraPosition position) {
    //required
}

}

在你的 Activity class:

ClusterManager<MyItem> cluster = new ClusterManager<MyItem>(context, map);

VisibleBoundsAlgorithm<MyItem> visibleBoundsAlgorithm = new VisibleBoundsAlgorithm<MyItem>(map);

cluster.setAlgorithm(visibleBoundsAlgorithm);
cluster.addItems(items);
cluster.cluster();

map.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() {
    @Override
    public void onCameraIdle() {
        visibleBoundsAlgorithm.updateBounds();
        cluster.cluster();
    }
});

如果有人有问题问我。