Merker set changes

- Use separate panes for each marker set
- Set pane z-index based on marker set priority
- Optimize zoom visibility checks
This commit is contained in:
James Lyne 2020-12-18 22:49:28 +00:00
parent f7d1ef4cfc
commit 0eea500098
2 changed files with 84 additions and 34 deletions

View File

@ -52,10 +52,16 @@ export default defineComponent({
} }
}, },
setup() { setup(props) {
const store = useStore(), const store = useStore(),
markerSettings = computed(() => store.state.components.markers), 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 { return {
markerSettings, markerSettings,

View File

@ -14,9 +14,14 @@
* limitations under the License. * 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 { 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; minZoom?: number;
maxZoom?: number; maxZoom?: number;
} }
@ -24,33 +29,44 @@ export interface DynmapLayerGroupOptions extends LayerOptions {
export default class DynmapLayerGroup extends LayerGroup { export default class DynmapLayerGroup extends LayerGroup {
// @ts-ignore // @ts-ignore
options: DynmapLayerGroupOptions; options: DynmapLayerGroupOptions;
_layerVisibility: Map<Layer, boolean>; _zoomLimitedLayers: Set<Layer>; //Layers which are zoom limited and should be checked on zoom
_layers: any; _layers: any;
_markerPane?: HTMLElement;
_vectorPane?: HTMLElement;
constructor(layers?: Layer[], options?: DynmapLayerGroupOptions) { _zoomEndCallback = () => this._updateLayerVisibility();
super(layers, options);
constructor(options: DynmapLayerGroupOptions) {
super([], options);
Util.setOptions(this, options); Util.setOptions(this, options);
this._layerVisibility = new Map(); this._zoomLimitedLayers = new Set();
} }
onAdd(map: LeafletMap) { onAdd(map: LeafletMap) {
map.on('zoomend', this._handleZoomChange, this); map.on('zoomend', this._zoomEndCallback, this);
this._handleZoomChange();
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; return this;
} }
onRemove(map: LeafletMap) { onRemove(map: LeafletMap) {
super.onRemove(map); super.onRemove(map);
this._layerVisibility.clear(); map.off('zoomend', this._zoomEndCallback, this);
map.off('zoomend', this._handleZoomChange, this);
return this; return this;
} }
clearLayers(): this { clearLayers(): this {
this._layerVisibility.clear(); this._zoomLimitedLayers.clear();
return super.clearLayers(); return super.clearLayers();
} }
@ -59,53 +75,81 @@ export default class DynmapLayerGroup extends LayerGroup {
this._layers[id] = layer; 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 zoomLimited = this._isLayerZoomLimited(layer);
const visible = this._isLayerVisible(layer, this._map.getZoom());
this._layerVisibility.set(layer, visible);
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); this._map.addLayer(layer);
} }
} else { } else {
this._layerVisibility.set(layer, false); this._map.addLayer(layer);
}
} }
return this; return this;
} }
removeLayer(layer: Layer): this { removeLayer(layer: Layer): this {
this._layerVisibility.delete(layer); this._zoomLimitedLayers.delete(layer);
return super.addLayer(layer); return super.addLayer(layer);
} }
_handleZoomChange() { _updateLayerVisibility(onAdd?: boolean) {
if(!this._map) { if(!this._map) {
return; return;
} }
const zoom = this._map.getZoom(); const zoom = this._map.getZoom();
//FIXME: Keep track of layers that actually have min/max zoom, to avoid pointless checking of every layer? //The whole group is zoom limited
if(this._isZoomLimited()) {
const visible = zoom >= (this.options.minZoom || -Infinity) && zoom <= (this.options.maxZoom || Infinity);
this.eachLayer((layer) => { this.eachLayer((layer) => {
const newVisibility = this._isLayerVisible(layer, zoom), //Per marker zoom limits take precedence, if present
currentVisibility = this._layerVisibility.get(layer); if(this._zoomLimitedLayers.has(layer)) {
this._isLayerVisible(layer, zoom) ? this._map.addLayer(layer) : this._map.removeLayer(layer);
if(newVisibility) { } else { //Otherwise apply group zoom limit
if(!currentVisibility) { visible ? this._map.addLayer(layer) : this._map.removeLayer(layer);
this._map.addLayer(layer);
} }
} else if(currentVisibility) {
this._map.removeLayer(layer);
}
this._layerVisibility.set(layer, newVisibility);
}, this); }, 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);
}
}
//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) { _isLayerVisible(layer: Layer, currentZoom: number) {
let minZoom = this.options.minZoom || -Infinity, let minZoom = -Infinity,
maxZoom = this.options.maxZoom || Infinity; maxZoom = Infinity;
if((layer as any).options && (layer as any).options.minZoom !== undefined) { if((layer as any).options && (layer as any).options.minZoom !== undefined) {
minZoom = (layer as any).options.minZoom; minZoom = (layer as any).options.minZoom;