使用百度地图的点聚合功能

前端之家收集整理的这篇文章主要介绍了使用百度地图的点聚合功能前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

百度地图的demo中,已经提供了点聚合功能

一、先大体了解下,主要关注点聚合里面的两个类:

1.ClusterItem接口

这个就是地图上一个一个独立的标记点。这个接口提供两个方法需要实现:
一个是提供marker的位置:

LatLng getPosition();

一个是提供marker的图标:

BitmapDescriptor getBitmapDescriptor();

我们自定义的点MyItem必须继承ClusterItem这个接口,才能被ClusterManager管理。

2,ClusterManager

顾名思义就是Cluster的管理类,其中有设置相应的点击事件。
聚合点的点击监听:

mClusterManager.setOnClusterClickListener(new OnClusterClickListener<BaiduNearbyActivity.MyItem>(){

聚合点中单项的点击监听:

mClusterManager.setOnClusterItemClickListener(new OnClusterItemClickListener<BaiduNearbyActivity.MyItem>(){

百度地图的demo中,是在MarkerClusterDemo.java中演示了点聚合功能,不过比较简单,不能满足实际应用的需求。

二、在其基础功能之上,做了如下改进:

1,给标记点传递数据;
2,实现聚合点的点击功能,点击后在地图上展开聚合点的内容
3,聚合的起始数目修改支持2个点也能聚合;
4,聚合的范围调整,避免聚合点图标的互相覆盖;
5,实现地图状态变化的监听;

1,给标记点传递数据;

其实就是新建一个类MyItem,继承ClusterItem接口,并增加数据成员来传递数据;
主要就是添加了一个Bundle成员,实现了Bundle的设置及使用的逻辑;
我在Bundle中也只是添加了一个字符串,在选择图标时(getBitmapDescriptor()),用于判断,可以选择不同的图标展示在地图上。
下面是MyItem类的实现:

/** * 每个Marker点,包含Marker点坐标以及图标 */
    public class MyItem implements ClusterItem {
        private final LatLng mPosition;
        private Bundle mBundle;

        public MyItem(LatLng latLng) {
            mPosition = latLng;
            mBundle = null;
        }
        public MyItem(LatLng latLng,Bundle bundle) {
            mPosition = latLng;
            mBundle = bundle;
        }
        @Override
        public LatLng getPosition() {
            return mPosition;
        }

        @Override
        public BitmapDescriptor getBitmapDescriptor() {
            int iconId = R.drawable.icon_gcoding;
            if(mBundle!=null){
                if("001".contentEquals(mBundle.getString("index"))) {
                    iconId = R.drawable.icon_marka;
                } else if("002".contentEquals(mBundle.getString("index"))) 

{
                    iconId = R.drawable.icon_markb;
                }
            }

            return BitmapDescriptorFactory                   .fromResource(iconId);//R.drawable.icon_gcoding);
        }

        public Bundle getBundle(){
            return mBundle;
        }

    }

在点击ClusterItem时使用Toast显示一下:

mClusterManager.setOnClusterItemClickListener(new ClusterManager.OnClusterItemClickListener<MyItem>() {
            @Override
            public boolean onClusterItemClick(MyItem item) {
                String showText = "点击单个Item";
                if(item.getBundle()!=null) {
                    showText += " index="+item.getBundle().getString("index");
                }
                Toast.makeText(MarkerClusterDemo.this,showText,Toast.LENGTH_SHORT).show();

                return false;
            }
        });

2,实现点击功能,点击后在地图上展开聚合点的内容

点击功能,demo中已经实现了,可是,点击聚合点,如何放大到恰好包含那些点?
这里用到了LatLngBounds类来进行地理范围管理,具体实现如下:

mClusterManager.setOnClusterClickListener(new ClusterManager.OnClusterClickListener<MyItem>() {
            @Override
            public boolean onClusterClick(Cluster<MyItem> cluster) {
                Toast.makeText(MarkerClusterDemo.this,"有" + cluster.getSize() + "个点",Toast.LENGTH_SHORT).show();

                List<MyItem> items = (List<MyItem>) cluster.getItems();
                LatLngBounds.Builder builder2 = new LatLngBounds.Builder();
                int i=0;
                for(MyItem myItem : items){
                    builder2 = builder2.include(myItem.getPosition());
                    Log.i("map","log: i="+ i++ +" pos="+myItem.getPosition().toString());
                }

                LatLngBounds latlngBounds = builder2.build();
                MapStatusUpdate u = MapStatusUpdateFactory.newLatLngBounds(latlngBounds,mMapView.getWidth(),mMapView.getHeight());
                mBaiduMap.animateMapStatus(u);
                Log.i("map","log: mBaiduMap.animateMapStatus(u)");

                return false;
            }
        });

对于聚合点的点击操作,需要注意进行传递:
ClusterManager.java中:

public boolean onMarkerClick(Marker marker) {
// return false;
        return getMarkerManager().onMarkerClick(marker);
    }

3,聚合的起始数目修改:4->1;

原demo中,要至少5个点才能聚合,而实际使用时,我可不能这样来实现,只要有两个点靠近了,也是要聚合的。
修改这个文件

com\baidu\mapapi\clusterutil\clustering\view\DefaultClusterRenderer.java

其中有个MIN_CLUSTER_SIZE,修改4->1:

private static final int MIN_CLUSTER_SIZE = 1;//4

查看判断逻辑,也就是使用MIN_CLUSTER_SIZE的地方:

/** * Determine whether the cluster should be rendered as individual markers or a cluster. */
    protected boolean shouldRenderAsCluster(Cluster<T> cluster) {
        return cluster.getSize() > MIN_CLUSTER_SIZE;
    }

4,聚合的范围调整,避免聚合点图标的互相覆盖;

运行时发现,点聚合后还是有点互相覆盖的问题,这样就应该有个聚合点负责的范围值,我通过测试,发现修改MAX_DISTANCE_AT_ZOOM有效,在这个文件中:

com\baidu\mapapi\clusterutil\clustering\algo\NonHierarchicalDistanceBasedAlgorithm.java

修改 NonHierarchicalDistanceBasedAlgorithm 类中的:

public static final int MAX_DISTANCE_AT_ZOOM = 300; // essentially 100 dp ->300dp

这样基本上就不再有重叠问题了。

5,实现地图变化的监听;

在实际使用过程中,发现一个问题,就是进行两次设置地图状态改变的监听,第一次设置的,就不管用了。

原来的代码中已经设置了一次监听:

mBaiduMap.setOnMapStatusChangeListener(new BaiduMap.OnMapStatusChangeListener() {

对点聚合功能,又设置了一次监听:

// 设置地图监听,当地图状态发生改变时,进行点聚合运算
mBaiduMap.setOnMapStatusChangeListener(mClusterManager);

然后,第一次设置的监听中的onMapStatusChangeFinish()走不到了。
感觉是对同一个BaiduMap,只允许设置一次地图状态监听:

setOnMapStatusChangeListener()

当时挺犯愁的:总不能添加了点聚合功能,导致原先的部分功能失效吧。

后来是在查看ClusterManager类的定义时发现了解决方法

public class ClusterManager<T extends ClusterItem> implements BaiduMap.OnMapStatusChangeListener,BaiduMap.OnMarkerClickListener {

原来,ClusterManager类里面已经有了nMapStatusChangeListener这个接口,可以由我们来实现这个接口中的方法

public void onMapStatusChangeStart(MapStatus mapStatus) {

    public void onMapStatusChangeFinish(MapStatus mapStatus) {

接下来又遇到一个问题:原来是在activity中直接响应地图改变,访问的变量都是本地的,现在转移到ClusterManager这个类中了,如何实现原来的动作呢?

最开始的想法,是直接传递context下来,然后强制转换类型,去调用原Activity的方法,应该也可以实现,可是,如果ClusterManager这个类被别的activity调用,这个强制转换就会带来问题了。

所以,还是使用一个handler,来进行消息的传递,在activity中来实现相应的响应动作。
这样,能很好的适应不同调用环境。

具体实现方法
在ClusterManager类中定义两个变量:

Handler handler;
    int result; //msg.what

添加一个对外的接口,用于设置这两个值:

public void setHandler(Handler handler,int result){
        this.handler = handler;
        this.result = result;
    }

然后,在地图状态发生变化完成时,使用handler发送消息:

@Override
    public void onMapStatusChangeFinish(MapStatus mapStatus) {

        Log.i("ClusterManger","onMapStatusChangeFinish");
        Message message = handler.obtainMessage(result);
        message.obj = mapStatus;
        handler.sendMessage(message);

    }

在activity中调用setHandler进行设置:

mClusterManager.setHandler(handler,MAP_STATUS_CHANGE); //设置handler

然后实现对消息的处理:

private final  int MAP_STATUS_CHANGE = 100;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case MAP_STATUS_CHANGE:
                    MapStatus mapStatus = (MapStatus) msg.obj;
                    if(mapStatus!=null){
                        Log.i("MarkerClusterDemo","mapStatus="+mapStatus.toString());
                        // to do : 判断地图状态,进行相应处理
                    }
                    break;
                default:
                    break;
            }
        }
    };

有图有真相:

第一张图,是都聚合为一个点的情况;
第二张图,是点击一次聚合点后的情况,进行地图展开;
第三张图,是再次点击聚合点后的情况,对新聚合点进行地图展开;
第四张图,进行缩放后,对比不同的标记点图标,并且点击了一个标记点;

demo下载地址:

http://download.csdn.net/detail/lintax/9737954

参考:

http://blog.csdn.net/javine/article/details/50186023
http://blog.csdn.net/u010635353/article/details/52386097

猜你在找的设计模式相关文章