/**
* layerVisibility.mjs
* 圖層可見性管理:tracked 清理、items 列表生成、可見數量計算
*/
import each from 'lodash-es/each.js'
import filter from 'lodash-es/filter.js'
import sortBy from 'lodash-es/sortBy.js'
import cloneDeep from 'lodash-es/cloneDeep.js'
import isNumber from 'lodash-es/isNumber.js'
/**
* 清除以指定前綴開頭的 layer 與 source
* @param {Object} map - MapLibre 地圖實例
* @param {String} prefix - 前綴字串
* @param {Array} trackedLayerIds - 目前追蹤的 layer ID 陣列
* @param {Array} trackedSourceIds - 目前追蹤的 source ID 陣列
* @returns {{ layerIds: Array, sourceIds: Array }} 更新後的追蹤陣列
*/
export function clearTrackedByPrefix(map, prefix, trackedLayerIds, trackedSourceIds) {
if (!map) return { layerIds: trackedLayerIds, sourceIds: trackedSourceIds }
//先清委派 listeners: 呼叫端(如 cluster 設定變更)清掉後會以同 layerId 重建並重新註冊,
//不清會逐次累積 → click 回呼被重複觸發
offDelegatedListenersByLayer(map, (id) => id.startsWith(prefix))
let rl = []; let rs = []
each(trackedLayerIds, (id) => {
id.startsWith(prefix) ? (map.getLayer(id) && map.removeLayer(id)) : rl.push(id)
})
each(trackedSourceIds, (id) => {
id.startsWith(prefix) ? (map.getSource(id) && map.removeSource(id)) : rs.push(id)
})
return { layerIds: rl, sourceIds: rs }
}
/**
* 反註冊委派 listeners 中綁定於符合條件 layer id 者(使註冊與圖層同壽命, 避免累積)。
* 注意: 依賴 maplibre 私有結構 map._delegatedListeners, 升級 maplibre 時須確認仍相容——
* 故全 codebase 僅此一份實作, 各清理路徑共用。
* @param {Object} map - MapLibre 地圖實例
* @param {Function} matchLayerId - (layerId) => Boolean
*/
export function offDelegatedListenersByLayer(map, matchLayerId) {
let dl = map._delegatedListeners
if (!dl || !map.off) return
each(Object.keys(dl), (type) => {
each([...(dl[type] || [])], (entry) => {
if (entry && entry.listener && filter(entry.layers || [], matchLayerId).length > 0) {
try {
map.off(type, entry.layers, entry.listener)
}
catch (e) {
/* ignore stale listener cleanup failures */
}
}
})
})
}
/**
* 建立統一的圖層列表(供圖層顯隱面板使用)
* @param {Array} imageSets
* @param {Array} pointSets
* @param {Array} polylineSets
* @param {Array} polygonSets
* @param {Array} geojsonSets
* @param {Array} contourSets
* @returns {Array} items - [{ name, msg, order, visible, updatePath }]
*/
export function buildItemsList(imageSets, pointSets, polylineSets, polygonSets, geojsonSets, contourSets) {
let items = []
let add = (n, m, o, v, p) => {
items.push({ name: n, msg: m, order: o, visible: v, updatePath: p })
}
each(imageSets, (v, k) => {
add(v.title, v.msg, v.order, v.visible, `imageSets.${k}.visible`)
})
each(pointSets, (v, k) => {
add(v.title, v.msg, v.order, v.visible, `pointSets.${k}.visible`)
})
each(polylineSets, (v, k) => {
add(v.title, v.msg, v.order, v.visible, `polylineSets.${k}.visible`)
})
each(polygonSets, (v, k) => {
add(v.title, v.msg, v.order, v.visible, `polygonSets.${k}.visible`)
})
each(geojsonSets, (v, k) => {
add(v.title, v.msg, v.order, v.visible, `geojsonSets.${k}.visible`)
})
each(contourSets, (v, k) => {
add(v.title, v.msg, v.order, v.visible, `contourSets.${k}.visible`)
})
let ord = sortBy(filter(items, (v) => isNumber(v.order)), 'order')
let rest = filter(items, (v) => !isNumber(v.order))
return cloneDeep([...ord, ...rest])
}
/**
* 計算可見圖層數量(純函式)
* @param {Array} arr - 含 visible 屬性的陣列
* @returns {Number}
*/
export function countVisible(arr) {
let i = 0
each(arr, (v) => {
if (v.visible) i++
})
return i
}