Android 百度地圖畫多邊形

  最近在研究百度地圖的sdk,而且項目中也需要實現一個在地圖上畫多邊形,並且可以拖拽,還要計算周長和麪積,還要判斷線段是否相交。當時我一看需求,感覺問題並不簡單,但是還是先默默地打開了百度。。。嘿嘿,下面說說思路:

1.點擊地圖的時候創建Marker點;

2.當大於1個marker的時候,可以畫線;

3.監聽地圖上marker的點擊和拖拽事件,如果最後一個點擊的marker的位置和第一個marker的位置重合,可以畫矩形;

4.拖拽marker,更新位置,並更新線和矩形,判斷線段是否相交。

  No code No BB,下面上代碼:


import android.graphics.Color
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import com.baidu.mapapi.map.*
import com.baidu.mapapi.model.LatLng
import com.baidu.mapapi.utils.AreaUtil
import com.baidu.mapapi.utils.DistanceUtil
import kotlinx.android.synthetic.main.activity_draw_map.*
import java.util.*

/**
 * Created by yuan7016 on 2020/04/02 15:15. <br/>
 * desc : 繪製地圖
 */
class DrawMapActivity : AppCompatActivity() {

    companion object {
        private const val TAG = "MAP"
    }

    private lateinit var baiduMap: BaiduMap

    private var hasDrawFinished = false//是否已經畫完
    private var mPolyline: Polyline? = null//折線對象
    private var mPolygon: Polygon? = null//多邊形對象
    private var currentDragMarkerIndex = 0//當前拖拽的marker index

