Preparation for overlay TileLayers

This commit is contained in:
James Lyne 2022-02-25 18:05:36 +00:00
parent 06ac12ba29
commit 80bb800e04
12 changed files with 154 additions and 83 deletions

View File

@ -17,7 +17,9 @@
<template>
<div class="map" :style="{backgroundColor: mapBackground }" v-bind="$attrs" :aria-label="mapTitle">
<template v-if="leaflet">
<MapLayer v-for="[name, map] in maps" :key="name" :map="map" :name="name" :leaflet="leaflet"></MapLayer>
<TileLayer v-for="[name, map] in maps" :key="name" :options="map" :leaflet="leaflet"></TileLayer>
<TileLayerOverlay v-for="[name, overlay] in overlays" :key="name" :options="overlay" :leaflet="leaflet"></TileLayerOverlay>
<PlayersLayer v-if="playerMarkersEnabled" :leaflet="leaflet"></PlayersLayer>
<MarkerSetLayer v-for="[name, markerSet] in markerSets" :key="name" :markerSet="markerSet" :leaflet="leaflet"></MarkerSetLayer>
@ -38,7 +40,7 @@
import {computed, ref, defineComponent} from "@vue/runtime-core";
import {CRS, LatLng, LatLngBounds, PanOptions, ZoomPanOptions} from 'leaflet';
import {useStore} from '@/store';
import MapLayer from "@/components/map/layer/MapLayer.vue";
import TileLayer from "@/components/map/layer/TileLayer.vue";
import PlayersLayer from "@/components/map/layer/PlayersLayer.vue";
import MarkerSetLayer from "@/components/map/layer/MarkerSetLayer.vue";
import CoordinatesControl from "@/components/map/control/CoordinatesControl.vue";
@ -52,11 +54,13 @@ import {LoadingControl} from "@/leaflet/control/LoadingControl";
import MapContextMenu from "@/components/map/MapContextMenu.vue";
import {LiveAtlasLocation, LiveAtlasPlayer, LiveAtlasMapViewTarget} from "@/index";
import LoginControl from "@/components/map/control/LoginControl.vue";
import TileLayerOverlay from "@/components/map/layer/TileLayerOverlay.vue";
export default defineComponent({
components: {
TileLayerOverlay,
MapContextMenu,
MapLayer,
TileLayer,
PlayersLayer,
MarkerSetLayer,
CoordinatesControl,
@ -72,6 +76,7 @@ export default defineComponent({
leaflet = undefined as any,
maps = computed(() => store.state.maps),
overlays = computed(() => store.state.currentMap?.overlays),
markerSets = computed(() => store.state.markerSets),
configuration = computed(() => store.state.configuration),
@ -100,6 +105,7 @@ export default defineComponent({
return {
leaflet,
maps,
overlays,
markerSets,
configuration,

View File

@ -15,38 +15,29 @@
-->
<script lang="ts">
import {defineComponent, onUnmounted, computed, watch} from "@vue/runtime-core";
import {Map} from 'leaflet';
import {computed, defineComponent, onUnmounted, watch} from "@vue/runtime-core";
import {useStore} from "@/store";
import {LiveAtlasTileLayerOptions} from "@/leaflet/tileLayer/LiveAtlasTileLayer";
import LiveAtlasLeafletMap from "@/leaflet/LiveAtlasLeafletMap";
import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition";
import {LiveAtlasTileLayer} from "@/leaflet/tileLayer/LiveAtlasTileLayer";
export default defineComponent({
props: {
name: {
type: String,
required: true
},
map: {
type: Object as () => LiveAtlasMapDefinition,
options: {
type: Object as () => LiveAtlasTileLayerOptions,
required: true
},
leaflet: {
type: Object as () => Map,
type: Object as () => LiveAtlasLeafletMap,
required: true,
}
},
setup(props) {
const store = useStore(),
active = computed(() => props.map === store.state.currentMap);
active = computed(() => props.options instanceof LiveAtlasMapDefinition && props.options === store.state.currentMap);
let layer: LiveAtlasTileLayer;
layer = store.state.currentMapProvider!.createTileLayer({
errorTileUrl: 'images/blank.png',
mapSettings: Object.freeze(JSON.parse(JSON.stringify(props.map))),
});
let layer = store.state.currentMapProvider!.createTileLayer(Object.freeze(JSON.parse(JSON.stringify(props.options))));
const enableLayer = () => props.leaflet.addLayer(layer),
disableLayer = () => layer.remove();

View File

@ -0,0 +1,52 @@
<!--
- Copyright 2022 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.
-->
<script lang="ts">
import {defineComponent, onUnmounted} from "@vue/runtime-core";
import {useStore} from "@/store";
import LiveAtlasLeafletMap from "@/leaflet/LiveAtlasLeafletMap";
import {LiveAtlasTileLayerOverlay} from "@/index";
export default defineComponent({
props: {
options: {
type: Object as () => LiveAtlasTileLayerOverlay,
required: true
},
leaflet: {
type: Object as () => LiveAtlasLeafletMap,
required: true,
}
},
setup(props) {
const store = useStore();
let layer = store.state.currentMapProvider!.createTileLayer(Object.freeze(JSON.parse(JSON.stringify(props.options.tileLayerOptions))));
props.leaflet.getLayerManager().addHiddenLayer(layer, props.options.label, props.options.priority);
onUnmounted(() => {
props.leaflet.getLayerManager().removeLayer(layer);
layer.remove();
});
},
render() {
return null;
},
});
</script>

9
src/index.d.ts vendored
View File

@ -193,16 +193,23 @@ interface LiveAtlasMapProvider {
register(formData: FormData): void;
}
interface LiveAtlasMarkerSet {
interface LiveAtlasOverlay {
id: string,
label: string;
hidden: boolean;
priority: number;
minZoom?: number;
maxZoom?: number;
}
interface LiveAtlasMarkerSet extends LiveAtlasOverlay {
showLabels?: boolean;
}
interface LiveAtlasTileLayerOverlay extends LiveAtlasOverlay {
tileLayerOptions: LiveAtlasTileLayerOptions;
}
interface LiveAtlasMarker {
id: string;
type: LiveAtlasMarkerType;

View File

@ -31,7 +31,6 @@ import {TileInformation} from "dynmap";
// noinspection JSUnusedGlobalSymbols
export class DynmapTileLayer extends LiveAtlasTileLayer {
private readonly _namedTiles: Map<any, any>;
private readonly _baseUrl: string;
private readonly _store: Store = useStore();
private readonly _night: ComputedRef<boolean>;
@ -41,12 +40,9 @@ export class DynmapTileLayer extends LiveAtlasTileLayer {
private _updateFrame: number = 0;
constructor(options: LiveAtlasTileLayerOptions) {
super('', options);
super(options);
this._mapSettings = options.mapSettings;
this._baseUrl = options.mapSettings.baseUrl;
this._namedTiles = Object.seal(new Map());
this._pendingUpdates = computed(() => !!this._store.state.pendingTileUpdates.length);
this._night = computed(() => this._store.getters.night);
}
@ -62,7 +58,7 @@ export class DynmapTileLayer extends LiveAtlasTileLayer {
});
this._nightUnwatch = watch(this._night, () => {
if(this._mapSettings.nightAndDay) {
if(this.options.nightAndDay) {
this.redraw();
}
});
@ -83,8 +79,7 @@ export class DynmapTileLayer extends LiveAtlasTileLayer {
}
private getTileUrlFromName(name: string, timestamp?: number) {
const path = escape(`${this._mapSettings.world.name}/${name}`);
let url = `${this._baseUrl}${path}`;
let url = this.options.baseUrl + name;
if(typeof timestamp !== 'undefined') {
url += (url.indexOf('?') === -1 ? `?timestamp=${timestamp}` : `&timestamp=${timestamp}`);
@ -164,21 +159,21 @@ export class DynmapTileLayer extends LiveAtlasTileLayer {
// izoom: max zoomed in = 0, max zoomed out = this.options.maxZoom
// zoomoutlevel: izoom < mapzoomin -> 0, else -> izoom - mapzoomin (which ranges from 0 till mapzoomout)
const izoom = this._getZoomForUrl(),
zoomoutlevel = Math.max(0, izoom - this._mapSettings.extraZoomLevels),
zoomoutlevel = Math.max(0, izoom - (this.options.extraZoomLevels || 0)),
scale = (1 << zoomoutlevel),
x = scale * coords.x,
y = scale * coords.y;
return {
prefix: this._mapSettings.prefix,
nightday: (this._mapSettings.nightAndDay && !this._night.value) ? '_day' : '',
prefix: this.options.prefix || '',
nightday: (this.options.nightAndDay && !this._night.value) ? '_day' : '',
scaledx: x >> 5,
scaledy: y >> 5,
zoom: this.zoomprefix(zoomoutlevel),
zoomprefix: (zoomoutlevel == 0) ? "" : (this.zoomprefix(zoomoutlevel) + "_"),
x: x,
y: y,
fmt: this._mapSettings.imageFormat || 'png'
fmt: this.options.imageFormat || 'png'
};
}

View File

@ -14,33 +14,66 @@
* limitations under the License.
*/
import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition";
import {Map as LeafletMap, Coords, DomUtil, DoneCallback, TileLayer, TileLayerOptions, Util} from "leaflet";
import {LiveAtlasInternalTiles, LiveAtlasTileElement} from "@/index";
import falseFn = Util.falseFn;
import {ImageFormat} from "dynmap";
export interface LiveAtlasTileLayerOptions extends TileLayerOptions {
mapSettings: LiveAtlasMapDefinition;
errorTileUrl: string;
export interface LiveAtlasTileLayerOptions {
baseUrl: string;
tileSize: number;
imageFormat: ImageFormat;
prefix?: string;
nightAndDay?: boolean;
nativeZoomLevels: number;
extraZoomLevels?: number;
minZoom?: number;
maxZoom?: number;
tileUpdateInterval?: number;
}
export interface LiveAtlasTileLayerInternalOptions extends TileLayerOptions {
baseUrl: string;
imageFormat: ImageFormat;
prefix: string;
nightAndDay: boolean;
extraZoomLevels: number;
tileUpdateInterval?: number;
}
// noinspection JSUnusedGlobalSymbols
export abstract class LiveAtlasTileLayer extends TileLayer {
declare options: LiveAtlasTileLayerOptions;
declare options: LiveAtlasTileLayerInternalOptions;
declare _tiles: LiveAtlasInternalTiles;
declare _url: string;
protected _mapSettings: LiveAtlasMapDefinition;
private readonly tileTemplate: LiveAtlasTileElement;
protected readonly tileTemplate: LiveAtlasTileElement;
protected readonly loadQueue: LiveAtlasTileElement[] = [];
private readonly loadingTiles: Set<LiveAtlasTileElement> = Object.seal(new Set());
private refreshTimeout?: ReturnType<typeof setTimeout>;
protected readonly loadingTiles: Set<LiveAtlasTileElement> = Object.seal(new Set());
protected refreshTimeout?: ReturnType<typeof setTimeout>;
private static genericLoadError = new Error('Tile failed to load');
protected static genericLoadError = new Error('Tile failed to load');
protected constructor(url: string, options: LiveAtlasTileLayerOptions) {
super(url, options);
protected constructor(options: LiveAtlasTileLayerOptions) {
super('', {
errorTileUrl: 'images/blank.png',
zoomReverse: true,
tileSize: options.tileSize,
maxNativeZoom: options.nativeZoomLevels,
minZoom: options.minZoom,
maxZoom: options.maxZoom || options.nativeZoomLevels + (options.extraZoomLevels || 0),
});
Util.setOptions(this, {
imageFormat: options.imageFormat,
baseUrl: options.baseUrl,
tileUpdateInterval: options.tileUpdateInterval,
nightAndDay: !!options.nightAndDay,
prefix: options.prefix || '',
extraZoomLevels: options.extraZoomLevels || 0,
nativeZoomLevels: options.nativeZoomLevels,
});
this._mapSettings = options.mapSettings;
this.tileTemplate = DomUtil.create('img', 'leaflet-tile') as LiveAtlasTileElement;
this.tileTemplate.style.width = this.tileTemplate.style.height = this.options.tileSize + 'px';
this.tileTemplate.alt = '';
@ -53,14 +86,6 @@ export abstract class LiveAtlasTileLayer extends TileLayer {
}
Object.seal(this.tileTemplate);
options.maxZoom = this._mapSettings.nativeZoomLevels + this._mapSettings.extraZoomLevels;
options.maxNativeZoom = this._mapSettings.nativeZoomLevels;
options.zoomReverse = true;
options.tileSize = this._mapSettings.tileSize;
options.minZoom = 0;
Util.setOptions(this, options);
}
// @method createTile(coords: Object, done?: Function): HTMLElement
@ -209,8 +234,8 @@ export abstract class LiveAtlasTileLayer extends TileLayer {
}
onAdd(map: LeafletMap): this {
if(this._mapSettings.tileUpdateInterval) {
this.refreshTimeout = setTimeout(() => this.handlePeriodicRefresh(), this._mapSettings.tileUpdateInterval);
if(this.options.tileUpdateInterval) {
this.refreshTimeout = setTimeout(() => this.handlePeriodicRefresh(), this.options.tileUpdateInterval);
}
return super.onAdd(map);
@ -229,6 +254,6 @@ export abstract class LiveAtlasTileLayer extends TileLayer {
this.refresh();
}
this.refreshTimeout = setTimeout(() => this.handlePeriodicRefresh(), this._mapSettings.tileUpdateInterval);
this.refreshTimeout = setTimeout(() => this.handlePeriodicRefresh(), this.options.tileUpdateInterval);
}
}

View File

@ -22,23 +22,15 @@ import {Coords, Util} from "leaflet";
// noinspection JSUnusedGlobalSymbols
export class OverviewerTileLayer extends LiveAtlasTileLayer {
private readonly _baseUrl: string;
constructor(options: LiveAtlasTileLayerOptions) {
super('', options);
super(options);
options.zoomReverse = false;
Util.setOptions(this, options);
this._mapSettings = options.mapSettings;
this._baseUrl = options.mapSettings.baseUrl;
Util.setOptions(this, {zoomReverse: false});
}
getTileUrl(coords: Coords): string {
let url = this._mapSettings.name;
const zoom = coords.z,
urlBase = this._mapSettings.prefix;
let url = this.options.prefix;
const zoom = coords.z;
if(coords.x < 0 || coords.x >= Math.pow(2, zoom) ||
coords.y < 0 || coords.y >= Math.pow(2, zoom)) {
@ -52,10 +44,10 @@ export class OverviewerTileLayer extends LiveAtlasTileLayer {
url += '/' + (x + 2 * y);
}
}
url = url + '.' + this._mapSettings.imageFormat;
url += `.${this.options.imageFormat}`;
// if(typeof overviewerConfig.map.cacheTag !== 'undefined') {
// url += '?c=' + overviewerConfig.map.cacheTag;
// }
return(this._baseUrl + urlBase + url);
return this.options.baseUrl + url;
}
}

View File

@ -19,11 +19,9 @@ import {Util} from "leaflet";
// noinspection JSUnusedGlobalSymbols
export class Pl3xmapTileLayer extends LiveAtlasTileLayer {
constructor(options: LiveAtlasTileLayerOptions) {
super(`${options.mapSettings.baseUrl}${options.mapSettings.world.name}/{z}/{x}_{y}.png`, options);
options.zoomReverse = false;
Util.setOptions(this, options);
constructor(map: LiveAtlasTileLayerOptions) {
super(map);
this._url = `${map.baseUrl}{z}/{x}_{y}.png`;
Util.setOptions(this, {zoomReverse: false});
}
}

View File

@ -14,11 +14,12 @@
* limitations under the License.
*/
import {Coordinate, LiveAtlasProjection, LiveAtlasWorldDefinition} from "@/index";
import {Coordinate, LiveAtlasProjection, LiveAtlasTileLayerOverlay, LiveAtlasWorldDefinition} from "@/index";
import {LatLng} from "leaflet";
import {ImageFormat} from "dynmap";
import {LiveAtlasTileLayerOptions} from "@/leaflet/tileLayer/LiveAtlasTileLayer";
export interface LiveAtlasMapDefinitionOptions {
export interface LiveAtlasMapDefinitionOptions extends LiveAtlasTileLayerOptions {
world: LiveAtlasWorldDefinition;
appendedWorld?: LiveAtlasWorldDefinition; // append_to_world
@ -45,9 +46,10 @@ export interface LiveAtlasMapDefinitionOptions {
tileUpdateInterval?: number;
center?: Coordinate;
overlays?: Map<string, LiveAtlasTileLayerOverlay>;
}
export default class LiveAtlasMapDefinition {
export default class LiveAtlasMapDefinition implements LiveAtlasTileLayerOptions {
readonly world: LiveAtlasWorldDefinition;
readonly appendedWorld?: LiveAtlasWorldDefinition;
@ -74,6 +76,7 @@ export default class LiveAtlasMapDefinition {
readonly tileUpdateInterval?: number;
readonly center?: Coordinate;
readonly overlays: Map<string, LiveAtlasTileLayerOverlay>;
readonly scale: number;
@ -105,6 +108,8 @@ export default class LiveAtlasMapDefinition {
this.tileUpdateInterval = options.tileUpdateInterval || undefined;
this.center = options.center || undefined;
this.overlays = options.overlays || new Map();
this.scale = (1 / Math.pow(2, this.nativeZoomLevels));
}

View File

@ -120,7 +120,7 @@ export default class OverviewerMapProvider extends MapProvider {
name: tileset.path,
displayName: tileset.name || tileset.path,
baseUrl: this.config,
baseUrl: `${this.config}${tileset.base}/${tileset.path}`,
tileSize,
projection: new OverviewerProjection({
upperRight: serverResponse.CONST.UPPERRIGHT,

View File

@ -187,7 +187,7 @@ export default class Pl3xmapMapProvider extends MapProvider {
displayName: 'Flat',
icon: world.icon ? `${this.config}images/icon/${world.icon}.png` : undefined,
baseUrl: `${this.config}tiles/`,
baseUrl: `${this.config}tiles/${w.name}/`,
imageFormat: 'png',
tileSize: 512,

View File

@ -143,7 +143,7 @@ export function buildWorlds(response: Configuration, config: DynmapUrlConfig): A
displayName: map.title,
icon: (map.icon || undefined) as string | undefined,
baseUrl: config.tiles,
baseUrl: `${config.tiles}${actualWorld.name}/`,
imageFormat: map['image-format'] || 'png',
tileSize,
projection: new DynmapProjection({