美文网首页
Android安卓高德地图实现多边形绘制与编辑

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

作者: 呀粥 | 来源:发表于2018-11-20 17:20 被阅读0次

    需求:使用高德地图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;
        }
    

    相关文章

      网友评论

          本文标题:Android安卓高德地图实现多边形绘制与编辑

          本文链接:https://www.haomeiwen.com/subject/rmxxqqtx.html