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(),
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,

View File

@ -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<Layer, boolean>;
_zoomLimitedLayers: Set<Layer>; //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;