天天看點

Android 基于百度地圖 自由多邊形覆寫物demo

需要實作的功能

1、按住多邊形可自由拖動 

2、長按多邊形頂點可以自由拖動多邊形頂點 

3、點選多邊形線條可以生成新的點 

4、點選多邊形頂點可以删除多邊形頂點

     先上效果圖。

Android 基于百度地圖 自由多邊形覆寫物demo

百度地圖支援的各種地圖覆寫物:地圖示注(Marker)、幾何圖形(點、折線、弧線、多邊形等)、POI檢索結果覆寫物、線路規劃結果覆寫物等。

自定義圖層:定位圖層、地形圖圖層、熱力圖圖層、瓦片圖層。   

我使用的 百度地圖版本:v5_0_0

代碼可以檢視github    https://github.com/oldbirdy/polygonDemoByBaidu 

首先在Application中初始化百度地圖SDK

public class DemoApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        // 在使用 SDK 各組間之前初始化 context 資訊,傳入 ApplicationContext
        SDKInitializer.initialize(this);
        //自4.3.0起,百度地圖SDK所有接口均支援百度坐标和國測局坐标,用此方法設定您使用的坐标類型.
        //包括BD09LL和GCJ02兩種坐标,預設是BD09LL坐标。
        SDKInitializer.setCoordType(CoordType.BD09LL);
    }
}
           

