From 84270bd4eb9a776b7af3756743d5519b776855aa Mon Sep 17 00:00:00 2001 From: hym Date: Mon, 6 Apr 2026 23:07:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=9F=A2=E9=87=8F=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/color-generator.js | 2 +- src/utils/mapbox-utils.js | 6 +- src/utils/vector-layer-utils.js | 148 ++++++++++++++++++++++++++++++-- 3 files changed, 144 insertions(+), 12 deletions(-) diff --git a/src/utils/color-generator.js b/src/utils/color-generator.js index 8d7c566..547e25b 100644 --- a/src/utils/color-generator.js +++ b/src/utils/color-generator.js @@ -66,7 +66,7 @@ export class ColorGenerator { positiveColor = '#1a9850' ) { const colors = []; - const midPoint = Math.floor(count / 2); + const midPoint = Math.floor(count + 1 / 2); // 负值部分 const negRGB = this.parseHexColor(negativeColor); diff --git a/src/utils/mapbox-utils.js b/src/utils/mapbox-utils.js index 7670946..8e2a987 100644 --- a/src/utils/mapbox-utils.js +++ b/src/utils/mapbox-utils.js @@ -86,11 +86,11 @@ export function initMapbox(mapId, options = {}) { map.on('load', () => { // 加载海岸线 if (options.coastlineOn || false) - loadVectorLayer("GSHHS_f_L1", ["line"], ["#5c71c3"], "visible", mapId, 'ougp'); + loadVectorLayer({ layerId: "GSHHS_f_L1", features: ["line"], colors: ["#5c71c3"], visibility: "visible", mapId, workspace: 'ougp' }); // 加载等深线 if (options.contourOn || false) { - loadVectorLayer("contour-4500m", ["line"], ["#FEFEFE"], "visible", mapId, 'ougp'); - loadVectorLayer("contour-6000m", ["line"], ["#FEFEFE"], "visible", mapId, 'ougp'); + loadVectorLayer({ layerId: "contour-4500m", features: ["line"], colors: ["#FEFEFE"], visibility: "visible", mapId, workspace: 'ougp' }); + loadVectorLayer({ layerId: "contour-6000m", features: ["line"], colors: ["#FEFEFE"], visibility: "visible", mapId, workspace: 'ougp' }); } }); } diff --git a/src/utils/vector-layer-utils.js b/src/utils/vector-layer-utils.js index 91783b6..03c4fb3 100644 --- a/src/utils/vector-layer-utils.js +++ b/src/utils/vector-layer-utils.js @@ -3,8 +3,21 @@ import { regInfo } from '@/api/dataSearch' let sourceLayer = undefined; let clickedLineIds = []; + // 存储可复用的每个地图对象的唯一 Popup 实例(key 为 mapId),以避免频繁创建/销毁 Popup 导致的性能问题和潜在内存泄漏 let sharedPopup = {}; +// 存储上一条点击的线 +let lastLineId; + +// 默认的线宽表达式 +export let lineWidthExpression = [ + "case", + ["boolean", ["feature-state", "hover"], false], + 4, + ["boolean", ["feature-state", "click"], false], + 2, + 1.8 +]; export function setMapLayoutProperty(mapId, visibility = "none") { // 设置所有图层的显示/隐藏状态(根据图层类型或名称过滤底图图层,避免误操作) @@ -16,7 +29,19 @@ export function setMapLayoutProperty(mapId, visibility = "none") { } }); } -export function loadVectorLayer(layerId, features = ["line", "symbol"], colors = ["#FEFEFE", "#FF0000"], visibility = "visible", mapId = "homeMap", workspace = 'dsds', textField = "name") { + +export function setMapPaintProperty({ mapId, property, value }) { + const layers = Vue.config.maps[mapId].getStyle().layers; + layers.forEach(layer => { + // 跳过底图和非测线图层 + if (layer.id !== '影像地图' && layer.type !== 'raster' && layer.id.endsWith("-line")) { + Vue.config.maps[mapId].setPaintProperty(layer.id, property, lineWidthExpression); + } + }); + // Vue.config.maps[mapId].setPaintProperty(layerId, "line-width", 14); +} + +export function loadVectorLayer({ layerId, features = ["line", "symbol"], colors = ["#FEFEFE", "#FF0000"], visibility = "visible", mapId = "homeMap", workspace = 'dsds', textField = "name", callbackOnClick }) { // 添加数据源 const sourceId = `vector-${layerId}`; let source = Vue.config.maps[mapId].getSource(sourceId); @@ -50,11 +75,11 @@ export function loadVectorLayer(layerId, features = ["line", "symbol"], colors = let i = 0; features.forEach((feature) => { - loadVectorFeature(layerId, feature, colors[i++], visibility, mapId, textField); + loadVectorFeature({ layerId, feature, color: colors[i++], visibility, mapId, textField, callbackOnClick }); }); } -export async function loadVectorFeature(layerId, feature, color, visibility, mapId, textField = "name") { +export async function loadVectorFeature({ layerId, feature, color, visibility, mapId, textField = "name", callbackOnClick }) { let sourceId = `vector-${layerId}`; // symbol // Slot 是 Mapbox 样式规范中的一个概念,它定义了图层在渲染过程中的位置和顺序。 @@ -147,9 +172,9 @@ export async function loadVectorFeature(layerId, feature, color, visibility, map changeCursor(`${sourceId}-${feature}`, mapId); if (layerId.includes("sample_line")) - await showSampleLineInfo(`${sourceId}-${feature}`, mapId); + await showSampleLineInfo(`${sourceId}-${feature}`, mapId, callbackOnClick); if (layerId.includes("sample_station")) - await showSampleStationInfo(`${sourceId}-${feature}`, mapId); + await showSampleStationInfo(`${sourceId}-${feature}`, mapId, callbackOnClick); else if (sourceId.includes("-SY") || sourceId.includes("-FDZ")) { await showDiveInfo(`${sourceId}-${feature}`, mapId); console.log(mapId, `${sourceId}-${feature}`); @@ -290,14 +315,20 @@ export function changeCursor(layerId, mapId = "homeMap", cursor = "pointer") { }); } -export function showSampleLineInfo(layerId, mapId = "homeMap") { +export function showSampleLineInfo(layerId, mapId = "homeMap", callbackOnClick = null) { Vue.config.maps[mapId].on("click", layerId, async (e) => { + // 在左侧列表中高亮对应项 + if (callbackOnClick && typeof callbackOnClick === "function") { + callbackOnClick(e.features[0]); + } + // 禁止点击事件穿透 - 判断同一个 event 是否已经触发,用于避免图层内标记点注册的点击事件重复触发(点位分布密集时容易出现) if (e.defaultPrevented) return; // 标记该 event 已触发,禁止点击事件穿透 if (typeof e.preventDefault === "function") e.preventDefault(); + // console.log("showSampleLineInfo: ", layerId, e.features[0]); const description = ` @@ -339,8 +370,13 @@ export function showSampleLineInfo(layerId, mapId = "homeMap") { }); } -export function showSampleStationInfo(layerId, mapId = "homeMap") { +export function showSampleStationInfo(layerId, mapId = "homeMap", callbackOnClick = null) { Vue.config.maps[mapId].on("click", layerId, async (e) => { + // 在左侧列表中高亮对应项 + if (callbackOnClick && typeof callbackOnClick === "function") { + callbackOnClick(e.features[0]); + } + // 禁止点击事件穿透 - 判断同一个 event 是否已经触发,用于避免图层内标记点注册的点击事件重复触发(点位分布密集时容易出现) if (e.defaultPrevented) return; @@ -361,7 +397,7 @@ export function showSampleStationInfo(layerId, mapId = "homeMap") { - + @@ -384,6 +420,102 @@ export function showSampleStationInfo(layerId, mapId = "homeMap") { }); } +export function showHighlightSampleInfo({ mapId, job_type, sample, layerId, sourceLayerId, callbackOnClick }) { + let description = ""; + let coordinate; + if (job_type == "测线作业") { + // // 恢复其它测线的线宽 + // // setMapPaintProperty({ mapId, property: "line-width", value: lineWidthExpression }); + // // 恢复上一条高亮测线的宽度 + // Vue.config.maps[mapId].setPaintProperty(lastLineId, "line-width", lineWidthExpression); + // // 高亮当前选择的测线 + // Vue.config.maps[mapId].setPaintProperty(layerId, "line-width", 5); + + // if (lastLineId) + // Vue.config.maps[mapId].setFeatureState( + // { sourceId: sourceLayerId, id: lastLineId }, + // { hover: false } + // ); + // Vue.config.maps[mapId].setFeatureState( + // { sourceId: sourceLayerId, id: layerId }, + // { hover: true } + // ); + + // if (lastLineId) + // Vue.config.maps[mapId].setPaintProperty(lastLineId, 'line-color', '#FFFFFF') + // Vue.config.maps[mapId].setPaintProperty(layerId, 'line-color', '#FFFF00') + // lastLineId = layerId; + + coordinate = [(Number(sample.start_lon) + Number(sample.end_lon)) / 2, (Number(sample.start_lat) + Number(sample.end_lat)) / 2]; + description = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + `; + } + else if (job_type == "站位作业") { + coordinate = [sample.sampling_lon, sample.sampling_lat]; + description = ` + + + + + + + + + + + + + + + + + + + + + + + + + + `; + } + showPopupDetails({ mapId, coordinate, description, maxWidth: "450px", callbackOnClose: callbackOnClick }); +} + export function showTrackInfo(layerId, mapId = "homeMap") { Vue.config.maps[mapId].on("click", layerId, async (e) => { // 禁止点击事件穿透 - 判断同一个 event 是否已经触发,用于避免图层内标记点注册的点击事件重复触发(点位分布密集时容易出现)