Android安卓高德地图实现多边形绘制与编辑

需求:使用高德地图api实现多边形的绘制与编辑,从而实现圈中房屋的效果。

在js中有相关插件,效果很好。点我跳转
但是在高德开放平台中并未找到安卓ios的相关插件或者方法,于是提交工单,5个工作日才能回复,算了不等了,自己写吧!

思路:

1.首先根据当前定位的点绘制一个矩形,在矩形的四个顶点(LatLng)创建Marker,并给四个顶点初始化一个状态(MyLatLng.ABLE)和一个索引值。
2.计算矩形四边的中点经纬度(LatLng),四个中点创建Marker,并给四个顶点初始化一个状态(MyLatLng.UNABLE)和一个索引值。
3.绘制多边形样式,并调用AMap.addPolygon方法创建多边形。
4.aMap.setOnMarkerDragListener给Marker添加拖拽事件
5.在onMarkerDragStart方法移除拖拽的Marker
6.在onMarkerDragEnd方法添加新的Marker并判断该点左右两个Marker的状态:如果状态为MyLatLng.ABLE,则表示该点拖拽后需要在该点两侧创建新Marker。如果状态为MyLatLng.UNABLE则不需要创建新的点,而是替换两侧的点的坐标(该坐标为新点到两侧点的中间点)。
7、重新根据新的点绘制多边形

效果图

444.png

代码:

1.集成高德地图和申请位置权限就不说了
2.初始化地图

  /**
     * 初始化地图
     */
    private void initMap() {
        if (aMap == null) {
            aMap = mMapView.getMap();
            MyLocationStyle myLocationStyle;
            myLocationStyle = new MyLocationStyle();//初始化定位蓝点样式类myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE);//连续定位、且将视角移动到地图中心点,定位点依照设备方向旋转,并且会跟随设备移动。(1秒1次定位)如果不设置myLocationType,默认也会执行此种模式。
            myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE_NO_CENTER);
            myLocationStyle.interval(2000); //设置连续定位模式下的定位间隔,只在连续定位模式下生效,单次定位模式下不会生效。单位为毫秒。
            aMap.setMyLocationStyle(myLocationStyle);//设置定位蓝点的Style
            aMap.setMinZoomLevel(20);//不让用户缩放地图
            aMap.moveCamera(CameraUpdateFactory.zoomTo(20));
            mlocationClient = new AMapLocationClient(this.getApplicationContext());
            locationOption = getDefaultOption();
            mlocationClient.setLocationOption(locationOption);
            mlocationClient.setLocationListener(locationListener);
            // 启动定位
            mlocationClient.startLocation();
        }
    }

    /**
     * 设置默认的定位参数
     */
    private AMapLocationClientOption getDefaultOption() {
        AMapLocationClientOption mOption = new AMapLocationClientOption();
        mOption.setLocationMode(
                AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);//可选,设置定位模式,可选的模式有高精度、仅设备、仅网络。默认为高精度模式
        mOption.setGpsFirst(false);//可选,设置是否gps优先,只在高精度模式下有效。默认关闭
        mOption.setHttpTimeOut(30000);//可选,设置网络请求超时时间。默认为30秒。在仅设备模式下无效
        mOption.setInterval(300000);//可选,设置定位间隔。默认为2秒
        mOption.setNeedAddress(true);//可选,设置是否返回逆地理地址信息。默认是true
        mOption.setOnceLocation(true);//可选,设置是否单次定位。默认是false
        mOption.setOnceLocationLatest(false);//可选,设置是否等待wifi刷新,默认为false.如果设置为true,会自动变为单次定位,持续定位时不要使用
        AMapLocationClientOption.setLocationProtocol(
                AMapLocationClientOption.AMapLocationProtocol.HTTP);//可选, 设置网络请求的协议。可选HTTP或者HTTPS。默认为HTTP
        mOption.setWifiScan(
                true); //可选,设置是否开启wifi扫描。默认为true,如果设置为false会同时停止主动刷新,停止以后完全依赖于系统刷新,定位位置可能存在误差
        return mOption;
    }

