說一說Android的地圖聚合

最近遇到一個需求,其中涉及到一些聚合的東西,給大家說說我的不成熟的小想法。國際慣例,先上黃圖:

cluster.gif
  • 首先說說什么是聚合,如果你不怎么用地圖的話,可能對聚合這個東西幾乎沒什么概念,聚合呢,其實就是將地圖上過于密集的覆蓋物集合到一塊,當?shù)貓D舒展開了,集合中的覆蓋物又會分布開,就是這么個效果。

  • 再來說說為什么要聚合,說到底就是讓交互變得更友善,沒聚合之前,圖上總共1400多個點,不能想象密集恐懼癥的人看了會有什么感覺,反正我自己看著也毛毛的;再一個呢,這么多的點,圖片加載渲染的時候難免會卡頓,聚合之后的話,會有效減少卡頓的現(xiàn)象。

  • 其實在我用過的地圖中,官方實現(xiàn)了聚合功能的只有百度地圖,其他都得自己來實現(xiàn)了,OK,進入我們的正題,到底如何實現(xiàn)聚合呢?

說下我寫的兩種聚合的方法:
第一種:以地圖上的某個點作為聚合點,以這個點的坐標為中心點,創(chuàng)建出一個Rect,再去計算在這個Rect中是否包含了其他的點,如果包含了,這些個點就合體成為了一個聚合點。


98ED363C-B79E-4B2D-ADCF-8B0EA0F15D24.png

好了,我們來寫一個聚合類:

public class Cluster {
    //聚合大小控制,就是控制Rect的寬高
    private int bounds;
    private PointF f;
    private MapView mapView;
    private List<PointF> ps = new ArrayList<PointF>();//用來存儲該聚合內有多少個覆蓋物,就是地圖上的Overlay

    public Cluster() {}

    //新建的時候會扔一個覆蓋物進來,如果沒有聚合產生那么這個聚合就是原來的覆蓋物
    public Cluster(PointF f, MapView mapView, int bounds) {
        this.f = f;
        this.mapView = mapView;
        this.bounds = bounds;
        ps.add(f);
    }

    //返回一個方形的范圍區(qū)域,用作判定聚合
    public Rect getRect() {
        float x = f.x;
        float y = f.y;
        //將地圖的坐標轉換成屏幕的坐標
        float[] floats = mapView.convertMapXYToScreenXY1(x, y);
        Rect rect = new Rect((int) floats[0], (int) floats[1], (int) (floats[0] + bounds), (int) (floats[1] + bounds));
        return rect;
    }

//如果被判定在聚合內,那么就將這個點加入聚合類中的集合
    public void addPoints(PointF p) {
        ps.add(p);
    }

//當所有的覆蓋物聚合計算完成后,次方法返回聚合的坐標
    public PointF getPosition(){

        if (ps.size() == 1) {
            return f;
        }
        float x = 0;
        float y = 0;
        for (PointF p : ps) {
             x += p.x;
            y += p.y;
        }
        x = x / ps.size();
        y = y / ps.size();

        return new PointF(x,y);
    }

}

接下來聚合的算法:

public void getCluster() {

       clusters.clear();
        newPoints.clear();
        //遍歷地圖上所有的覆蓋物進行聚合操作
        for (PointF mark : marks) {
            float[] floats1 = mapView.convertMapXYToScreenXY1(mark.x, mark.y);//地圖上的點轉換成屏幕坐標點
            int width = mapView.getWidth();
            int height = mapView.getHeight();
            //計算出屏幕中的點,不在屏幕中的不用聚合
            if (floats1[0] < 0 || floats1[1] < 0 || floats1[0] > width || floats1[1] > height) {
                continue;
            }

            boolean isIn = false;//是否已經(jīng)聚合
            //如果沒有的話就先創(chuàng)建一個聚合類扔進去
            if (clusters.size() == 0) {
                clusters.add(new Cluster(mark, mapView, 100));
            } else {//有了聚合類就開始計算點是否在聚合內

                for (Cluster cluster : clusters) {

                    float[] floats = mapView.convertMapXYToScreenXY1(mark.x, mark.y);

                    boolean isContian = cluster.getRect().contains((int) floats[0], (int) floats[1]);//是否在聚合內

                    if (isContian) {
                        cluster.addPoints(mark);
                        isIn = true;
                        break;
                    }

                }

                //如果不在那幾個聚合點內的話,重新添加到一個新的聚合類中去
                if (!isIn) {
                    clusters.add(new Cluster(mark, mapView, bounds));
                }
            }
        }

//將聚合中的重新計算取出
        for (Cluster cluster : clusters) {

          newPoints.add(new PointF(cluster.getPosition().x,cluster.getPosition().y));
        }


    }

注釋應該寫的挺清楚的,還是那句話,寫代碼之前多想想你要什么,就往這個聚合類中添加什么,慢慢的這個類就會越來越健壯。

接下來第二種方法:
將整個屏幕分成N個Rect,分別計算在某個Rect中有多少個覆蓋物,如果多于一個覆蓋物的話,那么這個就是聚合,否則,就是一個覆蓋物。

