Respect min/max zoom settings for marker sets

This commit is contained in:
James Lyne 2020-12-18 20:25:34 +00:00
parent fd26eee713
commit d6693b4c0d
14 changed files with 256 additions and 48 deletions

View File

@ -210,8 +210,8 @@ function buildMarker(marker: any): DynmapMarker {
dimensions: marker.dim ? marker.dim.split('x') : [16, 16], dimensions: marker.dim ? marker.dim.split('x') : [16, 16],
icon: marker.icon || "default", icon: marker.icon || "default",
isHTML: marker.markup || false, isHTML: marker.markup || false,
minZoom: marker.minzoom || undefined, minZoom: typeof marker.minzoom !== 'undefined' && marker.minzoom > -1 ? marker.minzoom : undefined,
maxZoom: marker.maxZoom || undefined, maxZoom: typeof marker.maxzoom !== 'undefined' && marker.maxzoom > -1 ? marker.maxzoom : undefined,
popupContent: marker.desc || undefined, popupContent: marker.desc || undefined,
}; };
} }
@ -244,8 +244,8 @@ function buildArea(area: any): DynmapArea {
x: area.x || [0, 0], x: area.x || [0, 0],
y: [area.ybottom || 0, area.ytop || 0], y: [area.ybottom || 0, area.ytop || 0],
z: area.z || [0, 0], z: area.z || [0, 0],
minZoom: area.minzoom || undefined, minZoom: typeof area.minzoom !== 'undefined' && area.minzoom > -1 ? area.minzoom : undefined,
maxZoom: area.maxZoom || undefined, maxZoom: typeof area.maxzoom !== 'undefined' && area.maxzoom > -1 ? area.maxzoom : undefined,
popupContent: area.desc || undefined, popupContent: area.desc || undefined,
}; };
} }
@ -276,8 +276,8 @@ function buildLine(line: any): DynmapLine {
}, },
label: line.label || '', label: line.label || '',
isHTML: line.markup || false, isHTML: line.markup || false,
minZoom: line.minzoom || undefined, minZoom: typeof line.minzoom !== 'undefined' && line.minzoom > -1 ? line.minzoom : undefined,
maxZoom: line.maxZoom || undefined, maxZoom: typeof line.maxzoom !== 'undefined' && line.maxzoom > -1 ? line.maxzoom : undefined,
popupContent: line.desc || undefined, popupContent: line.desc || undefined,
}; };
} }
@ -314,8 +314,8 @@ function buildCircle(circle: any): DynmapCircle {
label: circle.label || '', label: circle.label || '',
isHTML: circle.markup || false, isHTML: circle.markup || false,
minZoom: circle.minzoom || undefined, minZoom: typeof circle.minzoom !== 'undefined' && circle.minzoom > -1 ? circle.minzoom : undefined,
maxZoom: circle.maxZoom || undefined, maxZoom: typeof circle.maxzoom !== 'undefined' && circle.maxzoom > -1 ? circle.maxzoom : undefined,
popupContent: circle.desc || undefined, popupContent: circle.desc || undefined,
}; };
} }
@ -570,8 +570,8 @@ export default {
hidden: set.hide || false, hidden: set.hide || false,
priority: set.layerprio || 0, priority: set.layerprio || 0,
showLabels: set.showlabels || undefined, showLabels: set.showlabels || undefined,
minZoom: set.minzoom || undefined, minZoom: typeof set.minzoom !== 'undefined' && set.minzoom > -1 ? set.minzoom : undefined,
maxZoom: set.maxzoom || undefined, maxZoom: typeof set.maxzoom !== 'undefined' && set.maxzoom > -1 ? set.maxzoom : undefined,
markers, markers,
areas, areas,
lines, lines,

View File

@ -24,13 +24,13 @@
<script lang="ts"> <script lang="ts">
import {defineComponent, computed} from "@vue/runtime-core"; import {defineComponent, computed} from "@vue/runtime-core";
import {useStore} from "@/store"; import {useStore} from "@/store";
import {LayerGroup} from 'leaflet';
import {DynmapMarkerSet} from "@/dynmap"; import {DynmapMarkerSet} from "@/dynmap";
import Areas from "@/components/map/vector/Areas.vue"; import Areas from "@/components/map/vector/Areas.vue";
import Circles from "@/components/map/vector/Circles.vue"; import Circles from "@/components/map/vector/Circles.vue";
import Lines from "@/components/map/vector/Lines.vue"; import Lines from "@/components/map/vector/Lines.vue";
import Markers from "@/components/map/vector/Markers.vue"; import Markers from "@/components/map/vector/Markers.vue";
import DynmapMap from "@/leaflet/DynmapMap"; import DynmapMap from "@/leaflet/DynmapMap";
import DynmapLayerGroup from "@/leaflet/layer/DynmapLayerGroup";
export default defineComponent({ export default defineComponent({
components: { components: {
@ -55,7 +55,7 @@ export default defineComponent({
setup() { setup() {
const store = useStore(), const store = useStore(),
markerSettings = computed(() => store.state.components.markers), markerSettings = computed(() => store.state.components.markers),
layerGroup = new LayerGroup(); layerGroup = new DynmapLayerGroup();
return { return {
markerSettings, markerSettings,

View File

@ -16,12 +16,14 @@
<script lang="ts"> <script lang="ts">
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core"; import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
import {LayerGroup, Polyline, Polygon} from 'leaflet';
import {useStore} from "@/store"; import {useStore} from "@/store";
import {DynmapArea, DynmapMarkerSet} from "@/dynmap"; import {DynmapArea, DynmapMarkerSet} from "@/dynmap";
import {ActionTypes} from "@/store/action-types"; import {ActionTypes} from "@/store/action-types";
import {createArea, updateArea} from "@/util/areas"; import {createArea, updateArea} from "@/util/areas";
import Util from '@/util'; import Util from '@/util';
import DynmapLayerGroup from "@/leaflet/layer/DynmapLayerGroup";
import DynmapPolygon from "@/leaflet/vector/DynmapPolygon";
import DynmapPolyline from "@/leaflet/vector/DynmapPolyline";
export default defineComponent({ export default defineComponent({
props: { props: {
@ -30,7 +32,7 @@ export default defineComponent({
required: true, required: true,
}, },
layerGroup: { layerGroup: {
type: Object as () => LayerGroup, type: Object as () => DynmapLayerGroup,
required: true required: true
} }
}, },
@ -45,7 +47,7 @@ export default defineComponent({
return markerSetUpdates && markerSetUpdates.areaUpdates.length; return markerSetUpdates && markerSetUpdates.areaUpdates.length;
}), }),
layers = Object.freeze(new Map()) as Map<string, Polygon | Polyline>, layers = Object.freeze(new Map()) as Map<string, DynmapPolygon | DynmapPolyline>,
createAreas = () => { createAreas = () => {
const converter = Util.getPointConverter(); const converter = Util.getPointConverter();
@ -59,7 +61,7 @@ export default defineComponent({
}, },
deleteArea = (id: string) => { deleteArea = (id: string) => {
let area = layers.get(id) as Polyline; let area = layers.get(id) as DynmapPolyline;
if(!area) { if(!area) {
return; return;

View File

@ -16,12 +16,14 @@
<script lang="ts"> <script lang="ts">
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core"; import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
import {Polyline, LayerGroup, Polygon} from 'leaflet';
import {useStore} from "@/store"; import {useStore} from "@/store";
import {DynmapCircle, DynmapMarkerSet} from "@/dynmap"; import {DynmapCircle, DynmapMarkerSet} from "@/dynmap";
import {ActionTypes} from "@/store/action-types"; import {ActionTypes} from "@/store/action-types";
import {createCircle, updateCircle} from "@/util/circles"; import {createCircle, updateCircle} from "@/util/circles";
import Util from '@/util'; import Util from '@/util';
import DynmapPolyline from "@/leaflet/vector/DynmapPolyline";
import DynmapPolygon from "@/leaflet/vector/DynmapPolygon";
import DynmapLayerGroup from "@/leaflet/layer/DynmapLayerGroup";
export default defineComponent({ export default defineComponent({
props: { props: {
@ -30,7 +32,7 @@ export default defineComponent({
required: true, required: true,
}, },
layerGroup: { layerGroup: {
type: Object as () => LayerGroup, type: Object as () => DynmapLayerGroup,
required: true required: true
} }
}, },
@ -45,7 +47,7 @@ export default defineComponent({
return markerSetUpdates && markerSetUpdates.circleUpdates.length; return markerSetUpdates && markerSetUpdates.circleUpdates.length;
}), }),
layers = Object.freeze(new Map<string, Polyline | Polygon>()), layers = Object.freeze(new Map<string, DynmapPolyline | DynmapPolygon>()),
createCircles = () => { createCircles = () => {
const converter = Util.getPointConverter(); const converter = Util.getPointConverter();
@ -59,7 +61,7 @@ export default defineComponent({
}, },
deleteCircle = (id: string) => { deleteCircle = (id: string) => {
let circle = layers.get(id) as Polyline; let circle = layers.get(id) as DynmapPolyline;
if (!circle) { if (!circle) {
return; return;

View File

@ -16,12 +16,13 @@
<script lang="ts"> <script lang="ts">
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core"; import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
import {Polyline, LayerGroup, Polygon} from 'leaflet';
import {useStore} from "@/store"; import {useStore} from "@/store";
import {DynmapLine, DynmapMarkerSet} from "@/dynmap"; import {DynmapLine, DynmapMarkerSet} from "@/dynmap";
import {ActionTypes} from "@/store/action-types"; import {ActionTypes} from "@/store/action-types";
import {createLine, updateLine} from "@/util/lines"; import {createLine, updateLine} from "@/util/lines";
import Util from '@/util'; import Util from '@/util';
import DynmapPolyline from "@/leaflet/vector/DynmapPolyline";
import DynmapLayerGroup from "@/leaflet/layer/DynmapLayerGroup";
export default defineComponent({ export default defineComponent({
props: { props: {
@ -30,7 +31,7 @@ export default defineComponent({
required: true, required: true,
}, },
layerGroup: { layerGroup: {
type: Object as () => LayerGroup, type: Object as () => DynmapLayerGroup,
required: true required: true
} }
}, },
@ -45,7 +46,7 @@ export default defineComponent({
return markerSetUpdates && markerSetUpdates.lineUpdates.length; return markerSetUpdates && markerSetUpdates.lineUpdates.length;
}), }),
layers = Object.freeze(new Map<string, Polyline | Polygon>()), layers = Object.freeze(new Map<string, DynmapPolyline>()),
createLines = () => { createLines = () => {
const converter = Util.getPointConverter(); const converter = Util.getPointConverter();
@ -59,7 +60,7 @@ export default defineComponent({
}, },
deleteLine = (id: string) => { deleteLine = (id: string) => {
let line = layers.get(id) as Polyline; let line = layers.get(id) as DynmapPolyline;
if (!line) { if (!line) {
return; return;

View File

@ -16,11 +16,12 @@
<script lang="ts"> <script lang="ts">
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core"; import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
import {LayerGroup, Marker} from 'leaflet'; import {Marker} from 'leaflet';
import {useStore} from "@/store"; import {useStore} from "@/store";
import {DynmapMarker, DynmapMarkerSet} from "@/dynmap"; import {DynmapMarker, DynmapMarkerSet} from "@/dynmap";
import {ActionTypes} from "@/store/action-types"; import {ActionTypes} from "@/store/action-types";
import {createMarker, updateMarker} from "@/util/markers"; import {createMarker, updateMarker} from "@/util/markers";
import DynmapLayerGroup from "@/leaflet/layer/DynmapLayerGroup";
export default defineComponent({ export default defineComponent({
props: { props: {
@ -29,7 +30,7 @@ export default defineComponent({
required: true, required: true,
}, },
layerGroup: { layerGroup: {
type: Object as () => LayerGroup, type: Object as () => DynmapLayerGroup,
required: true required: true
} }
}, },

View File

@ -0,0 +1,120 @@
/*
* Copyright 2020 James Lyne
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Layer, Map as LeafletMap, LayerGroup, LayerOptions, Util} from "leaflet";
export interface DynmapLayerGroupOptions extends LayerOptions {
minZoom?: number;
maxZoom?: number;
}
export default class DynmapLayerGroup extends LayerGroup {
// @ts-ignore
options: DynmapLayerGroupOptions;
_layerVisibility: Map<Layer, boolean>;
_layers: any;
constructor(layers?: Layer[], options?: DynmapLayerGroupOptions) {
super(layers, options);
Util.setOptions(this, options);
this._layerVisibility = new Map();
}
onAdd(map: LeafletMap) {
map.on('zoomend', this._handleZoomChange, this);
this._handleZoomChange();
return this;
}
onRemove(map: LeafletMap) {
super.onRemove(map);
this._layerVisibility.clear();
map.off('zoomend', this._handleZoomChange, this);
return this;
}
clearLayers(): this {
this._layerVisibility.clear();
return super.clearLayers();
}
addLayer(layer: Layer) {
const id = this.getLayerId(layer);
this._layers[id] = layer;
if(this._map) {
const visible = this._isLayerVisible(layer, this._map.getZoom());
this._layerVisibility.set(layer, visible);
if(visible) {
this._map.addLayer(layer);
}
} else {
this._layerVisibility.set(layer, false);
}
return this;
}
removeLayer(layer: Layer): this {
this._layerVisibility.delete(layer);
return super.addLayer(layer);
}
_handleZoomChange() {
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);
if(newVisibility) {
if(!currentVisibility) {
this._map.addLayer(layer);
}
} else if(currentVisibility) {
this._map.removeLayer(layer);
}
this._layerVisibility.set(layer, newVisibility);
}, this);
}
_isLayerVisible(layer: Layer, currentZoom: number) {
let minZoom = this.options.minZoom || -Infinity,
maxZoom = this.options.maxZoom || Infinity;
if((layer as any).options && (layer as any).options.minZoom !== undefined) {
minZoom = (layer as any).options.minZoom;
}
if((layer as any).options && (layer as any).options.maxZoom !== undefined) {
maxZoom = (layer as any).options.maxZoom;
}
return currentZoom >= minZoom && currentZoom <= maxZoom;
}
}

View File

@ -16,8 +16,13 @@
import {MarkerOptions, Marker, Util, LatLngExpression, Icon} from 'leaflet'; import {MarkerOptions, Marker, Util, LatLngExpression, Icon} from 'leaflet';
export interface GenericMarkerOptions extends MarkerOptions {
minZoom?: number;
maxZoom?: number;
}
export class GenericMarker extends Marker { export class GenericMarker extends Marker {
constructor(latLng: LatLngExpression, options: MarkerOptions) { constructor(latLng: LatLngExpression, options: GenericMarkerOptions) {
super(latLng, options); super(latLng, options);
Util.setOptions(this, options); Util.setOptions(this, options);
} }

View File

@ -0,0 +1,29 @@
/*
* Copyright 2020 James Lyne
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {LatLngExpression, Polygon, PolylineOptions, Util} from "leaflet";
export interface DynmapPolygonOptions extends PolylineOptions {
minZoom?: number;
maxZoom?: number;
}
export default class DynmapPolygon extends Polygon {
constructor(latlngs: LatLngExpression[] | LatLngExpression[][] | LatLngExpression[][][], options?: DynmapPolygonOptions) {
super(latlngs, options);
Util.setOptions(this, options);
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2020 James Lyne
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {LatLngExpression, Polyline, PolylineOptions, Util} from "leaflet";
export interface DynmapPolylineOptions extends PolylineOptions {
minZoom?: number;
maxZoom?: number;
}
export default class DynmapPolyline extends Polyline {
constructor(latlngs: LatLngExpression[] | LatLngExpression[][], options?: DynmapPolylineOptions) {
super(latlngs, options);
Util.setOptions(this, options);
}
}

View File

@ -17,13 +17,23 @@
* limitations under the License. * limitations under the License.
*/ */
import {LatLngExpression, Polygon, Polyline} from "leaflet"; import {LatLngExpression} from "leaflet";
import {DynmapArea} from "@/dynmap"; import {DynmapArea} from "@/dynmap";
import DynmapPolyline from "@/leaflet/vector/DynmapPolyline";
import DynmapPolygon from "@/leaflet/vector/DynmapPolygon";
export const createArea = (options: DynmapArea, converter: Function): Polyline | Polygon => { export const createArea = (options: DynmapArea, converter: Function): DynmapPolyline | DynmapPolygon => {
const outline = !options.style.fillOpacity || (options.style.fillOpacity <= 0), const outline = !options.style.fillOpacity || (options.style.fillOpacity <= 0),
points = getPoints(options, converter, outline), points = getPoints(options, converter, outline),
area = outline ? new Polyline(points, options.style) : new Polygon(points, options.style); area = outline ? new DynmapPolyline(points, {
...options.style,
minZoom: options.minZoom,
maxZoom: options.maxZoom,
}) : new DynmapPolygon(points, {
...options.style,
minZoom: options.minZoom,
maxZoom: options.maxZoom,
});
if (options.label) { if (options.label) {
area.bindPopup(() => createPopup(options)); area.bindPopup(() => createPopup(options));
@ -32,7 +42,7 @@ export const createArea = (options: DynmapArea, converter: Function): Polyline |
return area; return area;
}; };
export const updateArea = (area: Polyline | Polygon | undefined, options: DynmapArea, converter: Function): Polyline | Polygon => { export const updateArea = (area: DynmapPolyline | DynmapPolygon | undefined, options: DynmapArea, converter: Function): DynmapPolyline | DynmapPolygon => {
const outline = (options.style && options.style.fillOpacity && (options.style.fillOpacity <= 0)) as boolean, const outline = (options.style && options.style.fillOpacity && (options.style.fillOpacity <= 0)) as boolean,
points = getPoints(options, converter, outline); points = getPoints(options, converter, outline);

View File

@ -18,18 +18,22 @@
*/ */
import {DynmapCircle} from "@/dynmap"; import {DynmapCircle} from "@/dynmap";
import {Polyline, Polygon, LatLngExpression} from "leaflet"; import {LatLngExpression} from "leaflet";
import DynmapPolyline from "@/leaflet/vector/DynmapPolyline";
import DynmapPolygon from "@/leaflet/vector/DynmapPolygon";
export const createCircle = (options: DynmapCircle, converter: Function): Polyline | Polygon => { export const createCircle = (options: DynmapCircle, converter: Function): DynmapPolyline | DynmapPolygon => {
const outline = !options.style.fillOpacity || (options.style.fillOpacity <= 0), const outline = !options.style.fillOpacity || (options.style.fillOpacity <= 0),
points = getCirclePoints(options, converter, outline); points = getCirclePoints(options, converter, outline),
let circle; circle = outline ? new DynmapPolyline(points, {
...options.style,
if(outline) { minZoom: options.minZoom,
circle = new Polyline(points, options.style); maxZoom: options.maxZoom,
} else { }) : new DynmapPolygon(points, {
circle = new Polygon(points, options.style); ...options.style,
} minZoom: options.minZoom,
maxZoom: options.maxZoom,
});
if(options.label) { if(options.label) {
circle.bindPopup(() => createPopup(options)); circle.bindPopup(() => createPopup(options));
@ -38,7 +42,7 @@ export const createCircle = (options: DynmapCircle, converter: Function): Polyli
return circle; return circle;
}; };
export const updateCircle = (circle: Polyline | Polygon | undefined, options: DynmapCircle, converter: Function): Polyline | Polygon => { export const updateCircle = (circle: DynmapPolyline | DynmapPolygon | undefined, options: DynmapCircle, converter: Function): DynmapPolyline | DynmapPolygon => {
const outline = (options.style && options.style.fillOpacity && (options.style.fillOpacity <= 0)) as boolean, const outline = (options.style && options.style.fillOpacity && (options.style.fillOpacity <= 0)) as boolean,
points = getCirclePoints(options, converter, outline); points = getCirclePoints(options, converter, outline);

View File

@ -18,11 +18,16 @@
*/ */
import {DynmapLine} from "@/dynmap"; import {DynmapLine} from "@/dynmap";
import {Polyline, Polygon, LatLngExpression} from "leaflet"; import {LatLngExpression} from "leaflet";
import DynmapPolyline from "@/leaflet/vector/DynmapPolyline";
export const createLine = (options: DynmapLine, converter: Function): Polyline | Polygon => { export const createLine = (options: DynmapLine, converter: Function): DynmapPolyline => {
const points = getLinePoints(options, converter), const points = getLinePoints(options, converter),
line = new Polyline(points, options.style); line = new DynmapPolyline(points, {
...options.style,
minZoom: options.minZoom,
maxZoom: options.maxZoom,
});
if(options.label) { if(options.label) {
line.bindPopup(() => createPopup(options)); line.bindPopup(() => createPopup(options));
@ -31,7 +36,7 @@ export const createLine = (options: DynmapLine, converter: Function): Polyline |
return line; return line;
}; };
export const updateLine = (line: Polyline | Polygon | undefined, options: DynmapLine, converter: Function): Polyline | Polygon => { export const updateLine = (line: DynmapPolyline | undefined, options: DynmapLine, converter: Function): DynmapPolyline => {
const points = getLinePoints(options, converter); const points = getLinePoints(options, converter);
if (!line) { if (!line) {

View File

@ -32,8 +32,8 @@ export const createMarker = (options: DynmapMarker, projection: DynmapProjection
showLabel: false, showLabel: false,
isHtml: options.isHTML, isHtml: options.isHTML,
}), }),
// maxZoom: this.options.maxZoom, maxZoom: options.maxZoom,
// minZoom: this.options.minZoom, minZoom: options.minZoom,
}); });
}; };