Hwf преди 5 месеца
родител
ревизия
67a11f6650

+ 9 - 0
package-lock.json

@@ -31,6 +31,7 @@
         "element-resize-detector": "^1.2.4",
         "file-saver": "^2.0.5",
         "fuse.js": "7.0.0",
+        "gcoord": "^1.0.7",
         "highlight.js": "11.9.0",
         "html2canvas": "^1.4.1",
         "image-conversion": "^2.1.1",
@@ -8201,6 +8202,14 @@
         "node": ">=10"
       }
     },
+    "node_modules/gcoord": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmmirror.com/gcoord/-/gcoord-1.0.7.tgz",
+      "integrity": "sha512-UCN2iSm69jBOYz2ma2eg5I5imp65Cj70rcTTfMNSNMvZpR1U6oGjmVh080aCvC/6lN1ClkuOoBeaLuebw9AZJg==",
+      "engines": {
+        "node": ">=16.11.0"
+      }
+    },
     "node_modules/gensync": {
       "version": "1.0.0-beta.2",
       "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz",

+ 1 - 0
package.json

@@ -39,6 +39,7 @@
     "element-resize-detector": "^1.2.4",
     "file-saver": "^2.0.5",
     "fuse.js": "7.0.0",
+    "gcoord": "^1.0.7",
     "highlight.js": "11.9.0",
     "html2canvas": "^1.4.1",
     "image-conversion": "^2.1.1",

BIN
src/assets/images/map/end.png


BIN
src/assets/images/map/mark.png


BIN
src/assets/images/map/start.png


+ 0 - 1
src/types/auto-imports.d.ts

@@ -43,7 +43,6 @@ declare global {
   const getCurrentInstance: typeof import('vue')['getCurrentInstance']
   const getCurrentScope: typeof import('vue')['getCurrentScope']
   const h: typeof import('vue')['h']
-  const iconFeature: typeof import('~icons/fe/ature')['default']
   const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
   const inject: typeof import('vue')['inject']
   const injectLocal: typeof import('@vueuse/core')['injectLocal']

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

@@ -971,6 +971,7 @@ export class olMap {
         image: icon
       })
     );
+    debugger
     this.traceFeature = new Feature({
       geometry: new LineString(lineArr)
     });

+ 211 - 118
src/views/globalMap/RightMenu/FixedPointAnalysis.vue

@@ -67,14 +67,14 @@
           <div v-for="(item, index) in routeData" :key="index" class="route-item2" @click="drawRoute(item)">
             <div class="text-box1">
               <div :class="'tag' + index">{{ getTag(index) }}</div>
-              <div class="text1">{{ item.policy }}</div>
+              <div class="text1">{{ item.strategy }}</div>
             </div>
             <div class="route-info">
               <div class="text-box2">
                 <i class="icon1" />
-                <div class="gradient-text2">{{ item.date[0] }}</div>
+                <div class="gradient-text2">{{ item.duration[0] }}</div>
                 <div class="text2">小时</div>
-                <div class="gradient-text2">{{ item.date[1] }}</div>
+                <div class="gradient-text2">{{ item.duration[1] }}</div>
                 <div class="text2">分钟</div>
               </div>
               <div class="line"></div>
@@ -94,15 +94,29 @@
 <script lang="ts" setup name="FixedPointAnalysis">
 import BigNumber from 'bignumber.js';
 import { getEmergencyRescuePointInfoList } from '@/api/globalMap';
+import markImg from '@/assets/images/map/mark.png';
+import startImg from '@/assets/images/map/start.png';
+import endImg from '@/assets/images/map/end.png';
 
 interface Props {
   modelValue: boolean;
   location?: string | number[];
+  activeMap: string;
 }
+const AMapType = ['vectorgraph', 'satellite'];
 const props = withDefaults(defineProps<Props>(), {});
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { emergency_resource, disaster_relief_material } = toRefs<any>(proxy?.useDict('emergency_resource', 'disaster_relief_material'));
 const emits = defineEmits(['update:modelValue']);