203BDB66-3DD2-4288-8456-EFEF14AA58B2.png

再來個聚合類:

public class MCluster {

    public boolean isClick() {
        return isClick;
    }

    public void setClick(boolean click) {
        isClick = click;
    }

    private boolean isClick;//是否可以點擊

    private String PntName;
    private String Unit;
    private float Value;

    public String getPntName() {
        return PntName;
    }

    public void setPntName(String pntName) {
        PntName = pntName;
    }

    public String getUnit() {
        return Unit;
    }

    public void setUnit(String unit) {
        Unit = unit;
    }

    public float getValue() {
        return Value;
    }

    public void setValue(float value) {
        Value = value;
    }

    //覆蓋物集合
    private List<PointF> ps = new ArrayList<PointF>();

    private Rect rect;
    public MCluster() {
    }

    public MCluster(Rect rect) {
        this.rect = rect;
    }

    public void addPoint(PointF pointF){

        ps.add(pointF);

    }
    
//將集合清空
    public void clear(){

        ps.clear();

    }

    public Rect getRect(){

        return rect;

    }

    //看這個聚合內是否有覆蓋物
    public boolean hasPoint(){

        if (ps.size() == 0) {
            return false;
        }

        return true;
    }

    //判斷是否是聚合,如果集合中點數(shù)大于1說明是聚合了,否則不聚合
    public boolean isCluster(){

        if (ps.size() == 1) {
            return false;
        }

        return true;
    }

    //計算坐標
    public MyPoint getPosition(){

        float x = 0;
        float y = 0;
        for (PointF p : ps) {

            x += p.x;
            y += p.y;
        }

        x = x / ps.size();
        y = y / ps.size();

        MyPoint myPoint = new MyPoint(x, y,isCluster(),PntName,Unit,Value,ps.size(),isClick());


        return  myPoint;

    }
    //得到聚合的數(shù)量
    public int getSize(){

        return ps.size();

    }
}

其實大同小異。
劃分聚合:

 private void makeCluster() {

        //以320像素密度為基礎設置Rect的寬高為50像素
        float base = 320;
        int width1 = (int) SPUtils.get(mapView.getContext(), "width", -1);//屏幕的寬
        int height1 = (int) SPUtils.get(mapView.getContext(), "height", -1);//屏幕的高
        int density = (int) SPUtils.get(mapView.getContext(), "density", -1);//屏幕的像素密度


        float scale =  (density/base);


        final float width = 50*scale;//Rect的寬高


        int round = Math.round(width);

        final int  h = height1/round;

       final int w = width1 / round;

       //將屏幕劃分成N個聚合區(qū)
        for (int j = 0; j < h+1; j++) {

            for (int i = 0; i < (w + 1); i++) {

                mClusters.add(new MCluster( new Rect(i * round,j * round,i * round + round,j * round + round)));

            }

        }

    }

接著看聚合的算法:

public void getNewCluster(){

//遍歷所有的覆蓋物
        for (Points mark : marks) {
            PointF pointF = mark.getPointF();
            if (pointF == null) {
                return;
            }
            float[] floats1 = mapView.convertMapXYToScreenXY1(pointF.x, pointF.y);//地圖上的點轉換成屏幕坐標點

            int x = (int) floats1[0];
            int y = (int) floats1[1];
            int width = mapView.getWidth();
            int height = mapView.getHeight();
            //計算出屏幕中的點,不在屏幕中的不用聚合
            if (x< 0 || y < 0 || x > width || y > height) {
                continue;
            }

            //遍歷所有的聚合
            for (MCluster mCluster : mClusters) {

                Rect rect = mCluster.getRect();

                //在聚合內
                if (rect.contains(x, y)) {

                    mCluster.addPoint(pointF);

                    mCluster.setClick(mark.isClick());
                    mCluster.setPntName(mark.getPntName());
                    mCluster.setUnit(mark.getUnit());
                    mCluster.setValue(mark.getValue());
                    break;
                }
            }
        }

        newPoints.clear();

        for (MCluster mCluster : mClusters) {

            if (mCluster.hasPoint()) {

                newPoints.add(mCluster.getPosition());



            }

        }
        //將聚合中的數(shù)據(jù)清除
        for (MCluster mCluster : mClusters) {

            mCluster.clear();
        }

    }

以上,哪里說的不對歡迎指正

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,276評論 25 708
  • 溫馨小貼士 本文慎入!泣血長文(花了鄙人近一周時間進行構思和寫作)?。?!吃透耗時大概15分鐘,需流量1MB,但讀后...
    運營喵怎樣煉成閱讀 2,870評論 8 54
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,680評論 19 139
  • 《我不想長大》 我不想長大 想一直在母親膝下玩耍 總以為自己還很小 可是母親已經(jīng)長滿華發(fā) 我不想長大 想像孩子一樣...
    白清風閱讀 316評論 0 0
  • ...在這歡樂的節(jié)日,不知為啥,總有些中國笨蛋叫囂要抵制雙旦節(jié)(圣誕和元旦),問問笨蛋, 在你抵制雙旦之前,以下這...
    咪咪魔魔閱讀 501評論 0 0

友情鏈接更多精彩內容