ソースを参照

森防视频详情 粤政图标点可视域

Hwf 4 ヶ月 前
コミット
af632cd271

+ 5 - 1
src/components/Map/YztMap/index.vue

@@ -153,6 +153,9 @@ const getMap = () => {
 const getScale = () => {
   return scale;
 };
+const getMapUtils = () => {
+  return mapUtils;
+};
 // 加载事件
 onMounted(() => {
   init();
@@ -162,9 +165,10 @@ onMounted(() => {
 onUnmounted(() => {
   window.removeEventListener('resize', handleResize);
 });
+provide('getMapUtils', getMapUtils);
 provide('getMap', getMap);
 provide('getScale', getScale);
-defineExpose({ addMarker, clearMarker });
+defineExpose({ getMapUtils, addMarker, clearMarker });
 </script>
 
 <style scoped>

+ 8 - 1
src/utils/olMap/olMap.ts

@@ -266,7 +266,8 @@ export class olMap {
       // 定义样式
       const style = new Style({
         image: new Icon({
-          anchor: [0.5, point.size[1]],
+          anchor: [0.5, 0.5],
+          scale: 0.146,
           anchorXUnits: 'fraction',
           anchorYUnits: 'pixels',
           src: point.icon
@@ -553,4 +554,10 @@ export class olMap {
       }
     });
   }
+  getVectorLayer() {
+    return this.vectorLayer;
+  }
+  getMap() {
+    return this.map;
+  }
 }

+ 258 - 23
src/views/globalMap/RightMenu/ForestDefenseVideo/DetailDialog.vue

@@ -49,8 +49,8 @@
               <div class="down-btn" />
               <div class="down-left-btn" />
               <div class="down-right-btn" />
-              <div class="left-btn" />
-              <div class="right-btn" />
+              <div class="left-btn" @click="handleControl('left')" />
+              <div class="right-btn" @click="handleControl('right')" />
               <div :class="!!isRefresh ? 'refresh-btn rotate-box' : 'refresh-btn'" @click="handleRefresh" />
             </div>
           </div>
@@ -73,9 +73,28 @@
 </template>
 
 <script lang="ts" setup>
+import * as turf from '@turf/turf';
+import Feature from 'ol/Feature';
+import Point from 'ol/geom/Point';
+import Style from 'ol/style/Style';
+import Fill from 'ol/style/Fill';
+import Stroke from 'ol/style/Stroke';
+import Icon from 'ol/style/Icon';
+import Circle from 'ol/geom/Circle';
+import { fromLonLat } from 'ol/proj';
+import videoImg from '@/assets/images/dotIcon/33_forest_defense_video.png';
+import VectorLayer from 'ol/layer/Vector';
+import VectorSource from 'ol/source/Vector';
+import Polygon from 'ol/geom/Polygon';
+
 const props = defineProps({
-  id: String
+  id: String,
+  activeMap: String
 });
+const mapUtils = inject('getMapUtils');
+let map, AMap, marker, circle, sector, vectorLayer;
+const AMapType = ['vectorgraph', 'satellite'];
+const YMapType = ['satellite2', 'satellite3', 'imageMap', 'imageMap2'];
 const tabs = ref([
   { label: '', icon: 'icon1' },
   { label: '', icon: 'icon2' },
@@ -90,13 +109,83 @@ let visibleRange = ref(false);
 let detailData = ref({
   title: '',
   address: '',
-  lng: '',
-  lat: '',
+  lng: 0,
+  lat: 0,
   type: '',
   speed: 0,
-  video_code: ''
+  video_code: '',
+  radius: 0,
+  startAngle: 0,
+  endAngle: 0
 });
 
+const getData = () => {
+  detailData.value = {
+    title: '高州市根子镇上炕村委会23211312',
+    address: '茂名市高州市坡心东南约400米',
+    lng: 110.819207,
+    lat: 21.711887,
+    type: '云台',
+    speed: 246,
+    video_code: '44092251001320000009',
+    radius: 10000,
+    startAngle: 0,
+    endAngle: 30
+  };
+  initDot();
+};
+// 加载地图标点
+const initDot = () => {
+  if (AMapType.includes(props.activeMap)) {
+    // 切换到高德
+    removeMarker();
+    removeVisibleRange();
+    map = mapUtils().getMap();
+    AMap = mapUtils().getAMap();
+    const icon = new AMap.Icon({
+      size: new AMap.Size(37, 40),
+      image: videoImg
+    });
+    // 创建点覆盖物
+    marker = new AMap.Marker({
+      position: new AMap.LngLat(detailData.value.lng, detailData.value.lat),
+      icon: icon,
+      offset: new AMap.Pixel(-18, -20)
+    });
+    map.add(marker);
+    if (visibleRange.value) {
+      addVisibleRange();
+    }
+  } else if (YMapType.includes(props.activeMap)) {
+    // 切换到粤政图
+    map = mapUtils().getMap();
+    vectorLayer = new VectorLayer({
+      source: new VectorSource({
+        features: []
+      })
+    });
+    map.addLayer(vectorLayer);
+    // 创建标注点
+    marker = new Feature({
+      geometry: new Point([detailData.value.lng, detailData.value.lat])
+    });
+    // 定义样式
+    const style = new Style({
+      image: new Icon({
+        // anchor: [0.5, 0.942],
+        anchor: [0.5, 0.5],
+        anchorXUnits: 'fraction',
+        anchorYUnits: 'pixels',
+        scale: 0.146,
+        src: videoImg
+      })
+    });
+
+    marker.setStyle(style);
+    vectorLayer.getSource().addFeature(marker);
+  }
+};
+
 // 调整转速
 const handleSpeed = (type) => {
   if (type === 'add') {
@@ -109,12 +198,56 @@ const handleSpeed = (type) => {
 // 焦距控制
 const handleFocusControl = (type) => {
   if (type === 'add') {
-
   } else {
-
   }
 };
 
+// 控制视角移动
+const handleControl = (type) => {
+  if (type === 'left') {
+    detailData.value.startAngle -= 1;
+    detailData.value.endAngle -= 1;
+  } else if (type === 'right') {
+    detailData.value.startAngle += 1;
+    detailData.value.endAngle += 1;
+  }
+  if (visibleRange.value) {
+    createSector();
+  }
+};
+// 新增扇形
+const createSector = () => {
+  removeSector();
+  const center = turf.point([detailData.value.lng, detailData.value.lat]);
+  const sectorData = turf.sector(center, detailData.value.radius / 1000, detailData.value.startAngle, detailData.value.endAngle);
+  if (AMapType.includes(props.activeMap)) {
+    sector = new AMap.Polyline({
+      path: sectorData.geometry.coordinates,
+      strokeColor: '#ffffff',
+      strokeOpacity: 1,
+      strokeWeight: 1,
+      fillColor: '#ffffff', // 填充色
+      fillOpacity: 0.2 // 填充透明度
+    });
+    map.add(sector);
+  } else if (YMapType.includes(props.activeMap)) {
+    sector = new Feature({
+      geometry: new Polygon(sectorData.geometry.coordinates)
+    });
+    sector.setStyle(
+      new Style({
+        fill: new Fill({
+          color: 'rgba(255, 255, 255, 0)'
+        }),
+        stroke: new Stroke({
+          color: 'rgba(255, 255, 255, 1)',
+          width: 1
+        })
+      })
+    );
+    vectorLayer.getSource().addFeature(sector);
+  }
+};
 let isRefresh = ref(false);
 // 刷新
 const handleRefresh = () => {
@@ -124,20 +257,122 @@ const handleRefresh = () => {
     isRefresh.value = false;
   }, 3000);
 };
-// 加载地图标点
-const initDot = () => {
 
-}
-onMounted(() => {
-  detailData.value = {
-    title: '高州市根子镇上炕村委会23211312',
-    address: '茂名市高州市坡心东南约400米',
-    lng: '110.819207',
-    lat: '21.711887',
-    type: '云台',
-    speed: 246,
-    video_code: '44092251001320000009'
-  };
+watch(visibleRange, () => {
+  if (visibleRange.value) {
+    addVisibleRange();
+  } else {
+    removeVisibleRange();
+  }
+});
+// 移除标点
+const removeMarker = () => {
+  if (!marker) return;
+  if (AMapType.includes(props.activeMap)) {
+    map.remove(marker);
+  } else if (YMapType.includes(props.activeMap)) {
+    const source = vectorLayer.getSource();
+    source.removeFeature(marker);
+  }
+  marker = null;
+};
+// 添加可视域
+const addVisibleRange = () => {
+  if (AMapType.includes(props.activeMap)) {
+    // 圆形范围
+    circle = new AMap.Circle({
+      center: new AMap.LngLat(detailData.value.lng, detailData.value.lat),
+      radius: detailData.value.radius,
+      strokeColor: '#ff0000', // 线颜色
+      strokeOpacity: 0.35, // 线透明度
+      strokeWeight: 1, // 线宽
+      fillColor: '#ff0000', // 填充色
+      fillOpacity: 0.2 // 填充透明度
+    });
+    map.add(circle);
+  } else if (YMapType.includes(props.activeMap)) {
+    const center = turf.point([detailData.value.lng, detailData.value.lat]);
+    const sectorData = turf.sector(center, detailData.value.radius / 1000, 0, 360);
+
+    circle = new Feature({
+      geometry: new Polygon(sectorData.geometry.coordinates)
+    });
+    circle.setStyle(
+      new Style({
+        fill: new Fill({
+          color: 'rgba(255, 0, 0, 0.2)'
+        }),
+        stroke: new Stroke({
+          color: 'rgba(255, 0, 0, 0.35)',
+          width: 1
+        })
+      })
+    );
+
+    // const center = [detailData.value.lng, detailData.value.lat];
+    // circle = new Feature({
+    //   geometry: new Circle(center, 100)
+    //   // geometry: new Circle(center, detailData.value.radius)
+    // });
+    // circle.setStyle(
+    //   new Style({
+    //     fill: new Fill({
+    //       color: 'rgba(255, 0, 0, 0.2)'
+    //     }),
+    //     stroke: new Stroke({
+    //       color: 'rgba(255, 0, 0, 0.35)',
+    //       width: 1
+    //     })
+    //   })
+    // );
+    vectorLayer.getSource().addFeature(circle);
+  }
+  createSector();
+};
+// 移除可视域
+const removeVisibleRange = () => {
+  if (circle) {
+    if (AMapType.includes(props.activeMap)) {
+      map.remove(circle);
+    } else if (YMapType.includes(props.activeMap)) {
+      const source = vectorLayer.getSource();
+      source.removeFeature(circle);
+    }
+    circle = null;
+  }
+  removeSector();
+};
+// 移除扇形
+const removeSector = () => {
+  if (sector) {
+    if (AMapType.includes(props.activeMap)) {
+      map.remove(sector);
+      sector = null;
+    } else if (YMapType.includes(props.activeMap)) {
+      const source = vectorLayer.getSource();
+      source.removeFeature(sector);
+    }
+  }
+};
+watch(
+  () => props.id,
+  () => {
+    getData();
+  },
+  {
+    immediate: true
+  }
+);
+watch(
+  () => props.activeMap,
+  (newMap, oldMap) => {
+    if ((AMapType.includes(newMap) && AMapType.includes(oldMap)) || (YMapType.includes(newMap) && YMapType.includes(oldMap))) return;
+    initDot();
+  }
+);
+onUnmounted(() => {
+  removeMarker();
+  removeVisibleRange();
 });
 </script>
 
@@ -279,7 +514,7 @@ onMounted(() => {
           .gradient-text2 {
             font-size: 44px;
             font-weight: normal;
-            font-font: "PingFang SC", sans-serif;
+            font-font: 'PingFang SC', sans-serif;
           }
           .gradient-text3 {
             font-weight: bold;
@@ -427,7 +662,7 @@ onMounted(() => {
       }
     }
     .operate-box {
-      margin-top:70px;
+      margin-top: 70px;
       display: flex;
       justify-content: space-between;
       align-items: center;

+ 5 - 2
src/views/globalMap/RightMenu/ForestDefenseVideo/index.vue

@@ -55,7 +55,7 @@
           <div class="common-btn-primary5">导出</div>
         </div>
         <div class="tree-box">
-          <el-tree ref="tree" :data="treeData">
+          <el-tree ref="tree" :data="treeData" default-expand-all>
             <template #default="{ node, data }">
               <span v-if="data.num1 && data.num2" class="custom-tree-node">
                 <span>{{ node.label }}</span>
@@ -69,7 +69,7 @@
         </div>
       </div>
     </div>
-    <DetailDialog v-if="!!detailId" :id="detailId" />
+    <DetailDialog v-if="!!detailId" :id="detailId" :activeMap="activeMap" />
   </div>
 </template>
 
@@ -77,6 +77,9 @@
 // 统计数据
 import DetailDialog from './DetailDialog.vue';
 
+const props = defineProps({
+  activeMap: String
+});
 const statisticsData = ref({
   data1: '',
   data2: ''

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

@@ -64,7 +64,7 @@
           <!--森林防火-->
           <Fireproofing v-if="menuState.menuData[menuState.activeIndex]?.name === '森林防火'" @handle-menu="handleMenu" />
           <!--森防视频-->
-          <ForestDefenseVideo v-if="menuState.menuData[menuState.activeIndex]?.name === '森防视频'" @handle-menu="handleMenu" />
+          <ForestDefenseVideo v-if="menuState.menuData[menuState.activeIndex]?.name === '森防视频'" :activeMap="activeMap" @handle-menu="handleMenu" />
           <!--防灾救援-->
           <Mitigation v-if="menuState.menuData[menuState.activeIndex]?.name === '防灾救援'" @handle-menu="handleMenu" />
           <!--易涝隐患点-->
@@ -122,6 +122,7 @@ import TyphoonVideo from './TyphoonVideo.vue';
 import RescueTeam from './RescueTeam.vue';
 interface Props {
   pointType: PointType[];
+  activeMap: string;
 }
 
 withDefaults(defineProps<Props>(), {});

+ 21 - 19
src/views/globalMap/index.vue

@@ -3,7 +3,7 @@
     <div class="global-map">
       <MapLogical v-if="activeMap === 'logical'" :map-data="mapData" />
       <YztMap
-        v-else-if="['satellite2', 'satellite3', 'imageMap', 'imageMap2'].includes(activeMap)"
+        v-else-if="YMapType.includes(activeMap)"
         ref="map2Ref"
         :active-map="activeMap"
         :point-type="pointType"
@@ -27,7 +27,7 @@
         @select-search-marker="selectSearchMarker"
       />
       <!--右侧菜单-->
-      <RightMenu ref="rightMenuRef" :point-type="pointType" />
+      <RightMenu ref="rightMenuRef" :active-map="activeMap" :point-type="pointType" />
       <!--更换地图类型-->
       <SwitchMapTool :active-map="activeMap" class="tool-box" @switch-map="switchMap" />
       <!--时间轴-->
@@ -60,6 +60,8 @@ import CommunicationSupport from '@/views/globalMap/RightMenu/CommunicationSuppo
 import GridPointRainfall from '@/views/globalMap/RightMenu/GridPointRainfall.vue';
 import EmergencyCrew from '@/views/globalMap/RightMenu/EmergencyCrew.vue';
 
+const AMapType = ['vectorgraph', 'satellite'];
+const YMapType = ['satellite2', 'satellite3', 'imageMap', 'imageMap2'];
 const rightMenuRef = ref(null);
 const mapData = reactive(logicalData);
 let map;
@@ -67,8 +69,8 @@ let mapRef = ref(null);
 let map2Ref = ref(null);
 let leftMenuRef = ref(null);
 let showMask = ref(true);
-//  vectorgraph satellite imageMap logical satellite2 satellite3
-let activeMap = ref('satellite');
+//  vectorgraph satellite imageMap imageMap3 logical satellite2 satellite3
+let activeMap = ref('imageMap');
 // 附近视频菜单数据
 let tempMenu = ref({
   name: '',
@@ -231,58 +233,58 @@ const handleAnalysisData = (data) => {
   rightMenuRef.value.handleMenu('空间分析', data);
 };
 const getMapUtils = () => {
-  if (['imageMap', 'satellite2', 'satellite3'].includes(activeMap.value)) {
-    return map2Ref.value.getMap();
-  } else if (['vectorgraph', 'satellite'].includes(activeMap.value)) {
+  if (YMapType.includes(activeMap.value)) {
+    return map2Ref.value.getMapUtils();
+  } else if (AMapType.includes(activeMap.value)) {
     return mapRef.value.getMapUtils();
   }
   return {};
 };
 // 获取地图元素操作
 const getMap = () => {
-  if (['imageMap', 'satellite2', 'satellite3'].includes(activeMap.value)) {
+  if (YMapType.includes(activeMap.value)) {
     return map2Ref.value.getMap();
-  } else if (['vectorgraph', 'satellite'].includes(activeMap.value)) {
+  } else if (AMapType.includes(activeMap.value)) {
     return mapRef.value.getMap();
   }
   return {};
 };
 const showDetail = (data, dataType) => {
-  if (['imageMap', 'satellite2', 'satellite3'].includes(activeMap.value)) {
+  if (YMapType.includes(activeMap.value)) {
     return map2Ref.value.handleHover(data, dataType);
-  } else if (['vectorgraph', 'satellite'].includes(activeMap.value)) {
+  } else if (AMapType.includes(activeMap.value)) {
     return mapRef.value.handleHover(data, dataType);
   }
   return {};
 };
 const getDrawTool = () => {
-  if (['imageMap', 'satellite2', 'satellite3'].includes(activeMap.value)) {
+  if (YMapType.includes(activeMap.value)) {
     return map2Ref.value.drawTool;
-  } else if (['vectorgraph', 'satellite'].includes(activeMap.value)) {
+  } else if (AMapType.includes(activeMap.value)) {
     return mapRef.value.drawTool;
   }
   return {};
 };
 const getMarkers = () => {
-  if (['imageMap', 'satellite2', 'satellite3'].includes(activeMap.value)) {
+  if (YMapType.includes(activeMap.value)) {
     return map2Ref.value.getMarkers();
-  } else if (['vectorgraph', 'satellite'].includes(activeMap.value)) {
+  } else if (AMapType.includes(activeMap.value)) {
     return mapRef.value.getMarkers();
   }
   return {};
 };
 const getPlaceSearch = () => {
-  if (['imageMap', 'satellite2', 'satellite3'].includes(activeMap.value)) {
+  if (YMapType.includes(activeMap.value)) {
     return map2Ref.value.getPlaceSearch();
-  } else if (['vectorgraph', 'satellite'].includes(activeMap.value)) {
+  } else if (AMapType.includes(activeMap.value)) {
     return mapRef.value.getPlaceSearch();
   }
   return {};
 };
 const trackPlayback = (data) => {
-  if (['imageMap', 'satellite2', 'satellite3'].includes(activeMap.value)) {
+  if (YMapType.includes(activeMap.value)) {
     return map2Ref.value.trackPlayback(data);
-  } else if (['vectorgraph', 'satellite'].includes(activeMap.value)) {
+  } else if (AMapType.includes(activeMap.value)) {
     return mapRef.value.trackPlayback(data);
   }
   return {};