+import gcoord from 'gcoord';
+import Icon from 'ol/style/Icon';
+import Feature from 'ol/Feature';
+import Point from 'ol/geom/Point';
+import Style from 'ol/style/Style';
+import { LineString } from 'ol/geom';
+import { Stroke } from 'ol/style';
+
+const amapKey = 'e45d4caa2bef3c84714a2ed9b1e27d98';
 let getMapUtils = inject('getMapUtils');
 let showAddress = ref(true);
 let routeData = ref([]);
@@ -169,105 +183,181 @@ const selectEvent = (item, unFitView) => {
     selectMarker.setMap(null);
     selectMarker = null;
   }
-  // 以 icon URL 的形式创建一个途经点
-  const icon = new AMap.Icon({
-    size: new AMap.Size(19, 31),
-    image: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png'
-  });
-  selectMarker = new AMap.Marker({
-    position: new AMap.LngLat(item.longitude, item.latitude),
-    icon: icon,
-    offset: new AMap.Pixel(-13, -30)
-  });
-  selectMarker.setMap(map);
-  if (!unFitView) {
-    map.setFitView([selectMarker]);
+  if (AMapType.includes(props.activeMap)) {
+    // 以 icon URL 的形式创建一个途经点
+    const icon = new AMap.Icon({
+      size: new AMap.Size(19, 31),
+      image: markImg
+    });
+    selectMarker = new AMap.Marker({
+      position: new AMap.LngLat(item.longitude, item.latitude),
+      icon: icon,
+      offset: new AMap.Pixel(-13, -30)
+    });
+    selectMarker.setMap(map);
+    if (!unFitView) {
+      map.setFitView([selectMarker]);
+    }
+  } else {
+    const icon = new Icon({
+      src: markImg,
+      width: 19,
+      height: 31
+    });
+    const feature = new Feature({
+      geometry: new Point([item.longitude, item.latitude])
+    });
+    feature.setStyle(
+      new Style({
+        image: icon
+      })
+    );
+    const vectorLayer = getMapUtils().getVectorLayer();
+    vectorLayer.getSource().addFeature(feature);
   }
   getList();
 };
 