Activity中處理覆寫物的拖拽

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_overlay);

        mMapView = (MapView) findViewById(R.id.bmapView);
        mBaiduMap = mMapView.getMap();
        MapStatusUpdate msu = MapStatusUpdateFactory.zoomTo(14.0f);
        mBaiduMap.setMapStatus(msu);
        combinationOverlayList = new ArrayList<>();
        initOverlay();

    }
       //初始化基礎資料
    public void initOverlay() {
        LatLng llA = new LatLng(39.963175, 116.400244);
        LatLng llB = new LatLng(39.942821, 116.369199);
        LatLng llC = new LatLng(39.939723, 116.425541);
        LatLng llD = new LatLng(39.906965, 116.401394);
        list = new ArrayList<>();
        list.add(llA);
        list.add(llB);
        list.add(llD);
        list.add(llC);

        final CombinationOverlay combinationOverlay = new CombinationOverlay(mMapView,list);
        combinationOverlayList.add(combinationOverlay);

        LatLng southwest = new LatLng(39.92235, 116.380338);
        LatLng northeast = new LatLng(39.947246, 116.414977);
        LatLngBounds bounds = new LatLngBounds.Builder().include(northeast)
                .include(southwest).build();

        MapStatusUpdate u = MapStatusUpdateFactory
                .newLatLng(bounds.getCenter());
        mBaiduMap.setMapStatus(u);

        mBaiduMap.setOnMapTouchListener(new BaiduMap.OnMapTouchListener() {
            @Override
            public void onTouch(MotionEvent motionEvent) {

                if(motionEvent.getAction()==MotionEvent.ACTION_DOWN){   //按下的時候 做處理
                    tempOverlay = null;
                    lastx =  motionEvent.getX();
                    lasty = motionEvent.getY();
                    Point point = new Point( (int)(motionEvent.getX()),(int) (motionEvent.getY()));
                    LatLng latlng = mBaiduMap.getProjection().fromScreenLocation(point);
//                    MapStatus.Builder builder = new MapStatus.Builder();

                    for(int i=0;i<combinationOverlayList.size();i++){
                       List<LatLng> list  = combinationOverlayList.get(i).getLatLngList();
                        if(SpatialRelationUtil.isPolygonContainsPoint(list,latlng)){   //判斷是否在多邊形裡面
                            //在多邊形内部
//                            createPopupView("提示消息", new OnClickListener() {
//                                @Override
//                                public void onClick(View v) {
//
//                                }
//                            }, new OnClickListener() {
//                                @Override
//                                public void onClick(View v) {
//
//                                }
//                            });
//                            infoWindow = new InfoWindow(popupView,latlng,0);
//                            mBaiduMap.showInfoWindow(infoWindow);
                            tempOverlay = combinationOverlayList.get(i);
                            mBaiduMap.getUiSettings().setScrollGesturesEnabled(false);
                            mBaiduMap.hideInfoWindow();
                            break;
                        }
                    }

                }else if(motionEvent.getAction()==MotionEvent.ACTION_MOVE){
                    if(tempOverlay!=null){
                        //全部根據手指的移動将其轉化成百度坐标
                        if(!isDrag){
                            offsetx = motionEvent.getX() - lastx;
                            offsety = motionEvent.getY() - lasty;
                            lastx =  motionEvent.getX();
                            lasty = motionEvent.getY();
                            tempOverlay.updateOverlayByPolygon(offsetx,offsety);
                        }

                    }
                }else if(motionEvent.getAction()==MotionEvent.ACTION_UP){
                    if( tempOverlay != null){
                        mBaiduMap.getUiSettings().setScrollGesturesEnabled(true);
                    }
                }
            }
        });
        
        mBaiduMap.setOnMapClickListener(new BaiduMap.OnMapClickListener() {
            @Override
            public void onMapClick(LatLng latLng) {

            }

            @Override
            public boolean onMapPoiClick(MapPoi mapPoi) {
                return false;
            }
        });
        //Marker點選事件
        mBaiduMap.setOnMarkerClickListener(new OnMarkerClickListener() {
            @Override
            public boolean onMarkerClick(Marker marker) {

                return updateMarkerClick(marker);
            }
        });
           //markertu
        mBaiduMap.setOnMarkerDragListener(new OnMarkerDragListener() {
            public void onMarkerDrag(Marker marker) {
                updateMarkerDrag(marker);
            }

            public void onMarkerDragEnd(Marker marker) {
                isDrag = false;
            }

            public void onMarkerDragStart(Marker marker) {
                isDrag = true;
            }
        });
        mBaiduMap.setOnMapClickListener(new BaiduMap.OnMapClickListener() {
            @Override
            public void onMapClick(LatLng latLng) {
                mBaiduMap.hideInfoWindow();

            }

            @Override
            public boolean onMapPoiClick(MapPoi mapPoi) {
                return false;
            }
        });
        mBaiduMap.setOnPolylineClickListener(new BaiduMap.OnPolylineClickListener() {
            @Override
            public boolean onPolylineClick(Polyline polyline) {
                mBaiduMap.hideInfoWindow();
                return updateLineClick(polyline);
            }
        });
    }

    /**
     * 更新marker點選
     * @param marker
     * @return
     */
    private boolean updateMarkerClick(final Marker marker) {
       for(int i=0;i < combinationOverlayList.size();i++){
           if(combinationOverlayList.get(i).getMarkerList().contains(marker)){

               Button button = new Button(getApplicationContext());
               button.setBackgroundResource(R.drawable.popup);
               LatLng ll = marker.getPosition();
               button.setText("删除目前點");
               button.setTextColor(Color.BLACK);
               final int finalI = i;
               button.setOnClickListener(new OnClickListener() {
                   public void onClick(View v) {
                       combinationOverlayList.get(finalI).updateOverlayByRemoveOneMarker(marker);

                   }
               });
               infoWindow = new InfoWindow(button,ll,-47);

               mBaiduMap.showInfoWindow(infoWindow);
               return true;
           }
       }
        return false;
    }

    /**
     * 更新線段點選
     * @param polyline
     * @return
     */
    private boolean updateLineClick(Polyline polyline){
        for(int i=0;i<combinationOverlayList.size();i++){
            if(combinationOverlayList.get(i).getPolylineList().contains(polyline)){
                combinationOverlayList.get(i).updateOverlayByLineClick(polyline);
                return true;
            }
        }
        return false;
    }

    private void updateMarkerDrag(Marker marker) {
        for(int i=0;i<combinationOverlayList.size();i++){
            if(combinationOverlayList.get(i).getMarkerList().contains(marker)){
                combinationOverlayList.get(i).updateOverlayByMarker(marker);
            }
        }

    }
           
自定義的一個組合覆寫物。會生成相應的覆寫物界面。根據覆寫物上的點,線,面的事件去更新整個覆寫物的狀态
           
public class CombinationOverlay {
    private MapView mMapView;
    private BaiduMap mBaiduMap;
    private List<LatLng> latLngList;

