需求如下:
- 在react项目中使用百度地图实现区域(电子围栏)的绘制
- 绘制的区域类型为:1、多边形 2、圆形
- 可绘制多个区域
- 区域不能有重叠
- 可重新编辑区域
代码如下:
index.tsx
import { useCallback, useEffect, useState } from 'react';
import { Space, Button, Radio, message } from 'antd';
import { Map, DrawingManager, MapApiLoaderHOC } from 'react-bmapgl';
import validator, { Type } from './utils';
interface OptionType {
value: 'polygon' | 'circle';
label: '多边形' | '圆形'
}
const TYPE: OptionType[] = [
{ value: 'polygon', label: '多边形' },
{ value: 'circle', label: '圆形' },
];
const IndexPage = () => {
const [list, setList] = useState<Type[]>([]);
// 当前绘制区域
const [current, setCurrent] = useState<any>();
// 当前绘制区域类型,默认绘制多边形
const [type, setType] = useState(TYPE[0].value)
// 是否开启绘制
const [isOpen, setIsOpen] = useState(true);
// 是否禁用继续绘制按钮
const [disabled, setDisabled] = useState(true);
// 是否处于编辑状态
const [isEdit, setIsEdit] = useState(false);
// 重新绘制
const restart = () => {
setIsOpen(true);
setList(list.slice(0, -1));
current.remove();
};
// 继续绘制
const next = () => {
setIsOpen(true);
};
// 编辑区域
const edit = () => {
if (isEdit) {
current.disableEditing();
handleValidator(current, type, list.slice(0, -1));
} else {
current.enableEditing();
}
setIsEdit(!isEdit);
};
// 区域重叠判断
const handleValidator = (overlay: any, drawingMode: any, list: any[]) => {
const flag = validator({ overlay, drawingMode }, list);
if (flag) {
setDisabled(false);
} else {
message.warning('区域重叠,请重新绘制');
setDisabled(true);
}
};
const NewDrawingManager = useCallback((props) => <DrawingManager {...props} />, [isOpen, type]);
return (
<div className="index-page" style={{ display: 'flex', flexDirection: 'column' }}>
<Space>
<Button size="small" type="primary" onClick={restart} disabled={isOpen}>重新绘制</Button>
<Button size="small" type="primary" onClick={next} disabled={disabled}>继续绘制</Button>
<Button size="small" type="primary" onClick={edit} disabled={isOpen}>{isEdit ? '保存编辑' : '编辑区域'}</Button>
</Space>
<Radio.Group value={type} onChange={e => setType(e.target.value)}>
{
TYPE.map(item => <Radio key={item.value} value={item.value}>{item.label}</Radio>)
}
</Radio.Group>
<div style={{ flex: 1, position: 'relative' }}>
<Map
center={new BMapGL.Point(116.404449, 39.914889)}
zoom={16}
enableScrollWheelZoom
style={{ height: '90vh' }}
>
<NewDrawingManager
isOpen={isOpen}
enableDrawingTool={false}
drawingMode={type}
onOverlaycomplete={(e: any, { drawingMode, overlay }: any) => {
setList([...list, { drawingMode, overlay }]);
setCurrent(overlay);
setIsOpen(false);
handleValidator(overlay, drawingMode, list);
}}
onOverlaycancel={(a: any, { overlay }: any) => {
setIsOpen(false);
// setCurrent(overlay);
setTimeout(() => setIsOpen(true), 200)
}}
/>
</Map>
</div>
</div>
);
}
export default MapApiLoaderHOC({ak: 'xxx'})(IndexPage);
引入GeoUtils.js
umirc.ts
import { defineConfig } from 'umi';
export default defineConfig({
headScripts: ['http://127.0.0.1:5500/GeoUtils.js'],
});
打包后会在head标签
里加入额外的脚本以便页面加载时在window上挂载BMapGLLib.GeoUtils
实例:
<script src="http://127.0.0.1:5500/GeoUtils.js"></script>
utils.ts 区域重叠判断
包含三种:
1. 多边形与多边形是否重叠
2. 多边形与圆形是否重叠
3. 圆形与圆形是否重叠
export interface Type {
overlay: any;
drawingMode: 'circle' | 'polygon';
}
export const BMAP_DRAWING_CIRCLE = "circle", // 鼠标画圆模式
BMAP_DRAWING_POLYGON = "polygon"; // 鼠标画多边形模式
// 多边形与多边形是否重叠
export const fn1 = (polygon1: Type['overlay'], polygon2: Type['overlay']) => {
const points1 = polygon1.getPath();
const points2 = polygon2.getPath();
return points1.some((item: any) => {
const point = new BMapGL.Point(item.lng, item.lat);
// @ts-ignore
return BMapGLLib.GeoUtils.isPointInPolygon(point, polygon2);
}) || points2.some((item: any) => {
const point = new BMapGL.Point(item.lng, item.lat);
// @ts-ignore
return BMapGLLib.GeoUtils.isPointInPolygon(point, polygon1);
});
}
// 多边形与圆形是否重叠
export const fn2 = (polygon: Type['overlay'], circle: Type['overlay']) => {
const points1 = polygon.getPath();
const points2 = circle.getPath();
return points1.some((item : any) => {
const point = new BMapGL.Point(item.lng, item.lat);
// @ts-ignore
return BMapGLLib.GeoUtils.isPointInCircle(point, circle)
}) || points2.some((item : any) => {
const point = new BMapGL.Point(item.lng, item.lat);
// @ts-ignore
return BMapGLLib.GeoUtils.isPointInPolygon(point, polygon);
});
}
// 圆形与圆形是否重叠
export const fn3 = (circle1: Type['overlay'], circle2: Type['overlay']) => {
const points1 = circle1.getPath();
const points2 = circle2.getPath();
console.log(points1, points2);
return points1.some((item : any) => {
const point = new BMapGL.Point(item.lng, item.lat);
// @ts-ignore
return BMapGLLib.GeoUtils.isPointInCircle(point, circle2)
}) || points2.some((item : any) => {
const point = new BMapGL.Point(item.lng, item.lat);
// @ts-ignore
return BMapGLLib.GeoUtils.isPointInCircle(point, circle1)
});
}
// 区域重叠校验是否通过 true-通过 false-不通过
export default ({ overlay, drawingMode }: Type, overlays: Type[]) => {
try {
let flag = false; // true-重叠 false-不重叠
overlays.forEach(item => {
if (!flag && drawingMode === BMAP_DRAWING_POLYGON && item.drawingMode === BMAP_DRAWING_POLYGON) {
flag = fn1(overlay, item.overlay);
}
if (!flag && drawingMode === BMAP_DRAWING_POLYGON && item.drawingMode === BMAP_DRAWING_CIRCLE) {
flag = fn2(overlay, item.overlay);
}
if (!flag && drawingMode === BMAP_DRAWING_CIRCLE && item.drawingMode === BMAP_DRAWING_POLYGON) {
flag = fn2(item.overlay, overlay);
}
if (drawingMode === BMAP_DRAWING_CIRCLE && item.drawingMode === BMAP_DRAWING_CIRCLE) {
flag = fn3(overlay, item.overlay);
}
});
return !flag;
} catch (error) {
console.log(error);
}
};
效果图:
相关链接:
- react-bmapgl文档:https://lbsyun.baidu.com/solutions/reactBmapDoc
- DrawingManager.js:https://github.com/huiyan-fe/BMapGLLib/blob/master/DrawingManager/src/DrawingManager.js
- DrawingManager.tsx:https://github.com/huiyan-fe/react-bmapgl/blob/master/src/Library/DrawingManager.tsx
- GeoUtils.js:https://github.com/huiyan-fe/BMapGLLib/blob/master/GeoUtils/src/GeoUtils.js