@supermapgis/iclient-maplibregl
Version:
@supermapgis/iclient-maplibregl 是一套基于 Maplibre GL 的云 GIS 网络客户端开发平台, 支持访问 SuperMap iServer / iEdge / iPortal / iManager / Online 的地图、服务和资源,为用户提供了完整专业的 GIS 能力, 同时提供了优秀的可视化功能。
579 lines (542 loc) • 18.6 kB
JavaScript
/* Copyright© 2000 - 2025 SuperMap Software Co.Ltd. All rights reserved.
* This program are made available under the terms of the Apache License, Version 2.0
* which accompanies this distribution and is available at http://www.apache.org/licenses/LICENSE-2.0.html.*/
import { Util } from "@supermapgis/iclient-common/commontypes/Util";
import CompositeSymbolRender from "./CompositeSymbolRender";
import SingleSymbolRender from "./SingleSymbolRender";
import SymbolManager from "./SymbolManager";
import { getImageKey, isMapboxExpression, isMultiSymbol, isPaintKey, validateSymbol } from "./SymbolUtil";
/**
* 符号图层管理器。
* @returns {Object}
* @private
*/
class SymbolHandler {
constructor(map) {
this.map = map;
this.symbolManager = new SymbolManager();
this.singleSymbolRender = new SingleSymbolRender(map);
this.compositeSymbolRender = new CompositeSymbolRender(map);
this._layerSymbols = {};// 图层与symbol的映射关系
}
_update(map) {
this.map = map;
return this;
}
/**
* 添加符号图层。
* @param {Object} layer
* @param {string} before
*/
addLayer(layer, before) {
if (typeof layer.symbol === 'string') {
const id = layer.symbol;
if (id) {
const symbol = this.getSymbol(id);
if (!symbol) {
return this.map.fire('error', {
error: new Error(`Symbol "${id}" could not be loaded. Please make sure you have added the symbol with map.addSymbol().`)
});
}
this.setSymbolTolayer(layer.id, id);
this.getSymbolRender(symbol).addLayer(layer, symbol, before);
}
} else if (isMapboxExpression(layer.symbol)) {
this.setSymbolTolayer(layer.id, layer.symbol);
this.addExpressionLayer(layer, before);
}
}
/**
* 更新图层上的 symbol。
* @param {string} layerId
* @param {string | array} symbol
*/
setSymbol(layerId, symbol) {
const layers = this.map.getStyle().layers;
const layerIndex = layers.findIndex(l => l.id === layerId);
if (layerIndex === -1) {
return this.map.fire('error', {
error: new Error(`Cannot set symbol "${symbol}" to non-existing layer "${layerId}".`)
});
}
this.map.removeLayer(layerId);
const beforeId = layers[layerIndex + 1] && layers[layerIndex + 1].id;
const layer = beforeId && this.map.style.getLayer(beforeId);
const before = (layer && layer.id) || (beforeId && this.getFirstLayerId(beforeId));
const orginLayer = layers[layerIndex];
this.addLayer({ ...orginLayer, paint: {}, layout: {
visibility: (orginLayer.layout && orginLayer.layout.visibility) || 'visible'
}, symbol }, before);
}
/**
* 处理 match 表达式为多个图层。
* @param {object} layer
* @returns {array}
*/
getMatchLayers(layer) {
const layers = [];
const filter = ["all"];
if (layer.filter) {
filter.push(layer.filter);
}
const defaultFilter = [];
const expression = layer.symbol.slice(2);
expression.forEach((r, index) => {
if (index % 2 === 1) {
layers.push({
...layer, "filter": [
...filter,
[
"==",
layer.symbol[1][1],
expression[index - 1]
]
], symbol: r
});
defaultFilter.push([
"!=",
layer.symbol[1][1],
expression[index - 1]
]);
} else if (index === expression.length - 1) {
layers.unshift({ ...layer, "filter": [
...filter,
...defaultFilter
], symbol: r });
}
});
return layers;
}
/**
* 处理 match 表达式为多个图层。
* @param {object} layer
* @returns {array}
*/
getCaseLayers(layer) {
const layers = [];
const filter = ["all"];
if (layer.filter) {
filter.push(layer.filter);
}
const defaultFilter = [];
const expression = layer.symbol.slice(1);
expression.forEach((r, index) => {
if (index % 2 === 1) {
layers.push({
...layer, "filter": [
...filter,
expression[index - 1]
], symbol: r
});
defaultFilter.push(['!', expression[index - 1]])
} else if (index === expression.length - 1) {
layers.unshift({ ...layer, "filter": [
...filter,
...defaultFilter
], symbol: r });
}
});
return layers;
}
/**
* 将 symbol 表达式拆成 filter。
* @param {object} layer
* @param {string} before
*/
addExpressionLayer(layer, before) {
const rules = {
match: this.getMatchLayers,
case: this.getCaseLayers
}
const getLayersFn = rules[layer.symbol[0]];
const layers = getLayersFn && getLayersFn(layer);
if (!layers) {
return this.map.fire('error', {
error: new Error(`This expressions not supported`)
});
}
layers.forEach((l) => {
l.id = Util.createUniqueID(`${layer.id}_compositeLayer_`);
this.compositeSymbolRender.addLayerId(layer.id, l.id);
this.addLayer(l, before);
});
}
/**
* 通过 symbol 判断使用管理器。
* @param {object | array} symbol
* @returns {SingleSymbolRender | CompositeSymbolRender}
*/
getSymbolRender(symbol) {
return isMultiSymbol(symbol) ? this.compositeSymbolRender : this.singleSymbolRender;
}
/**
* 将 Web 符号中的 image 添加到地图上。
* @param {object} symbol
* @param {object} image
*/
addSymbolImageToMap(symbol, image) {
const { type, name } = getImageKey(symbol);
const id = symbol[type] && symbol[type][name];
if (id && !this.map.hasImage(id)) {
// 如果需要使用到 image 的需要 addImage
this.map.addImage(id, image);
// 为了解决 sdf 问题,需要把 load 后的 image 信息存下
this.symbolManager.addImageInfo(id, image);
}
}
/**
* 给指定图层添加 symbol。
* @param {string} id
* @param {object} symbol
*/
addSymbol(id, symbol) {
if (this.getSymbol(id)) {
return this.map.fire('error', {
error: new Error('An symbol with this name already exists.')
});
}
if (validateSymbol(symbol)) {
this.symbolManager.addSymbol(id, symbol);
} else {
return this.map.fire('error', {
error: new Error('Symbol is not supported expressions.')
});
}
}
/**
* 设置 layer 对应的 symbol 属性值。
* @param {string} layerId
* @param {string | array} symbol
*/
setSymbolTolayer(layerId, symbol) {
this._layerSymbols[layerId] = symbol;
}
/**
* 通过 layerID 获取 symbol 属性值。
* @param {string} layerId
* @return {string | array} symbol
*/
getLayerSymbol(layerId) {
return this._layerSymbols[layerId];
}
/**
* 判断是否有 symbol。
* @return {boolean}
*/
hasSymbol() {
return Object.keys(this._layerSymbols).length > 0;
}
/**
* 删除 symbol。
* @param {string} id
*/
removeSymbol(id) {
this.symbolManager.removeSymbol(id);
}
/**
* 通过 symbolId 获取 symbol 内容。
* @param {string} symbolId
*/
getSymbol(symbolId) {
return this.symbolManager.getSymbol(symbolId);
}
/**
* 获取组合图层的子图层 IDs。
* @param {string} layerId
* @returns {array}
*/
getLayerIds(layerId) {
return this.compositeSymbolRender.getLayerIds(layerId) || [];
}
/**
* 获取子图层 ID 对应的组合图层。
* @param {string} layerId
* @returns {string}
*/
getLayerId(layerId) {
return this.compositeSymbolRender.getLayerId(layerId);
}
/**
* 删除图层 ID。
* @param {string} layerId
* @returns {string}
*/
removeLayerId(layerId) {
return this.compositeSymbolRender.removeLayerId(layerId);
}
/**
* 获取指定 ID 的 layer。
* @param {string} layerId
* @returns {object}
*/
getLayer(layerId) {
const layer = this.map.getLayerBySymbolBak(layerId);
const symbol = this.getLayerSymbol(layerId);
if (layer) {
return symbol ? { ...layer, symbol } : layer;
} else {
const layerIds = this.getLayerIds(layerId);
if (layerIds[0]) {
const reallayer = this.map.getLayerBySymbolBak(layerIds[0]);
return reallayer && { ...reallayer, symbol, id: layerId }
}
}
}
/**
* 删除指定图层。
* @param {string} layerId
*/
removeLayer(layerId) {
const layerIds = this.getLayerIds(layerId);
if (layerIds.length > 0) {
layerIds.forEach(id => this.map.style.removeLayer(id));
this.removeLayerId(layerId);
} else {
this.map.style.removeLayer(layerId);
}
}
/**
* 获取 style。
* @returns {object}
*/
getStyle() {
const style = this.map.style.serialize();
if (this.hasSymbol()) {
style.layers = style.layers.reduce((pre, layer) => {
const compositeId = this.getLayerId(layer.id);
if (compositeId) {
!pre.find(l => l.id === compositeId) && pre.push({ ...layer, symbol: this.getLayerSymbol(compositeId), id: compositeId })
} else if (this.getLayerSymbol(layer.id)) {
pre.push({ ...layer, symbol: this.getLayerSymbol(layer.id) })
} else {
pre.push(layer);
}
return pre;
}, []);
}
return style;
}
/**
* 获取组合图层的子图层 ID。
* @param {string} layerId
* @returns {string | undefined}
*/
getFirstLayerId(layerId) {
const layerIds = this.getLayerIds(layerId);
return layerIds[0];
}
/**
* 扩展 map 的 moveLayer。
* @param {string} layerId
* @param {string | undefined} beforeId
*/
moveLayer(layerId, beforeId) {
const layerIds = this.getLayerIds(layerId);
const layer = beforeId && this.map.style.getLayer(beforeId);
const realBeforeId = (layer && layer.id) || (beforeId && this.getFirstLayerId(beforeId));
if (layerIds.length > 0) {
layerIds.forEach(id => this.map.style.moveLayer(id, realBeforeId));
} else {
this.map.style.moveLayer(layerId, realBeforeId);
}
}
/**
* 扩展 map 的。setFilter。
* @param {string} layerId
* @param {Array} filter
* @param {object} options
*/
setFilter(layerId, filter, options) {
const symbol = this.getLayerSymbol(layerId);
if (isMapboxExpression(symbol)) {
// 如果 symbol 是数据驱动,filter需要重新计算
const realLayerId = this.getFirstLayerId(layerId);
this.map.style.setFilter(realLayerId, filter, options);
const symbol = this.getLayerSymbol(layerId);
this.setSymbol(layerId, symbol);
return;
}
const layerIds = this.getLayerIds(layerId);
if (layerIds.length > 0) {
layerIds.forEach(id => this.map.style.setFilter(id, filter, options));
} else {
this.map.style.setFilter(layerId, filter, options);
}
}
/**
* 扩展 map 的 getFilter。
* @param {string} layerId
* @returns {object}
*/
getFilter(layerId) {
const realLayerId = this.getFirstLayerId(layerId);
if (this.map.style.getLayer(realLayerId)) {
return this.map.style.getFilter(realLayerId);
}
}
/**
* 扩展 map 的 setLayerZoomRange。
* @param {string} layerId
* @param {number} minzoom
* @param {number} maxzoom
*/
setLayerZoomRange(layerId, minzoom, maxzoom) {
const layerIds = this.getLayerIds(layerId);
if (layerIds.length > 0) {
layerIds.forEach(id => this.map.style.setLayerZoomRange(id, minzoom, maxzoom));
} else {
this.map.style.setLayerZoomRange(layerId, minzoom, maxzoom);
}
}
/**
* 扩展 map 的 setPaintProperty。
* @param {string} layerId
* @param {string} name
* @param {*} value
* @param {object} options
*/
setPaintProperty(layerId, name, value, options) {
const layerIds = this.getLayerIds(layerId);
if (layerIds.length > 0) {
layerIds.forEach(id => this.map.style.setPaintProperty(id, name, value, options));
} else {
this.map.style.setPaintProperty(layerId, name, value, options);
}
}
/**
* 扩展 map 的 getPaintProperty。
* @param {string} layerId
* @param {string} name
* @returns {object}
*/
getPaintProperty(layerId, name) {
const realLayerId = this.getFirstLayerId(layerId);
return this.map.style.getPaintProperty(realLayerId, name);
}
/**
* 扩展 map 的 setLayoutProperty。
* @param {string} layerId
* @param {string} name
* @param {*} value
* @param {object} options
*/
setLayoutProperty(layerId, name, value, options) {
const layerIds = this.getLayerIds(layerId);
if (layerIds.length > 0) {
layerIds.forEach(id => this.map.style.setLayoutProperty(id, name, value, options));
} else {
this.map.style.setLayoutProperty(layerId, name, value, options);
}
}
/**
* 扩展 map 的 getLayoutProperty。
* @param {string} layerId
* @param {string} name
* @returns {object}
*/
getLayoutProperty(layerId, name) {
const realLayerId = this.getFirstLayerId(layerId);
return this.map.style.getLayoutProperty(realLayerId, name);
}
/**
* 遍历 this._layerSymbols,更新使用到symbolId的图层。
* @param {string} symbolId
*/
updateLayerSymbol(symbolId) {
Object.keys(this._layerSymbols).forEach(layerId => {
const symbol = this._layerSymbols[layerId];
if (symbol.includes(symbolId)) {
this.setSymbol(layerId, symbol);
}
})
}
/**
* 更新符号。
* @param {string} symbolId
* @param {object | array} symbol
*/
updateSymbol(symbolId, symbol) {
// symbol不存在
if (!this.getSymbol(symbolId)) {
return this.map.fire('error', {
error: new Error(`Symbol "${symbolId}" could not be loaded. Please make sure you have added the symbol with map.addSymbol().`)
});
}
if (validateSymbol(symbol)) {
// 更新symbol
this.symbolManager.addSymbol(symbolId, symbol);
this.updateLayerSymbol(symbolId);
} else {
return this.map.fire('error', {
error: new Error('Symbol is not supported expressions.')
});
}
}
/**
* 设置 symbol 属性值。
* @param {string} symbolId
* @param {number} symbolIndex
* @param {string} name
* @param {any} value
*/
setSymbolProperty(symbolId, symbolIndex, name, value) {
const symbol = this.getSymbol(symbolId);
// symbol 不存在
if (!symbol) {
return this.map.fire('error', {
error: new Error(`Symbol "${symbolId}" could not be loaded. Please make sure you have added the symbol with map.addSymbol().`)
});
}
// value 不支持表达式
if (isMapboxExpression(value)) {
return this.map.fire('error', {
error: new Error('Symbol value is not supported expressions.')
});
}
const paintOrLayout = isPaintKey(name) ? 'paint' : 'layout';
if (symbol.length > 0) {
const symbolChild = symbol[symbolIndex];
if (!symbolChild) {
return this.map.fire('error', {
error: new Error(`symbol[${symbolIndex}] does not exist.`)
});
}
if (!symbolChild[paintOrLayout]) {
symbolChild[paintOrLayout] = {};
}
Object.assign(symbolChild[paintOrLayout], { [name]: value });
} else {
if (!symbol[paintOrLayout]) {
symbol[paintOrLayout] = {};
}
Object.assign(symbol[paintOrLayout], { [name]: value });
}
// 更新 symbol
this.symbolManager.addSymbol(symbolId, symbol);
this.updateLayerSymbol(symbolId);
}
/**
* 获取 symbol 的属性值
* @param {string} symbolId
* @param {number} symbolIndex
* @param {string} name
* @returns {any}
*/
getSymbolProperty(symbolId, symbolIndex, name) {
const symbol = this.getSymbol(symbolId);
// symbol 不存在
if (!symbol) {
this.map.fire('error', {
error: new Error(`Symbol "${symbolId}" could not be loaded. Please make sure you have added the symbol with map.addSymbol().`)
});
return;
}
const paintOrLayout = isPaintKey(name) ? 'paint' : 'layout';
if (symbol.length > 0) {
return symbol[symbolIndex] && symbol[symbolIndex][paintOrLayout] && symbol[symbolIndex][paintOrLayout][name];
} else {
return symbol[paintOrLayout] && symbol[paintOrLayout][name];
}
}
}
export default SymbolHandler;