xjsaas-ui/src/components/map/fssm-map.vue
2024-10-17 10:38:27 +08:00

549 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!--
* @Author: SunTao 328867980@qq.com
* @Date: 2024-10-14 10:46:23
* @LastEditors: SunTao 328867980@qq.com
* @LastEditTime: 2024-10-17 10:33:59
* @FilePath: \znxjxt-ui\src\views\xj\inspection\task-management\components\fssm-map.vue
* @Description: 公共地图
-->
<template>
<div class="map-container">
<div ref="container" :id="`map-${mapId}`"></div>
<div ref="mapController" class="control-container">
<!-- 绘制图形 -->
<div class="draw-map" v-if="showDraw">
<i class="el-icon-edit" @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,
defaults as defaultInteractions,
} from "ol/interaction";
import * as styleExports from "ol/style";
import { Polygon } from "ol/geom";
export default {
name: "FssmMap",
props: {
// 接收传过来得中心点
centerPoint: {
type: Object,
default: () => {
return { lat: 123.30297096718999, lon: 41.87942945541742 };
},
},
// 接受传过来得地图层级
zoom: {
type: String,
default: "9",
},
// 是否显示绘图功能
showDraw: {
type: Boolean,
default: false,
},
// 是否显示地图放大缩小
showZoom: {
type: Boolean,
default: false,
},
// 是否显示切换地图底层按钮
showChange: {
type: Boolean,
default: false,
},
// 用于区分同一个页面多个地图的id
mapId: {
type: String,
default: "0",
},
// 接受传过来的绘制点位数组
editCoordinates: {
type: Array,
default: () => [],
},
},
data() {
return {
// 地图实例
instance: new Map(),
// 点击地图类型
mapType: "cva_c",
// 地图图层
mapLayers: [],
// 绘制图层
draw: null,
source: new VectorSource(),
// 绘制图形保存的点位
drawMarkers: [],
};
},
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");
// 增加绘图层
const drawLayer = new VectorLayer({
source: this.source,
id: "draw",
});
const feature = new Feature({
geometry: new Polygon([aa]),
});
drawLayer.getSource().addFeature(feature);
map.addLayer(drawLayer);
});
},
deep: true,
immediate: true,
},
},
mounted() {
this.initMap();
},
created() {
// this.changeImg("cva_c");
},
methods: {
/* 初始化openlayer地图 */
initMap() {
const tianditu_vec_c = new TileLayer({
title: "矢量底图",
id: "vec_c",
source: new XYZ({
url: "http://t{0-7}.tianditu.com/DataServer?x={x}&y={y}&l={z}&T=vec_c&tk=c691040443c68cda625755c5c3e2acc3",
projection: "EPSG:4326",
}),
visible: true,
});
const tianditu_cva_c = new TileLayer({
title: "矢量地图",
id: "cva_c",
source: new XYZ({
url: "http://t{0-7}.tianditu.com/DataServer?x={x}&y={y}&l={z}&T=cva_c&tk=c691040443c68cda625755c5c3e2acc3",
projection: "EPSG:4326",
}),
visible: true,
});
const tianditu_img_c = new TileLayer({
title: "影像地图",
id: "img_c",
source: new XYZ({
url: "http://t{0-7}.tianditu.com/DataServer?x={x}&y={y}&l={z}&T=img_c&tk=c691040443c68cda625755c5c3e2acc3",
projection: "EPSG:4326",
}),
visible: false,
});
const map = new Map({
target: `map-${this.mapId}`,
controls: defaultControls({
zoom: false,
attribution: false,
rotate: false,
}),
view: new View({
center: [this.centerPoint.lat, this.centerPoint.lon], //中心点经纬度
zoom: this.zoom, //图层缩放大小
projection: "EPSG:4326",
}),
layers: [tianditu_vec_c, tianditu_img_c, tianditu_cva_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;
}
);
// 线、面要素不做鼠标移入样式修改
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";
}
});
this.instance.set("map", map);
this.instance.set("overlay-list", []);
},
/* 绘制地图 */
drawMap() {
if (this.drawMarkers.length < 1) {
const map = this.instance.get("map");
// 增加绘图层
const drawLayer = new VectorLayer({
source: this.source,
id: "draw",
});
map.addLayer(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);
});
});
}
},
/* 监听绘制完成的事件 */
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.deletedraw();
this.drawMap();
// map.removeInteraction(this.draw);
} else {
const map = this.instance.get("map");
map.removeInteraction(this.draw);
}
this.$emit("endEoordinate", this.drawMarkers);
},
/* 检测交叉点位方法 */
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;
},
/* 检测交叉线段 */
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;
},
/* 删除绘制功能 */
deletedraw() {
const map = this.instance.get("map");
map.removeInteraction(this.draw);
const [layer] = map.getAllLayers().filter((item) => {
return item.get("id") === "draw";
});
map.removeLayer(layer);
this.draw = null;
this.source = new VectorSource();
this.drawMarkers = [];
this.$emit("endEoordinate", []);
},
/* 修改地图底图样式 */
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
*/
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%;
}
// 放大缩小控件
.control-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: absolute;
left: 1rem;
top: 1rem;
.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>