增加矢量图层相关操作逻辑
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