934 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			934 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <!--
 | ||
|  * @Author: SunTao 328867980@qq.com
 | ||
|  * @Date: 2024-10-14 10:46:23
 | ||
|  * @LastEditors: SunTao 328867980@qq.com
 | ||
|  * @LastEditTime: 2024-12-23 17:37:59
 | ||
|  * @FilePath: \znxjxt-ui\src\components\map\fssm-map.vue
 | ||
|  * @Description: 公共地图
 | ||
| -->
 | ||
| 
 | ||
| <template>
 | ||
|   <div class="map-container">
 | ||
|     <div ref="container" :id="`map-${mapId}`"></div>
 | ||
|     <!-- 地图弹窗 -->
 | ||
|     <div ref="mapPopup" class="ol-popup">
 | ||
|       <div ref="popupContent"></div>
 | ||
|     </div>
 | ||
|     <div ref="mapController" :style="controlStyle" class="control-container">
 | ||
|       <!-- 绘制线段 -->
 | ||
|       <div class="draw-line" v-if="showLine">
 | ||
|         <i class="el-icon-edit" @click="drawLineMap()"></i>
 | ||
|         <i class="el-icon-delete" @click="deleteLinedraw()"></i>
 | ||
|       </div>
 | ||
|       <!-- 绘制图形 -->
 | ||
|       <div class="draw-map" v-if="showDraw">
 | ||
|         <i class="el-icon-edit-outline" @click="drawMap()"></i>
 | ||
|         <i class="el-icon-delete" @click="deletedraw()"></i>
 | ||
|       </div>
 | ||
|       <!-- 切换底图控件 -->
 | ||
|       <div class="change-map" v-if="showChange">
 | ||
|         <i
 | ||
|           class="el-icon-picture-outline"
 | ||
|           :class="mapType === 'cva_c' ? 'change-map-click' : ''"
 | ||
|           @click="changeImg('cva_c')"
 | ||
|         ></i>
 | ||
|         <i
 | ||
|           class="el-icon-picture"
 | ||
|           :class="mapType === 'img_c' ? 'change-map-click' : ''"
 | ||
|           @click="changeImg('img_c')"
 | ||
|         ></i>
 | ||
|       </div>
 | ||
|       <!-- 放大缩小控件 -->
 | ||
|       <div class="bigButton map-btn" v-if="showZoom">
 | ||
|         <i class="el-icon-plus" @click="changeZoom(1, 0.5)"></i>
 | ||
|       </div>
 | ||
|       <div class="smallButton map-btn" v-if="showZoom">
 | ||
|         <i class="el-icon-minus" @click="changeZoom(-1, 0.5)"></i>
 | ||
|       </div>
 | ||
|     </div>
 | ||
|   </div>
 | ||
| </template>
 | ||
| 
 | ||
| <script>
 | ||
| import { Feature, Map, View } from "ol";
 | ||
| import XYZ from "ol/source/XYZ";
 | ||
| import { Tile as TileLayer } from "ol/layer";
 | ||
| import { defaults as defaultControls } from "ol/control";
 | ||
| import VectorLayer from "ol/layer/Vector";
 | ||
| import { Vector as VectorSource } from "ol/source";
 | ||
| import { Draw, Modify, Select, Snap } from "ol/interaction";
 | ||
| import * as styleExports from "ol/style";
 | ||
| import { Polygon, LineString } from "ol/geom";
 | ||
| import Overlay from "ol/Overlay";
 | ||
| 
 | ||