-const handleRoutes = (item) => {
-  const start = [item.longitude, item.latitude];
-  const end = [selectData.value.longitude, selectData.value.latitude];
+const handleRoutes = async (item) => {
+  const lnglat = gcoord.transform([item.longitude, item.latitude], gcoord.WGS84, gcoord.GCJ02);
+  const lnglat2 = gcoord.transform([selectData.value.longitude, selectData.value.latitude], gcoord.WGS84, gcoord.GCJ02);
+  const start = [lnglat[0], lnglat[1]];
+  const end = lnglat2;
   showAddress.value = true;
   routesAddress.value = item.address;
-  calculateRoutes(start, end, 0);
-};
-// 计算并展示三条路线
-const calculateRoutes = (start, end, index) => {
-  if (index >= 3) {
-    return;
-  }
-  // 不同的策略或参数来生成不同的路线
-  const policyMap = [
-    AMap.DrivingPolicy.LEAST_TIME, // 最短时间
-    AMap.DrivingPolicy.LEAST_DISTANCE, // 最短距离
-    AMap.DrivingPolicy.LEAST_FEE // 最少费用
-  ];
-  driving.setPolicy(policyMap[index]);
+  // 10速度最快 返回结果会躲避拥堵,路程较短,尽量缩短时间,与高德地图的默认策略也就是不进行任何勾选一致
+  // 16费用最低 返回的结果尽量不走高速,并且尽量规划收费较低甚至免费的路径结果,与高德地图的“避免收费&不走高速”策略一致
+  const url = `https://restapi.amap.com/v3/direction/driving?origin=${start}&destination=${end}&key=${amapKey}`;
+  const response = await fetch(url + '&strategy=10');
 
-  driving.search(new AMap.LngLat(start[0], start[1]), new AMap.LngLat(end[0], end[1]), function (status, result) {
-    if (status === 'complete' && result.info === 'OK') {
-      const route = result.routes[0];
-      if (index === 0) {
-        routeData.value = [];
-      }
-      route.date = formatDate(route.time);
-      routeData.value.push(route);
+  if (!!response.ok) {
+    const data = await response.json();
+    if (data.route && data.route.paths) {
+      data.route.paths.forEach((item) => {
+        item.duration = formatDate(item.duration);
+      });
+      routeData.value = data.route.paths;
+    } else {
+      routeData.value = [];
     }
-
-    // 计算下一条路线
-    calculateRoutes(start, end, index + 1);
-  });
+  }
+  const response2 = await fetch(url + '&strategy=16');
+  if (!!response2.ok) {
+    const data2 = await response2.json();
+    if (data2.route && data2.route.paths) {
+      data2.route.paths.forEach((item) => {
+        item.duration = formatDate(item.duration);
+      });
+      routeData.value = routeData.value.concat(data2.route.paths);
+    }
+  }
 };
+
 const drawRoute = (route) => {
   const path = parseRouteToPath(route);
-  if (!!startMarker) {
-    map.remove(startMarker);
-    startMarker = null;
-  }
-  if (!!endMarker) {
-    map.remove(endMarker);
-    endMarker = null;
-  }
-  if (!!routeLine) {
-    routeLine.setMap(null);
-    routeLine = null;
-  }
-  const icon1 = new AMap.Icon({
-    size: new AMap.Size(19, 31),
-    image: 'https://webapi.amap.com/theme/v1.3/markers/n/start.png'
-  });
-  const icon2 = new AMap.Icon({
-    size: new AMap.Size(19, 31),
-    image: 'https://webapi.amap.com/theme/v1.3/markers/n/end.png'
-  });
-  startMarker = new AMap.Marker({
-    position: path[0],
-    icon: icon1,
-    offset: new AMap.Pixel(-13, -30),
-    map: map
-  });
+  if (AMapType.includes(props.activeMap)) {
+    if (!!startMarker) {
+      map.remove(startMarker);
+      startMarker = null;
+    }
+    if (!!endMarker) {
+      map.remove(endMarker);
+      endMarker = null;
+    }
+    if (!!routeLine) {
+      routeLine.setMap(null);
+      routeLine = null;
+    }
+    const icon1 = new AMap.Icon({
+      size: new AMap.Size(19, 31),
+      image: startImg
+    });
+    const icon2 = new AMap.Icon({
+      size: new AMap.Size(19, 31),
+      image: endImg
+    });
+    startMarker = new AMap.Marker({
+      position: path[0],
+      icon: icon1,
+      offset: new AMap.Pixel(-13, -30),
+      map: map
+    });
 
-  endMarker = new AMap.Marker({
-    position: path[path.length - 1],
-    icon: icon2,
-    offset: new AMap.Pixel(-13, -30),
-    map: map
-  });
-  routeLine = new AMap.Polyline({
-    path: path,
-    isOutline: true,
-    outlineColor: '#ffeeee',
-    borderWeight: 2,
-    strokeWeight: 5,
-    strokeColor: '#0091ff',
-    lineJoin: 'round'
-  });
-  routeLine.setMap(map);
+    endMarker = new AMap.Marker({
+      position: path[path.length - 1],
+      icon: icon2,
+      offset: new AMap.Pixel(-13, -30),
+      map: map
+    });
+    routeLine = new AMap.Polyline({
+      path: path,
+      isOutline: true,
+      outlineColor: '#ffeeee',
+      borderWeight: 2,
+      strokeWeight: 5,
+      strokeColor: '#0091ff',
+      lineJoin: 'round'
+    });
+    routeLine.setMap(map);
 
-  // 调整视野达到最佳显示区域
-  map.setFitView([startMarker, endMarker, routeLine]);
+    // 调整视野达到最佳显示区域
+    map.setFitView([startMarker, endMarker, routeLine]);
+  } else {
+    const vectorLayer = getMapUtils().getVectorLayer();
+    const source = vectorLayer.getSource();
+    if (!!startMarker) {
+      source.removeFeaure(startMarker);
+      startMarker = null;
+    }
+    if (!!endMarker) {
+      source.removeFeaure(endMarker);
+      endMarker = null;
+    }
+    if (!!routeLine) {
+      source.removeFeaure(routeLine);
+      routeLine = null;
+    }
+    startMarker = new Feature({
+      geometry: new Point(path[0])
+    });
+    endMarker = new Feature({
+      geometry: new Point(path[path.length - 1])
+    });
+    startMarker.setStyle(
+      new Style({
+        image: new Icon({
+          src: startImg,
+          width: 19,
+          height: 31
+        })
+      })
+    );
+    endMarker.setStyle(
+      new Style({
+        image: new Icon({
+          src: endImg,
+          width: 19,
+          height: 31
+        })
+      })
+    );
+    routeLine = new Feature({
+      geometry: new LineString(path)
+    });
+    routeLine.setStyle(
+      new Style({
+        stroke: new Stroke({
+          color: '#0091ff', // 主线条颜色
+          width: 5, // 主线条宽度
+          lineCap: 'round', // 线帽样式(可选)
+          lineJoin: 'round' // 线连接样式
+        })
+      })
+    );
+    source.addFeature(startMarker);
+    source.addFeature(routeLine);
+    source.addFeature(endMarker);
+  }
 };
 
 // 解析DrivingRoute对象,构造成AMap.Polyline的path参数需要的格式
@@ -278,11 +368,19 @@ const parseRouteToPath = (route) => {
   for (var i = 0, l = route.steps.length; i < l; i++) {
     var step = route.steps[i];
 
-    for (var j = 0, n = step.path.length; j < n; j++) {
-      path.push(step.path[j]);
-    }
-  }
+    // 按分号分割字符串,得到一个包含经纬度对的数组
+    const coordinatesPairs = step.polyline.split(';');
+    // 将每个经纬度对进一步分割成经度和纬度,并转换为一个对象
+    path = coordinatesPairs.map((pair) => {
+      const [longitude, latitude] = pair.split(',');
+      return gcoord.transform([longitude, latitude], gcoord.GCJ02, gcoord.WGS84);
+      // return [parseFloat(longitude), parseFloat(latitude)];
+    });
 
+    // for (var j = 0, n = step.path.length; j < n; j++) {
+    //   path.push(step.path[j]);
+    // }
+  }
   return path;
 };
 function formatDate(seconds: number) {
@@ -304,15 +402,8 @@ function formatDate(seconds: number) {
   return [hours, minutes];
 }
 const getTag = (index) => {
-  let tag = '路线' + index;
-  if (index === 0) {
-    tag = '路线A';
-  } else if (index === 1) {
-    tag = '路线B';
-  } else if (index === 2) {
-    tag = '路线C';
-  }
-  return tag;
+  const arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
+  return '路线' + (arr[index] ? arr[index] : index);
 };
 // 搜索列表
 const getList = () => {
@@ -331,22 +422,24 @@ const getList = () => {
     total.value = res.total;
   });
 };
-onMounted(() => {
-  AMap = getMapUtils().getAMap();
-  map = getMapUtils().getMap();
-  geocoder = new AMap.Geocoder({
-    // city 指定进行编码查询的城市,支持传入城市名、adcode 和 citycode
-    city: '010'
-  });
+onMounted(async () => {
+  const gcj02Coord = gcoord.transform(props.location, gcoord.WGS84, gcoord.GCJ02);
   tempState.longitude = props.location[0];
   tempState.latitude = props.location[1];
-  geocoder.getAddress(props.location, (status, result) => {
-    if (status === 'complete' && result.info === 'OK') {
-      tempState.address = result.regeocode.formattedAddress;
-    }
-  });
-  //构造路线导航类
-  driving = new AMap.Driving({});
+  map = getMapUtils().getMap();
+  if (AMapType.includes(props.activeMap)) {
+    AMap = getMapUtils().getAMap();
+    //构造路线导航类
+    driving = new AMap.Driving({});
+  }
+  // 调用高德逆向地理编码 API
+  const response = await fetch(`https://restapi.amap.com/v3/geocode/regeo?key=${amapKey}&location=${gcj02Coord[0]},${gcj02Coord[1]}`);
+  const result = await response.json();
+
+  // 解析地址信息
+  if (result.status === '1' && result.regeocode) {
+    tempState.address = result.regeocode.formatted_address;
+  }
 });
 </script>
 
@@ -366,11 +459,11 @@ onMounted(() => {
     overflow-y: auto;
   }
   .search-box {
-    width: 1490px;
+    width: 100%;
     height: 150px;
     background: url('@/assets/images/electronicDisasterMapManage/box6.png') no-repeat;
     background-size: 100% 100%;
-    padding: 0 10px;
+    padding: 0 10px 0 30px;
     display: flex;
     justify-content: space-between;
     align-items: center;

+ 1 - 1
src/views/globalMap/RightMenu/GridPointRainfall.vue

@@ -1,6 +1,6 @@
 <template>
   <Dialog draggable custom-show title="格点雨量" :height="'1000px'" hide-footer @close="handleClose">
-    <div class="gradient-text title">雨量统计:{{address}}({{location[0]}}, {{location[1]}})</div>
+    <div class="gradient-text title">雨量统计:{{ address }}({{location[0]}}, {{location[1]}})</div>
     <Chart :option="chartOption" style="height: 700px" />
   </Dialog>
 </template>

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

@@ -54,7 +54,7 @@
           <!--实时标绘-->
           <OnlinePlotting v-if="menuState.menuData[menuState.activeIndex]?.name === '实时标绘'" :activeMap="activeMap" />
           <!--定点分析-->
-          <FixedPointAnalysis v-if="menuState.menuData[menuState.activeIndex]?.name === '定点分析'" :location="location2" />
+          <FixedPointAnalysis v-if="menuState.menuData[menuState.activeIndex]?.name === '定点分析'" :location="location2" :activeMap="activeMap" />
           <!--雨情监测-->
           <RainMonitor v-if="menuState.menuData[menuState.activeIndex]?.name === '雨情监测'" />
           <!--铁塔运行监测-->

+ 26 - 6
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('satellite3');
 // 附近视频菜单数据
 let tempMenu = ref({
   name: '',
@@ -300,7 +300,11 @@ watch(showNearbyVideos, () => {
     if (!!tempMenu.value && !!tempMenu.value.name) {
       leftMenuRef.value.setMenuChange(tempMenu.value, false);
       tempMenu.value = {};
-      map.off('click', handleClickMap);
+      if (AMapType.includes(activeMap.value)) {
+        map.off('click', handleClickMap);
+      } else {
+        map.un('click', handleClickMap);
+      }
     }
   }
 });
@@ -310,7 +314,11 @@ watch(showRainfall, () => {
     if (!!tempMenu.value && !!tempMenu.value.name) {
       leftMenuRef.value.setMenuChange(tempMenu.value, false);
       tempMenu.value = {};
-      map.off('click', handleClickMap);
+      if (AMapType.includes(activeMap.value)) {
+        map.off('click', handleClickMap);
+      } else {
+        map.un('click', handleClickMap);
+      }
     }
   }
 });
@@ -320,7 +328,11 @@ const handleShowVideo = (data) => {
   showNearbyVideos.value = true;
 };
 const handleClickMap = (e) => {
-  location.value = [e.lnglat.getLng(), e.lnglat.getLat()];
+  if (AMapType.includes(activeMap.value)) {
+    location.value = [e.lnglat.lng, e.lnglat.lat];
+  } else {
+    location.value = e.coordinate;
+  }
   if (!!tempMenu.value && tempMenu.value.name === '附近视频') {
     showNearbyVideos.value = true;
   } else if (!!tempMenu.value && tempMenu.value.name === '格点雨量') {
@@ -329,7 +341,11 @@ const handleClickMap = (e) => {
     const item = deepClone(tempMenu.value);
     const nowLocation = deepClone(location.value);
     tempMenu.value = {};
-    map.off('click', handleClickMap);
+    if (AMapType.includes(activeMap.value)) {
+      map.off('click', handleClickMap);
+    } else {
+      map.un('click', handleClickMap);
+    }
     rightMenuRef.value.updateMenu(item.checked ? '1' : '2', item, nowLocation);
   }
 };
@@ -352,7 +368,11 @@ const handleShowPeople = (data) => {
 // })
 onBeforeUnmount(() => {
   if (!!map) {
-    map.off('click', handleClickMap);
+    if (AMapType.includes(activeMap.value)) {
+      map.off('click', handleClickMap);
+    } else {
+      map.un('click', handleClickMap);
+    }
   }
 });