react-amap海量点优化
前言:高版本的react-amap 支持MassMarkers 组件用于一次性添加大量的标记点。本次优化的海量点是在低版本react-amap的基础上。官方推荐使用聚合useCluster属性来优化海量点的渲染。
直接附上代码:
import React, { Component } from "react";
import { Map, Markers, Polygon, InfoWindow } from 'react-amap'
import {
Form,
Switch,
message,
} from "antd";
import ActionsForm from "components/RoutePanel/ActionsForm";
import { MAP_AMAP_KEY, MAP_AMAP_VERSION } from 'constants/config'
import {
API_FETCH_ZONES_DEPT_POINT,
} from 'constants/api'
import fetch from 'common/fetch'
import styles from "./amap.css";
import { debounce } from 'lodash';
const polygonType = {
main: 'main',
adjacent: 'adjacent',
normal: 'normal',
}
// 获取区域样式
const getPolygonStyle = (pt) => {
switch (pt) {
case polygonType.main:
return {
fillColor: '#DC2626',
fillOpacity: 0.5,
strokeOpacity: 0.5,
strokeWeight: 2,
strokeColor: '#DC2626',
}
case polygonType.adjacent:
return {
fillColor: '#34D399',
fillOpacity: 0.5,
strokeOpacity: 1,
strokeWeight: 1,
strokeColor: '#34D399',
}
case polygonType.normal:
default:
return {
fillColor: '#333333',
fillOpacity: 0.3,
strokeOpacity: 0.5,
strokeWeight: 1,
strokeColor: '#333333',
}
}
}
export class ZoneNeighborhoodMap extends Component {
constructor(props) {
super(props)
this.map = null; // 地图实例
this.mapEvents = {
created: (mapInstance) => {
this.map = mapInstance;
this.map.on('zoomend', this.handleZoom);
},
close: () => {
this.map = null;
if (this.map) {
this.map.off('zoomend', this.handleZoom);
}
}
};
this.state = {
infoWindowPosition: null,
infoWindowTitle: '',
polygonActive: false,
pointReferShow: true, //运输点参考
pointReferData: [],
areaReferShow: true, //已绘制参考
areaReferData: [],
locTypes: [],
data: props.data,
// infoWindowPosition: {
// longitude: 120,
// latitude: 30,
// },
infoWindowVisible: false,
infoWindowData: {
id: "",
desc: "",
},
infoWindow: {
position: {
longitude: 120,
latitude: 30,
},
visible: false,
data: null,
},
useCluster:false
}
this.markerEvents = {
created: (e) => {},
mouseover: (e) => {
const position = e.target.getPosition();
this.setState({
infoWindow: {
position: {
longitude: position.lng,
latitude: position.lat,
},
visible: true,
data: e.target.getExtData(),
},
});
},
mouseout: (e) => {
this.setState({
infoWindow: {
position: {
longitude: 120,
latitude: 30,
},
visible: false,
data: null,
},
});
},
};
}
componentWillUnmount() {
if (this.map) {
this.map.destroy();
}
}
componentWillUnmount() {
if (this.map) {
this.map.destroy()
}
}
// 缩放事件处理函数
handleZoom = () => {
const zoomLevel = this.map.getZoom();
console.log('zoomLevel',zoomLevel)
if (zoomLevel > 8) {
this.setState({ useCluster: false });
} else {
this.setState({ useCluster: true });
}
};
setFitViewWithZoneId(zoneId) {
const fitOverlays = []
this.map.getAllOverlays().forEach(polygon => {
if (polygon.getExtData().zoneId === zoneId) {
fitOverlays.push(polygon)
}
})
if (fitOverlays.length) {
this.map.setFitView(fitOverlays, false, undefined, this.map.getZoom())
}
}
renderPolygons() {
const { mainZoneId, polygons, toggleAdjacentZone, adjacentZoneIds } = this.props
const l = polygons.length
const _polygons = []
for (let i = 0; i < l; i++) {
const detail = polygons[i]
if (detail.geoArea && detail.geoArea.length) {
let _polygonType = polygonType.normal
if (detail.zoneId === mainZoneId) {
_polygonType = polygonType.main
} else if (adjacentZoneIds.includes(detail.zoneId)) {
_polygonType = polygonType.adjacent
}
detail.geoArea.forEach((path, pathId) => {
_polygons.push(
<Polygon
path={path}
key={`${detail.id}:${pathId}`}
style={getPolygonStyle(_polygonType)}
events={{
click: () => {
if (detail.zoneId === mainZoneId) {
return
}
toggleAdjacentZone(detail.zoneId)
},
mousemove: (e) => {
this.setState(() => ({
infoWindowPosition: e.lnglat,
infoWindowTitle: detail.zoneDesc
}))
},
mouseout: () => {
this.setState(() => ({
infoWindowPosition: null
}))
}
}}
extData={{ zoneId: detail.zoneId }}
/>
)
})
}
}
return _polygons
}
renderInfoWindow() {
const { infoWindowPosition, infoWindowTitle } = this.state
if (!infoWindowPosition) {
return null
}
return <InfoWindow
position={{
longitude: infoWindowPosition.lng,
latitude: infoWindowPosition.lat,
}}
isCustom={true}
content={`<div style="pointer-events: none;background: #fff;border:1px solid silver;padding: 4px 8px;">${infoWindowTitle}</div>`}
offset={[2,-10]}
visible
/>
}
getPoint = ()=>{
fetch(API_FETCH_ZONES_DEPT_POINT, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({ deptId:this.props.deptId })
}).then((response) => {
if (response.ok) {
return response.json();
}
throw new Error("Bad response from server");
})
.then((json) => {
if(json.data instanceof Array){
if (!json.data.length) {
message.info("没有可显示的运输地点");
}else{
if(json.data.length > 500){
this.setState({
useCluster: true,
})
}
json.data.forEach(d=>d.position= { longitude: d.longitude, latitude: d.latitude })
this.setState({pointReferData:json.data},()=>{
if (!this.map) return; // 如果地图实例未初始化,则不执行
this.map.setFitView();
})
}
}
})
.catch(e=>{})
}
renderInfo = () => {
const { position, visible, data } = this.state.infoWindow;
const {locTypes} = this.state
if (!data) {
return null;
}
const locTypeItem = locTypes.find(t=>t.domVal==data.locType)
const tds = (
<div className={styles.info}>
<div className='inforow'>
<span>运输地点代码</span>
<span>{data.locId}</span>
</div>
<div className='inforow'>
<span>运输地点描述</span>
<span>{data.locDesc}</span>
</div>
<div className='inforow'>
<span>类型</span>
<span>{locTypeItem?locTypeItem.domValDesc:data.locType}</span>
</div>
<div className='inforow'>
<span>城市</span>
<span>{data.city}</span>
</div>
<div className='inforow'>
<span>省份</span>
<span>{data.province}</span>
</div>
<div className='inforow'>
<span>国家</span>
<span>{data.country}</span>
</div>
<div className='inforow'>
<span>经度</span>
<span>{data.longitude}</span>
</div>
<div className='inforow'>
<span>纬度</span>
<span>{data.latitude}</span>
</div>
<div className='inforow'>
<span>地址</span>
<span>{data.addr}</span>
</div>
<div className='inforow'>
<span>限行区域</span>
<span>{data.udzDesc1}({data.udz1})</span>
</div>
<div className='inforow'>
<span>业务区域</span>
<span>{data.udzDesc2}({data.udz2})</span>
</div>
</div>
);
return (
<InfoWindow
position={position}
visible={visible}
isCustom={true}
offset={[2, 2]}
>
{tds}
</InfoWindow>
);
};
pointRender = (extData) => {
return (
<div
style={{
color: "#4e72b8",
width: "8px",
height: "8px",
borderRadius: "50%",
background: "#4169E1",
textAlign: "center",
}}
/>
);
};
initPointReferData = () => {
if (!this.state.pointReferShow) {
this.setState({
pointReferData: [],
});
} else {
this.getPoint()
}
};
componentDidMount() {
this.initPointReferData();
}
// 运输点参考
// changePointReferShow = (checked) => {
// this.setState({ pointReferShow: checked }, () => {
// this.initPointReferData();
// });
// };
changePointReferShow = debounce((checked) => {
this.setState({ pointReferShow: checked }, () => {
this.initPointReferData();
});
}, 300); // 300ms 的防抖延迟
render() {
const {
polygonActive,
pointReferShow,
data,
pointReferData,
areaReferData,
areaReferShow,
} = this.state;
const option = {
amapkey: MAP_AMAP_KEY,
version: MAP_AMAP_VERSION,
mapStyle: 'amap://styles/whitesmoke',
// loading: this.renderLoading(),
status: {
resizeEnable: true,
},
plugins: ['ToolBar', 'Scale'],
events: this.mapEvents,
}
return (
<div style={{ width: "100%", height: "100vh", position: "relative" }}>
<ActionsForm>
<Form layout="inline">
<Form.Item>
<Switch
checkedChildren="显示地点"
unCheckedChildren="显示地点"
checked={pointReferShow}
onChange={this.changePointReferShow}
/>
</Form.Item>
</Form>
</ActionsForm>
<Map {...option}>
<Markers
markers={pointReferData}
offset={[0,0]}
render={this.pointRender}
events={this.markerEvents}
useCluster={this.state.useCluster}
/>
{this.renderPolygons()}
{this.renderInfoWindow()}
{this.renderInfo()}
</Map>
</div>
);
}
}
希望点的数量少于1000时直接展示所有点,不聚合
import React, { Component } from "react";
import PropTypes from "prop-types";
import Immutable from 'seamless-immutable';
import {
Map,
Polygon,
Markers,
PolyEditor,
MouseTool,
InfoWindow,
} from "react-amap";
import {
Form,
Button,
Switch,
Tooltip,
Modal,
message,
Menu,
Dropdown,
Popconfirm,
Icon,
} from "antd";
import isomorphicFetch from "isomorphic-fetch";
import {
API_FETCH_DOMAIN,
API_FETCH_ZONES_DEPT_POINT,
API_FETCH_ZONES_GROUP
} from 'constants/api'
import fetch from 'common/fetch'
import {
MAP_AMAP_KEY,
MAP_AMAP_DRIVING_KEY,
MAP_AMAP_VERSION,
} from "constants/config";
import ActionsForm from "components/RoutePanel/ActionsForm";
import ZoneCascader from "components/PcaCascader/ZoneCascader";
import pstyles from "./polygons.scss";
// 获取封闭polygon(首尾点坐标一致)
const _getClosedPolygon = (paths) => {
if (Array.isArray(paths) && paths.length > 2) {
const firstLocation = paths[0];
const lastLocation = paths[paths.length - 1];
if (
firstLocation[0] === lastLocation[0] &&
firstLocation[1] === lastLocation[1]
) {
return paths;
} else {
return [...paths, firstLocation];
}
} else {
return [];
}
};
class PolygonEdit extends Component {
constructor(props) {
super(props);
this.state = {
polygonActive: false,
pointReferShow: true, //运输点参考
pointReferData: [],
areaReferShow: false, //已绘制参考
areaReferData: [],
locTypes: [],
data: props.data,
mapEditable: props.mapEditable,
deleteMenuVisible: false,
hoverItemData: [],
adcode: "",
province: "",
provinceCode:"",
city: "",
cityCode:"",
area: "",
street: "",
polyline: "",
infoWindowPosition: {
longitude: 120,
latitude: 30,
},
infoWindowVisible: false,
infoWindowData: {
id: "",
desc: "",
},
infoWindow: {
position: {
longitude: 120,
latitude: 30,
},
visible: false,
data: null,
},
showLabels: true, // 默认显示悬浮名称
useCluster:false
};
const _this = this;
this.amap = null; // 地图
this.mouseTool = null; // 鼠标工具
this.amapEvents = {
created: (mapInstance) => {
_this.amap = mapInstance;
_this.amap.on('zoomend', this.handleZoom);
},
close: () => {
_this.amap = null;
if (_this.amap) {
_this.amap.off('zoomend', this.handleZoom);
}
},
};
this.polyEditorEvents = {
created: (ins) => {
_this.amap.setFitView();
},
addnode: () => {},
adjust: ({ lnglat, pixel, type, target } = option) => {},
removenode: () => {},
end: ({ type, target }) => {
this.polyEditorEnd(target, type);
},
};
this.polygonEvents = {
created: (ins) => {
_this.amap.setFitView();
},
};
this.toolEvents = {
created: (tool) => {
_this.mouseTool = tool;
},
draw({ obj }) {
_this.drawWhat(obj);
},
};
this.markerEvents = {
created: (e) => {},
mouseover: (e) => {
const position = e.target.getPosition();
this.setState({
infoWindow: {
position: {
longitude: position.lng,
latitude: position.lat,
},
visible: true,
data: e.target.getExtData(),
},
});
},
mouseout: (e) => {
this.setState({
infoWindow: {
position: {
longitude: 120,
latitude: 30,
},
visible: false,
data: null,
},
});
},
};
}
componentWillUnmount() {
if (this.amap) {
this.amap.destroy();
}
}
componentDidMount() {
if(this.props.isGroup){
this.initPointReferData();
this.initAreaReferData()
}
this.getLocType()
}
// 缩放事件处理函数
handleZoom = () => {
const zoomLevel = this.amap.getZoom();
console.log('zoomLevel', zoomLevel);
console.log('this.state.pointReferData.length', this.state.pointReferData.length);
// 判断点的数量是否小于1000,以及当前缩放等级
if (this.state.pointReferData.length > 1000 && zoomLevel < 10) {
this.setState({ useCluster: true });
} else {
this.setState({ useCluster: false });
}
};
getPoint = ()=>{
fetch(API_FETCH_ZONES_DEPT_POINT, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({ deptId:this.props.deptId })
}).then((response) => {
if (response.ok) {
return response.json();
}
throw new Error("Bad response from server");
})
.then((json) => {
if(json.data instanceof Array){
if (!json.data.length) {
message.info("没有可显示的运输地点");
}else{
json.data.forEach(d=>d.position= { longitude: d.longitude, latitude: d.latitude })
this.setState({pointReferData:json.data},()=>{
if (!this.amap) return; // 如果地图实例未初始化,则不执行
this.amap.setFitView();
})
}
}
})
.catch(e=>{})
}
renderPolygons = () => {
const { areaReferShow, areaReferData } = this.state;
const GROUP_KEY = {
unConfiguredGroups: 'unConfiguredGroups',
configuredGroups: 'configuredGroups'
}
const prepareGroups = (groups) => {
const _groups = Immutable
.asMutable(groups, { deep: true })
.map(group => ({
...group,
zoneDetail: (group.zoneDetail || []).map(detail => {
return {
...detail,
geoArea: JSON.parse(detail.geoArea)
}
})
}))
return {
groups,
[GROUP_KEY.unConfiguredGroups]: _groups
.filter(group => !group.lines.length)
.map(group => ({
...group,
lines: []
})),
[GROUP_KEY.configuredGroups]: _groups.filter(group => group.lines.length),
zoneOptions: _groups.map(group => ({
value: group.zoneId,
label: group.zoneDesc,
})),
polygons: _groups.reduce((polygons, group) => {
polygons.push(...group.zoneDetail.map(zoneDetail => ({ ...zoneDetail, zoneDesc: group.zoneDesc }))) // flat zoneDetail
return polygons
}, [])
}
}
const newData = prepareGroups(areaReferData)
const polygons = newData.polygons
const l = polygons.length
const _polygons = []
for (let i = 0; i < l; i++) {
const detail = polygons[i]
if (detail.geoArea && Array.isArray(detail.geoArea) && detail.geoArea.length) {
detail.geoArea.forEach((path, pathId) => {
_polygons.push(
<Polygon
path={path}
key={`${detail.id}:${pathId}`}
style={{
fillColor: '#333333',
fillOpacity: 0.3,
strokeOpacity: 0.5,
strokeWeight: 1,
strokeColor: '#333333',
}}
events={{
mousemove: (e) => {
this.setState(() => ({
infoWindowPosition: e.lnglat,
infoWindowTitle: detail.zoneDesc
}))
},
mouseout: () => {
this.setState(() => ({
infoWindowPosition: null
}))
}
}}
extData={{ zoneId: detail.zoneId }}
/>
)
})
}else{
console.log('detail.geoArea',detail.geoArea)
}
}
return _polygons
}
getArea = ()=>{
fetch(API_FETCH_ZONES_GROUP, {
method: 'POST',
credentials: 'include',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({ branchId:this.props.branchId,deptId:this.props.deptId})
}).then((response) => {
if (response.ok) {
return response.json();
}
throw new Error("Bad response from server");
})
.then((json) => {
if(json.data && json.data.data instanceof Array){
if (!json.data.data.length) {
message.info("没有可显示的区域");
}else{
this.setState({areaReferData:json.data.data})
}
}
})
.catch(e=>{
console.log('e',e)
})
}
renderInfoWindow() {
const { infoWindowPosition, infoWindowTitle, showLabels } = this.state
if(showLabels){
if (!infoWindowPosition) {
return null
}
return <InfoWindow
position={{
longitude: infoWindowPosition.lng,
latitude: infoWindowPosition.lat,
}}
isCustom={true}
content={`<div style="pointer-events: none;background: #fff;border:1px solid silver;padding: 4px 8px;">${infoWindowTitle}</div>`}
offset={[2,2]}
visible
/>
}
}
initPointReferData = () => {
if (!this.state.pointReferShow) {
this.setState({
pointReferData: [],
});
} else {
this.getPoint()
}
};
initAreaReferData = () => {
if (!this.state.areaReferShow) {
this.setState({
areaReferData: [],
});
} else {
this.getArea()
}
};
renderLoading = () => {
const loadingStyle = {
position: "relative",
height: "100%",
width: "100%",
display: "flex",
justifyContent: "center",
alignItems: "center",
};
return <div style={loadingStyle}>Loading Map...</div>;
};
// 运输点参考
changePointReferShow = (checked) => {
this.setState({ pointReferShow: checked }, () => {
this.initPointReferData();
});
};
// 已绘制区域参考
changeAreaReferShow = (checked) => {
this.setState({ areaReferShow: checked }, () => {
this.initAreaReferData();
});
};
// 是否悬浮显示名称
toggleLabels = (checked) => {
this.setState({ showLabels: checked });
};
pointRender = (extData) => {
return (
<div
style={{
color: "#4e72b8",
width: "8px",
height: "8px",
borderRadius: "50%",
background: "#4169E1",
textAlign: "center",
}}
/>
);
};
getLocType=()=>{
fetch(`${API_FETCH_DOMAIN}LOCATION_TYPE`,{
credentials: 'include'
})
.then((response) => {
if (response.ok) {
return response.json();
}
throw new Error("Bad response from server");
})
.then((json) => {
if(json.data instanceof Array){
this.setState({locTypes:json.data})
}
})
.catch(e=>{})
}
renderInfo = () => {
const { position, visible, data } = this.state.infoWindow;
const {locTypes} = this.state
if (!data) {
return null;
}
const locTypeItem = locTypes.find(t=>t.domVal==data.locType)
const tds = (
<div className={pstyles.pinfo}>
<div className='inforow'>
<span>运输地点代码</span>
<span>{data.locId}</span>
</div>
<div className='inforow'>
<span>运输地点描述</span>
<span>{data.locDesc}</span>
</div>
<div className='inforow'>
<span>类型</span>
<span>{locTypeItem?locTypeItem.domValDesc:data.locType}</span>
</div>
<div className='inforow'>
<span>城市</span>
<span>{data.city}</span>
</div>
<div className='inforow'>
<span>省份</span>
<span>{data.province}</span>
</div>
<div className='inforow'>
<span>国家</span>
<span>{data.country}</span>
</div>
<div className='inforow'>
<span>经度</span>
<span>{data.longitude}</span>
</div>
<div className='inforow'>
<span>纬度</span>
<span>{data.latitude}</span>
</div>
<div className='inforow'>
<span>地址</span>
<span>{data.addr}</span>
</div>
<div className='inforow'>
<span>限行区域</span>
<span>{data.udzDesc1}({data.udz1})</span>
</div>
<div className='inforow'>
<span>业务区域</span>
<span>{data.udzDesc2}({data.udz2})</span>
</div>
</div>
);
return (
<InfoWindow
position={position}
visible={visible}
isCustom={true}
offset={[2, 2]}
>
{tds}
</InfoWindow>
);
};
// polygon edit switch
togglePolygon = (checked, e) => {
if (checked) {
// open edit and close add if it is drawing
if (this.mouseTool) {
this.mouseTool.close();
}
this.setState({ polygonActive: true, isDrawingPolygon: false });
} else {
// close edit
this.setState({ polygonActive: false });
}
};
// polygon add switch
toggleDrawPolygon = (checked, e) => {
if (checked) {
if (this.mouseTool) {
this.mouseTool.polygon();
this.setState({ isDrawingPolygon: true });
message.success("鼠标左键双击或右键单击完成当前多边形!");
}
} else {
if (this.mouseTool) {
this.mouseTool.close();
this.setState({ isDrawingPolygon: false });
}
}
};
// finish polygon draw
drawWhat = (obj) => {
const paths = obj.getPath();
let data = this.state.data.slice();
const pathData = paths.map((item) => [item.lng, item.lat]);
if (pathData.length > 2) {
if (this.amap) {
this.amap.remove(obj);
}
data.push(_getClosedPolygon(pathData));
this.setState({ data });
message.success(
`您成功绘制了一个${
paths.length
}边形,可继续绘制或点击结束多边形绘制按钮`
);
}
};
// polygon editor end
polyEditorEnd = (target, type) => {
const paths = target.getPath();
let isSinglePath = false; // 是否是单围栏
const pathData = paths.map((item, index) => {
if (Array.isArray(item)) {
const itemPaths = item.map((element) => [element.lng, element.lat]);
return _getClosedPolygon(itemPaths);
} else {
isSinglePath = true;
return [item.lng, item.lat];
}
});
this.setState({
data: isSinglePath ? [_getClosedPolygon(pathData)] : pathData,
});
};
// 多边形删除下拉菜单
deleteMenuVisibleChange = (flag) => {
this.setState({ deleteMenuVisible: flag });
};
// 删除多边形
handleMenuClick = (e) => {
this.handleDeletePath(e.key);
};
handleDeletePath = (key) => {
const path = [...this.state.data];
path.splice(key, 1);
this.setState({ data: path, deleteMenuVisible: false });
};
handleMenuHover = (key) => {
this.setState({ hoverItemData: this.state.data[key] });
};
handleChangeAdcode = ({ adcode, province, city, area, street,provinceCode,cityCode }) => {
this.setState({ adcode, province, city, area, street,provinceCode, cityCode});
};
handleAdcodeCancle = () => {
this.setState({
adcode: "",
province: "",
provinceCode:"",
city: "",
cityCode:"",
area: "",
street: "",
});
};
handleAdcodeConfirm = () => {
const { adcode, provinceCode, cityCode, area, street, mapEditable } = this.state;
if (adcode) {
this.setState({
mapEditable: "N",
adcode: "",
polygonActive: false,
isDrawingPolygon: false,
});
if (street) {
isomorphicFetch(`/city-tiles/${provinceCode}_${cityCode}.json`)
.then((response) => {
if (response.ok) {
return response.json();
}
throw new Error("Bad response from server");
})
.then((json) => {
if (json.status === "0") {
throw new Error("No districts from server");
}
try {
const streetItemData = json.features.find(
(s) => s.properties.subdistrict == street
).geometry.coordinates;
if (this.mouseTool) {
this.mouseTool.close();
}
this.setState({
data: streetItemData, // some data
});
if (this.amap) {
this.amap.setFitView();
}
} catch (e) {
message.error("获取行政区划数据失败!");
}
// const polyline = (json.districts && json.districts[0]) ? json.districts[0].polyline : [];
// const data = polyline.split('|').map(block => block.split(';').map(pointer => pointer.split(',').map(lnglat => Number(lnglat))));
// if (this.mouseTool){
// this.mouseTool.close();
// }
// this.setState({
// data: data, // some data
// })
// if (this.amap) {
// this.amap.setFitView();
// }
})
.catch((error) => {
message.error("获取行政区划数据失败!");
this.setState({
mapEditable: "Y",
province: "",
provinceCode:"",
city: "",
cityCode:"",
area: "",
street: "",
data: [],
});
});
} else {
// fetch polyline data
isomorphicFetch(
`//restapi.amap.com/v3/config/district?key=e17fafe279209e4b3a303cc907347277&keywords=${adcode}&subdistrict=0&extensions=all`
)
.then((response) => {
if (response.ok) {
return response.json();
}
throw new Error("Bad response from server");
})
.then((json) => {
if (json.status === "0") {
throw new Error("No districts from server");
}
const polyline =
json.districts && json.districts[0]
? json.districts[0].polyline
: [];
const data = polyline
.split("|")
.map((block) =>
block
.split(";")
.map((pointer) =>
pointer.split(",").map((lnglat) => Number(lnglat))
)
);
if (this.mouseTool) {
this.mouseTool.close();
}
this.setState({
data: data, // some data
});
if (this.amap) {
this.amap.setFitView();
}
})
.catch((error) => {
message.error("获取行政区划数据失败!");
this.setState({
mapEditable: "Y",
province: "",
provinceCode:"",
city: "",
cityCode:"",
area: "",
street: "",
data: [],
});
});
}
} else {
if (mapEditable === "N") {
this.setState({
mapEditable: "Y",
adcode: "",
province: "",
provinceCode:"",
city: "",
cityCode:"",
area: "",
street: "",
data: [],
});
}
}
};
handleFinished = () => {
const {
polygonActive,
data,
adcode,
province,
city,
area,
street,
mapEditable,
} = this.state;
let result = {
geoArea:
Array.isArray(data) && data.length > 0 ? JSON.stringify(data) : null,
mapEditable,
};
if (mapEditable === "N") {
Object.assign(result, {
country: "中国",
state: province,
city,
district: area,
subdistrict: street,
});
}
if (polygonActive) {
this.setState({ polygonActive: false });
Modal.confirm({
title: "您的多边形调整还未结束,是否结束?",
onOk: () => {
this.props.onConfirm(result);
},
onCancel: () => {
this.setState({ polygonActive: true });
},
});
} else {
this.props.onConfirm(result);
}
};
render() {
const {
polygonActive,
isDrawingPolygon,
pointReferShow,
data,
pointReferData,
areaReferData,
areaReferShow,
deleteMenuVisible,
hoverItemData,
adcode,
mapEditable,
infoWindowPosition,
infoWindowVisible,
infoWindowData,
showLabels,
useCluster
} = this.state;
const { onCancle, isGroup } = this.props;
const option = {
amapkey: MAP_AMAP_KEY,
version: MAP_AMAP_VERSION,
mapStyle: "amap://styles/whitesmoke",
loading: this.renderLoading(),
status: {
resizeEnable: true,
},
plugins: ["ToolBar", "Scale"],
events: this.amapEvents,
};
return (
<div style={{ width: "100%", height: "100vh", position: "relative" }}>
<ActionsForm>
{isGroup ? (
<Form layout="inline">
<Form.Item>
<Popconfirm
title={
<ZoneCascader
adcode={adcode}
onChange={this.handleChangeAdcode}
/>
}
icon={
<Tooltip title="选择行政区划。此操作将首先清空已有围栏,已选择的行政区划围栏不允许编辑">
<Icon type="question-circle-o" />
</Tooltip>
}
onCancel={this.handleAdcodeCancle}
onConfirm={this.handleAdcodeConfirm}
>
<Button type="primary">行政区划选择{isGroup}</Button>
</Popconfirm>
</Form.Item>
<Form.Item>
<Switch
disabled={mapEditable === "N"}
checkedChildren="调整"
unCheckedChildren="调整"
checked={polygonActive}
onChange={this.togglePolygon}
/>
</Form.Item>
<Form.Item>
<Switch
disabled={mapEditable === "N"}
checkedChildren="新增"
unCheckedChildren="新增"
checked={isDrawingPolygon}
onChange={this.toggleDrawPolygon}
/>
</Form.Item>
<Form.Item>
<Dropdown
overlay={
<Menu onClick={this.handleMenuClick}>
{data &&
data.length > 0 &&
data.map((item, index) => (
<Menu.Item key={index}>
<span
onMouseOver={() => this.handleMenuHover(index)}
>{`${index + 1} 删除`}</span>
</Menu.Item>
))}
</Menu>
}
onVisibleChange={this.deleteMenuVisibleChange}
visible={deleteMenuVisible}
disabled={mapEditable === "N"}
>
<Button>删除</Button>
</Dropdown>
</Form.Item>
<Form.Item>
<Button onClick={onCancle}>取消</Button>
</Form.Item>
<Form.Item>
<Button type="primary" onClick={this.handleFinished}>
完成
</Button>
</Form.Item>
<Form.Item>
<Switch
checkedChildren="显示地点"
unCheckedChildren="显示地点"
checked={pointReferShow}
onChange={this.changePointReferShow}
/>
</Form.Item>
<Form.Item>
<Switch
checkedChildren="显示其他区域"
unCheckedChildren="显示其他区域"
checked={areaReferShow}
onChange={this.changeAreaReferShow}
/>
</Form.Item>
<Form.Item>
<Switch
checkedChildren="显示区域名称"
unCheckedChildren="隐藏区域名称"
checked={showLabels}
onChange={this.toggleLabels}
/>
</Form.Item>
</Form>
) : (
<Form layout="inline">
<Form.Item>
<Button type="primary" onClick={onCancle}>
关闭
</Button>
</Form.Item>
</Form>
)}
</ActionsForm>
<Map {...option}>
<Markers
markers={pointReferData}
offset={[0,0]}
render={this.pointRender}
events={this.markerEvents}
useCluster={useCluster}
/>
{isGroup && <MouseTool events={this.toolEvents} />}
{isGroup ? (
<Polygon
path={data}
style={{ fillOpacity: 0.3, strokeOpacity: 0.5, strokeWeight: 1 }}
events={this.polygonEvents}
>
<PolyEditor
active={
polygonActive && data.length > 0 && data.flat().length <= 100
}
events={this.polyEditorEvents}
/>
</Polygon>
) : (
data.map((item, index) => (
<Polygon
events={this.polygonEvents}
key={index}
path={item}
style={{
fillOpacity: 0.3,
strokeOpacity: 0.5,
strokeWeight: 1,
}}
/>
))
)}
<Polygon
visible={deleteMenuVisible}
path={[hoverItemData]}
zIndex={100}
style={{
fillColor: "red",
fillOpacity: 0.3,
strokeOpacity: 0.5,
strokeWeight: 1,
}}
/>
{this.renderInfo()}
{this.renderPolygons()}
{this.renderInfoWindow()}
</Map>
</div>
);
}
}
PolygonEdit.propTypes = {
isGroup: PropTypes.bool, // 是:data 为单个 polygon 数据;否: data 为多个 polygon 数据
data: PropTypes.array,
onCancle: PropTypes.func,
onConfirm: PropTypes.func,
};
PolygonEdit.defaultProps = {
isGroup: true,
data: [],
onCancle: () => {},
onConfirm: () => {},
};
export default PolygonEdit;