    /**
     * 所有的位置點
     */
    private val allPositionList = ArrayList<LatLng>()
    /**
     * 全部marker集合
     */
    private val allMarkersList = ArrayList<Marker>()


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_draw_map)


        //返回
        iv_back.setOnClickListener {
            finish()
        }

        rl_delete_map.setOnClickListener {
            //清除地圖上的所有覆蓋物
            baiduMap.clear()
            allPositionList.clear()
            allMarkersList.clear()
            hasDrawFinished = false
            mPolyline?.remove()
            mPolygon?.remove()
            mPolyline = null
            mPolygon = null

            tvMapPerimeter.text = "周長: 0米"
            tvMapArea.text = "面積: 0畝"
        }

        baiduMap = mapView.map
        val uiSettings = baiduMap.uiSettings
        baiduMap.setViewPadding(0, 0, 0, 150)
        baiduMap.showMapIndoorPoi(false)//設置是否顯示室內圖標註


        //地圖點擊
        baiduMap.setOnMapClickListener(object : BaiduMap.OnMapClickListener {
            override fun onMapClick(latLng: LatLng) {
                Log.e(TAG, "===onMapClick==經度=" + latLng.longitude + "==緯度=" + latLng.latitude)

                if (!hasDrawFinished) {
                    addMarkers(latLng)
                }
            }

            //單擊地圖中的POI點
            override fun onMapPoiClick(mapPoi: MapPoi) {
                val position = mapPoi.position
                Log.e(TAG, "===onMapPoiClick==經度=" + position.longitude + "==緯度=" + position.latitude)

                if (!hasDrawFinished) {
                    addMarkers(position)
                }

            }
        })


        //marker點擊
        baiduMap.setOnMarkerClickListener(BaiduMap.OnMarkerClickListener { marker ->

            val position = marker.position
 
            if (hasDrawFinished) {
                Log.e(TAG, "====setOnMarkerClickListener======已經封閉了=======")
            } else {
                Log.e(TAG, "====setOnMarkerClickListener======2======")
                if (allMarkersList[0] == marker) {
                    //如果當前點擊的marker是第一個marker 可以封閉多邊形了
                    //封閉圖形
                    hasDrawFinished = true

                    //添加marker
                    addMarkers(position)

                    //畫多邊形
                    drawPolygon()

                    return@OnMarkerClickListener true
                }
            }
            false
        })


        //marker拖拽
        baiduMap.setOnMarkerDragListener(object : BaiduMap.OnMarkerDragListener {
            override fun onMarkerDrag(marker: Marker) {
                val position = marker.position
        
                if (allPositionList.size > 1) {//改變線

                    //獲取線上的位置點  更新位置
                    mPolyline?.points?.let {
                        it[currentDragMarkerIndex] = position
                        //更新數據
                        allPositionList[currentDragMarkerIndex] = position
                        mPolyline?.setPoints(it)
                    }

                    //計算周長
                    calculatePerimeter()

                    if (hasDrawFinished) {
                        //改變多邊形
                        drawPolygon()
                    }
                } else {
                    //如果只有一個marker  改變第一個marker的位置
                    allMarkersList[0] = marker
                    allPositionList[0] = position
                }
            }

            override fun onMarkerDragEnd(marker: Marker) {
                val position = marker.position
                Log.w(TAG, "===onMarkerDragEnd==經度=" + position.longitude + "==緯度=" + position.latitude)
            }

            override fun onMarkerDragStart(marker: Marker) {
                currentDragMarkerIndex = allMarkersList.indexOf(marker)
                Log.w(TAG, "===onMarkerDragStart=$currentDragMarkerIndex")
            }
        })

    }

    /**
     * 添加marker
     */
    private fun addMarkers(latLng: LatLng) {

        if (allPositionList.size == 0) {
            //創建新marker
            val bitmapDescriptor = CommonUtil.getBitmapDescriptor(this, R.drawable.layer_list_bg_shape_green_marker)

            //構建MarkerOption,用於在地圖上添加Marker
            val option = MarkerOptions()
                    .position(latLng)
                    .alpha(0.8f)
                    .anchor(0.5f,0.5f)
                    .draggable(true)//可以拖拽
                    .icon(bitmapDescriptor)

            val marker = baiduMap.addOverlay(option) as Marker

            allMarkersList.add(marker)
            allPositionList.add(latLng)

        } else {
            if (hasDrawFinished) {
                //已經畫完了 把當前marker和第一個marker相連接
                drawLine(allPositionList[allPositionList.size - 1], allPositionList[0])
            } else {
                //大於2 開始劃線
                Log.w(TAG, "===addMarker===大於2 開始劃線")

                val bitmapDescriptor = CommonUtil.getBitmapDescriptor(this, R.drawable.layer_list_bg_shape_green_marker)
                //構建MarkerOption,用於在地圖上添加Marker
                val option = MarkerOptions()
                        .position(latLng)
                        .alpha(0.8f)
                        .anchor(0.5f,0.5f)
                        .draggable(true)//可以拖拽
                        .icon(bitmapDescriptor)
                val marker = baiduMap.addOverlay(option) as Marker

                allMarkersList.add(marker)
                allPositionList.add(latLng)

                val currentPointIndex = allPositionList.indexOf(latLng)

                drawLine(allPositionList[currentPointIndex - 1], latLng)
            }
        }

    }


    /**
     * 畫線
     *
     * @param startPoint
     * @param endPoint
     */
    private fun drawLine(startPoint: LatLng, endPoint: LatLng) {
        if (mPolyline == null) {
            val points = ArrayList<LatLng>()
            if (!points.contains(startPoint)) {
                points.add(startPoint)
            }
            if (!points.contains(endPoint)) {
                points.add(endPoint)
            }
            //設置折線的屬性
            val polylineOptions = PolylineOptions()
                    .width(5) //線的寬度
                    .color(resources.getColor(R.color.green_right_line_color))
                    .points(points)//points count can not less than 2

            //創建折線對象
            mPolyline = baiduMap.addOverlay(polylineOptions) as Polyline?

        } else {
            //獲取折線上原來的數據
            val linePoints = mPolyline?.points

            //添加新的數據
            linePoints?.let {
                //判斷一下是否已經包含這個位置點了
                if (!it.contains(startPoint)) {
                    linePoints.add(startPoint)
                }
                if (!it.contains(endPoint)) {
                    linePoints.add(endPoint)
                }
            }

            //更新線數據
            mPolyline?.points = linePoints
        }

        Log.e(TAG, "===drawLine==折線上的數據個數=" + mPolyline?.points?.size)

        //計算周長
        calculatePerimeter()
    }


    /**
     * 畫多邊形,並判斷是否相交,改變顏色
     */
    private fun drawPolygon() {
        if (mPolygon == null) {
            //創建多邊形
            val polygonOptions = PolygonOptions()
                    .points(allPositionList)
                    .fillColor(resources.getColor(R.color.green_right_map_color))
                    .stroke(Stroke(5, resources.getColor(R.color.green_right_line_color)))//邊框寬度和顏色

            //判斷是否相交
            if (allPositionList.size > 3) {
                val approvedPolygon = CommonUtil.isApprovedPolygon(allPositionList)
                if (approvedPolygon) {
                    polygonOptions.fillColor(resources.getColor(R.color.green_right_map_color))
                    polygonOptions.stroke(Stroke(5, resources.getColor(R.color.green_right_line_color)))
                } else {
                    //改變顏色
                    polygonOptions.fillColor(resources.getColor(R.color.red_error_map_color))
                    polygonOptions.stroke(Stroke(5, resources.getColor(R.color.red_error_line_color)))
                }
            }
            //創建多邊形
            mPolygon = baiduMap.addOverlay(polygonOptions) as Polygon?
        } else {
            //更新多邊形
            if (allPositionList.size > 3) {
                val approvedPolygon = CommonUtil.isApprovedPolygon(allPositionList)
                if (approvedPolygon) {
                    mPolygon?.fillColor = resources.getColor(R.color.green_right_map_color)
                    mPolygon?.stroke = (Stroke(5, resources.getColor(R.color.green_right_line_color)))
                } else {
                    //改變顏色
                    mPolygon?.fillColor = resources.getColor(R.color.red_error_map_color)
                    mPolygon?.stroke = (Stroke(5, resources.getColor(R.color.red_error_line_color)))
                }
            }

            mPolygon?.points = allPositionList


        }

        //計算面積
        calculateArea()
    }


    /**
     * 計算周長
     */
    private fun calculatePerimeter() {
        val points = mPolyline?.points
        var length = 0.0

        points?.let {
            for (i in 0 until it.size - 1) {
                length += DistanceUtil.getDistance(it[i], it[i + 1])
            }
            val format = String.format("%.2f", length)
            tvMapPerimeter.text = "周長:" + format + "米"
        }
    }


    /**
     * 計算面積
     */
    private fun calculateArea() {
        mPolygon?.points?.let {
            // 計算多邊形面積,返回單位:平方米
            val polygonArea = AreaUtil.calculateArea(it)
            // 轉換成畝  1平方米(㎡)=0.0015畝
            val mu = 0.0015f

            val area = polygonArea * mu

            tvMapArea.text = "面積:" + String.format("%.2f", area) + "畝"
        }

    }


    override fun onResume() {
        super.onResume()
        mapView.onResume()
    }

    override fun onPause() {
        super.onPause()
        mapView.onPause()
    }


    override fun onDestroy() {
        baiduMap.clear()
        mapView.onDestroy()
        super.onDestroy()
    }


}

 其他工具類:

