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 interaction from 'ol/interaction' import Modify from "ol/interaction/Modify" import {createBox} from "ol/interaction/Draw" import * as sphere from "ol/sphere"; import {unByKey} from "ol/Observable"; import {isValue} from "@/utils/util"; // @ts-ignore import PointIcon from "@/assets/images/gis-layout/gis-layout-tools_tool-bz_icon.png" import {v4} from "uuid"; 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.1)', // 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} 千米
${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} 平方公里
${parseFloat(String(km * 0.38610)).toFixed( 2 )} 平方英里`; } else { output = `${Math.round(area * 100) / 100} ` + " m2"; } 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) const featEnd = evt.feature featEnd.set('isInit', true) featEnd.set(layerFlag[0], layerFlag[1]) featEnd.setId(v4()) } 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'))) // } }) })) } export const refreshModify = (map, _source) => { const oldModify = map.getInteractions().getArray().filter(v => v.get(modifyFlag[0]) === modifyFlag[1]) if (oldModify) { map.removeInteraction(oldModify[0]) } 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 { } }) }