    BitmapDescriptor bdA = BitmapDescriptorFactory
            .fromResource(R.drawable.icon_marka);

    private List<List<LatLng>> lineListList;
    private Polygon polygonOverlay;
    private List<Marker> markerList;
    private List<Polyline> polylineList;

    private Stroke stroke = new Stroke(5, 0xAA00FF00);

    public CombinationOverlay(MapView mMapView, List<LatLng> latLngList) {
        this.mMapView = mMapView;
        this.latLngList = latLngList;
        if(latLngList.size()<3){
            throw new IllegalArgumentException("點數小于3,無法構成多邊形");
        }
        mBaiduMap = mMapView.getMap();
        initZiyuan();
    }

    private void initZiyuan() {
        PolygonOptions polygonOptions = new PolygonOptions();
        polygonOptions.points(latLngList);
        polygonOptions.stroke(stroke);
        polygonOptions.fillColor(0xAAFFFF00);
        polygonOverlay = (Polygon)mBaiduMap.addOverlay(polygonOptions);

        markerList = new ArrayList<>();
        for(int i = 0;i < latLngList.size();i++){
//            latLngList =
            MarkerOptions markerOptions = new MarkerOptions().position(latLngList.get(i)).icon(bdA).draggable(true);
            Marker marker= (Marker)mBaiduMap.addOverlay(markerOptions);
            markerList.add(marker);
        }

      
        lineListList = new ArrayList<>();
        polylineList = new ArrayList<>();
        for(int i=0;i<latLngList.size();i++){
            List<LatLng> latLngLineList = new ArrayList<>();
            latLngLineList.add(latLngList.get(i));
            if(i < latLngList.size()-1){
                latLngLineList.add(latLngList.get(i+1));
            }else{
                latLngLineList.add(latLngList.get(0));
            }
            lineListList.add(latLngLineList);
            PolylineOptions polylineOptions =  new PolylineOptions().points(latLngLineList).color(0xAAFFFF00).focus(true).width(10);
            Polyline polyline  = (Polyline)mBaiduMap.addOverlay(polylineOptions);
            polylineList.add(polyline);
        }
    }

    public Polygon getPolygonOverlay() {
        return polygonOverlay;
    }

    public void setPolygonOverlay(Polygon polygonOverlay) {
        this.polygonOverlay = polygonOverlay;
    }

    public List<Marker> getMarkerList() {
        return markerList;
    }

    public void setMarkerList(List<Marker> markerList) {
        this.markerList = markerList;
    }

    public List<Polyline> getPolylineList() {
        return polylineList;
    }

    public void setPolylineList(List<Polyline> polylineList) {
        this.polylineList = polylineList;
    }

    public List<LatLng> getLatLngList() {
        return latLngList;
    }

    /**
     * 更新覆寫物的位置
     */
    public void updateOverlayByMarker(Marker marker){
       int position =  markerList.indexOf(marker);
       if(position==-1){
           return;
       }
       latLngList.set(position,marker.getPosition());
       polygonOverlay.setPoints(latLngList);
       if(position==0){  //第一個點   更新第一條線和最後一條線
           lineListList.get(position).set(0,marker.getPosition());  //更新第一個點的坐标
           polylineList.get(position).setPoints(lineListList.get(position));

           lineListList.get(lineListList.size()-1).set(1,marker.getPosition()); //更新第二個點
           polylineList.get(polylineList.size()-1).setPoints(lineListList.get(polylineList.size()-1 ));
       }else{
           lineListList.get(position).set(0,marker.getPosition());  //更新第一個點
           polylineList.get(position).setPoints(lineListList.get(position));

           lineListList.get(position-1).set(1,marker.getPosition());
           polylineList.get(position-1).setPoints(lineListList.get(position-1));
       }
    }

    /**
     * 根據偏移量更新顯示位置
     * @param offsetx
     * @param offsety
     */
    public void updateOverlayByPolygon(float offsetx,float offsety){
        latLngList =  MapUtils.getLatLngByOffset(mMapView,latLngList,offsetx,offsety);
        polygonOverlay.setPoints( latLngList);
        for(int i=0;i < markerList.size();i++){
            markerList.get(i).setPosition(latLngList.get(i));
        }

        lineListList.clear();
        for(int i=0;i<latLngList.size();i++){
            List<LatLng> latLngLineList = new ArrayList<>();
            latLngLineList.add(latLngList.get(i));
            if(i < latLngList.size()-1){
                latLngLineList.add(latLngList.get(i+1));
            }else{
                latLngLineList.add(latLngList.get(0));
            }
            lineListList.add(latLngLineList);

            polylineList.get(i).setPoints(latLngLineList);
        }
//

    }



