|
@@ -0,0 +1,393 @@
|
|
|
+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 * as extent from 'ol/extent'
|
|
|
+import * as proj from 'ol/proj'
|
|
|
+import * as interaction from 'ol/interaction'
|
|
|
+import * as format from "ol/format";
|
|
|
+import Modify from "ol/interaction/Modify"
|
|
|
+import Draw, {createBox, createRegularPolygon} from "ol/interaction/Draw"
|
|
|
+import * as sphere from "ol/sphere";
|
|
|
+import {unByKey} from "ol/Observable";
|
|
|
+import {formatPosition} from "@/utils/easyMap";
|
|
|
+import {isValue} from "@/utils/util";
|
|
|
+import {fromCircle} from "ol/geom/Polygon";
|
|
|
+import PointIcon from '@/assets/images/gis-layout/gis-layout-tools_tool-bz_icon.png'
|
|
|
+
|
|
|
+const globalLineDash = [
|
|
|
+ [0, 0], //实线
|
|
|
+ [15, 15], //长虚线
|
|
|
+ [5, 5] //虚线
|
|
|
+]
|
|
|
+const layerFlag = ['layerName', 'toolDrawViewsLayer']
|
|
|
+const drawFlag = ['interactionName', 'toolDrawInteraction']
|
|
|
+const modifyFlag = ['interactionName', 'toolModifyInteraction']
|
|
|
+const baseDrawConfig = {
|
|
|
+ // 样式字段
|
|
|
+ text: null, // 要素上显示的文字,默认无文字
|
|
|
+ pointIcon: PointIcon, // Point的图标
|
|
|
+ pointScale: 1, // Point的缩放,默认1
|
|
|
+ pointOffset: [0, 24], // Point的偏移量,默认[0, 0]
|
|
|
+ rectangle: false,
|
|
|
+ lineColor: '#2860F1', // LineString的线段颜色,默认蓝色
|
|
|
+ lineWidth: 2, // LineString的线段宽度,默认1
|
|
|
+ lineType: 0, // LineString的线段类型索引,默认0,实线,取globalLineDash数组索引
|
|
|
+ lineDash: null, // LineString的线段类型,默认null,优先级比lineType高
|
|
|
+ polyColor: 'rgba(20, 129, 241, 0.3)', // Polygon的填充色,默认蓝色
|
|
|
+ polyBorderColor: '#2860F1', // Polygon的边框颜色,默认蓝色
|
|
|
+ polyBorderWidth: 2, // Polygon的边框宽度,默认1
|
|
|
+ polyBorderType: 0, // Polygon的边框类型索引,默认0,实线,取globalLineDash数组索引
|
|
|
+ polyBorderDash: null, // Polygon的边框类型,默认null,优先级比polyBorderType高
|
|
|
+ // 业务字段
|
|
|
+ show: false, // 标绘样式选项是否显示
|
|
|
+ featureType: 'Point', // 标绘的要素类型
|
|
|
+ isPoint: false, // 是否可以标绘点
|
|
|
+ isLineString: false, // 是否可以标绘线
|
|
|
+ isPolygon: false, // 是否可以标绘面
|
|
|
+ showPosition: false, // 是否显示经纬度输入框
|
|
|
+ refreshStyleFunc: () => {}, // 刷新标绘样式方法
|
|
|
+}
|
|
|
+export const getBaseDrawConfig = () => {
|
|
|
+ return JSON.parse(JSON.stringify(baseDrawConfig))
|
|
|
+}
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * @param map
|
|
|
+ * @param arr 要标绘的数组,每个对象的数据在要素中保存的属性为 'val'
|
|
|
+ * @param arr > wkt:wkt格式坐标
|
|
|
+ * @param arr > styles:要素的样式,优先级最高
|
|
|
+ * @param arr > text:styles为空的时候,要素上显示的文字,默认无文字
|
|
|
+ * @param arr > textOffsetY:styles为空的时候,要素上显示的文字Y轴偏移量,默认Point-30,其他0
|
|
|
+ * @param arr > pointIcon:styles为空的时候,Point的图标,默认圆形
|
|
|
+ * @param arr > pointScale:styles为空的时候,Point的缩放,默认1
|
|
|
+ * @param arr > pointOffset:styles为空的时候,Point的偏移量,默认[0, 0]
|
|
|
+ * @param arr > lineColor:styles为空的时候,LineString的线段颜色,默认蓝色
|
|
|
+ * @param arr > lineWidth:styles为空的时候,LineString的线段宽度,默认1
|
|
|
+ * @param arr > lineType:styles为空的时候,LineString的线段类型索引,默认0,实线,取globalLineDash数组索引
|
|
|
+ * @param arr > lineDash:styles为空的时候,LineString的线段类型,默认null,优先级比lineType高
|
|
|
+ * @param arr > polyColor:styles为空的时候,Polygon的填充色,默认蓝色
|
|
|
+ * @param arr > polyBorderColor:styles为空的时候,Polygon的边框颜色,默认蓝色
|
|
|
+ * @param arr > polyBorderWidth:styles为空的时候,Polygon的边框宽度,默认1
|
|
|
+ * @param arr > polyBorderType:styles为空的时候,Polygon的边框类型索引,默认0,实线,取globalLineDash数组索引
|
|
|
+ * @param arr > polyBorderDash:styles为空的时候,Polygon的边框类型,默认null,优先级比polyBorderType高
|
|
|
+ */
|
|
|
+
|
|
|
+let toolDrawTooltipElement;
|
|
|
+let toolDrawHelpTooltipElement;
|
|
|
+export const draw = (map, obj) => {
|
|
|
+ return new Promise((resolve => {
|
|
|
+ if (!isValue(obj.textOffsetY)) {
|
|
|
+ obj.textOffsetY = (obj.featureType === 'Point' ? -30 : 0)
|
|
|
+ }
|
|
|
+ let commonStyle = (showText, featureType = undefined) => {
|
|
|
+ if ((featureType ?? obj.featureType) === 'Point') {
|
|
|
+ return new style.Style({
|
|
|
+ image: obj.pointIcon ? new style.Icon({
|
|
|
+ src: obj.pointIcon,
|
|
|
+ scale: obj.pointScale,
|
|
|
+ displacement: obj.pointOffset
|
|
|
+ }) : new style.Circle({
|
|
|
+ radius: 10,
|
|
|
+ fill: new style.Fill({
|
|
|
+ color: '#e810dd',
|
|
|
+ }),
|
|
|
+ scale: obj.pointScale,
|
|
|
+ displacement: obj.pointOffset
|
|
|
+ }),
|
|
|
+ text: (showText && obj.text) ? new style.Text({
|
|
|
+ font: "16px bold 微软雅黑",
|
|
|
+ text: obj.text,
|
|
|
+ fill: new style.Fill({
|
|
|
+ color: '#ffffff'
|
|
|
+ }),
|
|
|
+ stroke: new style.Stroke({
|
|
|
+ color: '#D26CDB',
|
|
|
+ width: 2
|
|
|
+ }),
|
|
|
+ offsetY: obj.textOffsetY,
|
|
|
+ }) : undefined,
|
|
|
+ })
|
|
|
+ } else if ((featureType ?? obj.featureType) === 'LineString') {
|
|
|
+ return new style.Style({
|
|
|
+ stroke: new style.Stroke({
|
|
|
+ color: obj.lineColor,
|
|
|
+ width: obj.lineWidth,
|
|
|
+ lineDash: obj.lineDash ?? globalLineDash[Number(obj.lineType)]
|
|
|
+ }),
|
|
|
+ text: (showText && obj.text) ? new style.Text({
|
|
|
+ font: "16px bold 微软雅黑",
|
|
|
+ text: obj.text,
|
|
|
+ fill: new style.Fill({
|
|
|
+ color: '#ffffff'
|
|
|
+ }),
|
|
|
+ stroke: new style.Stroke({
|
|
|
+ color: '#D26CDB',
|
|
|
+ width: 2
|
|
|
+ }),
|
|
|
+ offsetY: obj.textOffsetY,
|
|
|
+ }) : undefined,
|
|
|
+ })
|
|
|
+ } else if ((featureType ?? obj.featureType) === 'Polygon' || (featureType ?? obj.featureType) === 'Circle') {
|
|
|
+ return new style.Style({
|
|
|
+ stroke: new style.Stroke({
|
|
|
+ color: obj.polyBorderColor,
|
|
|
+ width: obj.polyBorderWidth,
|
|
|
+ lineDash: obj.polyBorderDash ?? globalLineDash[Number(obj.polyBorderType)]
|
|
|
+ }),
|
|
|
+ fill: new style.Fill({
|
|
|
+ color: obj.polyColor,
|
|
|
+ }),
|
|
|
+ text: (showText && obj.text) ? new style.Text({
|
|
|
+ font: "16px bold 微软雅黑",
|
|
|
+ text: obj.text,
|
|
|
+ fill: new style.Fill({
|
|
|
+ color: '#ffffff'
|
|
|
+ }),
|
|
|
+ stroke: new style.Stroke({
|
|
|
+ color: '#D26CDB',
|
|
|
+ width: 2
|
|
|
+ }),
|
|
|
+ offsetY: obj.textOffsetY,
|
|
|
+ }) : undefined,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const reset = () => {
|
|
|
+ const oldLayer = map.getLayers().getArray().filter(v => v.get(layerFlag[0]) === layerFlag[1])
|
|
|
+ if (oldLayer) {
|
|
|
+ map.removeLayer(oldLayer[0])
|
|
|
+ }
|
|
|
+ const oldDraw = map.getInteractions().getArray().filter(v => v.get(drawFlag[0]) === drawFlag[1])
|
|
|
+ if (oldDraw) {
|
|
|
+ map.removeInteraction(oldDraw[0])
|
|
|
+ }
|
|
|
+ const oldModify = map.getInteractions().getArray().filter(v => v.get(modifyFlag[0]) === modifyFlag[1])
|
|
|
+ if (oldModify) {
|
|
|
+ map.removeInteraction(oldModify[0])
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!toolDrawTooltipElement) {
|
|
|
+ // reset()
|
|
|
+ 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: 9999,
|
|
|
+ source: _source,
|
|
|
+ style: (feat: any) => {
|
|
|
+ feat.setStyle(commonStyle(true, feat.getGeometry().getType()))
|
|
|
+ },
|
|
|
+ });
|
|
|
+ _vector.set(layerFlag[0], layerFlag[1])
|
|
|
+ map.addLayer(_vector);
|
|
|
+ }
|
|
|
+ const modifyInteraction = new Modify({
|
|
|
+ source: _source,
|
|
|
+ });
|
|
|
+ modifyInteraction.set(modifyFlag[0], modifyFlag[1])
|
|
|
+ map.addInteraction(modifyInteraction)
|
|
|
+ modifyInteraction.on('modifyend', evt => {
|
|
|
+ try {
|
|
|
+ const feat = evt.features.item(0)
|
|
|
+ } catch {
|
|
|
+ }
|
|
|
+ })
|
|
|
+ if (!obj.wkt) {
|
|
|
+ let sketch;
|
|
|
+ let helpTooltip;
|
|
|
+ let measureTooltip;
|
|
|
+ let continueMsg = '双击结束标绘';
|
|
|
+ const geodesicCheckbox = true;//测地学方式对象
|
|
|
+ const createMeasureTooltip = () => {
|
|
|
+ const id = 'toolDrawTooltipElementId'
|
|
|
+ if (toolDrawTooltipElement) {
|
|
|
+ map.removeOverlay(map.getOverlayById(id))
|
|
|
+ toolDrawTooltipElement.parentNode.removeChild(toolDrawTooltipElement);
|
|
|
+ }
|
|
|
+ toolDrawTooltipElement = document.createElement('div');
|
|
|
+ toolDrawTooltipElement.className = 'tooltip tooltip-measure';
|
|
|
+ measureTooltip = new ol.Overlay({
|
|
|
+ id,
|
|
|
+ element: toolDrawTooltipElement,
|
|
|
+ offset: [0, -15],
|
|
|
+ positioning: 'bottom-center'
|
|
|
+ });
|
|
|
+ map.addOverlay(measureTooltip);
|
|
|
+ }
|
|
|
+ const createHelpTooltip = () => {
|
|
|
+ const id = 'toolDrawHelpTooltipElementId'
|
|
|
+ if (toolDrawHelpTooltipElement) {
|
|
|
+ map.removeOverlay(map.getOverlayById(id))
|
|
|
+ toolDrawHelpTooltipElement.parentNode.removeChild(toolDrawHelpTooltipElement);
|
|
|
+ }
|
|
|
+ toolDrawHelpTooltipElement = document.createElement('div');
|
|
|
+ toolDrawHelpTooltipElement.className = 'tooltip hidden';
|
|
|
+ helpTooltip = new ol.Overlay({
|
|
|
+ id,
|
|
|
+ element: toolDrawHelpTooltipElement,
|
|
|
+ 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 = 'baseDrawName'
|
|
|
+ const draw = new interaction.Draw({
|
|
|
+ source: _source,//测量绘制层数据源
|
|
|
+ type: obj.rectangle ? 'LineString' : obj.featureType, //几何图形类型
|
|
|
+ // @ts-ignore
|
|
|
+ // geometryFunction: createBox(),
|
|
|
+ geometryFunction: obj.rectangle ? createBox() : null,
|
|
|
+ // geometryFunction: createRegularPolygon(6),
|
|
|
+ style: commonStyle(obj.featureType === 'Point' ? true : false),
|
|
|
+ });
|
|
|
+ draw.set('showText', obj.featureType === 'Point' ? true : false)
|
|
|
+ draw.set(drawFlag[0], drawFlag[1])
|
|
|
+ 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()
|
|
|
+ }
|
|
|
+ toolDrawTooltipElement.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]);
|
|
|
+ // 标绘的时候不需要最终结果dom
|
|
|
+ map.removeOverlay(map.getOverlayById('baseDrawHelpTooltipElementId'))
|
|
|
+ toolDrawTooltipElement.parentNode.removeChild(toolDrawTooltipElement);
|
|
|
+ sketch = null; //置空当前绘制的要素对象
|
|
|
+ toolDrawTooltipElement = null; //置空测量工具提示框对象
|
|
|
+ toolDrawHelpTooltipElement.parentNode.removeChild(toolDrawHelpTooltipElement);
|
|
|
+ toolDrawHelpTooltipElement = 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)
|
|
|
+ }
|
|
|
+ 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; //绘制线时提示相应内容
|
|
|
+ // }
|
|
|
+ }
|
|
|
+ toolDrawHelpTooltipElement.innerHTML = helpMsg; //将提示信息设置到对话框中显示
|
|
|
+ helpTooltip.setPosition(evt.coordinate);//设置帮助提示框的位置
|
|
|
+ toolDrawHelpTooltipElement.classList.remove('hidden');//移除帮助提示框的隐藏样式进行显示
|
|
|
+ };
|
|
|
+ map.on('pointermove', pointerMoveHandler); //地图容器绑定鼠标移动事件,动态显示帮助提示框内容
|
|
|
+ //地图绑定鼠标移出事件,鼠标移出时为帮助提示框设置隐藏样式
|
|
|
+ try {
|
|
|
+ map.getViewport().on('mouseout', () => {
|
|
|
+ toolDrawHelpTooltipElement.addClass('hidden');
|
|
|
+ });
|
|
|
+ } catch (e) {
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ resolve(() => {
|
|
|
+ const oldLayer = map.getLayers().getArray().filter(v => v.get(layerFlag[0]) === layerFlag[1])
|
|
|
+ if (oldLayer) {
|
|
|
+ oldLayer[0].setStyle(commonStyle(true))
|
|
|
+ }
|
|
|
+ // const oldDraw = map.getInteractions().getArray().filter(v => v.get(drawFlag[0]) === drawFlag[1])
|
|
|
+ // if (oldDraw) {
|
|
|
+ // oldDraw[0].setStyle(commonStyle(oldDraw[0].get('showText')))
|
|
|
+ // }
|
|
|
+ })
|
|
|
+ }))
|
|
|
+}
|