3.定位的监听

  /**
     * 定位监听
     */
    AMapLocationListener locationListener = new AMapLocationListener() {
        @Override
        public void onLocationChanged(AMapLocation amapLocation) {
            if (null != amapLocation) {
                //errCode等于0代表定位成功,其他的为定位失败,具体的可以参照官网定位错误码说明
                if (amapLocation.getErrorCode() == 0) {
                    latitude = amapLocation.getLatitude();
                    longitude = amapLocation.getLongitude();
                    aMap.animateCamera(CameraUpdateFactory.newLatLng(new LatLng(latitude, longitude)));
                    createRectangle();
                }
            }
        }
    };

4.绘制可编辑多边形并添加拖拽事件

/**
     * 绘制可编辑多边形
     */
    private void createRectangle() {
        if (latitude != null && longitude != null) {
            //创建一个中心点坐标
            DPoint centerPoint = new DPoint();
            //设置中心点纬度
            centerPoint.setLatitude(latitude);
            //设置中心点经度
            centerPoint.setLongitude(longitude);
            //初始化数据
            initRectangle();
            //初始化多边形的polygonOptions
            for (int i = 0; i < allLatLngs.size(); i++) {
                polygonOptions.add(allLatLngs.get(i).getLatLng());
            }
            //初始化8个点的marker
            addMarker(true);
            //绘制区域颜色
            createAreaStyle();
            //创建多边形
            aMap.addPolygon(polygonOptions);
            //添加拖拽事件(必须要长按才可以拖拽)
            aMap.setOnMarkerDragListener(new AMap.OnMarkerDragListener() {
                @Override
                public void onMarkerDragStart(Marker marker) {
                    //开始拖拽时,把集合里的该点删除掉
                    int i = (int) marker.getObject();
                    Log.e(TAG, "移除第" + i + "个");
                    allLatLngs.remove(i);
                    refreshPolygonOptions();
                }

                @Override
                public void onMarkerDrag(Marker marker) {

                }

                @Override
                public void onMarkerDragEnd(Marker marker) {
                    //拖拽结束时,创建新点
                    allLatLngs.add((int) marker.getObject(), new MyLatLng(new LatLng(marker.getPosition().latitude, marker.getPosition().longitude), MyLatLng.ABLE));
                    //判断是否需要创建新的点
                    if (!isCreateMarker(marker)) {
                        //不需要
                        //如果拖拽的是状态为0的点,则不需要创建新的点,而是替换两侧的点的坐标(注意是替换set方法)。
                        replaceTwoMarker(marker);
                        refreshPolygonOptions();
                        addMarker(true);
                        createAreaStyle();
                        aMap.addPolygon(polygonOptions);
                    } else {
                        //需要
                        refreshPolygonOptions();
                        addMarker(true);
                        createAreaStyle();
                        aMap.addPolygon(polygonOptions);
                        //在拖拽点两侧添加maker
                        addTwoMarker(marker);
                        addMarker(false);
                    }
                }
            });
        }
    }
