需求:使用高德地图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、重新根据新的点绘制多边形
效果图
代码:
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;
}