    /**
     * 點選線條觸發
     * @param polyline
     */
    public void updateOverlayByLineClick(Polyline polyline){
        int positon = polylineList.indexOf(polyline);
        if(-1==positon){
            return;
        }
        LatLng latLng = MapUtils.getCenterOfLines(mMapView,polyline.getPoints());  //得到中心點
        latLngList.add(positon+1,latLng);
        removeCombinationOverlay();
        initZiyuan();
    }


    public void updateOverlayByRemoveOneMarker(Marker marker){
        int positon = markerList.indexOf(marker);
        if(-1==positon){
            return;
        }
        if(markerList.size()<4){
            Toast.makeText(mMapView.getContext(), "不能移除目前點,移除後無法構成多邊形", Toast.LENGTH_SHORT).show();
        }else{
            latLngList.remove(positon);
            removeCombinationOverlay();
            initZiyuan();
        }
        mBaiduMap.hideInfoWindow();
    }



    /**
     * 移除覆寫物
     */
    public void removeCombinationOverlay(){
        polygonOverlay.remove();

        for(int i=0;i<markerList.size();i++){
            markerList.get(i).remove();
        }

        for(int i=0;i<polylineList.size();i++){
            polylineList.get(i).remove();
        }
        markerList.clear();
        polylineList.clear();
        lineListList.clear();

    }

}
           

一個工具類:

public class MapUtils {

    /**
     * 擷取線段中心點坐标
     * @param mPoints
     * @return
     */
    public static LatLng getCenterOfLines(MapView mapView, @Size(2) List<LatLng> mPoints){
        if(mPoints.size()!=2){
            throw new IllegalArgumentException("線段點個數應為2個");
        }
        Projection projection = mapView.getMap().getProjection();
        Point point0 = projection.toScreenLocation(mPoints.get(0));
        Point point1 = projection.toScreenLocation(mPoints.get(1));
        Point point =  getCenterOfScrrenTwoPoint(point0,point1);
        return projection.fromScreenLocation(point);
    }

    /**
     * 擷取螢幕上兩點的中心坐标
     * @return
     */
    public static Point getCenterOfScrrenTwoPoint(Point point0,Point point1){
        return new Point((int)((point0.x+point1.x)/2),(int)((point0.y+point1.y)/2));
    }

    /**
     * 根據偏移量擷取新的坐标值
     * @param list
     * @param offsetx
     * @param offsety
     * @return
     */
    public static List<LatLng> getLatLngByOffset(MapView mapView,List<LatLng> list,float offsetx,float offsety){
        Projection projection = mapView.getMap().getProjection();

        for(int i=0;i<list.size();i++){
            Point tempPoint = projection.toScreenLocation(list.get(i));
            tempPoint.offset((int)offsetx,(int)offsety);
            list.set(i,projection.fromScreenLocation(tempPoint));
        }
        return list;
    }

    //計算多邊形重心  也可計算面積
    public static LatLng getCenterOfGravityPoint(List<LatLng> mPoints) {
        double area = 0.0;//多邊形面積
        double Gx = 0.0, Gy = 0.0;// 重心的x、y
        for (int i = 1; i <= mPoints.size(); i++) {
            double iLat = mPoints.get(i % mPoints.size()).latitude;
            double iLng = mPoints.get(i % mPoints.size()).longitude;
            double nextLat = mPoints.get(i - 1).latitude;
            double nextLng = mPoints.get(i - 1).longitude;
            double temp = (iLat * nextLng - iLng * nextLat) / 2.0;
            area += temp;
            Gx += temp * (iLat + nextLat) / 3.0;
            Gy += temp * (iLng + nextLng) / 3.0;
        }
        Gx = Gx / area;
        Gy = Gy / area;
        return new LatLng(Gx, Gy);
    }
}