diff --git a/src/components/map/layer/MarkerSetLayer.vue b/src/components/map/layer/MarkerSetLayer.vue index f58ccc1..55f44d2 100644 --- a/src/components/map/layer/MarkerSetLayer.vue +++ b/src/components/map/layer/MarkerSetLayer.vue @@ -52,10 +52,16 @@ export default defineComponent({ } }, - setup() { + setup(props) { const store = useStore(), markerSettings = computed(() => store.state.components.markers), - layerGroup = new DynmapLayerGroup(); + layerGroup = new DynmapLayerGroup({ + id: props.markerSet.id, + minZoom: props.markerSet.minZoom, + maxZoom: props.markerSet.maxZoom, + showLabels: props.markerSet.showLabels || store.state.components.markers.showLabels, + priority: props.markerSet.priority, + }); return { markerSettings, diff --git a/src/leaflet/layer/DynmapLayerGroup.ts b/src/leaflet/layer/DynmapLayerGroup.ts index 9b8c44f..c20d3b6 100644 --- a/src/leaflet/layer/DynmapLayerGroup.ts +++ b/src/leaflet/layer/DynmapLayerGroup.ts @@ -14,9 +14,14 @@ * limitations under the License. */ -import {Layer, Map as LeafletMap, LayerGroup, LayerOptions, Util} from "leaflet"; +import {Layer, Map as LeafletMap, LayerGroup, LayerOptions, Util, Marker, Path} from "leaflet"; export interface DynmapLayerGroupOptions extends LayerOptions { + id: string; //Added to the name of layer group panes + showLabels: boolean; + priority: number; //Added to the z-index of layer group panes + + //Zoom limits for the whole group, can be overridden by layers minZoom?: number; maxZoom?: number; } @@ -24,33 +29,44 @@ export interface DynmapLayerGroupOptions extends LayerOptions { export default class DynmapLayerGroup extends LayerGroup { // @ts-ignore options: DynmapLayerGroupOptions; - _layerVisibility: Map; + _zoomLimitedLayers: Set; //Layers which are zoom limited and should be checked on zoom _layers: any; + _markerPane?: HTMLElement; + _vectorPane?: HTMLElement; - constructor(layers?: Layer[], options?: DynmapLayerGroupOptions) { - super(layers, options); + _zoomEndCallback = () => this._updateLayerVisibility(); + + constructor(options: DynmapLayerGroupOptions) { + super([], options); Util.setOptions(this, options); - this._layerVisibility = new Map(); + this._zoomLimitedLayers = new Set(); } onAdd(map: LeafletMap) { - map.on('zoomend', this._handleZoomChange, this); - this._handleZoomChange(); + map.on('zoomend', this._zoomEndCallback, this); + + this._map = map; + this._markerPane = map.createPane(`${this.options.id}-markers`); + this._vectorPane = map.createPane(`${this.options.id}-vectors`); + + this._markerPane.style.zIndex = (401 + this.options.priority).toString(); + this._vectorPane.style.zIndex = (400 + this.options.priority).toString(); + + this._updateLayerVisibility(true); return this; } onRemove(map: LeafletMap) { super.onRemove(map); - this._layerVisibility.clear(); - map.off('zoomend', this._handleZoomChange, this); + map.off('zoomend', this._zoomEndCallback, this); return this; } clearLayers(): this { - this._layerVisibility.clear(); + this._zoomLimitedLayers.clear(); return super.clearLayers(); } @@ -59,53 +75,81 @@ export default class DynmapLayerGroup extends LayerGroup { this._layers[id] = layer; + if (layer instanceof Marker) { + layer.options.pane = `${this.options.id}-markers`; + } else if (layer instanceof Path) { + layer.options.pane = `${this.options.id}-vectors`; + } - if(this._map) { - const visible = this._isLayerVisible(layer, this._map.getZoom()); - this._layerVisibility.set(layer, visible); + const zoomLimited = this._isLayerZoomLimited(layer); - if(visible) { + if (zoomLimited) { + this._zoomLimitedLayers.add(layer); + } + + if (this._map) { + //If layer is zoom limited, only add to map if it should be visible + if (zoomLimited) { + if (this._isLayerVisible(layer, this._map.getZoom())) { + this._map.addLayer(layer); + } + } else { this._map.addLayer(layer); } - } else { - this._layerVisibility.set(layer, false); } return this; } removeLayer(layer: Layer): this { - this._layerVisibility.delete(layer); + this._zoomLimitedLayers.delete(layer); return super.addLayer(layer); } - _handleZoomChange() { + _updateLayerVisibility(onAdd?: boolean) { if(!this._map) { return; } const zoom = this._map.getZoom(); - //FIXME: Keep track of layers that actually have min/max zoom, to avoid pointless checking of every layer? - this.eachLayer((layer) => { - const newVisibility = this._isLayerVisible(layer, zoom), - currentVisibility = this._layerVisibility.get(layer); + //The whole group is zoom limited + if(this._isZoomLimited()) { + const visible = zoom >= (this.options.minZoom || -Infinity) && zoom <= (this.options.maxZoom || Infinity); - if(newVisibility) { - if(!currentVisibility) { - this._map.addLayer(layer); - } - } else if(currentVisibility) { - this._map.removeLayer(layer); + this.eachLayer((layer) => { + //Per marker zoom limits take precedence, if present + if(this._zoomLimitedLayers.has(layer)) { + this._isLayerVisible(layer, zoom) ? this._map.addLayer(layer) : this._map.removeLayer(layer); + } else { //Otherwise apply group zoom limit + visible ? this._map.addLayer(layer) : this._map.removeLayer(layer); } + }, this); + //Group isn't zoom limited, but some individual markers are + } else if(this._zoomLimitedLayers.size) { + this._zoomLimitedLayers.forEach((layer) => { + this._isLayerVisible(layer, zoom) ? this._map.addLayer(layer) : this._map.removeLayer(layer); + }); + //Nothing is zoom limited, but we've just been added to the map + } else if(onAdd) { + this.eachLayer(this._map.addLayer, this._map); + } + } - this._layerVisibility.set(layer, newVisibility); - }, this); + //Returns if this layer group has zoom limits defined + _isZoomLimited() { + return this.options.maxZoom !== undefined || this.options.minZoom !== undefined; + } + + //Returns if the given layer has its own zoom limits defined + _isLayerZoomLimited(layer: Layer) { + return ((layer as any).options && (layer as any).options.minZoom !== undefined) + && ((layer as any).options && (layer as any).options.maxZoom !== undefined); } _isLayerVisible(layer: Layer, currentZoom: number) { - let minZoom = this.options.minZoom || -Infinity, - maxZoom = this.options.maxZoom || Infinity; + let minZoom = -Infinity, + maxZoom = Infinity; if((layer as any).options && (layer as any).options.minZoom !== undefined) { minZoom = (layer as any).options.minZoom;