增加矢量图层相关操作逻辑
This commit is contained in:
parent
6658118fba
commit
2a193a9dff
|
|
@ -0,0 +1,237 @@
|
||||||
|
import Vue from 'vue'
|
||||||
|
import { regInfo } from '@/api/dataSearch'
|
||||||
|
|
||||||
|
let sourceLayer = undefined;
|
||||||
|
let clickedLineIds = [];
|
||||||
|
|
||||||
|
export function loadVectorLayer(layerId, features = ["line", "symbol"], colors = ["#FEFEFE", "#FF0000"], visibility = "visible", mapId = "home", workspace = 'dsds') {
|
||||||
|
// 添加数据源
|
||||||
|
const sourceId = `vector-${layerId}`;
|
||||||
|
let source = Vue.config.maps[mapId].getSource(sourceId);
|
||||||
|
if (!source)
|
||||||
|
Vue.config.maps[mapId].addSource(sourceId, {
|
||||||
|
type: 'vector',
|
||||||
|
scheme: 'tms',
|
||||||
|
tiles: [`${Vue.config.baseUrl}/geoserver/gwc/service/tms/1.0.0/${workspace}:${layerId}@EPSG:900913@pbf/{z}/{x}/{y}.pbf`],
|
||||||
|
minzoom: 0,
|
||||||
|
maxzoom: 30,
|
||||||
|
generateId: true
|
||||||
|
});
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
features.forEach((feature) => {
|
||||||
|
loadVectorFeature(layerId, feature, colors[i++], visibility, mapId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadVectorFeature(layerId, feature, color, visibility, mapId) {
|
||||||
|
let sourceId = `vector-${layerId}`;
|
||||||
|
// symbol
|
||||||
|
// Slot 是 Mapbox 样式规范中的一个概念,它定义了图层在渲染过程中的位置和顺序。
|
||||||
|
let slot = "foreground";
|
||||||
|
let layout = {
|
||||||
|
"symbol-placement": "point",
|
||||||
|
"text-rotation-alignment": "auto",
|
||||||
|
"text-field": ["get", "name"],
|
||||||
|
"text-font": ["literal", ["Arial Unicode MS Regular", "DIN Offc Pro Italic", "Open Sans Regular"]],
|
||||||
|
"text-size": 12,
|
||||||
|
"text-variable-anchor": ["top", "bottom", "left", "right"],
|
||||||
|
"text-radial-offset": 1,
|
||||||
|
"text-justify": "auto",
|
||||||
|
"text-optional": true,
|
||||||
|
"text-allow-overlap": false,
|
||||||
|
"text-ignore-placement": false,
|
||||||
|
'symbol-spacing': 1000, // 像素间距,增大值可减少标注密度
|
||||||
|
// 'text-max-angle': 300, // 角度越大,标注越少
|
||||||
|
"icon-optional": true,
|
||||||
|
"icon-allow-overlap": false,
|
||||||
|
"icon-ignore-placement": false,
|
||||||
|
"icon-image": "dot.png",
|
||||||
|
"icon-size": 1,
|
||||||
|
"visibility": visibility
|
||||||
|
};
|
||||||
|
let paint = {
|
||||||
|
"text-color": color,
|
||||||
|
"text-halo-color": "#FFFFFF",
|
||||||
|
"text-halo-width": 0.1,
|
||||||
|
"icon-opacity": 1,
|
||||||
|
// slot: "middle"
|
||||||
|
};
|
||||||
|
if (feature === "line") {
|
||||||
|
slot = "top";
|
||||||
|
layout = {
|
||||||
|
"visibility": visibility
|
||||||
|
};
|
||||||
|
paint = {
|
||||||
|
"line-opacity": 1,
|
||||||
|
"line-width": [
|
||||||
|
"case",
|
||||||
|
["boolean", ["feature-state", "hover"], false],
|
||||||
|
4,
|
||||||
|
["boolean", ["feature-state", "click"], false],
|
||||||
|
2,
|
||||||
|
1.8
|
||||||
|
],
|
||||||
|
"line-color": [
|
||||||
|
"case",
|
||||||
|
["boolean", ["feature-state", "click"], false],
|
||||||
|
"red",
|
||||||
|
["boolean", ["feature-state", "hover"], false],
|
||||||
|
"yellow",
|
||||||
|
color
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let layer = Vue.config.maps[mapId].getLayer(`${sourceId}-${feature}`);
|
||||||
|
if (!layer) {
|
||||||
|
Vue.config.maps[mapId].addLayer({
|
||||||
|
id: `${sourceId}-${feature}`,
|
||||||
|
'type': feature,
|
||||||
|
'source': sourceId,
|
||||||
|
'source-layer': layerId,
|
||||||
|
'layout': layout,
|
||||||
|
'paint': paint,
|
||||||
|
// 'slot': 'foreground'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击可交互的图层,显示相关信息
|
||||||
|
await setHighlight(`${sourceId}-${feature}`, sourceId, mapId);
|
||||||
|
changeCursor(`${sourceId}-${feature}`, mapId);
|
||||||
|
await showTrackInfo(`${sourceId}-${feature}`, mapId);
|
||||||
|
|
||||||
|
return sourceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setHighlight(layerId, sourceId, mapId = "home") {
|
||||||
|
let hoveredLineId = null;
|
||||||
|
let clickedLineId = null;
|
||||||
|
Vue.config.maps[mapId].on('click', layerId, (e) => {
|
||||||
|
if (!e.features || e.features.length <= 0)
|
||||||
|
return;
|
||||||
|
sourceLayer = e.features[0].sourceLayer;
|
||||||
|
// 获取当前单击的线的选择状态
|
||||||
|
let state = Vue.config.maps[mapId].getFeatureState({
|
||||||
|
source: sourceId,
|
||||||
|
sourceLayer: sourceLayer,
|
||||||
|
id: e.features[0].id
|
||||||
|
});
|
||||||
|
if (clickedLineId) {
|
||||||
|
Vue.config.maps[mapId].setFeatureState({ source: sourceId, sourceLayer: sourceLayer, id: clickedLineId }, { click: false });
|
||||||
|
}
|
||||||
|
clickedLineId = e.features[0].id;
|
||||||
|
if (state.click === true) {
|
||||||
|
// 再次单击时取消选择
|
||||||
|
Vue.config.maps[mapId].setFeatureState({ source: sourceId, sourceLayer: sourceLayer, id: clickedLineId }, { click: false });
|
||||||
|
// 隐藏信息框
|
||||||
|
// document.getElementById("cable-overlay")!.style.display = 'none';
|
||||||
|
// 过滤掉当前单击的线
|
||||||
|
clickedLineIds = clickedLineIds.filter((item) => item !== clickedLineId);
|
||||||
|
} else {
|
||||||
|
Vue.config.maps[mapId].setFeatureState({ source: sourceId, sourceLayer: sourceLayer, id: clickedLineId }, { click: true });
|
||||||
|
// 显示信息框
|
||||||
|
// if (sourceId === 'GMSL Cable Data')
|
||||||
|
// document.getElementById("cable-overlay")!.style.display = 'block';
|
||||||
|
// 单击时加入选择列表(用于单击 ESC 键时取消选择状态)
|
||||||
|
clickedLineIds.push(clickedLineId);
|
||||||
|
// document.removeEventListener("keydown", removeHighlight, true);
|
||||||
|
document.addEventListener("keydown", (e) => {
|
||||||
|
removeHighlight(e, sourceId);
|
||||||
|
}, { once: true }); // once:一个布尔值,表示 listener 在添加之后最多只调用一次。如果为 true,listener 会在其被调用之后自动移除。
|
||||||
|
// }, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// When the user moves their mouse over the state-fill layer, we'll update the feature state for the feature under the mouse.
|
||||||
|
Vue.config.maps[mapId].on('mousemove', layerId, (e) => {
|
||||||
|
if (e.features.length <= 0)
|
||||||
|
return;
|
||||||
|
sourceLayer = e.features[0].sourceLayer;
|
||||||
|
if (hoveredLineId) {
|
||||||
|
Vue.config.maps[mapId].setFeatureState(
|
||||||
|
{ source: sourceId, sourceLayer: sourceLayer, id: hoveredLineId },
|
||||||
|
{ hover: false }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
hoveredLineId = e.features[0].id;
|
||||||
|
Vue.config.maps[mapId].setFeatureState(
|
||||||
|
{ source: sourceId, sourceLayer: sourceLayer, id: hoveredLineId },
|
||||||
|
{ hover: true }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// When the mouse leaves the state-fill layer, update the feature state of the previously hovered feature.
|
||||||
|
Vue.config.maps[mapId].on('mouseleave', layerId, () => {
|
||||||
|
if (hoveredLineId && sourceLayer) {
|
||||||
|
Vue.config.maps[mapId].setFeatureState(
|
||||||
|
{ source: sourceId, sourceLayer: sourceLayer, id: hoveredLineId },
|
||||||
|
{ hover: false }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
hoveredLineId = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function changeCursor(layerId, mapId = "home", cursor = "pointer") {
|
||||||
|
Vue.config.maps[mapId].on("mouseenter", layerId, () => {
|
||||||
|
Vue.config.maps[mapId].getCanvas().style.cursor = cursor;
|
||||||
|
});
|
||||||
|
|
||||||
|
Vue.config.maps[mapId].on("mouseleave", layerId, () => {
|
||||||
|
Vue.config.maps[mapId].getCanvas().style.cursor = "";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showTrackInfo(layerId, mapId = "home") {
|
||||||
|
Vue.config.maps[mapId].on("click", layerId, async (e) => {
|
||||||
|
// 禁止点击事件穿透 - 判断同一个 event 是否已经触发,用于避免图层内标记点注册的点击事件重复触发(点位分布密集时容易出现)
|
||||||
|
if (e.defaultPrevented)
|
||||||
|
return;
|
||||||
|
// 标记该 event 已触发,禁止点击事件穿透
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
let coordinate = e.lngLat;
|
||||||
|
let match = layerId.match(/vector-(.*)-[(symbol)|(line)]/);
|
||||||
|
if (!match)
|
||||||
|
return;
|
||||||
|
const voyageId = match[1];
|
||||||
|
regInfo({ voyage_name: voyageId }).then(res => {
|
||||||
|
const info = res.map.voyage;
|
||||||
|
const description = `
|
||||||
|
<table class="popup-table" style="min-width:380px;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2"><a style="color:#0000FF" target="_balnk" title="点击查看航次详情" href="${Vue.config.baseUrl}/ds/#/container/dataDetail?voyage_name=${info.voyage_name}">航次信息</a></td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tr>
|
||||||
|
<td>航次编号: <b>${info.voyage_name}</b></td>
|
||||||
|
<td>航次首席: <b>${info.voyage_officer}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>科考船舶: <b>${info.ship_name}</b></td>
|
||||||
|
<td>资助机构: <b>${info.funding_org}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>离港日期: <b>${info.voyage_start_date}</b></td>
|
||||||
|
<td>返港日期: <b>${info.voyage_end_date}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">航次概况: <b>${info.voyage_remark}</b></td>
|
||||||
|
</tr>
|
||||||
|
</table>`;
|
||||||
|
|
||||||
|
showPopupDetails(mapId, coordinate, description, "420px");
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showPopupDetails(mapId, coordinate, description, maxWidth = "300px", closeButton = true, closeOnClick = true, options = {}) {
|
||||||
|
new window.mapboxgl.Popup({ offset: {}, className: "my-class", closeButton: closeButton, closeOnClick: closeOnClick })
|
||||||
|
.setLngLat(coordinate)
|
||||||
|
.setHTML(description)
|
||||||
|
.setOffset(10)
|
||||||
|
.setMaxWidth(maxWidth)
|
||||||
|
.addTo(Vue.config.maps[mapId]);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue