最近在研究百度地图的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.
* 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()
/**
* 全部marker集合
*/
private val allMarkersList = ArrayList()
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()
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 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