public class CommonUtil {

    /**
     * 判斷多邊形是否相交
     * @param list
     * @return  true 不相交,  false 相交
     */
    public static boolean isApprovedPolygon(List<LatLng> list) {
        //當多邊形的邊大於3個的時候才需要判斷是否相交,取一個邊,和它不相鄰的邊比較是否相交
        for (int i = 1; i < list.size(); i++) {
            //基線
            LatLng latLngA = list.get(i-1);
            LatLng latLngB = list.get(i);

            //需要對比的線段
            for (int j = i+2;j<=list.size();j++){
                if ( j==list.size()){
                    if (i !=1 ) {
                        LatLng latLngC = list.get(list.size() - 1);
                        LatLng latLngD = list.get(0);
                        if (intersection(latLngA.longitude, latLngA.latitude, latLngB.longitude, latLngB.latitude, latLngC.longitude, latLngC.latitude, latLngD.longitude, latLngD.latitude)) {
                            return false;
                        }
                    }
                }else {
                    LatLng latLngC = list.get(j - 1);
                    LatLng latLngD = list.get(j);
                    if (intersection(latLngA.longitude, latLngA.latitude, latLngB.longitude, latLngB.latitude, latLngC.longitude, latLngC.latitude, latLngD.longitude, latLngD.latitude)) {
                        return false;
                    }
                }
            }
        }
        return true;
    }



    /**兩【線段】是否相交
     * @param l1x1 線段1的x1
     * @param l1y1 線段1的y1
     * @param l1x2 線段1的x2
     * @param l1y2 線段1的y2
     * @param l2x1 線段2的x1
     * @param l2y1 線段2的y1
     * @param l2x2 線段2的x2
     * @param l2y2 線段2的y2
     * @return 是否相交
     */
    public static boolean intersection(double l1x1, double l1y1, double l1x2, double l1y2,
                                       double l2x1, double l2y1, double l2x2, double l2y2)
    {

        // 快速排斥實驗 首先判斷兩條線段在 x 以及 y 座標的投影是否有重合。 有一個爲真,則代表兩線段必不可交。
        if (Math.max(l1x1,l1x2) < Math.min(l2x1 ,l2x2)
                || Math.max(l1y1,l1y2) < Math.min(l2y1,l2y2)
                || Math.max(l2x1,l2x2) < Math.min(l1x1,l1x2)
                || Math.max(l2y1,l2y2) < Math.min(l1y1,l1y2))
        {
            return false;
        }

        // 跨立實驗  如果相交則矢量叉積異號或爲零,大於零則不相交
        if ((((l1x1 - l2x1) * (l2y2 - l2y1) - (l1y1 - l2y1) * (l2x2 - l2x1))
                * ((l1x2 - l2x1) * (l2y2 - l2y1) - (l1y2 - l2y1) * (l2x2 - l2x1))) > 0
                || (((l2x1 - l1x1) * (l1y2 - l1y1) - (l2y1 - l1y1) * (l1x2 - l1x1))
                * ((l2x2 - l1x1) * (l1y2 - l1y1) - (l2y2 - l1y1) * (l1x2 - l1x1))) > 0)
        {
            return false;
        }

        return true;
    }
}

這樣畫線,畫多邊形的demo就做好了,看看效果圖吧!

 

 

//===============================================================================================

參考文章:https://www.jianshu.com/p/f48d72a61459 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章