Browse Source

粤政图 空间分析绘制功能

Hwf 5 months ago
parent
commit
92aa69e325

+ 2 - 2
src/hooks/AMap/useAMap.ts

@@ -322,8 +322,8 @@ export function useAMap(options) {
         polygon.show();
       });
     } else {
-      data = convertCoordinates(data);
-      // 遮罩部分
+      // data = convertCoordinates(data);
+      // 遮罩部分d
       // const outer = [
       //   new AMap.LngLat(-180, 90, true),
       //   new AMap.LngLat(-180, -90, true),

+ 84 - 13
src/utils/olMap/olMap.ts

@@ -25,7 +25,7 @@ import { fromLonLat } from 'ol/proj';
 import axios from 'axios';
 import { fromExtent } from 'ol/geom/Polygon';
 import { LinearRing, LineString, Polygon } from 'ol/geom';
-import {Graticule} from "ol/layer";
+import { Graticule } from 'ol/layer';
 import { getPointsCenter, mergeGeoJsonPolygons } from '@/utils/gisUtils';
 import { Cluster } from 'ol/source';
 import CircleStyle from 'ol/style/Circle';
@@ -34,11 +34,15 @@ import { Draw, Select } from 'ol/interaction';
 import { click } from 'ol/events/condition';
 import Circle from 'ol/geom/Circle';
 import { hexToRgba } from '@/utils';
+import { createBox } from 'ol/interaction/Draw';
 
 const tk = 'a8df87f1695d224d2679aa805c1268d9';
 const commonUrl = import.meta.env.VITE_APP_BASE_API2 + 'api/oneShare/proxyHandler/gd/';
 // proj4.defs('EPSG:4490', '+proj=longlat +ellps=GRS80 +no_defs +type=crs');
-proj4.defs('EPSG:4490', 'GEOGCS["China Geodetic Coordinate System 2000",DATUM["China_2000",SPHEROID["CGCS2000",6378137,298.257222101,AUTHORITY["EPSG","1024"]],AUTHORITY["EPSG","1043"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4490"]]');
+proj4.defs(
+  'EPSG:4490',
+  'GEOGCS["China Geodetic Coordinate System 2000",DATUM["China_2000",SPHEROID["CGCS2000",6378137,298.257222101,AUTHORITY["EPSG","1024"]],AUTHORITY["EPSG","1043"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4490"]]'
+);
 // proj4.defs('EPSG:4525', '+proj=tmerc +lat_0=0 +lon_0=111 +k=1 +x_0=37500000 +y_0=0 +ellps=GRS80 +units=m +no_defs +type=crs');
 register(proj4);
 const projection = new Projection({
@@ -173,7 +177,7 @@ export class olMap {
     // 添加新的图层
     if (Array.isArray(options.id)) {
       for (const layer of options.id) {
-        if (typeof layer === "string") {
+        if (typeof layer === 'string') {
           await this.formatXml(layer); // 等待当前 layer 处理完成
         } else {
           await this.formatXml(layer.id, layer.minZoom, layer.maxZoom, layer.zIndex, layer.visible); // 等待当前 layer 处理完成
@@ -211,11 +215,11 @@ export class olMap {
       TILECOL: '{x}',
       TILEROW: '{y}',
       TILEMATRIX: '{z}',
-      tk: key,
+      tk: key
     };
     const newParams = Object.assign({}, params, { LAYER: dataType });
     let paramsStr = '';
-    for (let [k, v] of Object.entries(newParams)) {
+    for (const [k, v] of Object.entries(newParams)) {
       paramsStr += `${k}=${v}&`;
     }
     return `${baseUrl}${dataType}_${projType}/wmts?${paramsStr.slice(0, -1)}`;
@@ -317,9 +321,14 @@ export class olMap {
   drawGraphics2(newOptions: MouseTool) {
     const typeList = {
       circle: 'Circle',
-      // rectangle: 'Rectangle',
+      rectangle: 'Circle',
       polygon: 'Polygon'
     };
+    let geometryFunction = null;
+    if (newOptions.graphicsType === 'rectangle') {
+      // 绘制矩形的方法
+      geometryFunction = createBox();
+    }
     if (!!typeList[newOptions.graphicsType]) {
       this.drawOptions = {
         type: newOptions.graphicsType,
@@ -327,7 +336,7 @@ export class olMap {
         strokeOpacity: 1,
         strokeWeight: 1,
         fillColor: newOptions.color,
-        fillOpacity: newOptions.drawType === '1' ? 0 : 0.5,
+        fillOpacity: newOptions.drawType === '1' ? '0' : '0.5',
         strokeStyle: 'solid'
       };
       this.closeDraw();
@@ -344,6 +353,7 @@ export class olMap {
       this.drawTool = new Draw({
         source: this.drawVector.getSource(),
         type: typeList[newOptions.graphicsType],
+        geometryFunction: geometryFunction,
         style: style
       });
       // 添加绘制交互到地图
@@ -372,7 +382,7 @@ export class olMap {
 
     if (Array.isArray(newLayers)) {
       for (const layer of newLayers) {
-        if (typeof layer === "string") {
+        if (typeof layer === 'string') {
           await this.formatXml(layer); // 等待当前 layer 处理完成
         } else {
           await this.formatXml(layer.id, layer.minZoom, layer.maxZoom, layer.zIndex, layer.visible); // 等待当前 layer 处理完成
@@ -707,12 +717,13 @@ export class olMap {
     const graticule = new Graticule({
       name: 'Graticule',
       showLabels: true, // 为每条刻度线绘制一个带有各自纬度/经度的标签
-      wrapX: false,     // 是否水平重复经纬网
+      wrapX: false, // 是否水平重复经纬网
       targetSize: 230,
       zIndex: 99999,
-      strokeStyle: new Stroke({ // 用于绘制刻度线的样式
+      strokeStyle: new Stroke({
+        // 用于绘制刻度线的样式
         color: '#dcdcdc', // 线条颜色
-        width: 1       // 线条宽度
+        width: 1 // 线条宽度
       }),
       lonLabelStyle: new Text({
         font: '12px Calibri,sans-serif',
@@ -732,9 +743,9 @@ export class olMap {
       })
     });
     if (visible) {
-      this.map.addLayer(graticule)
+      this.map.addLayer(graticule);
     } else {
-      this.removeLayer('Graticule')
+      this.removeLayer('Graticule');
     }
   }
   /**
@@ -750,9 +761,69 @@ export class olMap {
       }
     });
   }
+  // 创建图形
+  createGraphics(data: any) {
+    if (data.type === 'circle') {
+      // 绘制圆形
+      return this.createCircle(data);
+    } else if (['polygon', 'rectangle'].includes(data.type)) {
+      // 绘制矩形、多边形
+      return this.createPolygon(data);
+    }
+    // else if (data.type === 'marker') {
+    //   // 绘制图标
+    //   return createMarker(data);
+    // } else if (data.type === 'measureArea') {
+    //   // 绘制面积
+    //   return createMeasureArea(data);
+    // } else if (data.type === 'text') {
+    //   // 文字
+    //   return addText(data);
+    // } else if (data.type === 'straightLine') {
+    //   // 直线
+    //   return createStraightLine(data);
+    // }
+  }
+  createCircle(data) {
+    const circle = new Circle(data.center, data.radius);
+    const feature = new Feature(circle);
+    feature.setStyle(
+      new Style({
+        stroke: new Stroke({
+          color: hexToRgba(data.strokeColor, data.strokeOpacity),
+          width: data.strokeWeight
+        }),
+        fill: new Fill({
+          color: hexToRgba(data.fillColor, data.fillOpacity)
+        })
+      })
+    );
+    this.drawVector.getSource().addFeature(feature);
+    return feature;
+  }
+  createPolygon(data) {
+    const polygon = new Polygon([data.path]);
+    const feature = new Feature(polygon);
+    feature.setStyle(
+      new Style({
+        stroke: new Stroke({
+          color: hexToRgba(data.strokeColor, data.strokeOpacity),
+          width: data.strokeWeight
+        }),
+        fill: new Fill({
+          color: hexToRgba(data.fillColor, data.fillOpacity)
+        })
+      })
+    );
+    this.drawVector.getSource().addFeature(feature);
+    return feature;
+  }
   getVectorLayer() {
     return this.vectorLayer;
   }
+  getDrawVector() {
+    return this.drawVector;
+  }
   getMap() {
     return this.map;
   }

+ 150 - 72
src/views/globalMap/RightMenu/DrawTools.vue

@@ -52,9 +52,6 @@
     </div>
     <div class="draw-item" @click="openDraw">{{ mouseToolState.drawing ? '关闭绘制' : '开启绘制' }}</div>
     <div class="draw-item" @click="handleUndo">撤销</div>
-    <div id="menu-box">
-      <div class="menu-item">删除</div>
-    </div>
   </div>
 </template>
 
@@ -63,6 +60,8 @@ import { useHistory } from '@/hooks/useHistory';
 import { nanoid } from 'nanoid';
 import { deepClone } from '@/utils';
 import * as turf from '@turf/turf';
+import Overlay from 'ol/Overlay';
+import YMap from '@/components/Map/YMap.vue';
 
 interface ListItem {
   label: string;
@@ -74,6 +73,7 @@ const props = defineProps({
 const AMapType = ['vectorgraph', 'satellite'];
 const getDrawTool = inject('getDrawTool');
 const getMap = inject('getMap');
+const getMapUtils = inject('getMapUtils');
 const emits = defineEmits(['handleAnalysisData']);
 const { currentState, commit, undo, history, future } = useHistory();
 const mouseToolState = reactive({
@@ -84,6 +84,13 @@ const mouseToolState = reactive({
   // 图形形状  circle圆形 rectangle矩形 polygon多边形
   graphicsType: 'circle'
 });
+let popup;
+let map = computed(() => {
+  return getMap();
+});
+let mapUtils = computed(() => {
+  return getMapUtils();
+});
 watch(
   mouseToolState,
   () => {
@@ -97,10 +104,14 @@ watch(
         drawTool.getMouseTool().on('draw', onDraw);
       } else {
         drawTool.getMouseTool().on('drawend', onDraw2);
+        map.value.un('click', onMapClick);
       }
     } else {
       mouseToolState.drawing = false;
       drawTool.closeDraw();
+      if (!AMapType.includes(props.activeMap)) {
+        map.value.on('click', onMapClick);
+      }
     }
   },
   {
@@ -234,61 +245,38 @@ const onDraw2 = (event) => {
   const geometryType = geometry.getType();
   const id = nanoid();
   feature.set('id', id);
+  feature.set('dotType', 'analysisSpatial');
   const data: any = {
     id: id,
     type: drawOptions.graphicsType,
-    // color: feature._opts.strokeColor,
-    // drawType: feature._opts.fillOpacity === 0 ? '1' : '2'
+    color: drawOptions.color,
+    drawType: drawOptions.drawType
   };
-  // 获取绘制的几何信息(包括经纬度)
-  if (geometryType === 'Point') {
-    const coordinates = geometry.getCoordinates();
-    console.log('绘制了一个点:', coordinates);
-  } else if (geometryType === 'LineString') {
-    const coordinates = geometry.getCoordinates();
-    console.log('绘制了一条线:', coordinates);
-    // 线的坐标是点的数组,每个点都是一个经纬度数组
-  } else if (geometryType === 'Polygon') {
-    const coordinates = geometry.getCoordinates();
-    console.log('绘制了一个多边形:', coordinates);
-    // 多边形的坐标是环的数组,每个环是点的数组,每个点都是一个经纬度数组
-  } else if (geometryType === 'Circle') {
+  feature.set('extraData', data);
+  if (geometryType === 'Circle') {
     data.center = geometry.getCenter();
     data.radius = geometry.getRadius();
     const data2 = turf.sector(data.center, data.radius, 0, 360);
     const pathArr = data2.geometry.coordinates[0];
     data.path = pathArr;
-    console.log('绘制了一个圆:', data);
+  } else if (geometryType === 'Polygon') {
+    const coordinates = geometry.getCoordinates();
+    data.path = coordinates[0];
   }
-  // const path = geometry.getCoordinates();
-  // debugger
-  // // 将AMap.LngLat对象数组转换为经纬度数组
-  // const pathArr = path.map((lngLat) => {
-  //   // 返回经度和纬度的数组
-  //   return [lngLat.lng, lngLat.lat];
-  // });
-  // data.path = pathArr;
   overlays.push(feature);
   overlaysData.push(data);
   commit(deepClone(overlaysData));
   // 右击进入编辑
-  feature.on('contextmenu', handleRightClick);
+  let map = getMap();
+  map.on('contextmenu', handleRightClick2);
   if (overlaysData.length === 1) {
     analysisSpatial(data);
   }
-  // // 点击空间分析
-  // feature.on('click', function () {
-  //   // 没在编辑时
-  //   if (!mouseToolState.drawing) {
-  //     analysisSpatial(data);
-  //   }
-  // });
 };
 let rightClickObj;
 // 图形右击事件
 let initContextMenu = false;
 const handleRightClick = (event) => {
-  debugger
   rightClickObj = event.target;
   const contextMenu = getDrawTool().getContextMenu();
   if (!initContextMenu) {
@@ -305,13 +293,67 @@ const handleRightClick = (event) => {
   }
   contextMenu.open(getMap(), event.lnglat);
 };
+const handleRightClick2 = (event) => {
+  event.preventDefault(); // 阻止默认的右键菜单弹出
+  map.value.un('click', onMapClick);
+  if (popup) {
+    map.value.removeOverlay(popup);
+    popup = undefined;
+  }
+  rightClickObj = map.value.forEachFeatureAtPixel(event.pixel, (feature2) => {
+    return feature2;
+  });
+  let dotType = rightClickObj.get('dotType');
+  if (!!rightClickObj && dotType === 'analysisSpatial') {
+    const dom = document.createElement('div');
+    const dom2 = document.createElement('div');
+    dom.id = 'analysisSpatial-popup';
+    dom2.className = 'menu-item';
+    dom2.innerHTML = '删除';
+    dom2.onclick = function () {
+      map.value.on('click', onMapClick);
+      deleteGraphics();
+    };
+    dom.appendChild(dom2);
+    popup = new Overlay({
+      element: dom,
+      autoPan: true,
+      positioning: 'bottom-right',
+      stopEvent: false,
+      offset: [0, 0]
+    });
+
+    const geometry = rightClickObj.getGeometry();
+    const geometryType = geometry.getType();
+    let coordinate = [];
+    if (geometryType === 'Circle') {
+      coordinate = geometry.getCenter();
+    } else {
+      const points = geometry.getCoordinates();
+      const features = turf.points(points[0]);
+      const center = turf.center(features);
+      if (center && center.geometry && center.geometry.coordinates) {
+        coordinate = center.geometry.coordinates;
+      }
+    }
+    popup.setPosition(coordinate);
+    map.value.addOverlay(popup);
+  }
+};
 // 删除图形
 const deleteGraphics = () => {
-  const id = rightClickObj.getExtData()?.id;
-  if (id) {
+  let id;
+  if (AMapType.includes(props.activeMap)) {
+    id = rightClickObj.getExtData()?.id;
+  } else {
+    unMapClick();
+    id = rightClickObj.get('id');
+  }
+  if (!!id) {
     for (let i = 0; i < overlays.length; i++) {
       const overlay = Array.isArray(overlays[i]) ? overlays[i][0] : overlays[i];
-      if (overlay?.getExtData().id === id) {
+      const itemId = AMapType.includes(props.activeMap) ? overlay?.getExtData().id : overlay.get('id');
+      if (itemId === id) {
         removeOverlayByIndex(i);
         commit(deepClone(overlaysData));
         rightClickObj = null;
@@ -351,14 +393,21 @@ const handleUndo = () => {
           strokeOpacity: 1,
           strokeWeight: '1',
           fillColor: restoreData.color,
-          fillOpacity: restoreData.drawType === '1' ? 0 : 0.5
+          fillOpacity: restoreData.drawType === '1' ? '0' : '0.5'
         };
         if (restoreData.type === 'circle') {
           newData.center = restoreData.center;
           newData.radius = restoreData.radius;
         }
-        const obj = getDrawTool().createGraphics(newData);
-        overlays.push(obj);
+        let obj;
+        if (AMapType.includes(props.activeMap)) {
+          obj = getDrawTool().createGraphics(newData);
+        } else {
+          obj = getDrawTool().createGraphics(newData);
+        }
+        if (!!obj) {
+          overlays.push(obj);
+        }
       }
     }
     undo();
@@ -369,15 +418,28 @@ const handleUndo = () => {
 // 根据索引移除覆盖物
 const removeOverlayByIndex = (index: number) => {
   const map = getMap();
-  if (Array.isArray(overlays[index])) {
-    overlays[index].forEach((overlay) => {
+  if (AMapType.includes(props.activeMap)) {
+    if (Array.isArray(overlays[index])) {
+      overlays[index].forEach((overlay) => {
+        // 移除地图上覆盖物
+        map.remove(overlay);
+      });
+    } else {
       // 移除地图上覆盖物
-      map.remove(overlay);
-    });
+      map.remove(overlays[index]);
+    }
   } else {
-    // 移除地图上覆盖物
-    map.remove(overlays[index]);
+    if (Array.isArray(overlays[index])) {
+      overlays[index].forEach((overlay) => {
+        // 移除地图上覆盖物
+        mapUtils.value.getDrawVector().getSource().removeFeature(overlay);
+      });
+    } else {
+      // 移除地图上覆盖物
+      mapUtils.value.getDrawVector().getSource().removeFeature(overlays[index]);
+    }
   }
+
   overlays.splice(index, 1);
   overlaysData.splice(index, 1);
 };
@@ -410,30 +472,35 @@ const analysisSpatial = (data) => {
   }
   emits('handleAnalysisData', location);
 };
+const onMapClick = (event) => {
+  // 没在编辑时
+  if (!mouseToolState.drawing && event.pixel) {
+    const feature = map.value.forEachFeatureAtPixel(event.pixel, (feature2) => {
+      return feature2;
+    });
+    const data = feature.get('extraData');
+    analysisSpatial(data);
+  }
+  unMapClick(event);
+};
+const unMapClick = (event) => {
+  const overlayElement = popup?.getElement();
+  const isClickOnOverlay =
+    event && overlayElement && (overlayElement.contains(event.originalEvent.target) || overlayElement === event.originalEvent.target);
+  if (!isClickOnOverlay && popup) {
+    map.value.removeOverlay(popup);
+    popup = undefined;
+  }
+};
 onMounted(() => {
-  // 监听右击事件
-  getMap().on('contextmenu', (event) => {
-    event.preventDefault(); // 阻止默认上下文菜单
-
-    // 获取右击位置的要素
-    const featuresAtPixel = getMap().getFeaturesAtPixel(event.pixel);
-
-    // 检查是否有要素在右击位置
-    if (featuresAtPixel && featuresAtPixel.length > 0) {
-      // 遍历要素并检查条件(这里可以根据需要添加条件)
-      featuresAtPixel.forEach(function(feature) {
-        // 执行你想要的动作,例如显示一个信息框或弹出菜单
-        console.log('右击了要素:', feature.get('id')); // 假设要素有一个'id'属性
-
-        // 你可以在这里显示一个自定义的右击菜单
-        // 例如,创建一个<div>元素,设置其内容和样式,然后添加到DOM中
-        // 并监听菜单项的点击事件来执行相应的动作
-      });
-    }
-  });
-})
+  if (!AMapType.includes(props.activeMap)) {
+    map.value.on('click', onMapClick);
+  }
+});
 onBeforeUnmount(() => {
-  //
+  if (!AMapType.includes(props.activeMap)) {
+    map.value.un('click', onMapClick);
+  }
 });
 </script>
 
@@ -497,9 +564,20 @@ onBeforeUnmount(() => {
 .active {
   background-color: rgba(22, 73, 142, 0.5);
 }
-.menu-box {
+</style>
+<style>
+#analysisSpatial-popup {
+  background-color: #ffffff;
+  border-radius: 4px;
+  cursor: pointer;
+  &:hover {
+    background-color: #f3f3ee;
+  }
   .menu-item {
-    font-size: 32px;
+    font-size: 14px;
+    padding: 0 8px;
+    height: 28px;
+    line-height: 28px;
   }
 }
 </style>

+ 1 - 1
src/views/globalMap/RightMenu/ForestDefenseVideo/DetailDialog.vue

@@ -28,7 +28,7 @@
         </div>
       </div>
       <div class="video-box">
-        <HKVideo :dot_data="detailData" style="height: 100%" />
+        <HKVideo :dot_data="detailData" autoplay style="height: 100%" />
       </div>
       <div class="control-container">
         <div class="common-title-box">操作台</div>

+ 2 - 2
src/views/globalMap/index.vue

@@ -73,7 +73,7 @@ let map2Ref = ref(null);
 let leftMenuRef = ref(null);
 let showMask = ref(true);
 //  vectorgraph satellite imageMap imageMap3 logical satellite2 satellite3
-let activeMap = ref('satellite');
+let activeMap = ref('imageMap');
 // 附近视频菜单数据
 let tempMenu = ref({
   name: '',
@@ -164,7 +164,7 @@ const clickMenu = (item, dataList) => {
         '易涝隐患点',
         '无人机',
         '铁塔运行监测',
-        '物资与装备',
+        // '物资与装备',
         '通讯保障',
         '路网视频',
         '江湖河库',