CzRger 1 year ago
parent
commit
a1f6cd4786

BIN
src/assets/images/gis-layout/gis-layout-tools_tool-bz.png


BIN
src/assets/images/gis-layout/gis-layout-tools_tool-bz_icon.png


BIN
src/assets/images/gis-layout/gis-layout-tools_tool-cjl.png


BIN
src/assets/images/gis-layout/gis-layout-tools_tool-cjxmj.png


BIN
src/assets/images/gis-layout/gis-layout-tools_tool-cymj.png


BIN
src/assets/images/gis-layout/gis-layout-tools_tool-hdbx.png


BIN
src/assets/images/gis-layout/gis-layout-tools_tool-hjx.png


BIN
src/assets/images/gis-layout/gis-layout-tools_tool-hx.png


BIN
src/assets/images/gis-layout/gis-layout-tools_tool-hy.png


+ 3 - 3
src/components/easyMap/func/measure.ts

@@ -259,8 +259,8 @@ export default function Measure (map, typeSelect) {
         };
         map.on('pointermove', pointerMoveHandler); //地图容器绑定鼠标移动事件,动态显示帮助提示框内容
         //地图绑定鼠标移出事件,鼠标移出时为帮助提示框设置隐藏样式
-        map.getViewport().on('mouseout', () => {
-            helpTooltipElement.addClass('hidden');
-        });
+        // map.getViewport().on('mouseout', () => {
+        //     helpTooltipElement.addClass('hidden');
+        // });
     }
 }

+ 0 - 0
src/views/gis/layout/tools/map.scss


+ 393 - 0
src/views/gis/layout/tools/tool-draw.ts

@@ -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')))
+            // }
+        })
+    }))
+}

+ 59 - 3
src/views/gis/layout/tools/tool.vue

@@ -1,7 +1,28 @@
 <template>
-  <div class="tool-com">
-    <div class="item">
-
+  <div class="tool-com __box-shadow">
+    <div class="item" @click="mapFunc.measure('line')">
+      <img src="@/assets/images/gis-layout/gis-layout-tools_tool-cjl.png" alt=""/>测距离
+    </div>
+    <div class="item" @click="mapFunc.measure('circle')">
+      <img src="@/assets/images/gis-layout/gis-layout-tools_tool-cymj.png" alt=""/>测圆面积
+    </div>
+    <div class="item" @click="mapFunc.measure('rectangle')">
+      <img src="@/assets/images/gis-layout/gis-layout-tools_tool-cjxmj.png" alt=""/>测矩形面积
+    </div>
+    <div class="item" @click="handleDraw({featureType: 'Point'})">
+      <img src="@/assets/images/gis-layout/gis-layout-tools_tool-bz.png" alt=""/>标注
+    </div>
+    <div class="item" @click="handleDraw({featureType: 'LineString'})">
+      <img src="@/assets/images/gis-layout/gis-layout-tools_tool-hx.png" alt=""/>画线
+    </div>
+    <div class="item" @click="handleDraw({featureType: 'Polygon', rectangle: true})">
+      <img src="@/assets/images/gis-layout/gis-layout-tools_tool-hjx.png" alt=""/>画矩形
+    </div>
+    <div class="item" @click="handleDraw({featureType: 'Polygon'})">
+      <img src="@/assets/images/gis-layout/gis-layout-tools_tool-hdbx.png" alt=""/>画多边形
+    </div>
+    <div class="item" @click="handleDraw({featureType: 'Circle'})">
+      <img src="@/assets/images/gis-layout/gis-layout-tools_tool-hy.png" alt=""/>画圆
     </div>
   </div>
 </template>
@@ -22,6 +43,10 @@ import {
 import {useStore} from 'vuex'
 import {useRouter, useRoute} from 'vue-router'
 import {ElMessage, ElMessageBox} from "element-plus";
+import './map.scss'
+import * as ToolDraw from './tool-draw'
+import {draw} from "./tool-draw";
+import * as BaseDraw from "@/components/easyMap/func/base-draw";
 
 export default defineComponent({
   name: '',
@@ -40,14 +65,45 @@ export default defineComponent({
     const route = useRoute();
     const that = (getCurrentInstance() as ComponentInternalInstance).appContext.config.globalProperties
     const state = reactive({})
+    const handleDraw = (obj) => {
+      const config = Object.assign(ToolDraw.getBaseDrawConfig(), obj)
+      if (config.featureType === 'Point') {
+        config.isPoint = true
+      } else if (config.featureType === 'LineString') {
+        config.isLineString = true
+      } else if (config.featureType === 'Polygon') {
+        config.isPolygon = true
+      }
+      ToolDraw.draw(props.map, config)
+    }
     onMounted(() => {
     })
     return {
       ...toRefs(state),
+      handleDraw
     }
   },
 })
 </script>
 
 <style scoped lang="scss">
+.tool-com {
+  padding: 12px 18px;
+  background-color: #FFFFFF;
+  .item {
+    width: 100%;
+    height: 36px;
+    display: flex;
+    align-items: center;
+    border-radius: 4px;
+    margin: 4px 0;
+    cursor: pointer;
+    &:hover {
+      background-color: rgba(170, 198, 238, 0.2);
+    }
+    >img {
+      margin: 0 10px;
+    }
+  }
+}
 </style>