|
@@ -0,0 +1,305 @@
|
|
|
+import * as ol from 'ol'
|
|
|
+import * as style from 'ol/style'
|
|
|
+import * as layer from 'ol/layer'
|
|
|
+import * as source from 'ol/source'
|
|
|
+import * as geom from 'ol/geom'
|
|
|
+import {fromCircle} from 'ol/geom/Polygon';
|
|
|
+import * as proj from 'ol/proj'
|
|
|
+import * as interaction from 'ol/interaction'
|
|
|
+import * as coordinate from 'ol/coordinate'
|
|
|
+import * as control from 'ol/control'
|
|
|
+import * as sphere from 'ol/sphere'
|
|
|
+import { unByKey } from 'ol/Observable'
|
|
|
+import {createBox} from "ol/interaction/Draw";
|
|
|
+import {Circle, LineString, Polygon} from "ol/geom";
|
|
|
+import {formatPosition} from '@/utils/easyMap'
|
|
|
+
|
|
|
+const layerFlag = ['layerName', 'selectDrawLayer']
|
|
|
+let drawTooltipElement;
|
|
|
+let drawHelpTooltipElement;
|
|
|
+const typeMapper = new Map([
|
|
|
+ ['line', 'LineString'],
|
|
|
+ ['rectangle', 'LineString'],
|
|
|
+ ['polygon', 'Polygon'],
|
|
|
+ ['circle', 'Circle'],
|
|
|
+])
|
|
|
+let oldDrawFeature
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * @param map
|
|
|
+ * @param typeSelect line线,rectangle矩形,polygon多边形,circle圆形
|
|
|
+ */
|
|
|
+export default function SelectDraw (map, typeSelect) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ if (!drawTooltipElement) {
|
|
|
+ let _source
|
|
|
+ const realLayer = map.getLayers().getArray().filter(v => v.get(layerFlag[0]) === layerFlag[1])
|
|
|
+ if (realLayer[0]) {
|
|
|
+ _source = realLayer[0].getSource()
|
|
|
+ } else {
|
|
|
+ _source = new source.Vector(); //图层数据源
|
|
|
+ const _vector = new layer.Vector({
|
|
|
+ zIndex: 6,
|
|
|
+ source: _source,
|
|
|
+ style: new style.Style({ //图层样式
|
|
|
+ fill: new style.Fill({
|
|
|
+ color: 'rgba(46,129,255,0.15)' //填充颜色
|
|
|
+ }),
|
|
|
+ stroke: new style.Stroke({
|
|
|
+ color: '#2E81FF', //边框颜色
|
|
|
+ width: 2, // 边框宽度
|
|
|
+ lineDash: [10, 10]
|
|
|
+ }),
|
|
|
+ image: new style.Circle({
|
|
|
+ radius: 7,
|
|
|
+ fill: new style.Fill({
|
|
|
+ color: '#ffcc33'
|
|
|
+ })
|
|
|
+ })
|
|
|
+ })
|
|
|
+ });
|
|
|
+ _vector.set(layerFlag[0], layerFlag[1])
|
|
|
+ map.addLayer(_vector);
|
|
|
+ }
|
|
|
+ let sketch;
|
|
|
+ let helpTooltip;
|
|
|
+ // let measureTooltip;
|
|
|
+ let continueMsg = '双击结束标绘';
|
|
|
+ const geodesicCheckbox = true;//测地学方式对象
|
|
|
+ const createMeasureTooltip = () => {
|
|
|
+ const id = 'selectDrawTooltipElementId'
|
|
|
+ if (drawTooltipElement) {
|
|
|
+ map.removeOverlay(map.getOverlayById(id))
|
|
|
+ drawTooltipElement.parentNode.removeChild(drawTooltipElement);
|
|
|
+ }
|
|
|
+ drawTooltipElement = document.createElement('div');
|
|
|
+ drawTooltipElement.className = 'tooltip tooltip-measure';
|
|
|
+ // measureTooltip = new ol.Overlay({
|
|
|
+ // id,
|
|
|
+ // element: drawTooltipElement,
|
|
|
+ // offset: [0, -15],
|
|
|
+ // positioning: 'bottom-center'
|
|
|
+ // });
|
|
|
+ // map.addOverlay(measureTooltip);
|
|
|
+ }
|
|
|
+ const createHelpTooltip = () => {
|
|
|
+ const id = 'selectDrawHelpTooltipElementId'
|
|
|
+ if (drawHelpTooltipElement) {
|
|
|
+ map.removeOverlay(map.getOverlayById(id))
|
|
|
+ drawHelpTooltipElement.parentNode.removeChild(drawHelpTooltipElement);
|
|
|
+ }
|
|
|
+ drawHelpTooltipElement = document.createElement('div');
|
|
|
+ drawHelpTooltipElement.className = 'tooltip hidden';
|
|
|
+ helpTooltip = new ol.Overlay({
|
|
|
+ id,
|
|
|
+ element: drawHelpTooltipElement,
|
|
|
+ offset: [15, 0],
|
|
|
+ positioning: 'center-left'
|
|
|
+ });
|
|
|
+ map.addOverlay(helpTooltip);
|
|
|
+ }
|
|
|
+ const formatLength = (line) => {
|
|
|
+ // 获取投影坐标系
|
|
|
+ const sourceProj = map.getView().getProjection();
|
|
|
+ // ol/sphere里有getLength()和getArea()用来测量距离和区域面积,默认的投影坐标系是EPSG:3857, 其中有个options的参数,可以设置投影坐标系
|
|
|
+ const length = sphere.getLength(line, {projection: sourceProj});
|
|
|
+ // const length = getLength(line);
|
|
|
+ let output;
|
|
|
+ if (length > 100) {
|
|
|
+ const km = Math.round((length / 1000) * 100) / 100;
|
|
|
+ output = `${km} 千米 <br>${parseFloat(String(km * 0.53995)).toFixed(2)} 海里`;
|
|
|
+ } else {
|
|
|
+ output = `${Math.round(length * 100) / 100} m`;
|
|
|
+ }
|
|
|
+ return output;
|
|
|
+ };
|
|
|
+ //获取圆的面积
|
|
|
+ const getCircleArea = (circle, projection) => {
|
|
|
+ const P = 3.14
|
|
|
+ const radius = getCircleRadius(circle, projection)
|
|
|
+ return P * radius * radius
|
|
|
+ }
|
|
|
+//获取圆的半径
|
|
|
+ const getCircleRadius = (circle, projection) => {
|
|
|
+ return circle.getRadius() * projection.getMetersPerUnit()
|
|
|
+ }
|
|
|
+ const formatArea = (polygon, type= 'polygon') => {
|
|
|
+ let area
|
|
|
+ const sourceProj = map.getView().getProjection();
|
|
|
+ // 获取投影坐标系
|
|
|
+ if (type === 'polygon') {
|
|
|
+ area = sphere.getArea(polygon, {
|
|
|
+ projection: sourceProj,
|
|
|
+ });
|
|
|
+ } else if (type === 'circle') {
|
|
|
+ area = getCircleArea(polygon, sourceProj)
|
|
|
+ }
|
|
|
+ let output;
|
|
|
+ if (area > 10000) {
|
|
|
+ const km = Math.round((area / 1000000) * 100) / 100;
|
|
|
+ output = `${km} 平方公里<br>${parseFloat(String(km * 0.38610)).toFixed(
|
|
|
+ 2
|
|
|
+ )} 平方英里`;
|
|
|
+ } else {
|
|
|
+ output = `${Math.round(area * 100) / 100} ` + " m<sup>2</sup>";
|
|
|
+ }
|
|
|
+ return output;
|
|
|
+ };
|
|
|
+ const addInteraction = () => {
|
|
|
+ const id = 'selectDrawName'
|
|
|
+ const draw = new interaction.Draw({
|
|
|
+ source: _source,//测量绘制层数据源
|
|
|
+ // @ts-ignore
|
|
|
+ type: typeMapper.get(typeSelect), //几何图形类型
|
|
|
+ // @ts-ignore
|
|
|
+ geometryFunction: typeSelect === 'rectangle' ? createBox() : null,
|
|
|
+ style: new style.Style({
|
|
|
+ fill: new style.Fill({
|
|
|
+ color: "rgba(46,129,255,0.15)",
|
|
|
+ }),
|
|
|
+ stroke: new style.Stroke({
|
|
|
+ color: "#2E81FF",
|
|
|
+ width: 2,
|
|
|
+ lineDash: [10, 10]
|
|
|
+ }),
|
|
|
+ image: new style.Circle({
|
|
|
+ radius: 5,
|
|
|
+ stroke: new style.Stroke({
|
|
|
+ color: "rgba(0, 0, 0, 0.7)",
|
|
|
+ }),
|
|
|
+ fill: new style.Fill({
|
|
|
+ color: "rgba(255, 255, 255, 0.2)",
|
|
|
+ }),
|
|
|
+ }),
|
|
|
+ }),
|
|
|
+ });
|
|
|
+ draw.set(id, id)
|
|
|
+ createMeasureTooltip(); //创建测量工具提示框
|
|
|
+ createHelpTooltip(); //创建帮助提示框
|
|
|
+ map.addInteraction(draw);
|
|
|
+ let listener;
|
|
|
+ //绑定交互绘制工具开始绘制的事件
|
|
|
+ const drawstartHandle = (evt) => {
|
|
|
+ sketch = evt.feature; //绘制的要素
|
|
|
+ let tooltipCoord = evt.coordinate;// 绘制的坐标
|
|
|
+ //绑定change事件,根据绘制几何类型得到测量长度值或面积值,并将其设置到测量工具提示框中显示
|
|
|
+ listener = sketch.getGeometry().on('change', function (evt) {
|
|
|
+ const geom = evt.target
|
|
|
+ let output;
|
|
|
+ if (geom.getType() === 'LineString') {
|
|
|
+ output = formatLength(geom);//长度值
|
|
|
+ tooltipCoord = geom.getLastCoordinate();//坐标
|
|
|
+ } else if (geom.getType() === 'Polygon') {
|
|
|
+ output = formatArea(geom);//面积值
|
|
|
+ tooltipCoord = geom.getInteriorPoint().getCoordinates();//坐标
|
|
|
+ } else if (geom.getType() === 'Circle') {
|
|
|
+ output = formatArea(geom, 'circle');//面积值
|
|
|
+ tooltipCoord = geom.getCenter()
|
|
|
+ }
|
|
|
+ drawTooltipElement.innerHTML = output;//将测量值设置到测量工具提示框中显示
|
|
|
+ // measureTooltip.setPosition(tooltipCoord);//设置测量工具提示框的显示位置
|
|
|
+ });
|
|
|
+ }
|
|
|
+ draw.on('drawstart', drawstartHandle);
|
|
|
+ //绑定交互绘制工具结束绘制的事件
|
|
|
+ const copy = (value) => {
|
|
|
+ const str = document.createElement('input')
|
|
|
+ str.setAttribute('value', value)
|
|
|
+ document.body.appendChild(str)
|
|
|
+ str.select()
|
|
|
+ document.execCommand('copy')
|
|
|
+ document.body.removeChild(str)
|
|
|
+ }
|
|
|
+ const drawendHandle = (evt) => {
|
|
|
+ map.removeInteraction(map.getInteractions().getArray().filter(v => v.get(id) === id)[0]);
|
|
|
+ // const del = document.createElement("div");
|
|
|
+ // del.className = "lineDel";
|
|
|
+ // drawTooltipElement.append(del);
|
|
|
+ // del.onclick = () => {
|
|
|
+ // _source.removeFeature(evt.feature)
|
|
|
+ // const b = del.parentElement.parentElement
|
|
|
+ // b.parentElement.removeChild(b);
|
|
|
+ // const g = evt.feature.getGeometry()
|
|
|
+ // if (g.getType() === 'LineString') {
|
|
|
+ // const w = `LINESTRING(${g.getCoordinates().map(v => v[0] + ' ' + v[1]).join(',')})`
|
|
|
+ // copy(w)
|
|
|
+ // } else if (g.getType() === 'Polygon') {
|
|
|
+ // const w = `POLYGON(${g.getCoordinates().map(v => '(' + v.map(c => c[0] + ' ' + c[1]) + ')').join(',')})`
|
|
|
+ // copy(w)
|
|
|
+ // }
|
|
|
+ // };
|
|
|
+ // drawTooltipElement.className = 'tooltip tooltip-static'; //设置测量提示框的样式
|
|
|
+ // measureTooltip.setOffset([0, -7]);
|
|
|
+ // 标绘的时候不需要最终结果dom
|
|
|
+ map.removeOverlay(map.getOverlayById('selectDrawHelpTooltipElementId'))
|
|
|
+ // drawTooltipElement.parentNode.removeChild(drawTooltipElement);
|
|
|
+ sketch = null; //置空当前绘制的要素对象
|
|
|
+ drawTooltipElement = null; //置空测量工具提示框对象
|
|
|
+ drawHelpTooltipElement.parentNode.removeChild(drawHelpTooltipElement);
|
|
|
+ drawHelpTooltipElement = null; //置空测量工具提示框对象
|
|
|
+ unByKey(listener);
|
|
|
+ draw.un('drawstart', drawstartHandle);
|
|
|
+ draw.un('drawend', drawendHandle);
|
|
|
+ map.removeInteraction(map.getInteractions().getArray().filter(v => v.get(id) === id)[0]);
|
|
|
+ map.un('pointermove', pointerMoveHandler)
|
|
|
+ if (oldDrawFeature) {
|
|
|
+ _source.removeFeature(oldDrawFeature)
|
|
|
+ }
|
|
|
+ oldDrawFeature = evt.feature
|
|
|
+ const gm = oldDrawFeature.getGeometry()
|
|
|
+ let wkt = ''
|
|
|
+ const gType = gm.getType()
|
|
|
+ if (gType === 'Polygon') {
|
|
|
+ wkt = formatPosition.cpnTwpn(gm.getCoordinates())
|
|
|
+ } else if (gType === 'LineString') {
|
|
|
+ wkt = formatPosition.clTwl(gm.getCoordinates())
|
|
|
+ } else if (gType === 'Circle') {
|
|
|
+ const circlePoly = fromCircle(gm, 128)
|
|
|
+ oldDrawFeature.setGeometry(circlePoly)
|
|
|
+ wkt = formatPosition.cpnTwpn(circlePoly.getCoordinates())
|
|
|
+ }
|
|
|
+ resolve({feature: oldDrawFeature, wkt: wkt})
|
|
|
+ }
|
|
|
+ draw.on('drawend', drawendHandle);
|
|
|
+ }
|
|
|
+ addInteraction(); //调用加载绘制交互控件方法,添加绘图进行测量
|
|
|
+ const pointerMoveHandler = (evt) => {
|
|
|
+ if (evt.dragging) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ let helpMsg = '单击开始标绘';//当前默认提示信息
|
|
|
+ //判断绘制几何类型设置相应的帮助提示信息
|
|
|
+ if (sketch) {
|
|
|
+ const geom = sketch.getGeometry()
|
|
|
+ helpMsg = continueMsg;
|
|
|
+ // if (geom.getType() === 'Polygon') {
|
|
|
+ // helpMsg = continueMsg; //绘制多边形时提示相应内容
|
|
|
+ // } else if (geom.getType() === 'LineString') {
|
|
|
+ // helpMsg = continueMsg; //绘制线时提示相应内容
|
|
|
+ // }
|
|
|
+ }
|
|
|
+ drawHelpTooltipElement.innerHTML = helpMsg; //将提示信息设置到对话框中显示
|
|
|
+ helpTooltip.setPosition(evt.coordinate);//设置帮助提示框的位置
|
|
|
+ drawHelpTooltipElement.classList.remove('hidden');//移除帮助提示框的隐藏样式进行显示
|
|
|
+ };
|
|
|
+ map.on('pointermove', pointerMoveHandler); //地图容器绑定鼠标移动事件,动态显示帮助提示框内容
|
|
|
+ //地图绑定鼠标移出事件,鼠标移出时为帮助提示框设置隐藏样式
|
|
|
+ try {
|
|
|
+ map.getViewport().on('mouseout', () => {
|
|
|
+ drawHelpTooltipElement.addClass('hidden');
|
|
|
+ });
|
|
|
+ } catch (e) {
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ reject('正在标绘中,请勿重复操作!')
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+export const SelectDrawClear = (map) => {
|
|
|
+ const realLayer = map.getLayers().getArray().filter(v => v.get(layerFlag[0]) === layerFlag[1])
|
|
|
+ if (realLayer[0]) {
|
|
|
+ realLayer[0].getSource().clear()
|
|
|
+ oldDrawFeature = null
|
|
|
+ }
|
|
|
+}
|