| export default {
 | ||
|   name: "FssmMap",
 | ||
|   props: {
 | ||
|     // 接收传过来得中心点
 | ||
|     centerPoint: {
 | ||
|       type: Array,
 | ||
|       default: () => {
 | ||
|         return [123.30297096718999, 41.87942945541742];
 | ||
|       },
 | ||
|     },
 | ||
|     // 接受传过来得地图层级
 | ||
|     zoom: {
 | ||
|       type: String,
 | ||
|       default: "10",
 | ||
|     },
 | ||
|     // 是否显示绘制多边形功能
 | ||
|     showDraw: {
 | ||
|       type: Boolean,
 | ||
|       default: false,
 | ||
|     },
 | ||
|     // 是否显示绘制线段功能
 | ||
|     showLine: {
 | ||
|       type: Boolean,
 | ||
|       default: false,
 | ||
|     },
 | ||
|     // 是否显示地图放大缩小
 | ||
|     showZoom: {
 | ||
|       type: Boolean,
 | ||
|       default: false,
 | ||
|     },
 | ||
|     // 是否显示切换地图底层按钮
 | ||
|     showChange: {
 | ||
|       type: Boolean,
 | ||
|       default: false,
 | ||
|     },
 | ||
|     // 地图控件位置
 | ||
|     controlStyle: {
 | ||
|       type: Object,
 | ||
|       default: () => {
 | ||
|         return {
 | ||
|           top: 0,
 | ||
|         };
 | ||
|       },
 | ||
|     },
 | ||
|     // 用于区分同一个页面多个地图的id
 | ||
|     mapId: {
 | ||
|       type: String,
 | ||
|       default: "0",
 | ||
|     },
 | ||
|     // 接受传过来的绘制点位数组
 | ||
|     editCoordinates: {
 | ||
|       type: Array,
 | ||
|       default: () => [],
 | ||
|     },
 | ||
|     // 接收传过来的绘制线段数组
 | ||
|     editCoordinatesLine: {
 | ||
|       type: Array,
 | ||
|       default: () => [],
 | ||
|     },
 | ||
|     // 接收传过来的底图类型
 | ||
|     baseMap: {
 | ||
|       type: String,
 | ||
|       default: "cva_c",
 | ||
|     },
 | ||
|   },
 | ||
|   data() {
 | ||
|     return {
 | ||
|       // 地图实例
 | ||
|       instance: new Map(),
 | ||
|       // 点击地图类型
 | ||
|       mapType: "cva_c",
 | ||
|       // 地图图层
 | ||
|       mapLayers: [],
 | ||
|       // 绘制多边形图层
 | ||
|       draw: null,
 | ||
|       // 绘制线段图层
 | ||
|       drawLine: null,
 | ||
|       // 绘制图层源
 | ||
|       source: new VectorSource(),
 | ||
|       // 绘制线段图层源
 | ||
|       sourceLine: new VectorSource(),
 | ||
|       // 当前地图层级
 | ||
|       sendZoom: "",
 | ||
|       // 当前选中得图层
 | ||
|       selectSingClick: null,
 | ||
|       // 绘制图形保存的点位
 | ||
|       drawMarkers: [],
 | ||
|       // 绘制多边形图层layer
 | ||
|       drawLayer: null,
 | ||
|       // 绘制线段保存的点位
 | ||
|       drawLineMarkers: [],
 | ||
|       // 绘制线段图层layer
 | ||
|       drawLineLayer: null,
 | ||
|       // 绘制弹窗图层
 | ||
|       overlayDialog: null,
 | ||
|     };
 | ||
|   },
 | ||
|   watch: {
 | ||
|     /* 监听传过来的坐标区域 */
 | ||
|     editCoordinates: {
 | ||
|       handler(val) {
 | ||
|         this.$nextTick(() => {
 | ||
|           const aa = val.map((item) => {
 | ||
|             return [item[0] * 1, item[1] * 1];
 | ||
|           });
 | ||
|           this.drawMarkers = aa;
 | ||
|           const map = this.instance.get("map");
 | ||
|           // 增加snap吸附功能 ,还有就是snap交互可以在编辑和画features时候保持拓扑结构
 | ||
|           const snap = new Snap({
 | ||
|             source: this.source,
 | ||
|           });
 | ||
|           map.addInteraction(snap);
 | ||
|           // 添加修改元素使得绘制的多边形可以编辑
 | ||
|           const modify = new Modify({ source: this.source });
 | ||
|           map.addInteraction(modify);
 | ||
|           // 增加绘图层
 | ||
|           this.drawLayer = new VectorLayer({
 | ||
|             source: this.source,
 | ||
|             id: "draw",
 | ||
|             style: new styleExports.Style({
 | ||
|               stroke: new styleExports.Stroke({
 | ||
|                 color: "blue",
 | ||
|                 size: 2,
 | ||
|               }),
 | ||
|             }),
 | ||
|           });
 | ||
|           const feature = new Feature({
 | ||
|             geometry: new Polygon([aa]),
 | ||
|           });
 | ||
|           this.drawLayer.getSource().addFeature(feature);
 | ||
|           map.addLayer(this.drawLayer);
 | ||
|           modify.on("modifyend", (e) => {
 | ||
|             e.features.forEach((feature) => {
 | ||
|               this.drawMarkers = feature.getGeometry().getCoordinates().flat();
 | ||
|               this.$emit("endEoordinate", this.drawMarkers);
 | ||
|             });
 | ||
|           });
 | ||
|         });
 | ||
|       },
 | ||
|       deep: true,
 | ||
|       immediate: true,
 | ||
|     },
 | ||
| 
 | ||
|     /* 监听传过来的绘制线段坐标 */
 | ||
|     editCoordinatesLine: {
 | ||
|       handler(val) {
 | ||
|         this.$nextTick(() => {
 | ||
|           const aa = val.map((item) => {
 | ||
|             return [item[0] * 1, item[1] * 1];
 | ||
|           });
 | ||
|           this.drawLineMarkers = aa;
 | ||
|           const map = this.instance.get("map");
 | ||
|           // 增加snap吸附功能 ,还有就是snap交互可以在编辑和画features时候保持拓扑结构
 | ||
|           const snap = new Snap({
 | ||
|             source: this.sourceLine,
 | ||
|           });
 | ||
|           map.addInteraction(snap);
 | ||
|           // 添加修改元素使得绘制的多边形可以编辑
 | ||
|           const modify = new Modify({ source: this.sourceLine });
 | ||
|           map.addInteraction(modify);
 | ||
|           // 增加绘图层
 | ||
|           this.drawLineLayer = new VectorLayer({
 | ||
|             source: this.sourceLine,
 | ||
|             id: "drawLine",
 | ||
|             style: new styleExports.Style({
 | ||
|               stroke: new styleExports.Stroke({
 | ||
|                 color: "red",
 | ||
|                 size: 4,
 | ||
|               }),
 | ||
|             }),
 | ||
|           });
 | ||
|           const feature = new Feature({
 | ||
|             geometry: new LineString(aa),
 | ||
|           });
 | ||
|           this.drawLineLayer.getSource().addFeature(feature);
 | ||
|           map.addLayer(this.drawLineLayer);
 | ||
|           modify.on("modifyend", (e) => {
 | ||
|             e.features.forEach((feature) => {
 | ||
|               this.drawLineMarkers = feature.getGeometry().getCoordinates();
 | ||
|               this.$emit("endEoordinateLine", this.drawLineMarkers);
 | ||
|             });
 | ||
|           });
 | ||
|         });
 | ||
|       },
 | ||
|       immediate: true,
 | ||
|       deep: true,
 | ||
|     },
 | ||
| 
 | ||
|     /* 监听传过来的底图类型 */
 | ||
|     baseMap: {
 | ||
|       handler(val) {
 | ||
|         this.changeImg(val);
 | ||
|       },
 | ||
|       immediate: true,
 | ||
|       deep: true,
 | ||
|     },
 | ||
|   },
 | ||
|   mounted() {
 | ||
|     this.initMap();
 | ||
|   },
 | ||
|   created() {
 | ||
|     // this.changeImg("cva_c");
 | ||
|   },
 | ||
|   methods: {
 | ||
|     /**
 | ||
|      * @description: 初始化openlayer地图
 | ||
|      * @return {*}
 | ||
|      */
 | ||
|     initMap() {
 | ||
|       // 内网
 | ||
|       // const tianditu_vec_c = new TileLayer({
 | ||
|       //   className: "baseLayerClass",
 | ||
|       //   title: "矢量底图",
 | ||
|       //   id: "vec_c",
 | ||
|       //   source: new XYZ({
 | ||
|       //     url: "http://10.60.5.242:25033/v3/tile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}",
 | ||
|       //     // projection: "EPSG:4326",
 | ||
|       //   }),
 | ||
|       //   visible: true,
 | ||
|       // });
 | ||
|       // const tianditu_cva_c = new TileLayer({
 | ||
|       //   className: "baseLayerClass",
 | ||
|       //   title: "矢量地图",
 | ||
|       //   id: "cva_c",
 | ||
|       //   source: new XYZ({
 | ||
|       //     url: "http://10.60.5.242:25003/v3/tile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}",
 | ||
|       //     // projection: "EPSG:4326",
 | ||
|       //   }),
 | ||
|       //   visible: true,
 | ||
|       // });
 | ||
|       // 外网
 | ||
|       const tianditu_cva_c = new TileLayer({
 | ||
|         className: "baseLayerClass",
 | ||
|         title: "矢量地图",
 | ||
|         id: "cva_c",
 | ||
|         source: new XYZ({
 | ||
|           url: "https://webrd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}",
 | ||
|           // projection: "EPSG:4326",
 | ||
|         }),
 | ||
|         visible: true,
 | ||
|       });
 | ||
|       const tianditu_img_c = new TileLayer({
 | ||
|         title: "影像地图",
 | ||
|         id: "img_c",
 | ||
|         source: new XYZ({
 | ||
|           url: "https://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
 | ||
|           // projection: "EPSG:4326",
 | ||
|         }),
 | ||
|         visible: false,
 | ||
|       });
 | ||
|       // 弹窗图层
 | ||
|       const overlays = new Overlay({
 | ||
|         element: this.$refs.mapPopup,
 | ||
|         autoPan: {
 | ||
|           animation: {
 | ||
|             duration: 250,
 | ||
|           },
 | ||
|         },
 | ||
|       });
 | ||
|       const map = new Map({
 | ||
|         target: `map-${this.mapId}`,
 | ||
|         controls: defaultControls({
 | ||
|           zoom: false,
 | ||
|           attribution: false,
 | ||
|           rotate: false,
 | ||
|         }),
 | ||
|         // 弹窗图层数组
 | ||
|         overlays: [overlays],
 | ||
|         view: new View({
 | ||
|           center: this.centerPoint, //中心点经纬度
 | ||
|           zoom: this.zoom, //图层缩放大小
 | ||
|           projection: "EPSG:4326",
 | ||
|           minZoom: 7,
 | ||
|           maxZoom: 18,
 | ||
|         }),
 | ||
|         // 内网
 | ||
|         // layers: [tianditu_cva_c, tianditu_vec_c, tianditu_img_c],
 | ||
|         // 外网
 | ||
|         layers: [tianditu_cva_c, tianditu_img_c],
 | ||
|       });
 | ||
|       // 图层点击事件
 | ||
|       map.on("singleclick", (e) => {
 | ||
|         const featureClick = map.forEachFeatureAtPixel(
 | ||
|           map.getEventPixel(e.originalEvent),
 | ||
|           (feature) => {
 | ||
|             return feature;
 | ||
|           }
 | ||
|         );
 | ||
|         // 如果有图层则返回图层点击
 | ||
|         if (featureClick) {
 | ||
|           this.$emit("feature-click", featureClick);
 | ||
|         } else {
 | ||
|           // 没有图层则返回地图点击事件
 | ||
|           this.$emit("map-click", e);
 | ||
|         }
 | ||
|       });
 | ||
|       // 图层双击事件
 | ||
|       map.on("dblclick", (e) => {
 | ||
|         const featureDblclick = map.forEachFeatureAtPixel(
 | ||
|           map.getEventPixel(e.originalEvent),
 | ||
|           (feature) => {
 | ||
|             return feature;
 | ||
|           }
 | ||
|         );
 | ||
|         if (featureDblclick) {
 | ||
|           this.$emit("feature-dblclick", featureDblclick);
 | ||
|         }
 | ||
|       });
 | ||
|       // 鼠标移入事件
 | ||
|       map.on("pointermove", (e) => {
 | ||
|         const feature = map.forEachFeatureAtPixel(
 | ||
|           map.getEventPixel(e.originalEvent),
 | ||
|           (mapFeature) => {
 | ||
|             return mapFeature;
 | ||
|           }
 | ||
|         );
 | ||
|         // 获取弹窗图层
 | ||
|         const [dislogLay] = map.getOverlays().getArray();
 | ||
|         // 打印或处理经纬度
 | ||
|         // this.$emit("pointer-move", { feature, coordinate });
 | ||
|         if (feature && feature.getGeometry().getType() === "LineString") {
 | ||
|           // 获取鼠标当前位置的像素坐标
 | ||
|           const pixel = e.pixel;
 | ||
|           // 将像素坐标转换为地理坐标(经纬度)
 | ||
|           const coordinate = map.getCoordinateFromPixel(pixel);
 | ||
|           // 获取弹窗数据
 | ||
|           const popupData = feature.get("data");
 | ||
|           // 获取弹窗内元素并赋值
 | ||
|           this.$refs.popupContent.innerHTML = `<span>路段名称:</span><span>${popupData.name}</span><br/>
 | ||
|           <span>病害数:</span><span>${popupData.count}个</span><br/>
 | ||
|           <span>起始桩号:</span><span>${popupData.stakeStart}</span><br/>
 | ||
|           <span>终止桩号:</span><span>${popupData.stakeEnd}</span>`;
 | ||
|           dislogLay.setPosition(coordinate);
 | ||
|         } else {
 | ||
|           dislogLay.setPosition(undefined);
 | ||
|         }
 | ||
|         // 线、面要素不做鼠标移入样式修改
 | ||
|         // if (feature) {
 | ||
|         //   if (feature.getGeometry()?.getType() === "Point") {
 | ||
|         //     map.getTargetElement().style.cursor = "pointer";
 | ||
|         //     this.$emit("pointer-move", feature);
 | ||
|         //   } else {
 | ||
|         //     map.getTargetElement().style.cursor = "auto";
 | ||
|         //   }
 | ||
|         // } else {
 | ||
|         //   map.getTargetElement().style.cursor = "auto";
 | ||
|         // }
 | ||
|       });
 | ||
| 
 | ||
|       // 地图缩放级别事件
 | ||
|       map.on("moveend", (e) => {
 | ||
|         const zoom = map.getView().getZoom().toFixed(); //获取当前地图的缩放级别
 | ||
|         this.$emit("map-moveend", zoom);
 | ||
|       });
 | ||
|       // 图层选择事件
 | ||
|       this.selectSingClick = new Select();
 | ||
|       map.addInteraction(this.selectSingClick);
 | ||
|       this.selectSingClick.on("select", (e) => {
 | ||
|         this.$emit("feature-select", e);
 | ||
|         // let selectedFeatures = e.selected;
 | ||
|         // if (selectedFeatures.length > 0) {
 | ||
|         //   let feature = selectedFeatures[0];
 | ||
|         //   let features = feature.get("features");
 | ||
|         //   console.log(feature.getProperties(),'fff');
 | ||
|         //   if(feature.getProperties().type!=="line"){
 | ||
| 
 | ||
|         //   }
 | ||
|         //   if (features.length === 1) {
 | ||
|         //     // 单个点位
 | ||
|         //     // 执行之前的业务逻辑
 | ||
|         //     // 获取点击的图层信息
 | ||
|         //     const selectFeature = feature.getProperties().features[0];
 | ||
|         //   } else {
 | ||
|         //     // 聚合点
 | ||
|         //     // 放大地图层级
 | ||
|         //     map.getView().animate({
 | ||
|         //       center: feature.getGeometry().getCoordinates(),
 | ||
|         //       zoom: map.getView().getZoom() + 1,
 | ||
|         //     });
 | ||
|         //   }
 | ||
|         // }
 | ||
|       });
 | ||
|       // 图层缩放事件监听
 | ||
|       map.getView().on("change:resolution", () => {
 | ||
|         if (this.sendZoom !== map.getView().getZoom().toFixed()) {
 | ||
|           this.sendZoom = map.getView().getZoom().toFixed();
 | ||
|           this.$emit("map-zoom", this.sendZoom);
 | ||
|         }
 | ||
|       });
 | ||
|       this.instance.set("map", map);
 | ||
|       this.instance.set("overlay-list", []);
 | ||
|     },
 | ||
| 
 | ||
|     /**
 | ||
|      * @description: 删除图层选择对象
 | ||
|      * @return {*}
 | ||
|      */
 | ||
|     removeSelectClick() {
 | ||
|       this.selectSingClick.getFeatures().clear();
 | ||
|     },
 | ||
| 
 | ||
|     /**
 | ||
|      * @description: 绘制多边形地图
 | ||
|      * @return {*}
 | ||
|      */
 | ||
|     drawMap() {
 | ||
|       if (this.drawMarkers.length < 1) {
 | ||
|         const map = this.instance.get("map");
 | ||
|         // 增加绘图层
 | ||
|         this.drawLayer = new VectorLayer({
 | ||
|           source: this.source,
 | ||
|           id: "draw",
 | ||
|         });
 | ||
|         map.addLayer(this.drawLayer);
 | ||
|         // 增加snap吸附功能 ,还有就是snap交互可以在编辑和画features时候保持拓扑结构
 | ||
|         const snap = new Snap({
 | ||
|           source: this.source,
 | ||
|         });
 | ||
|         map.addInteraction(snap);
 | ||
|         // 添加修改元素使得绘制的多边形可以编辑
 | ||
|         const modify = new Modify({ source: this.source });
 | ||
|         map.addInteraction(modify);
 | ||
|         this.draw = new Draw({
 | ||
|           source: this.source,
 | ||
|           type: "Polygon",
 | ||
|           style: new styleExports.Style({
 | ||
|             image: new styleExports.Circle({
 | ||
|               radius: 5,
 | ||
|               fill: new styleExports.Fill({
 | ||
|                 color: "blue",
 | ||
|               }),
 | ||
|             }),
 | ||
|             stroke: new styleExports.Stroke({
 | ||
|               color: "blue",
 | ||
|               width: 2,
 | ||
|             }),
 | ||
|             fill: new styleExports.Fill({
 | ||
|               color: "rgba(0, 0, 255, 0.2)",
 | ||
|             }),
 | ||
|           }),
 | ||
|         });
 | ||
|         map.addInteraction(this.draw);
 | ||
|         this.draw.on("drawend", (e) => {
 | ||
|           this.drawend(e);
 | ||
|         });
 | ||
|         modify.on("modifyend", (e) => {
 | ||
|           e.features.forEach((feature) => {
 | ||
|             this.drawMarkers = feature.getGeometry().getCoordinates().flat();
 | ||
|             this.$emit("endEoordinate", this.drawMarkers);
 | ||
|           });
 | ||
|         });
 | ||
|       }
 | ||
|     },
 | ||
| 
 | ||
|     /**
 | ||
|      * @description: 监听绘制多边形完成的事件
 | ||
|      * @param {*} event
 | ||
|      * @return {*}
 | ||
|      */
 | ||
|     drawend(event) {
 | ||
|       const feature = event.feature;
 | ||
|       const geometry = feature.getGeometry();
 | ||
|       // 获取区域点位
 | ||
|       const coordinates = geometry.getCoordinates()[0];
 | ||
|       this.drawMarkers = coordinates;
 | ||
|       // 在 drawend 结束后检查是否有交叉的线段
 | ||
|       if (coordinates.length > 3 && this.isSelfCrossing(coordinates)) {
 | ||
|         this.$modal.msgWarning("线段交叉,请重新绘制");
 | ||
|         this.$nextTick(() => {
 | ||
|           this.deletedraw();
 | ||
|           this.drawMap();
 | ||
|         });
 | ||
|       } else {
 | ||
|         const map = this.instance.get("map");
 | ||
|         map.removeInteraction(this.draw);
 | ||
|       }
 | ||
|       this.$emit("endEoordinate", this.drawMarkers);
 | ||
|     },
 | ||
| 
 | ||
|     /**
 | ||
|      * @description: 绘制线段地图
 | ||
|      * @return {*}
 | ||
|      */
 | ||
|     drawLineMap() {
 | ||
|       if (this.drawLineMarkers.length < 1) {
 | ||
|         const map = this.instance.get("map");
 | ||
|         // 增加绘图层
 | ||
|         this.drawLineLayer = new VectorLayer({
 | ||
|           source: this.sourceLine,
 | ||
|           id: "drawLine",
 | ||
|         });
 | ||
|         map.addLayer(this.drawLineLayer);
 | ||
|         // 增加snap吸附功能 ,还有就是snap交互可以在编辑和画features时候保持拓扑结构
 | ||
|         const snap = new Snap({
 | ||
|           source: this.sourceLine,
 | ||
|         });
 | ||
|         map.addInteraction(snap);
 | ||
|         // 添加修改元素使得绘制的多边形可以编辑
 | ||
|         const modify = new Modify({ source: this.sourceLine });
 | ||
|         map.addInteraction(modify);
 | ||
|         this.drawLine = new Draw({
 | ||
|           source: this.sourceLine,
 | ||
|           type: "LineString",
 | ||
|           style: new styleExports.Style({
 | ||
|             image: new styleExports.Circle({
 | ||
|               radius: 5,
 | ||
|               fill: new styleExports.Fill({
 | ||
|                 color: "blue",
 | ||
|               }),
 | ||
|             }),
 | ||
|             stroke: new styleExports.Stroke({
 | ||
|               color: "blue",
 | ||
|               width: 2,
 | ||
|             }),
 | ||
|             fill: new styleExports.Fill({
 | ||
|               color: "rgba(0, 0, 255, 0.2)",
 | ||
|             }),
 | ||
|           }),
 | ||
|         });
 | ||
|         map.addInteraction(this.drawLine);
 | ||
|         this.drawLine.on("drawend", (e) => {
 | ||
|           this.drawLineend(e);
 | ||
|         });
 | ||
|         modify.on("modifyend", (e) => {
 | ||
|           e.features.forEach((feature) => {
 | ||
|             this.drawLineMarkers = feature.getGeometry().getCoordinates();
 | ||
|             this.$emit("endEoordinateLine", this.drawLineMarkers);
 | ||
|           });
 | ||
|         });
 | ||
|       }
 | ||
|     },
 | ||
| 
 | ||
|     /**
 | ||
|      * @description: 监听绘制线段完成的事件
 | ||
|      * @param {*} e
 | ||
|      * @return {*}
 | ||
|      */
 | ||
|     drawLineend(e) {
 | ||
|       const feature = e.feature;
 | ||
|       const geometry = feature.getGeometry();
 | ||
|       // 获取区域点位
 | ||
|       const coordinates = geometry.getCoordinates();
 | ||
|       this.drawLineMarkers = coordinates;
 | ||
|       this.$emit("endEoordinateLine", this.drawLineMarkers);
 | ||
|       const map = this.instance.get("map");
 | ||
|       map.removeInteraction(this.drawLine);
 | ||
|     },
 | ||
| 
 | ||
|     /**
 | ||
|      * @description: 检测交叉点位方法
 | ||
|      * @param {*} coordinates
 | ||
|      * @return {*}
 | ||
|      */
 | ||
|     isSelfCrossing(coordinates) {
 | ||
|       for (let i = 0; i < coordinates.length - 1; i++) {
 | ||
|         const segment1 = [coordinates[i], coordinates[i + 1]];
 | ||
|         for (let j = i + 2; j < coordinates.length - 1; j++) {
 | ||
|           const segment2 = [coordinates[j], coordinates[j + 1]];
 | ||
| 
 | ||
|           if (this.doSegmentsCross(segment1, segment2)) {
 | ||
|             return true;
 | ||
|           }
 | ||
|         }
 | ||
|       }
 | ||
|       return false;
 | ||
|     },
 | ||
| 
 | ||
|     /**
 | ||
|      * @description: 检测交叉线段
 | ||
|      * @param {*} segment1
 | ||
|      * @param {*} segment2
 | ||
|      * @return {*}
 | ||
|      */
 | ||
|     doSegmentsCross(segment1, segment2) {
 | ||
|       const [p1, p2] = segment1;
 | ||
|       const [q1, q2] = segment2;
 | ||
| 
 | ||
|       function crossProduct(p, q) {
 | ||
|         return p[0] * q[1] - p[1] * q[0];
 | ||
|       }
 | ||
| 
 | ||
|       function subtractPoints(p, q) {
 | ||
|         return [p[0] - q[0], p[1] - q[1]];
 | ||
|       }
 | ||
| 
 | ||
|       const r = subtractPoints(p2, p1);
 | ||
|       const s = subtractPoints(q2, q1);
 | ||
|       const uNumerator = crossProduct(subtractPoints(q1, p1), r);
 | ||
|       const denominator = crossProduct(r, s);
 | ||
| 
 | ||
|       if (denominator === 0) {
 | ||
|         return false; // 线段平行或共线
 | ||
|       }
 | ||
| 
 | ||
|       const u = uNumerator / denominator;
 | ||
|       const t = crossProduct(subtractPoints(q1, p1), s) / denominator;
 | ||
| 
 | ||
|       // 判断是否相交并且不是共线
 | ||
|       return t > 0 && t < 1 && u > 0 && u < 1;
 | ||
|     },
 | ||
| 
 | ||
|     /**
 | ||
|      * @description: 删除绘制多边形功能
 | ||
|      * @return {*}
 | ||
|      */
 | ||
|     deletedraw() {
 | ||
|       const map = this.instance.get("map");
 | ||
|       map.removeInteraction(this.draw);
 | ||
|       this.drawLayer.getSource().clear();
 | ||
|       map.removeLayer(this.drawLayer);
 | ||
|       this.draw = null;
 | ||
|       this.source = new VectorSource();
 | ||
|       this.drawMarkers = [];
 | ||
|       this.$emit("endEoordinate", []);
 | ||
|     },
 | ||
| 
 | ||
|     /**
 | ||
|      * @description: 删除绘制线段功能
 | ||
|      * @return {*}
 | ||
|      */
 | ||
|     deleteLinedraw() {
 | ||
|       const map = this.instance.get("map");
 | ||
|       map.removeInteraction(this.drawLine);
 | ||
|       this.drawLineLayer.getSource().clear();
 | ||
|       map.removeLayer(this.drawLineLayer);
 | ||
|       this.drawLine = null;
 | ||
|       this.source = new VectorSource();
 | ||
|       this.drawLineMarkers = [];
 | ||
|       this.$emit("endEoordinateLine", []);
 | ||
|     },
 | ||
| 
 | ||
|     /**
 | ||
|      * @description: 修改地图底图样式
 | ||
|      * @param {*} item
 | ||
|      * @return {*}
 | ||
|      */
 | ||
|     changeImg(item) {
 | ||
|       if (this.mapType !== item) {
 | ||
|         this.mapType = item;
 | ||
|         this.$nextTick(() => {
 | ||
|           if (item === "img_c") {
 | ||
|             const layer = this.instance
 | ||
|               .get("map")
 | ||
|               .getAllLayers()
 | ||
|               .filter((itemInfo) => {
 | ||
|                 return (
 | ||
|                   itemInfo.get("id") === "cva_c" ||
 | ||
|                   itemInfo.get("id") === "vec_c"
 | ||
|                 );
 | ||
|               });
 | ||
|             layer.forEach((it) => {
 | ||
|               it.setVisible(false);
 | ||
|             });
 | ||
|             const layerDisabled = this.instance
 | ||
|               .get("map")
 | ||
|               .getAllLayers()
 | ||
|               .filter((itemInfo) => {
 | ||
|                 return itemInfo.get("id") === "img_c";
 | ||
|               });
 | ||
|             layerDisabled.forEach((it) => {
 | ||
|               it.setVisible(true);
 | ||
|             });
 | ||
|           } else if (item === "cva_c") {
 | ||
|             const layer = this.instance
 | ||
|               .get("map")
 | ||
|               .getAllLayers()
 | ||
|               .filter((itemInfo) => {
 | ||
|                 return (
 | ||
|                   itemInfo.get("id") === "cva_c" ||
 | ||
|                   itemInfo.get("id") === "vec_c"
 | ||
|                 );
 | ||
|               });
 | ||
|             layer.forEach((it) => {
 | ||
|               it.setVisible(true);
 | ||
|             });
 | ||
|             const layerDisabled = this.instance
 | ||
|               .get("map")
 | ||
|               .getAllLayers()
 | ||
|               .filter((itemInfo) => {
 | ||
|                 return itemInfo.get("id") === "img_c";
 | ||
|               });
 | ||
|             layerDisabled.forEach((it) => {
 | ||
|               it.setVisible(false);
 | ||
|             });
 | ||
|           }
 | ||
|         });
 | ||
|       }
 | ||
|     },
 | ||
| 
 | ||
|     /**
 | ||
|      * @description: 切换当前位置
 | ||
|      * @param {Array} position 中心点位置
 | ||
|      * @param {number} zoom 地图缩放等级
 | ||
|      * @param {number} rotation 地图旋转角度
 | ||
|      * @param {number} duration 地图垂直深度
 | ||
|      * @return
 | ||
|      */
 | ||
|     changePosition(position, zoom, rotation, duration) {
 | ||
|       const map = this.instance.get("map");
 | ||
|       if (map) {
 | ||
|         map.getView().animate({
 | ||
|           center: position, // 地图中心点位
 | ||
|           zoom: zoom || 12, // 地图缩放等级
 | ||
|           rotation: rotation || undefined, // 旋转角度
 | ||
|           duration: duration || 1000, // 动画加载时间
 | ||
|         });
 | ||
|       }
 | ||
|     },
 | ||
| 
 | ||
|     /**
 | ||
|      * @description: 清空地图图层
 | ||
|      * @return
 | ||
|      */
 | ||
|     clearMapFeature() {
 | ||
|       const map = this.instance.get("map");
 | ||
|       const [layer] = map.getAllLayers().filter((item) => item.get("type"));
 | ||
|       map.removeLayer(layer);
 | ||
|     },
 | ||
| 
 | ||
|     /**
 | ||
|      * @description: 根据类型清除地图线段图层
 | ||
|      * @return {*}
 | ||
|      */
 | ||
|     clearMapLine() {
 | ||
|       const map = this.instance.get("map");
 | ||
|       const [layer] = map
 | ||
|         .getAllLayers()
 | ||
|         .filter((item) => item.get("type") === "line");
 | ||
|       map.removeLayer(layer);
 | ||
|     },
 | ||
| 
 | ||
|     /**
 | ||
|      * @description: 调整地图缩放等级
 | ||
|      * @return
 | ||
|      */
 | ||
|     changeZoom(type = 1, zoomStep) {
 | ||
|       const map = this.instance.get("map");
 | ||
|       if (map) {
 | ||
|         const step = zoomStep || 0.5;
 | ||
|         // 获取当前地图缩放等级
 | ||
|         const currentZoom = map.getView().getZoom();
 | ||
|         map.getView().animate({
 | ||
|           zoom: currentZoom + step * type,
 | ||
|         });
 | ||
|       }
 | ||
|     },
 | ||
|   },
 | ||
| };
 | ||
