From ff77f09025bc062e6f81d1e42392cc509c96a3c7 Mon Sep 17 00:00:00 2001 From: James Lyne Date: Tue, 11 Jan 2022 16:08:30 +0000 Subject: [PATCH] Move marker set contents outside the store, other small optimisations. Improves marker set creation performance by avoiding traversal of all markers when watching the marker set. Various other small changes to reduce allocations when creating markers --- src/components/map/vector/Areas.vue | 5 ++- src/components/map/vector/Circles.vue | 5 ++- src/components/map/vector/Lines.vue | 5 ++- src/components/map/vector/Markers.vue | 5 ++- src/index.d.ts | 5 ++- src/providers/DynmapMapProvider.ts | 37 ++++++++--------- src/providers/Pl3xmapMapProvider.ts | 58 +++++++++++++-------------- src/store/mutation-types.ts | 1 + src/store/mutations.ts | 44 +++++++++++++------- src/store/state.ts | 10 ++++- src/util/dynmap.ts | 31 +++++++------- 11 files changed, 116 insertions(+), 90 deletions(-) diff --git a/src/components/map/vector/Areas.vue b/src/components/map/vector/Areas.vue index 1becc7a..99b12cc 100644 --- a/src/components/map/vector/Areas.vue +++ b/src/components/map/vector/Areas.vue @@ -23,6 +23,7 @@ import LiveAtlasLayerGroup from "@/leaflet/layer/LiveAtlasLayerGroup"; import LiveAtlasPolygon from "@/leaflet/vector/LiveAtlasPolygon"; import LiveAtlasPolyline from "@/leaflet/vector/LiveAtlasPolyline"; import {LiveAtlasArea, LiveAtlasMarkerSet} from "@/index"; +import {nonReactiveState} from "@/store/state"; export default defineComponent({ props: { @@ -51,7 +52,7 @@ export default defineComponent({ createAreas = () => { const converter = currentMap.value!.locationToLatLng.bind(currentMap.value); - props.set.areas.forEach((area: LiveAtlasArea, id: string) => { + nonReactiveState.markers.get(props.set.id)!.areas.forEach((area: LiveAtlasArea, id: string) => { const layer = createArea(area, converter); layers.set(id, layer); @@ -103,7 +104,7 @@ export default defineComponent({ if(newValue && (!oldValue || oldValue.world === newValue.world)) { const converter = newValue.locationToLatLng.bind(newValue); - for (const [id, area] of props.set.areas) { + for (const [id, area] of nonReactiveState.markers.get(props.set.id)!.areas) { updateArea(layers.get(id), area, converter); } } diff --git a/src/components/map/vector/Circles.vue b/src/components/map/vector/Circles.vue index b25dd17..e6893ca 100644 --- a/src/components/map/vector/Circles.vue +++ b/src/components/map/vector/Circles.vue @@ -23,6 +23,7 @@ import LiveAtlasPolyline from "@/leaflet/vector/LiveAtlasPolyline"; import LiveAtlasPolygon from "@/leaflet/vector/LiveAtlasPolygon"; import LiveAtlasLayerGroup from "@/leaflet/layer/LiveAtlasLayerGroup"; import {LiveAtlasCircle, LiveAtlasMarkerSet} from "@/index"; +import {nonReactiveState} from "@/store/state"; export default defineComponent({ props: { @@ -51,7 +52,7 @@ export default defineComponent({ createCircles = () => { const converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap); - props.set.circles.forEach((circle: LiveAtlasCircle, id: string) => { + nonReactiveState.markers.get(props.set.id)!.circles.forEach((circle: LiveAtlasCircle, id: string) => { const layer = createCircle(circle, converter); layers.set(id, layer); @@ -103,7 +104,7 @@ export default defineComponent({ if(newValue && (!oldValue || oldValue.world === newValue.world)) { const converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap); - for (const [id, circle] of props.set.circles) { + for (const [id, circle] of nonReactiveState.markers.get(props.set.id)!.circles) { updateCircle(layers.get(id), circle, converter); } } diff --git a/src/components/map/vector/Lines.vue b/src/components/map/vector/Lines.vue index fc22aff..dfdfc41 100644 --- a/src/components/map/vector/Lines.vue +++ b/src/components/map/vector/Lines.vue @@ -22,6 +22,7 @@ import {createLine, updateLine} from "@/util/lines"; import LiveAtlasPolyline from "@/leaflet/vector/LiveAtlasPolyline"; import LiveAtlasLayerGroup from "@/leaflet/layer/LiveAtlasLayerGroup"; import {LiveAtlasLine, LiveAtlasMarkerSet} from "@/index"; +import {nonReactiveState} from "@/store/state"; export default defineComponent({ props: { @@ -50,7 +51,7 @@ export default defineComponent({ createLines = () => { const converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap); - props.set.lines.forEach((line: LiveAtlasLine, id: string) => { + nonReactiveState.markers.get(props.set.id)!.lines.forEach((line: LiveAtlasLine, id: string) => { const layer = createLine(line, converter); layers.set(id, layer); @@ -102,7 +103,7 @@ export default defineComponent({ if(newValue && (!oldValue || oldValue.world === newValue.world)) { const converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap); - for (const [id, line] of props.set.lines) { + for (const [id, line] of nonReactiveState.markers.get(props.set.id)!.lines) { updateLine(layers.get(id), line, converter); } } diff --git a/src/components/map/vector/Markers.vue b/src/components/map/vector/Markers.vue index cd9b0a4..01de292 100644 --- a/src/components/map/vector/Markers.vue +++ b/src/components/map/vector/Markers.vue @@ -22,6 +22,7 @@ import {ActionTypes} from "@/store/action-types"; import {createMarker, updateMarker} from "@/util/markers"; import LiveAtlasLayerGroup from "@/leaflet/layer/LiveAtlasLayerGroup"; import {LiveAtlasMarker, LiveAtlasMarkerSet} from "@/index"; +import {nonReactiveState} from "@/store/state"; export default defineComponent({ props: { @@ -50,7 +51,7 @@ export default defineComponent({ createMarkers = () => { const converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap); - props.set.markers.forEach((marker: LiveAtlasMarker, id: string) => { + nonReactiveState.markers.get(props.set.id)!.markers.forEach((marker: LiveAtlasMarker, id: string) => { const layer = createMarker(marker, converter); layers.set(id, layer); @@ -102,7 +103,7 @@ export default defineComponent({ if(newValue && (!oldValue || oldValue.world === newValue.world)) { const converter = currentMap.value!.locationToLatLng.bind(store.state.currentMap); - for (const [id, marker] of props.set.markers) { + for (const [id, marker] of nonReactiveState.markers.get(props.set.id)!.markers) { updateMarker(layers.get(id), marker, converter); } } diff --git a/src/index.d.ts b/src/index.d.ts index a3559a1..206c968 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -228,7 +228,10 @@ interface LiveAtlasMarkerSet { minZoom?: number; maxZoom?: number; showLabels?: boolean; - markers: Map; +} + +interface LiveAtlasMarkerSetContents { + markers: Map, areas: Map; lines: Map; circles: Map; diff --git a/src/providers/DynmapMapProvider.ts b/src/providers/DynmapMapProvider.ts index 1d634b3..7a48780 100644 --- a/src/providers/DynmapMapProvider.ts +++ b/src/providers/DynmapMapProvider.ts @@ -16,7 +16,7 @@ import { HeadQueueEntry, - LiveAtlasMarkerSet, + LiveAtlasMarkerSet, LiveAtlasMarkerSetContents, LiveAtlasPlayer, LiveAtlasServerDefinition, LiveAtlasWorldDefinition @@ -47,11 +47,14 @@ export default class DynmapMapProvider extends MapProvider { private updateTimestamp: Date = new Date(); private updateInterval: number = 3000; + private markerSets: Map = new Map(); + private markers = new Map(); + constructor(config: LiveAtlasServerDefinition) { super(config); } - private async getMarkerSets(world: LiveAtlasWorldDefinition): Promise> { + private async getMarkerSets(world: LiveAtlasWorldDefinition): Promise { const url = `${this.config.dynmap!.markers}_markers_/marker_${world.name}.json`; if(this.markersAbort) { @@ -61,7 +64,6 @@ export default class DynmapMapProvider extends MapProvider { this.markersAbort = new AbortController(); const response = await this.getJSON(url, this.markersAbort.signal); - const sets: Map = new Map(); response.sets = response.sets || {}; @@ -71,21 +73,16 @@ export default class DynmapMapProvider extends MapProvider { } const set: MarkerSet = response.sets[key], - markers = buildMarkers(set.markers || {}), - circles = buildCircles(set.circles || {}), - areas = buildAreas(set.areas || {}), - lines = buildLines(set.lines || {}); + markerSet = buildMarkerSet(key, set); - sets.set(key, { - ...buildMarkerSet(key, set), - markers, - circles, - areas, - lines, - }); + this.markerSets.set(key, markerSet); + this.markers.set(key, Object.seal({ + markers: buildMarkers(set.markers || {}), + areas: buildAreas(set.areas || {}), + lines: buildLines(set.lines || {}), + circles: buildCircles(set.circles || {}), + })); } - - return sets; } async loadServerConfiguration(): Promise { @@ -115,9 +112,13 @@ export default class DynmapMapProvider extends MapProvider { } async populateWorld(world: LiveAtlasWorldDefinition): Promise { - const markerSets = await this.getMarkerSets(world); + await this.getMarkerSets(world); - this.store.commit(MutationTypes.SET_MARKER_SETS, markerSets); + this.store.commit(MutationTypes.SET_MARKER_SETS, this.markerSets); + this.store.commit(MutationTypes.SET_MARKERS, this.markers); + + this.markerSets.clear(); + this.markers.clear(); } private async getUpdate(): Promise { diff --git a/src/providers/Pl3xmapMapProvider.ts b/src/providers/Pl3xmapMapProvider.ts index 1276fe0..f063ef6 100644 --- a/src/providers/Pl3xmapMapProvider.ts +++ b/src/providers/Pl3xmapMapProvider.ts @@ -22,7 +22,7 @@ import { LiveAtlasDimension, LiveAtlasLine, LiveAtlasMarker, - LiveAtlasMarkerSet, + LiveAtlasMarkerSet, LiveAtlasMarkerSetContents, LiveAtlasPartialComponentConfig, LiveAtlasPlayer, LiveAtlasServerConfig, @@ -56,6 +56,9 @@ export default class Pl3xmapMapProvider extends MapProvider { components: LiveAtlasPartialComponentConfig, }> = new Map(); + private markerSets: Map = new Map(); + private markers = new Map(); + constructor(config: LiveAtlasServerDefinition) { super(config); } @@ -220,7 +223,7 @@ export default class Pl3xmapMapProvider extends MapProvider { return components; } - private async getMarkerSets(world: LiveAtlasWorldDefinition): Promise> { + private async getMarkerSets(world: LiveAtlasWorldDefinition): Promise { const url = `${this.config.pl3xmap}tiles/${world.name}/markers.json`; if(this.markersAbort) { @@ -230,10 +233,9 @@ export default class Pl3xmapMapProvider extends MapProvider { this.markersAbort = new AbortController(); const response = await Pl3xmapMapProvider.getJSON(url, this.markersAbort.signal); - const sets: Map = new Map(); if(!Array.isArray(response)) { - return sets; + return; } response.forEach(set => { @@ -277,27 +279,19 @@ export default class Pl3xmapMapProvider extends MapProvider { } }); - - const e = { + this.markerSets.set(id, { id, label: set.name || "Unnamed set", hidden: set.hide || false, priority: set.order || 0, - showLabels: false, - markers, - circles, - areas, - lines, - }; - - sets.set(id, e); + showLabels: false + }); + this.markers.set(id, Object.seal({markers, circles, areas, lines})); }); - - return sets; } private static buildMarker(marker: any): LiveAtlasMarker { - return Object.seal({ + return { location: { x: marker.point?.x || 0, y: 0, @@ -308,11 +302,11 @@ export default class Pl3xmapMapProvider extends MapProvider { label: (marker.tooltip || '').trim(), isLabelHTML: true - }); + }; } private static buildRectangle(area: any): LiveAtlasArea { - return Object.seal({ + return { style: { stroke: typeof area.stroke !== 'undefined' ? !!area.stroke : true, color: area.color || '#3388ff', @@ -334,11 +328,11 @@ export default class Pl3xmapMapProvider extends MapProvider { tooltipContent: area.tooltip, popupContent: area.popup, isPopupHTML: true, - }); + }; } private static buildArea(area: any): LiveAtlasArea { - return Object.seal({ + return { style: { stroke: typeof area.stroke !== 'undefined' ? !!area.stroke : true, color: area.color || '#3388ff', @@ -355,11 +349,11 @@ export default class Pl3xmapMapProvider extends MapProvider { tooltipContent: area.tooltip, popupContent: area.popup, isPopupHTML: true, - }); + }; } private static buildLine(line: any): LiveAtlasLine { - return Object.seal({ + return { style: { stroke: typeof line.stroke !== 'undefined' ? !!line.stroke : true, color: line.color || '#3388ff', @@ -371,11 +365,11 @@ export default class Pl3xmapMapProvider extends MapProvider { tooltipContent: line.tooltip, popupContent: line.popup, isPopupHTML: true, - }); + }; } private static buildCircle(circle: any): LiveAtlasCircle { - return Object.seal({ + return { location: { x: circle.center?.x || 0, y: 0, @@ -396,7 +390,7 @@ export default class Pl3xmapMapProvider extends MapProvider { tooltipContent: circle.tooltip, popupContent: circle.popup, isPopupHTML: true - }); + }; } async loadServerConfiguration(): Promise { @@ -427,14 +421,18 @@ export default class Pl3xmapMapProvider extends MapProvider { } async populateWorld(world: LiveAtlasWorldDefinition) { - const markerSets = await this.getMarkerSets(world), - worldConfig = this.worldComponents.get(world.name); + const worldConfig = this.worldComponents.get(world.name); + await this.getMarkerSets(world); this.playerUpdateInterval = this.worldPlayerUpdateIntervals.get(world.name) || 3000; this.markerUpdateInterval = this.worldMarkerUpdateIntervals.get(world.name) || 3000; - this.store.commit(MutationTypes.SET_MARKER_SETS, markerSets); + this.store.commit(MutationTypes.SET_MARKER_SETS, this.markerSets); + this.store.commit(MutationTypes.SET_MARKERS, this.markers); this.store.commit(MutationTypes.SET_COMPONENTS, worldConfig!.components); + + this.markerSets.clear(); + this.markers.clear(); } private async getPlayers(): Promise> { @@ -453,7 +451,7 @@ export default class Pl3xmapMapProvider extends MapProvider { players.add({ name: (player.name || '').toLowerCase(), uuid: player.uuid, - displayName: player.name || "", + displayName: player.display_name || player.name || "", health: player.health || 0, armor: player.armor || 0, sort: 0, diff --git a/src/store/mutation-types.ts b/src/store/mutation-types.ts index b3de3b2..17e6227 100644 --- a/src/store/mutation-types.ts +++ b/src/store/mutation-types.ts @@ -23,6 +23,7 @@ export enum MutationTypes { SET_WORLDS = 'setWorlds', SET_COMPONENTS = 'setComponents', SET_MARKER_SETS = 'setMarkerSets', + SET_MARKERS = 'setMarkers', SET_WORLD_STATE = 'setWorldState', ADD_MARKER_SET_UPDATES = 'addMarkerSetUpdates', ADD_TILE_UPDATES = 'addTileUpdates', diff --git a/src/store/mutations.ts b/src/store/mutations.ts index 176067f..21f0396 100644 --- a/src/store/mutations.ts +++ b/src/store/mutations.ts @@ -16,7 +16,7 @@ import {MutationTree} from "vuex"; import {MutationTypes} from "@/store/mutation-types"; -import {State} from "@/store/state"; +import {nonReactiveState, State} from "@/store/state"; import { DynmapMarkerSetUpdates, DynmapTileUpdate @@ -44,7 +44,7 @@ import { LiveAtlasPartialComponentConfig, LiveAtlasComponentConfig, LiveAtlasUIModal, - LiveAtlasSidebarSectionState + LiveAtlasSidebarSectionState, LiveAtlasMarkerSetContents } from "@/index"; import DynmapMapProvider from "@/providers/DynmapMapProvider"; import Pl3xmapMapProvider from "@/providers/Pl3xmapMapProvider"; @@ -61,7 +61,8 @@ export type Mutations = { [MutationTypes.SET_SERVER_MESSAGES](state: S, messages: LiveAtlasServerMessageConfig): void [MutationTypes.SET_WORLDS](state: S, worlds: Array): void [MutationTypes.SET_COMPONENTS](state: S, components: LiveAtlasPartialComponentConfig | LiveAtlasComponentConfig): void - [MutationTypes.SET_MARKER_SETS](state: S, worlds: Map): void + [MutationTypes.SET_MARKER_SETS](state: S, markerSets: Map): void + [MutationTypes.SET_MARKERS](state: S, markers: Map): void [MutationTypes.SET_WORLD_STATE](state: S, worldState: LiveAtlasWorldState): void [MutationTypes.ADD_MARKER_SET_UPDATES](state: S, updates: Map): void [MutationTypes.ADD_TILE_UPDATES](state: S, updates: Array): void @@ -259,6 +260,7 @@ export const mutations: MutationTree & Mutations = { [MutationTypes.SET_MARKER_SETS](state: State, markerSets: Map) { state.markerSets.clear(); state.pendingSetUpdates.clear(); + nonReactiveState.markers.clear(); for(const entry of markerSets) { state.markerSets.set(entry[0], entry[1]); @@ -268,6 +270,21 @@ export const mutations: MutationTree & Mutations = { circleUpdates: [], lineUpdates: [], }); + nonReactiveState.markers.set(entry[0], { + markers: new Map(), + areas: new Map(), + lines: new Map(), + circles: new Map(), + }); + } + }, + + //Sets the existing marker sets from the last marker fetch + [MutationTypes.SET_MARKERS](state: State, markers: Map) { + nonReactiveState.markers.clear(); + + for(const entry of markers) { + nonReactiveState.markers.set(entry[0], entry[1]); } }, @@ -291,10 +308,6 @@ export const mutations: MutationTree & Mutations = { priority: entry[1].payload.priority, label: entry[1].payload.label, hidden: entry[1].payload.hidden, - markers: Object.freeze(new Map()) as Map, - areas: Object.freeze(new Map()) as Map, - circles: Object.freeze(new Map()) as Map, - lines: Object.freeze(new Map()) as Map, }); state.pendingSetUpdates.set(entry[0], { @@ -310,6 +323,7 @@ export const mutations: MutationTree & Mutations = { } const set = state.markerSets.get(entry[0]) as LiveAtlasMarkerSet, + setContents = nonReactiveState.markers.get(entry[0]) as LiveAtlasMarkerSetContents, setUpdates = state.pendingSetUpdates.get(entry[0]) as DynmapMarkerSetUpdates; //Delete the set if it has been deleted @@ -332,33 +346,33 @@ export const mutations: MutationTree & Mutations = { //Update non-reactive lists for(const update of entry[1].markerUpdates) { if(update.removed) { - set.markers.delete(update.id); + setContents.markers.delete(update.id); } else { - set.markers.set(update.id, update.payload as LiveAtlasMarker); + setContents.markers.set(update.id, update.payload as LiveAtlasMarker); } } for(const update of entry[1].areaUpdates) { if(update.removed) { - set.areas.delete(update.id); + setContents.areas.delete(update.id); } else { - set.areas.set(update.id, update.payload as LiveAtlasArea); + setContents.areas.set(update.id, update.payload as LiveAtlasArea); } } for(const update of entry[1].circleUpdates) { if(update.removed) { - set.circles.delete(update.id); + setContents.circles.delete(update.id); } else { - set.circles.set(update.id, update.payload as LiveAtlasCircle); + setContents.circles.set(update.id, update.payload as LiveAtlasCircle); } } for(const update of entry[1].lineUpdates) { if(update.removed) { - set.lines.delete(update.id); + setContents.lines.delete(update.id); } else { - set.lines.set(update.id, update.payload as LiveAtlasLine); + setContents.lines.set(update.id, update.payload as LiveAtlasLine); } } diff --git a/src/store/state.ts b/src/store/state.ts index 4fe5f1b..682f180 100644 --- a/src/store/state.ts +++ b/src/store/state.ts @@ -35,7 +35,8 @@ import { LiveAtlasServerConfig, LiveAtlasChat, LiveAtlasUIModal, - LiveAtlasSidebarSectionState + LiveAtlasSidebarSectionState, + LiveAtlasMarkerSetContents } from "@/index"; import LiveAtlasMapDefinition from "@/model/LiveAtlasMapDefinition"; @@ -189,7 +190,8 @@ export const state: State = { messages: [], }, - markerSets: new Map(), //Markers from world_markers.json. Contents of each set isn't reactive for performance reasons. + markerSets: new Map(), //Markers sets from world_markers.json, doesn't include the markers themselves for performance reasons + pendingSetUpdates: new Map(), //Pending updates to markers/areas/etc for each marker set pendingTileUpdates: [], //Pending updates to map tiles @@ -274,3 +276,7 @@ export const state: State = { }, } }; + +export const nonReactiveState = Object.freeze({ + markers: new Map(), +}); diff --git a/src/util/dynmap.ts b/src/util/dynmap.ts index b933d24..e996d5e 100644 --- a/src/util/dynmap.ts +++ b/src/util/dynmap.ts @@ -285,14 +285,14 @@ export function buildMarker(data: Marker): LiveAtlasMarker { let dimensions; if(data.dim) { - dimensions = data.dim.split('x').slice(0, 2).map(value => parseInt(value)); + dimensions = data.dim.split('x').filter(value => !isNaN(Number(value))); if(dimensions.length !== 2) { dimensions = undefined; } } - return Object.seal({ + return { label: data.label || '', //Dynmap#2288 currently means markup:false markers are still encoded //The planned solution for this is to always treat everything as HTML, so we'll do that here @@ -307,7 +307,7 @@ export function buildMarker(data: Marker): LiveAtlasMarker { minZoom: typeof data.minzoom !== 'undefined' && data.minzoom > -1 ? data.minzoom : undefined, maxZoom: typeof data.maxzoom !== 'undefined' && data.maxzoom > -1 ? data.maxzoom : undefined, popupContent: data.desc || undefined, - }); + }; } export function buildAreas(data: any): Map { @@ -325,12 +325,7 @@ export function buildAreas(data: any): Map { } export function buildArea(area: MarkerArea): LiveAtlasArea { - const opacity = area.fillopacity || 0, - x = area.x || [0, 0], - y: [number, number] = [area.ybottom || 0, area.ytop || 0], - z = area.z || [0, 0]; - - return Object.seal({ + return { style: { color: area.color || '#ff0000', opacity: area.opacity || 1, @@ -338,8 +333,12 @@ export function buildArea(area: MarkerArea): LiveAtlasArea { fillColor: area.fillcolor || '#ff0000', fillOpacity: area.fillopacity || 0, }, - outline: !opacity, - points: getPoints(x, y, z, !opacity), + outline: !area.fillopacity, + points: getPoints( + area.x || [0, 0], + [area.ybottom || 0, area.ytop || 0], + area.z || [0, 0], + !area.fillopacity), minZoom: typeof area.minzoom !== 'undefined' && area.minzoom > -1 ? area.minzoom : undefined, maxZoom: typeof area.maxzoom !== 'undefined' && area.maxzoom > -1 ? area.maxzoom : undefined, @@ -347,7 +346,7 @@ export function buildArea(area: MarkerArea): LiveAtlasArea { //The planned solution for this is to always treat everything as HTML, so we'll do that here isPopupHTML: true, //area.desc ? true : area.markup || false, popupContent: area.desc || area.label || undefined, - }); + }; } export function buildLines(data: any): Map { @@ -365,7 +364,7 @@ export function buildLines(data: any): Map { } export function buildLine(line: MarkerLine): LiveAtlasLine { - return Object.seal({ + return { style: { color: line.color || '#ff0000', opacity: line.opacity || 1, @@ -379,7 +378,7 @@ export function buildLine(line: MarkerLine): LiveAtlasLine { //The planned solution for this is to always treat everything as HTML, so we'll do that here isPopupHTML: true, popupContent: line.desc || line.label || undefined, - }); + }; } export function buildCircles(data: any): Map { @@ -397,7 +396,7 @@ export function buildCircles(data: any): Map { } export function buildCircle(circle: MarkerCircle): LiveAtlasCircle { - return Object.seal({ + return { location: { x: circle.x || 0, y: circle.y || 0, @@ -418,7 +417,7 @@ export function buildCircle(circle: MarkerCircle): LiveAtlasCircle { //The planned solution for this is to always treat everything as HTML, so we'll do that here isPopupHTML: true, popupContent: circle.desc || circle.label || undefined, - }); + }; } export function buildUpdates(data: Array, lastUpdate: Date) {