xjsaas-ui/src/components/map/fssm-map.vue

618 lines
17 KiB
Vue
Raw Normal View History

2024-10-15 13:14:38 +08:00
<!--
* @Author: SunTao 328867980@qq.com
* @Date: 2024-10-14 10:46:23
* @LastEditors: SunTao 328867980@qq.com
* @LastEditTime: 2024-10-31 13:58:48
2024-10-15 13:14:38 +08:00
* @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>
2024-10-17 10:38:27 +08:00
<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">
2024-10-15 13:14:38 +08:00
<i class="el-icon-plus" @click="changeZoom(1, 0.5)"></i>
</div>
2024-10-17 10:38:27 +08:00
<div class="smallButton map-btn" v-if="showZoom">
2024-10-15 13:14:38 +08:00
<i class="el-icon-minus" @click="changeZoom(-1, 0.5)"></i>
</div>
</div>
</div>
</template>
<script>
2024-10-17 10:38:27 +08:00
import { Feature, Map, View } from "ol";
2024-10-15 13:14:38 +08:00
import XYZ from "ol/source/XYZ";
import { Tile as TileLayer } from "ol/layer";
import { defaults as defaultControls } from "ol/control";
2024-10-17 10:38:27 +08:00
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";
2024-10-15 13:14:38 +08:00
export default {
name: "FssmMap",
props: {
// 接收传过来得中心点
centerPoint: {
type: Object,
default: () => {
return { lat: 123.30297096718999, lon: 41.87942945541742 };
},
},
// 接受传过来得地图层级
zoom: {
type: String,
default: "10",
2024-10-17 10:38:27 +08:00
},
// 是否显示绘图功能
showDraw: {
type: Boolean,
default: false,
2024-10-15 13:14:38 +08:00
},
// 是否显示地图放大缩小
showZoom: {
type: Boolean,
2024-10-17 10:38:27 +08:00
default: false,
2024-10-15 13:14:38 +08:00
},
2024-10-17 10:38:27 +08:00
// 是否显示切换地图底层按钮
showChange: {
type: Boolean,
default: false,
},
// 用于区分同一个页面多个地图的id
2024-10-15 13:14:38 +08:00
mapId: {
type: String,
default: "0",
},
2024-10-17 10:38:27 +08:00
// 接受传过来的绘制点位数组
editCoordinates: {
type: Array,
default: () => [],
},
2024-10-18 17:31:35 +08:00
// 接收传过来的底图类型
baseMap: {
type: String,
default: "cva_c",
},
2024-10-15 13:14:38 +08:00
},
data() {
return {
// 地图实例
instance: new Map(),
2024-10-17 10:38:27 +08:00
// 点击地图类型
mapType: "cva_c",
// 地图图层
mapLayers: [],
// 绘制图层
draw: null,
source: new VectorSource(),
// 绘制图形保存的点位
drawMarkers: [],
// 当前地图层级
sendZoom: "",
2024-10-15 13:14:38 +08:00
};
},
2024-10-17 10:38:27 +08:00
watch: {
2024-10-18 17:31:35 +08:00
/* 监听传过来的坐标区域 */
2024-10-17 10:38:27 +08:00
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,
},
2024-10-18 17:31:35 +08:00
/* 监听传过来的底图类型 */
baseMap: {
handler(val) {
this.changeImg(val);
2024-10-18 17:31:35 +08:00
},
immediate: true,
deep: true,
},
2024-10-17 10:38:27 +08:00
},
2024-10-15 13:14:38 +08:00
mounted() {
this.initMap();
},
2024-10-17 10:38:27 +08:00
created() {
// this.changeImg("cva_c");
},
2024-10-15 13:14:38 +08:00
methods: {
/* 初始化openlayer地图 */
initMap() {
2024-10-17 10:38:27 +08:00
const tianditu_vec_c = new TileLayer({
2024-10-25 17:29:08 +08:00
className: "baseLayerClass",
2024-10-17 10:38:27 +08:00
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=1eb44fae5b9dc454442b322e9a41d233",
2024-10-17 10:38:27 +08:00
projection: "EPSG:4326",
}),
visible: true,
});
const tianditu_cva_c = new TileLayer({
2024-10-25 17:29:08 +08:00
className: "baseLayerClass",
2024-10-17 10:38:27 +08:00
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=1eb44fae5b9dc454442b322e9a41d233",
2024-10-17 10:38:27 +08:00
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=1eb44fae5b9dc454442b322e9a41d233",
2024-10-17 10:38:27 +08:00
projection: "EPSG:4326",
}),
visible: false,
});
2024-10-15 13:14:38 +08:00
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",
minZoom: 7,
maxZoom: 14,
2024-10-15 13:14:38 +08:00
}),
2024-10-17 10:38:27 +08:00
layers: [tianditu_vec_c, tianditu_img_c, tianditu_cva_c],
2024-10-15 13:14:38 +08:00
});
// 图层点击事件
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";
}
});
// 地图缩放级别事件
map.on("moveend", (e) => {
const zoom = map.getView().getZoom().toFixed(); //获取当前地图的缩放级别
this.$emit("map-moveend", zoom);
});
// 图层选择事件
let selectSingClick = new Select({ style: null });
map.addInteraction(selectSingClick);
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);
}
});
2024-10-15 13:14:38 +08:00
this.instance.set("map", map);
this.instance.set("overlay-list", []);
},
2024-10-17 10:38:27 +08:00
/* 绘制地图 */
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);
});
}
});
}
},
2024-10-15 13:14:38 +08:00
/**
* @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);
},
/* 根据类型清除地图线段图层 */
clearMapLine() {
const map = this.instance.get("map");
const [layer] = map
.getAllLayers()
.filter((item) => item.get("type") === "line");
map.removeLayer(layer);
},
2024-10-15 13:14:38 +08:00
/**
* @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%;
2024-10-17 10:38:27 +08:00
position: relative;
2024-10-15 13:14:38 +08:00
#map-0 {
width: 100%;
height: 100%;
}
#map-1 {
width: 100%;
height: 100%;
}
2024-10-17 10:38:27 +08:00
// 放大缩小控件
2024-10-15 13:14:38 +08:00
.control-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: absolute;
left: 1rem;
top: 1rem;
2024-10-17 10:38:27 +08:00
.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;
}
2024-10-15 13:14:38 +08:00
}
}
}
</style>