| </script>
 | ||
| 
 | ||
| <style lang="scss" scoped>
 | ||
| .map-container {
 | ||
|   width: 100%;
 | ||
|   height: 100%;
 | ||
|   position: relative;
 | ||
| 
 | ||
|   #map-0 {
 | ||
|     width: 100%;
 | ||
|     height: 100%;
 | ||
|   }
 | ||
| 
 | ||
|   #map-1 {
 | ||
|     width: 100%;
 | ||
|     height: 100%;
 | ||
|   }
 | ||
| 
 | ||
|   // 地图弹窗样式
 | ||
|   .ol-popup{
 | ||
|     width:10rem;
 | ||
|     position: absolute;
 | ||
|     margin: 0.5rem;
 | ||
| 
 | ||
|     > div {
 | ||
|       padding: 0.5rem;
 | ||
|       box-sizing: border-box;
 | ||
|       background: rgba(79, 79, 79, 0.7);
 | ||
|       border-radius: 0.2rem;
 | ||
|       border: 1px solid #3976f1;
 | ||
|       font-size: 0.7rem;
 | ||
|       color: #ffffff;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   // 放大缩小控件
 | ||
|   .control-container {
 | ||
|     display: flex;
 | ||
|     flex-direction: column;
 | ||
|     justify-content: center;
 | ||
|     align-items: center;
 | ||
|     position: absolute;
 | ||
|     // left: 1rem;
 | ||
|     // top: 1rem;
 | ||
|     .draw-line {
 | ||
|       width: 1.5rem;
 | ||
|       height: 3rem;
 | ||
|       background-color: rgb(198, 216, 216);
 | ||
|       display: flex;
 | ||
|       align-items: center;
 | ||
|       justify-content: center;
 | ||
|       flex-direction: column;
 | ||
| 
 | ||
|       i {
 | ||
|         cursor: pointer;
 | ||
|         padding: 0.1rem 0;
 | ||
|         font-size: 1.2rem;
 | ||
|       }
 | ||
| 
 | ||
|       .change-map-click {
 | ||
|         background-color: rgb(240, 240, 240);
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     .draw-map {
 | ||
|       width: 1.5rem;
 | ||
|       height: 3rem;
 | ||
|       background-color: rgb(198, 216, 216);
 | ||
|       display: flex;
 | ||
|       align-items: center;
 | ||
|       justify-content: center;
 | ||
|       flex-direction: column;
 | ||
| 
 | ||
|       i {
 | ||
|         cursor: pointer;
 | ||
|         padding: 0.1rem 0;
 | ||
|         font-size: 1.2rem;
 | ||
|       }
 | ||
| 
 | ||
|       .change-map-click {
 | ||
|         background-color: rgb(240, 240, 240);
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     .change-map {
 | ||
|       display: flex;
 | ||
|       align-items: center;
 | ||
|       justify-content: center;
 | ||
|       flex-direction: column;
 | ||
|       width: 1.5rem;
 | ||
|       height: 3rem;
 | ||
|       background-color: rgb(198, 216, 216);
 | ||
| 
 | ||
|       i {
 | ||
|         cursor: pointer;
 | ||
|         padding: 0.1rem 0;
 | ||
|         font-size: 1.2rem;
 | ||
|       }
 | ||
| 
 | ||
|       .change-map-click {
 | ||
|         background-color: rgb(240, 240, 240);
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     .map-btn {
 | ||
|       width: 1.5rem;
 | ||
|       background-color: rgb(198, 216, 216);
 | ||
|       height: 1.5rem;
 | ||
|       display: flex;
 | ||
|       margin: 0.1rem 0;
 | ||
|       align-items: center;
 | ||
|       justify-content: center;
 | ||
| 
 | ||
|       i {
 | ||
|         cursor: pointer;
 | ||
|         padding: 0.1rem 0;
 | ||
|         font-size: 1.2rem;
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| }
 | ||
| </style>
 |