Browse Source

高德地图 边界遮罩

Hwf 4 months ago
parent
commit
6733d31bf6
6 changed files with 476 additions and 364 deletions
  1. 362 326
      package-lock.json
  2. 1 1
      package.json
  3. 4 6
      src/components/Map/index.vue
  4. 71 31
      src/hooks/AMap/useAMap.ts
  5. 18 0
      src/types/components.d.ts
  6. 20 0
      src/utils/gisUtils.ts

File diff suppressed because it is too large
+ 362 - 326
package-lock.json


+ 1 - 1
package.json

@@ -20,7 +20,7 @@
     "@amap/amap-jsapi-loader": "^1.0.1",
     "@element-plus/icons-vue": "2.3.1",
     "@highlightjs/vue-plugin": "2.1.0",
-    "@turf/turf": "^7.0.0",
+    "@turf/turf": "^7.1.0",
     "@vueup/vue-quill": "1.2.0",
     "@vueuse/core": "^10.9.0",
     "animate.css": "4.1.1",

+ 4 - 6
src/components/Map/index.vue

@@ -128,8 +128,7 @@ const mapUtils = useAMap({
     map.on('zoomchange', zoomChangeHandler);
     // 添加遮罩
     if (props.showMask) {
-      // creatMask2(mmJson, { strokeWeight: 2 });
-      creatMask([{ strokeWeight: 2 }]);
+      creatMask2(mmJson, { strokeWeight: 2 });
     }
     drawTool.initMouseTool({ container: 'aMap', map, AMap });
     handleResize();
@@ -199,9 +198,8 @@ const {
   showInfo,
   hideInfo,
   handleHover,
-  creatMask,
   creatMask2,
-  removeMask,
+  removeMask2,
   trackPlayback
 } = { ...mapUtils };
 const handlePointDetails = (data) => {
@@ -419,9 +417,9 @@ watch(
   () => props.showMask,
   () => {
     if (props.showMask) {
-      creatMask([{ strokeWeight: 2 }]);
+      creatMask2(mmJson, { strokeWeight: 2 });
     } else {
-      removeMask();
+      removeMask2();
     }
   }
 );

+ 71 - 31
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;
@@ -267,49 +267,85 @@ export function useAMap(options) {
       });
     });
   };
+  const removeMask = () => {
+    if (!!maskPolygon) {
+      map.remove(maskPolygon);
+      maskPolygon = null;
+    }
+  };
+  let maskPolygon2 = [];
   const creatMask2 = (data, option) => {
-    // 外多边形坐标数组和内多边形坐标数组
+    data = convertCoordinates(data);
+    // 边界部分
+    data.features.forEach((feature) => {
+      if (feature.geometry.type === 'Polygon') {
+        const polygonPath = feature.geometry.coordinates[0].map(function(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 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: 0,
       strokeWeight: option.strokeWeight ? option.strokeWeight : 1,
       fillColor: option.fillColor ? option.fillColor : '#10243b',
       fillOpacity: option.fillOpacity ? option.fillOpacity : 0.65
     });
     map.add(maskPolygon);
   };
-  const removeMask = () => {
+  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) => {
@@ -529,10 +565,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) =>
@@ -570,6 +609,7 @@ export function useAMap(options) {
     creatMask,
     removeMask,
     creatMask2,
+    removeMask2,
     trackPlayback,
     drawData
   };

+ 18 - 0
src/types/components.d.ts

@@ -21,6 +21,7 @@ declare module 'vue' {
     ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
     ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
     ElButton: typeof import('element-plus/es')['ElButton']
+    ElCard: typeof import('element-plus/es')['ElCard']
     ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
     ElCol: typeof import('element-plus/es')['ElCol']
     ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
@@ -37,22 +38,34 @@ declare module 'vue' {
     ElIcon: typeof import('element-plus/es')['ElIcon']
     ElImage: typeof import('element-plus/es')['ElImage']
     ElInput: typeof import('element-plus/es')['ElInput']
+    ElLink: typeof import('element-plus/es')['ElLink']
     ElMenu: typeof import('element-plus/es')['ElMenu']
     ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
     ElOption: typeof import('element-plus/es')['ElOption']
     ElPagination: typeof import('element-plus/es')['ElPagination']
+    ElPopover: typeof import('element-plus/es')['ElPopover']
+    ElRadio: typeof import('element-plus/es')['ElRadio']
+    ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
     ElRow: typeof import('element-plus/es')['ElRow']
     ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
     ElSelect: typeof import('element-plus/es')['ElSelect']
     ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
     ElSkeletonItem: typeof import('element-plus/es')['ElSkeletonItem']
     ElSlider: typeof import('element-plus/es')['ElSlider']
+    ElStep: typeof import('element-plus/es')['ElStep']
+    ElSteps: typeof import('element-plus/es')['ElSteps']
     ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
     ElSwitch: typeof import('element-plus/es')['ElSwitch']
+    ElTable: typeof import('element-plus/es')['ElTable']
+    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
+    ElTabPane: typeof import('element-plus/es')['ElTabPane']
+    ElTabs: typeof import('element-plus/es')['ElTabs']
     ElText: typeof import('element-plus/es')['ElText']
     ElTimeline: typeof import('element-plus/es')['ElTimeline']
     ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
+    ElTooltip: typeof import('element-plus/es')['ElTooltip']
     ElTree: typeof import('element-plus/es')['ElTree']
+    ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
     ElUpload: typeof import('element-plus/es')['ElUpload']
     FileUpload: typeof import('./../components/FileUpload/index.vue')['default']
     FooterSection: typeof import('./../components/FooterSection/index.vue')['default']
@@ -63,6 +76,7 @@ declare module 'vue' {
     HikvisionPlayer: typeof import('./../components/HKVideo/hikvision-player.vue')['default']
     HKVideo: typeof import('./../components/HKVideo/index.vue')['default']
     IconSelect: typeof import('./../components/IconSelect/index.vue')['default']
+    IEpUploadFilled: typeof import('~icons/ep/upload-filled')['default']
     IFrame: typeof import('./../components/iFrame/index.vue')['default']
     ImagePreview: typeof import('./../components/ImagePreview/index.vue')['default']
     ImageUpload: typeof import('./../components/ImageUpload/index.vue')['default']
@@ -97,6 +111,10 @@ declare module 'vue' {
     VideoContainer2: typeof import('./../components/HKVideo/video-container2.vue')['default']
     VideoTagEdit: typeof import('./../components/VideoTagEdit/index.vue')['default']
     YMap: typeof import('./../components/Map/YMap.vue')['default']
+    YMapold: typeof import('./../components/Map/YMapold.vue')['default']
     YztMap: typeof import('./../components/Map/YztMap/index.vue')['default']
   }
+  export interface ComponentCustomProperties {
+    vLoading: typeof import('element-plus/es')['ElLoadingDirective']
+  }
 }

+ 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, {"fill": "#0f0"});
+    } else if (feature.geometry.type === 'MultiPolygon') {
+      return turf.multiPolygon(feature.geometry.coordinates, {"fill": "#0f0"});
+    }
+  });
+  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;
+};

Some files were not shown because too many files changed in this diff