Hwf пре 4 месеци
родитељ
комит
6b2097a185
6 измењених фајлова са 2523 додато и 68 уклоњено
  1. 2416 0
      package-lock.json
  2. 1 0
      package.json
  3. 3 5
      src/components/Map/index.vue
  4. 82 54
      src/hooks/AMap/useAMap.ts
  5. 1 9
      src/types/auto-imports.d.ts
  6. 20 0
      src/utils/gisUtils.ts

Разлика између датотеке није приказан због своје велике величине
+ 2416 - 0
package-lock.json


+ 1 - 0
package.json

@@ -21,6 +21,7 @@
     "@amap/amap-jsapi-loader": "^1.0.1",
     "@element-plus/icons-vue": "2.3.1",
     "@highlightjs/vue-plugin": "2.1.0",
+    "@turf/turf": "^7.1.0",
     "@univerjs/core": "^0.4.1",
     "@univerjs/design": "^0.4.1",
     "@univerjs/docs": "^0.4.1",

+ 3 - 5
src/components/Map/index.vue

@@ -114,7 +114,6 @@ const mapUtils = useAMap({
     // 添加遮罩
     if (props.showMask) {
       creatMask2(mmJson, { strokeWeight: 2 });
-      // creatMask([{ strokeWeight: 2 }]);
     }
     drawTool.initMouseTool({ container: 'aMap', map, AMap });
     placeSearch = new AMap.PlaceSearch({
@@ -190,9 +189,8 @@ const {
   showInfo,
   hideInfo,
   handleHover,
-  creatMask,
   creatMask2,
-  removeMask,
+  removeMask2,
   trackPlayback
 } = { ...mapUtils };
 const handlePointDetails = (data) => {
@@ -409,9 +407,9 @@ watch(
   () => props.showMask,
   () => {
     if (props.showMask) {
-      creatMask([{ strokeWeight: 2 }]);
+      creatMask2(mmJson, { strokeWeight: 2 });
     } else {
-      removeMask();
+      removeMask2();
     }
   }
 );

+ 82 - 54
src/hooks/AMap/useAMap.ts

@@ -1,7 +1,7 @@
 import AMapLoader from '@amap/amap-jsapi-loader';
 import { nanoid } from 'nanoid';
 import { deepClone } from '@/utils';
-import { wgs_gcj_encrypts } from '@/utils/gisUtils';
+import { mergeGeoJsonPolygons, wgs_gcj_encrypts } from '@/utils/gisUtils';
 
 export function useAMap(options) {
   let AMap, map, nowLayer, labelsLayer, scale, cluster;
@@ -20,19 +20,7 @@ export function useAMap(options) {
       version: !!options.version ? options.version : '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
       plugins: options.plugins
         ? options.plugins
-        : [
-            'AMap.Scale',
-            'AMap.RangingTool',
-            'AMap.MouseTool',
-            'AMap.PolygonEditor',
-            'AMap.MarkerCluster',
-            'AMap.DistrictSearch',
-            'AMap.MoveAnimation',
-            'AMap.Driving',
-            'AMap.Geocoder',
-            'AMap.PlaceSearch',
-            'AMap.GeoJSON'
-          ]
+        : ['AMap.Scale', 'AMap.RangingTool', 'AMap.MouseTool', 'AMap.PolygonEditor', 'AMap.MarkerCluster', 'AMap.DistrictSearch', 'AMap.MoveAnimation', 'AMap.Driving', 'AMap.Geocoder', 'AMap.PlaceSearch', 'AMap.GeoJSON']
     }).then((res) => {
       AMap = res;
       map = new AMap.Map(options.el ? options.el : 'aMap', {
@@ -95,7 +83,7 @@ export function useAMap(options) {
     }
     addPoints = points;
     const count = points.length;
-    const _renderClusterMarker = function (context) {
+    const _renderClusterMarker = function(context) {
       // 聚合中点个数
       const clusterCount = context.count;
       const div = document.createElement('div');
@@ -116,7 +104,7 @@ export function useAMap(options) {
         map.setZoomAndCenter(map.getZoom() + 1, bounds.getCenter());
       });
     };
-    const _renderMarker = function (context) {
+    const _renderMarker = function(context) {
       const content =
         '<div style="display: flex;flex-direction: column;align-items: center;justify-content: center">' +
         '<div style="background: url(' +
@@ -132,7 +120,7 @@ export function useAMap(options) {
       context.marker.setContent(content);
       context.marker.setOffset(offset);
       context.marker.setExtData(context.data[0]);
-      context.marker.on('click', function (e) {
+      context.marker.on('click', function(e) {
         const extData = e.target.getExtData();
         let index = 0;
         let index2 = 0;
@@ -254,7 +242,7 @@ export function useAMap(options) {
     new AMap.DistrictSearch({
       extensions: 'all',
       subdistrict: 0
-    }).search(name, function (status, result) {
+    }).search(name, function(status, result) {
       // 外多边形坐标数组和内多边形坐标数组
       const outer = [
         new AMap.LngLat(-360, 90, true),
@@ -264,7 +252,7 @@ export function useAMap(options) {
       ];
       options.forEach((option) => {
         const holes = result.districtList[0].boundaries;
-        const pathArray = [outer];
+        let pathArray = [outer];
         pathArray.push.apply(pathArray, holes);
         maskPolygon = new AMap.Polygon({
           pathL: pathArray,
@@ -285,43 +273,80 @@ export function useAMap(options) {
       maskPolygon = null;
     }
   };
+  let maskPolygon2 = [];
   const creatMask2 = (data, option) => {
-    // 外多边形坐标数组和内多边形坐标数组
+    data = convertCoordinates(data);
+    // 遮罩部分
     const outer = [
-      new AMap.LngLat(-360, 90, true),
-      new AMap.LngLat(-360, -90, true),
-      new AMap.LngLat(360, -90, true),
-      new AMap.LngLat(360, 90, true)
+      new AMap.LngLat(-180, 90, true),
+      new AMap.LngLat(-180, -90, true),
+      new AMap.LngLat(180, -90, true),
+      new AMap.LngLat(180, 90, true)
     ];
-    //outer
     const pathArray = [outer];
-    data = convertCoordinates(data);
-    data.features.forEach((item, index) => {
-      let arr = [];
-      const geometry = item.geometry;
-      geometry.coordinates.forEach((coordinate) => {
-        if (geometry.type === 'MultiPolygon') {
-          coordinate[0].forEach((coordinate2) => {
-            arr.push(coordinate2);
-          });
-          pathArray.push(arr);
-          arr = [];
-        } else {
-          arr.push(coordinate);
-        }
-      });
-      if (arr.length == 0) return;
-      pathArray.push(arr);
+    // 合并区边界
+    const data2 = mergeGeoJsonPolygons(data);
+    data2.geometry.coordinates.forEach((coords) => {
+      pathArray.push(coords[0]);
     });
     maskPolygon = new AMap.Polygon({
       path: pathArray,
       strokeColor: option.strokeColor ? option.strokeColor : '#268ab9',
-      strokeOpacity: option.strokeOpacity ? option.strokeOpacity : 1,
+      strokeOpacity: 1,
       strokeWeight: option.strokeWeight ? option.strokeWeight : 1,
       fillColor: option.fillColor ? option.fillColor : '#10243b',
       fillOpacity: option.fillOpacity ? option.fillOpacity : 0.65
     });
     map.add(maskPolygon);
+    // 边界部分
+    data.features.forEach((feature) => {
+      if (feature.geometry.type === 'Polygon') {
+        const polygonPath = feature.geometry.coordinates[0].map((coord) => {
+          return [coord[0], coord[1]];
+        });
+        const polygon = new AMap.Polygon({
+          path: polygonPath,
+          strokeColor: option.strokeColor ? option.strokeColor : '#268ab9',
+          strokeOpacity: option.strokeOpacity ? option.strokeOpacity : 1,
+          strokeWeight: option.strokeWeight ? option.strokeWeight : 1,
+          fillOpacity: 0
+        });
+        maskPolygon2.push(polygon);
+        map.add(polygon);
+      } else if (feature.geometry.type === 'MultiPolygon') {
+        feature.geometry.coordinates.forEach((polygonCoords) => {
+          const polygonPath = polygonCoords.map((ring) => {
+            return ring.map((coord) => {
+              return [coord[0], coord[1]];
+            });
+          });
+          const outerPath = polygonPath[0];
+          const innerPaths = polygonPath.slice(1);
+          const polygon = new AMap.Polygon({
+            path: outerPath,
+            holes: innerPaths,
+            strokeColor: option.strokeColor ? option.strokeColor : '#268ab9',
+            strokeOpacity: option.strokeOpacity ? option.strokeOpacity : 1,
+            strokeWeight: option.strokeWeight ? option.strokeWeight : 1,
+            fillOpacity: 0
+          });
+          maskPolygon2.push(polygon);
+          map.add(polygon);
+        });
+      }
+    });
+  };
+  const removeMask2 = () => {
+    if (!!maskPolygon) {
+      map.remove(maskPolygon);
+      maskPolygon = null;
+    }
+    if (maskPolygon2 && maskPolygon2.length > 0) {
+      maskPolygon2.forEach((polygon) => {
+        map.remove(polygon);
+      });
+      maskPolygon2 = [];
+    }
   };
   let moveMarker, movePolyline, movePassedPolyline, timerId;
   const trackPlayback = (lineArr) => {
@@ -359,12 +384,12 @@ export function useAMap(options) {
       strokeWeight: 6 //线宽
     });
 
-    moveMarker.on('moving', function (e) {
+    moveMarker.on('moving', function(e) {
       movePassedPolyline.setPath(e.passedPath);
       map.setCenter(e.target.getPosition(), true);
     });
 
-    moveMarker.on('moveend', function (e) {
+    moveMarker.on('moveend', function(e) {
       index++;
       if (index === lineArr.length - 1) {
         timerId = setTimeout(() => {
@@ -374,16 +399,16 @@ export function useAMap(options) {
         }, 5000);
       }
     });
-    moveMarker.moveAlngg(lineArr, {
+    moveMarker.moveAlong(lineArr, {
       // 每一段的时长
       duration: 1000, //可根据实际采集时间间隔设置
-      // JSAPI2.0 是否延道路自动设置角度在 moveAlngg 里设置
+      // JSAPI2.0 是否延道路自动设置角度在 moveAlong 里设置
       autoRotation: true
     });
   };
 
   const drawData = (data) => {
-    const res = [];
+    let res = [];
     data.forEach((item) => {
       let graphic;
       if (['rectangle', 'polygon', 'anyLine'].includes(item.type)) {
@@ -460,7 +485,7 @@ export function useAMap(options) {
       } else if (item.type === 'marker') {
         // 创建标注点
         const marker = new AMap.Marker({
-          position: new AMap.LngLat(item.lnggitude, item.latitude), // 标注点的位置
+          position: new AMap.LngLat(item.longitude, item.latitude), // 标注点的位置
           icon: new AMap.Icon({
             size: item.size, //图标所处区域大小
             image: item.icon
@@ -509,7 +534,6 @@ export function useAMap(options) {
     data.id = id;
     return { text, data };
   };
-
   const convertCoordinates = (geoJson) => {
     const features = geoJson.features.map((feature) => {
       const geometry = feature.geometry;
@@ -542,10 +566,13 @@ export function useAMap(options) {
         );
         newGeometry = { ...geometry, coordinates: newCoordinates };
       } else if (geometry.type === 'Polygon') {
-        const newCoordinates = geometry.coordinates.map((ring) => {
-          const obj = wgs_gcj_encrypts(ring);
-          return obj;
-        });
+        const newCoordinates = geometry.coordinates.map((polygon) =>
+          polygon.map((coords) => {
+            const [x, y] = coords;
+            const obj = wgs_gcj_encrypts([{ lng: x, lat: y }])[0];
+            return [obj.lng, obj.lat];
+          })
+        );
         newGeometry = { ...geometry, coordinates: newCoordinates };
       } else if (geometry.type === 'MultiPolygon') {
         const newCoordinates = geometry.coordinates.map((polygon) =>
@@ -583,6 +610,7 @@ export function useAMap(options) {
     creatMask,
     removeMask,
     creatMask2,
+    removeMask2,
     trackPlayback,
     drawData
   };

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

@@ -78,7 +78,6 @@ declare global {
   const onStartTyping: typeof import('@vueuse/core')['onStartTyping']
   const onUnmounted: typeof import('vue')['onUnmounted']
   const onUpdated: typeof import('vue')['onUpdated']
-  const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
   const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
   const provide: typeof import('vue')['provide']
   const provideLocal: typeof import('@vueuse/core')['provideLocal']
@@ -190,7 +189,6 @@ declare global {
   const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
   const useGamepad: typeof import('@vueuse/core')['useGamepad']
   const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
-  const useId: typeof import('vue')['useId']
   const useIdle: typeof import('@vueuse/core')['useIdle']
   const useImage: typeof import('@vueuse/core')['useImage']
   const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll']
@@ -207,7 +205,6 @@ declare global {
   const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery']
   const useMemoize: typeof import('@vueuse/core')['useMemoize']
   const useMemory: typeof import('@vueuse/core')['useMemory']
-  const useModel: typeof import('vue')['useModel']
   const useMounted: typeof import('@vueuse/core')['useMounted']
   const useMouse: typeof import('@vueuse/core')['useMouse']
   const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement']
@@ -255,7 +252,6 @@ declare global {
   const useStyleTag: typeof import('@vueuse/core')['useStyleTag']
   const useSupported: typeof import('@vueuse/core')['useSupported']
   const useSwipe: typeof import('@vueuse/core')['useSwipe']
-  const useTemplateRef: typeof import('vue')['useTemplateRef']
   const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
   const useTextDirection: typeof import('@vueuse/core')['useTextDirection']
   const useTextSelection: typeof import('@vueuse/core')['useTextSelection']
@@ -307,7 +303,7 @@ declare global {
 // for type re-export
 declare global {
   // @ts-ignore
-  export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
+  export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
   import('vue')
 }
 // for vue template auto import
@@ -388,7 +384,6 @@ declare module 'vue' {
     readonly onStartTyping: UnwrapRef<typeof import('@vueuse/core')['onStartTyping']>
     readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
     readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
-    readonly onWatcherCleanup: UnwrapRef<typeof import('vue')['onWatcherCleanup']>
     readonly pausableWatch: UnwrapRef<typeof import('@vueuse/core')['pausableWatch']>
     readonly provide: UnwrapRef<typeof import('vue')['provide']>
     readonly provideLocal: UnwrapRef<typeof import('@vueuse/core')['provideLocal']>
@@ -500,7 +495,6 @@ declare module 'vue' {
     readonly useFullscreen: UnwrapRef<typeof import('@vueuse/core')['useFullscreen']>
     readonly useGamepad: UnwrapRef<typeof import('@vueuse/core')['useGamepad']>
     readonly useGeolocation: UnwrapRef<typeof import('@vueuse/core')['useGeolocation']>
-    readonly useId: UnwrapRef<typeof import('vue')['useId']>
     readonly useIdle: UnwrapRef<typeof import('@vueuse/core')['useIdle']>
     readonly useImage: UnwrapRef<typeof import('@vueuse/core')['useImage']>
     readonly useInfiniteScroll: UnwrapRef<typeof import('@vueuse/core')['useInfiniteScroll']>
@@ -517,7 +511,6 @@ declare module 'vue' {
     readonly useMediaQuery: UnwrapRef<typeof import('@vueuse/core')['useMediaQuery']>
     readonly useMemoize: UnwrapRef<typeof import('@vueuse/core')['useMemoize']>
     readonly useMemory: UnwrapRef<typeof import('@vueuse/core')['useMemory']>
-    readonly useModel: UnwrapRef<typeof import('vue')['useModel']>
     readonly useMounted: UnwrapRef<typeof import('@vueuse/core')['useMounted']>
     readonly useMouse: UnwrapRef<typeof import('@vueuse/core')['useMouse']>
     readonly useMouseInElement: UnwrapRef<typeof import('@vueuse/core')['useMouseInElement']>
@@ -565,7 +558,6 @@ declare module 'vue' {
     readonly useStyleTag: UnwrapRef<typeof import('@vueuse/core')['useStyleTag']>
     readonly useSupported: UnwrapRef<typeof import('@vueuse/core')['useSupported']>
     readonly useSwipe: UnwrapRef<typeof import('@vueuse/core')['useSwipe']>
-    readonly useTemplateRef: UnwrapRef<typeof import('vue')['useTemplateRef']>
     readonly useTemplateRefsList: UnwrapRef<typeof import('@vueuse/core')['useTemplateRefsList']>
     readonly useTextDirection: UnwrapRef<typeof import('@vueuse/core')['useTextDirection']>
     readonly useTextSelection: UnwrapRef<typeof import('@vueuse/core')['useTextSelection']>

+ 20 - 0
src/utils/gisUtils.ts

@@ -1,3 +1,4 @@
+import * as turf from '@turf/turf';
 //转换常数
 const x_pi = (3.14159265358979324 * 3000.0) / 180.0;
 const pi = 3.14159265358979324;
@@ -161,3 +162,22 @@ export const gcj_wgs_encrypts = (latlngs) => {
   }
   return point;
 };
+
+// 合并边界多边形 如茂名市区级边界合并为茂名市边界
+export const mergeGeoJsonPolygons = (geoJsonData) => {
+  const polygons = geoJsonData.features.map((feature) => {
+    if (feature.geometry.type === 'Polygon') {
+      return turf.polygon(feature.geometry.coordinates);
+    } else if (feature.geometry.type === 'MultiPolygon') {
+      return turf.multiPolygon(feature.geometry.coordinates);
+    }
+  });
+  if (polygons.length <= 1) {
+    return polygons[0] || null;
+  }
+  let merged = turf.union(turf.featureCollection([polygons[0], polygons[1]]));
+  for (let i = 2; i < polygons.length; i++) {
+    merged = turf.union(turf.featureCollection([merged, polygons[i]]));
+  }
+  return merged;
+};

Неке датотеке нису приказане због велике количине промена