/**
     * 生成一个长方形的四个坐标点
     */
    private void initRectangle() {
        BigDecimal currentLatBd = new BigDecimal(latitude);
        BigDecimal currentLongBd = new BigDecimal(longitude);
        BigDecimal b3 = new BigDecimal("0.0002");
        BigDecimal b4 = new BigDecimal("0.0003");
        MyLatLng latLng1 = new MyLatLng(new LatLng(currentLatBd.subtract(b3).doubleValue(), currentLongBd.add(b3).doubleValue()), MyLatLng.ABLE);
        MyLatLng latLng2 = new MyLatLng(new LatLng(currentLatBd.subtract(b3).doubleValue(), currentLongBd.subtract(b3).doubleValue()), MyLatLng.ABLE);
        MyLatLng latLng3 = new MyLatLng(new LatLng(currentLatBd.add(b3).doubleValue(), currentLongBd.subtract(b3).doubleValue()), MyLatLng.ABLE);
        MyLatLng latLng4 = new MyLatLng(new LatLng(currentLatBd.add(b3).doubleValue(), currentLongBd.add(b3).doubleValue()), MyLatLng.ABLE);

        MyLatLng latLng12 = getCenterPoint(latLng1, latLng2);
        MyLatLng latLng23 = getCenterPoint(latLng2, latLng3);
        MyLatLng latLng34 = getCenterPoint(latLng3, latLng4);
        MyLatLng latLng41 = getCenterPoint(latLng4, latLng1);

        allLatLngs.add(latLng1);
        allLatLngs.add(latLng12);
        allLatLngs.add(latLng2);
        allLatLngs.add(latLng23);
        allLatLngs.add(latLng3);
        allLatLngs.add(latLng34);
        allLatLngs.add(latLng4);
        allLatLngs.add(latLng41);
        //设置显示范围(如果不需要,把后面的代码注释掉)
        LatLngBounds latLngBounds = new LatLngBounds(
                new LatLng(currentLatBd.subtract(b4).doubleValue(), currentLongBd.subtract(b4).doubleValue()),
                new LatLng(currentLatBd.add(b4).doubleValue(), currentLongBd.add(b4).doubleValue()));
        aMap.setMapStatusLimits(latLngBounds);
    }

    /**
     * 根据总得点集合,刷新polygonOptions集合的数据
     */
    private void refreshPolygonOptions() {
        if (polygonOptions.getPoints().size() != MyLatLng.UNABLE) {
            polygonOptions.getPoints().clear();
        }
        for (int i = 0; i < allLatLngs.size(); i++) {
            polygonOptions.add(allLatLngs.get(i).getLatLng());
        }
    }

    /**
     * 判断拖拽的点松手后是否需要创建新点
     */
    private boolean isCreateMarker(Marker marker) {
        int index = (int) marker.getObject();
        if (index == 0) {
            Log.e(TAG, "需要添加两个Marker");
            return false;
        }
        if (index == allLatLngs.size() - 1) {
            if (allLatLngs.get(index - 1).getState() == MyLatLng.ABLE && allLatLngs.get(0).getState() == MyLatLng.ABLE) {
                Log.e(TAG, "需要添加两个Marker");
                return true;
            }
        }
        if (allLatLngs.get(index - 1).getState() == MyLatLng.ABLE) {
            Log.e(TAG, "需要添加两个Marker");
            return true;
        }
        Log.e(TAG, "不需要添加两个Marker");
        return false;
    }

    /**
     * 在拖拽点两侧添加maker
     */
    private void addTwoMarker(Marker marker) {
        int index = (int) marker.getObject();
        if (index == 0) {
            //判断拖拽的点两侧点的状态,如果为1,则可以新建两个点
            if (allLatLngs.get(1).getState() == MyLatLng.ABLE && allLatLngs.get(allLatLngs.size() - 1).getState() == MyLatLng.ABLE) {
                MyLatLng centerPoint1 = getCenterPoint(allLatLngs.get(0), allLatLngs.get(1));
                MyLatLng centerPoint2 = getCenterPoint(allLatLngs.get(0), allLatLngs.get(allLatLngs.size() - 1));
                allLatLngs.add(1, centerPoint1);
                allLatLngs.add(allLatLngs.size() - 1, centerPoint2);
                Log.e(TAG, "在第" + 1 + "个添加marker");
                Log.e(TAG, "在第" + (allLatLngs.size() - 1) + "个添加marker");
            }
            return;
        }
        if (index == allLatLngs.size() - 1) {
            if (allLatLngs.get(index - 1).getState() == MyLatLng.ABLE && allLatLngs.get(0).getState() == MyLatLng.ABLE) {
                MyLatLng centerPoint1 = getCenterPoint(allLatLngs.get(index - 1), allLatLngs.get(index));
                MyLatLng centerPoint2 = getCenterPoint(allLatLngs.get(index), allLatLngs.get(0));
                allLatLngs.add(index, centerPoint1);
                allLatLngs.add(index + 2, centerPoint2);
                Log.e(TAG, "在第" + index + "个添加marker");
                Log.e(TAG, "在第" + (index + 2) + "个添加marker");
                return;
            }
        }
        if (allLatLngs.get(index - 1).getState() == MyLatLng.ABLE && allLatLngs.get(index + 1).getState() == MyLatLng.ABLE) {
            MyLatLng centerPoint1 = getCenterPoint(allLatLngs.get(index - 1), allLatLngs.get(index));
            MyLatLng centerPoint2 = getCenterPoint(allLatLngs.get(index), allLatLngs.get(index + 1));
            allLatLngs.add(index, centerPoint1);
            allLatLngs.add(index + 2, centerPoint2);
            Log.e(TAG, "在第" + (index) + "个添加marker");
            Log.e(TAG, "在第" + (index + 2) + "个添加marker");
        }
    }

    /**
     * 如果拖拽的是状态为1的点,则不需要创建新的点,而是替换两侧的点的坐标(注意是替换set方法)。
     */
    private void replaceTwoMarker(Marker marker) {
        int index = (int) marker.getObject();
        if (index == 0) {
            MyLatLng centerPoint1 = getCenterPoint(allLatLngs.get(0), allLatLngs.get(2));
            MyLatLng centerPoint2 = getCenterPoint(allLatLngs.get(0), allLatLngs.get(allLatLngs.size() - 2));
            allLatLngs.set(1, centerPoint1);
            allLatLngs.set(allLatLngs.size() - 1, centerPoint2);
            Log.e(TAG, "替换第" + 1 + "个marker");
            Log.e(TAG, "替换第" + (allLatLngs.size() - 1) + "个marker");
            return;
        }
        if (index == allLatLngs.size() - 2) {
            MyLatLng centerPoint1 = getCenterPoint(allLatLngs.get(0), allLatLngs.get(index));
            MyLatLng centerPoint2 = getCenterPoint(allLatLngs.get(index), allLatLngs.get(index - 2));
            allLatLngs.set(index + 1, centerPoint1);
            allLatLngs.set(index - 1, centerPoint2);
            Log.e(TAG, "替换第" + (index + 1) + "个marker");
            Log.e(TAG, "替换第" + (index - 1) + "个marker");
            return;
        }
        MyLatLng centerPoint1 = getCenterPoint(allLatLngs.get(index - 2), allLatLngs.get(index));
        MyLatLng centerPoint2 = getCenterPoint(allLatLngs.get(index + 2), allLatLngs.get(index));
        allLatLngs.set(index - 1, centerPoint1);
        allLatLngs.set(index + 1, centerPoint2);
        Log.e(TAG, "替换第" + (index - 1) + "个marker");
        Log.e(TAG, "替换第" + (index + 1) + "个marker");
    }

    /**
     * 获取两个点的中心点坐标
     * @param latLng1
     * @param latLng2
     * @return
     */
    private MyLatLng getCenterPoint(MyLatLng latLng1, MyLatLng latLng2) {
        BigDecimal bdLL1Lat = new BigDecimal(latLng1.getLatLng().latitude);
        BigDecimal bdLL1Lng = new BigDecimal(latLng1.getLatLng().longitude);
        BigDecimal bdLL2Lat = new BigDecimal(latLng2.getLatLng().latitude);
        BigDecimal bdLL2Lng = new BigDecimal(latLng2.getLatLng().longitude);
        BigDecimal d1 = (bdLL1Lat.add(bdLL2Lat)).divide(new BigDecimal("2"));
        BigDecimal d2 = (bdLL1Lng.add(bdLL2Lng)).divide(new BigDecimal("2"));
        return new MyLatLng(new LatLng(d1.doubleValue(), d2.doubleValue()), MyLatLng.UNABLE);
    }

    /**
     * 绘制图形的颜色样式
     */
    private void createAreaStyle() {
        int strokeColor = Color.parseColor("#00FFFF");
        int fillColor = Color.parseColor("#11000000");
        for (MyLatLng myLatLng : allLatLngs) {
            polygonOptions.add(myLatLng.getLatLng());
        } // 设置多边形的边框颜色,32位 ARGB格式,默认为黑色
        polygonOptions.strokeColor(strokeColor); // 设置多边形的边框宽度,单位:像素
        polygonOptions.strokeWidth(10); // 设置多边形的填充颜色,32位ARGB格式
        polygonOptions.fillColor(fillColor); // 注意要加前两位的透明度 // 在地图上添加一个多边形(polygon)对象
    }

    /**
     * 添加marker
     * @param isClear
     */
    private void addMarker(boolean isClear) {
        if (isClear) {
            aMap.clear();
        }
        for (int i = 0; i < allLatLngs.size(); i++) { // 在地图上添一组图片标记(marker)对象,并设置是否改变地图状态以至于所有的marker对象都在当前地图可视区域范围内显示
            MarkerOptions options = new MarkerOptions();
            options.position(allLatLngs.get(i).getLatLng()).draggable(true).visible(true);
            Marker marker = aMap.addMarker(options);
            marker.setObject(i);
            if (allLatLngs.get(i).getState() == MyLatLng.ABLE) {
                marker.setIcon(BitmapDescriptorFactory.fromResource(R.drawable.checkbox_fill));
            } else {
                marker.setIcon(BitmapDescriptorFactory.fromResource(R.drawable.checkbox_empty));
            }
        }
    }

5.附:自定义MyLatLng

public class MyLatLng {
    public static final int ABLE = 1;
    public static final int UNABLE = 0;
    private LatLng latLng;
    private int state;

    public MyLatLng(LatLng latLng, int state) {
        this.latLng = latLng;
        this.state = state;
    }

    public LatLng getLatLng() {
        return latLng;
    }

    public void setLatLng(LatLng latLng) {
        this.latLng = latLng;
    }

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }
}

升级:

实现效果不是很好,Marker点需要长按才可以使用。所以使用setOnMapTouchListener替代setOnMarkerDragListener。

aMap.setOnMapTouchListener(new AMap.OnMapTouchListener() {
                @Override
                public void onTouch(MotionEvent motionEvent) {
                    switch (motionEvent.getAction()) {
                        case MotionEvent.ACTION_DOWN:
                            float down_x = motionEvent.getX();
                            float down_y = motionEvent.getY();
                            Point downPoint = new Point();
                            downPoint.set((int) down_x, (int) down_y);
                            LatLng downLatLng = aMap.getProjection().fromScreenLocation(downPoint);
                            nearestLatLngIndex = getNearestLatLng(downLatLng);
                            if (nearestLatLngIndex != -1) {
                                //说明用户想拖拽该点
                                //开始拖拽时,把集合里的该点删除掉
                                allLatLngs.remove(nearestLatLngIndex);
                                refreshPolygonOptions();
                            }
                            break;
                        case MotionEvent.ACTION_MOVE:
                            break;
                        case MotionEvent.ACTION_UP:
                            float up_x = motionEvent.getX();
                            float up_y = motionEvent.getY();
                            Point upPoint = new Point();
                            upPoint.set((int) up_x, (int) up_y);
                            LatLng upLatLng = aMap.getProjection().fromScreenLocation(upPoint);
                            //拖拽结束时,创建新点
                            allLatLngs.add(nearestLatLngIndex, new MyLatLng(upLatLng, MyLatLng.ABLE));
                            //判断是否需要创建新的点
                            if (!isCreateMarker(allMarkers.get(nearestLatLngIndex))) {
                                //不需要
                                //如果拖拽的是状态为0的点,则不需要创建新的点,而是替换两侧的点的坐标(注意是替换set方法)。
                                replaceTwoMarker(allMarkers.get(nearestLatLngIndex));
                                refreshPolygonOptions();
                                addMarker(true);
                                createAreaStyle();
                                aMap.addPolygon(polygonOptions);
                            } else {
                                //需要
                                refreshPolygonOptions();
                                addMarker(true);
                                createAreaStyle();
                                aMap.addPolygon(polygonOptions);
                                //在拖拽点两侧添加maker
                                addTwoMarker(allMarkers.get(nearestLatLngIndex));
                                addMarker(false);
                            }
                            break;
                    }
                }
            });

 /**
   * 获取所有点里离该点最近的点的索引值,阈值为2,如果所有值都比2大,则表示没有最近的点(返回-1)
     *
     * @param latLng
     */
    @NonNull
    private int getNearestLatLng(LatLng latLng) {
        for (int i = 0; i < allLatLngs.size(); i++) {
            float distance = AMapUtils.calculateLineDistance(latLng, allLatLngs.get(i).getLatLng());
            Log.e(TAG, distance + "");
            if (((int) distance) < 2) {
                return i;
            }
        }
        return -1;
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容