forked from MapComplete/MapComplete
		
	refactoring(maplibre): add pointRendering
This commit is contained in:
		
							parent
							
								
									4f2bbf4b54
								
							
						
					
					
						commit
						1b3609b13f
					
				
					 10 changed files with 316 additions and 122 deletions
				
			
		| 
						 | 
					@ -186,6 +186,14 @@ export class BBox {
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    toLngLat(): [[number, number], [number, number]] {
 | 
				
			||||||
 | 
					        return [
 | 
				
			||||||
 | 
					            [this.minLon, this.minLat],
 | 
				
			||||||
 | 
					            [this.maxLon, this.maxLat],
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public asGeoJson<T>(properties: T): Feature<Polygon, T> {
 | 
					    public asGeoJson<T>(properties: T): Feature<Polygon, T> {
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
            type: "Feature",
 | 
					            type: "Feature",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,6 @@
 | 
				
			||||||
import UserRelatedState from "./UserRelatedState"
 | 
					import UserRelatedState from "./UserRelatedState"
 | 
				
			||||||
import { Store, Stores, UIEventSource } from "../UIEventSource"
 | 
					import { Store, Stores, UIEventSource } from "../UIEventSource"
 | 
				
			||||||
import BaseLayer from "../../Models/BaseLayer"
 | 
					 | 
				
			||||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
 | 
					import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
 | 
				
			||||||
import AvailableBaseLayers from "../Actors/AvailableBaseLayers"
 | 
					 | 
				
			||||||
import Attribution from "../../UI/BigComponents/Attribution"
 | 
					import Attribution from "../../UI/BigComponents/Attribution"
 | 
				
			||||||
import Minimap, { MinimapObj } from "../../UI/Base/Minimap"
 | 
					import Minimap, { MinimapObj } from "../../UI/Base/Minimap"
 | 
				
			||||||
import { Tiles } from "../../Models/TileRange"
 | 
					import { Tiles } from "../../Models/TileRange"
 | 
				
			||||||
| 
						 | 
					@ -43,10 +41,6 @@ export default class MapState extends UserRelatedState {
 | 
				
			||||||
     The leaflet instance of the big basemap
 | 
					     The leaflet instance of the big basemap
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public leafletMap = new UIEventSource<any /*L.Map*/>(undefined, "leafletmap")
 | 
					    public leafletMap = new UIEventSource<any /*L.Map*/>(undefined, "leafletmap")
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * A list of currently available background layers
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public availableBackgroundLayers: Store<BaseLayer[]>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * The current background layer
 | 
					     * The current background layer
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,8 @@ import { Feature, Polygon } from "geojson"
 | 
				
			||||||
import * as editorlayerindex from "../assets/editor-layer-index.json"
 | 
					import * as editorlayerindex from "../assets/editor-layer-index.json"
 | 
				
			||||||
import * as globallayers from "../assets/global-raster-layers.json"
 | 
					import * as globallayers from "../assets/global-raster-layers.json"
 | 
				
			||||||
import { BBox } from "../Logic/BBox"
 | 
					import { BBox } from "../Logic/BBox"
 | 
				
			||||||
 | 
					import { Store, Stores } from "../Logic/UIEventSource"
 | 
				
			||||||
 | 
					import { GeoOperations } from "../Logic/GeoOperations"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class AvailableRasterLayers {
 | 
					export class AvailableRasterLayers {
 | 
				
			||||||
    public static EditorLayerIndex: (Feature<Polygon, EditorLayerIndexProperties> &
 | 
					    public static EditorLayerIndex: (Feature<Polygon, EditorLayerIndexProperties> &
 | 
				
			||||||
| 
						 | 
					@ -33,6 +35,35 @@ export class AvailableRasterLayers {
 | 
				
			||||||
        properties: AvailableRasterLayers.osmCartoProperties,
 | 
					        properties: AvailableRasterLayers.osmCartoProperties,
 | 
				
			||||||
        geometry: BBox.global.asGeometry(),
 | 
					        geometry: BBox.global.asGeometry(),
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static layersAvailableAt(
 | 
				
			||||||
 | 
					        location: Store<{ lon: number; lat: number }>
 | 
				
			||||||
 | 
					    ): Store<RasterLayerPolygon[]> {
 | 
				
			||||||
 | 
					        const availableLayersBboxes = Stores.ListStabilized(
 | 
				
			||||||
 | 
					            location.mapD((loc) => {
 | 
				
			||||||
 | 
					                const lonlat: [number, number] = [loc.lon, loc.lat]
 | 
				
			||||||
 | 
					                return AvailableRasterLayers.EditorLayerIndex.filter((eliPolygon) =>
 | 
				
			||||||
 | 
					                    BBox.get(eliPolygon).contains(lonlat)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        const available = Stores.ListStabilized(
 | 
				
			||||||
 | 
					            availableLayersBboxes.map((eliPolygons) => {
 | 
				
			||||||
 | 
					                const loc = location.data
 | 
				
			||||||
 | 
					                const lonlat: [number, number] = [loc.lon, loc.lat]
 | 
				
			||||||
 | 
					                const matching: RasterLayerPolygon[] = eliPolygons.filter((eliPolygon) => {
 | 
				
			||||||
 | 
					                    if (eliPolygon.geometry === null) {
 | 
				
			||||||
 | 
					                        return true // global ELI-layer
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    return GeoOperations.inside(lonlat, eliPolygon)
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                matching.unshift(AvailableRasterLayers.osmCarto)
 | 
				
			||||||
 | 
					                matching.push(...AvailableRasterLayers.globalLayers)
 | 
				
			||||||
 | 
					                return matching
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        return available
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class RasterLayerUtils {
 | 
					export class RasterLayerUtils {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,12 +5,13 @@ import { TagUtils } from "../../Logic/Tags/TagUtils"
 | 
				
			||||||
import { Utils } from "../../Utils"
 | 
					import { Utils } from "../../Utils"
 | 
				
			||||||
import Svg from "../../Svg"
 | 
					import Svg from "../../Svg"
 | 
				
			||||||
import WithContextLoader from "./WithContextLoader"
 | 
					import WithContextLoader from "./WithContextLoader"
 | 
				
			||||||
import { UIEventSource } from "../../Logic/UIEventSource"
 | 
					import { Store } from "../../Logic/UIEventSource"
 | 
				
			||||||
import BaseUIElement from "../../UI/BaseUIElement"
 | 
					import BaseUIElement from "../../UI/BaseUIElement"
 | 
				
			||||||
import { FixedUiElement } from "../../UI/Base/FixedUiElement"
 | 
					import { FixedUiElement } from "../../UI/Base/FixedUiElement"
 | 
				
			||||||
import Img from "../../UI/Base/Img"
 | 
					import Img from "../../UI/Base/Img"
 | 
				
			||||||
import Combine from "../../UI/Base/Combine"
 | 
					import Combine from "../../UI/Base/Combine"
 | 
				
			||||||
import { VariableUiElement } from "../../UI/Base/VariableUIElement"
 | 
					import { VariableUiElement } from "../../UI/Base/VariableUIElement"
 | 
				
			||||||
 | 
					import { OsmTags } from "../OsmFeature"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class PointRenderingConfig extends WithContextLoader {
 | 
					export default class PointRenderingConfig extends WithContextLoader {
 | 
				
			||||||
    private static readonly allowed_location_codes = new Set<string>([
 | 
					    private static readonly allowed_location_codes = new Set<string>([
 | 
				
			||||||
| 
						 | 
					@ -164,7 +165,7 @@ export default class PointRenderingConfig extends WithContextLoader {
 | 
				
			||||||
        return PointRenderingConfig.FromHtmlMulti(htmlDefs, rotation, false, defaultPin)
 | 
					        return PointRenderingConfig.FromHtmlMulti(htmlDefs, rotation, false, defaultPin)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public GetSimpleIcon(tags: UIEventSource<any>): BaseUIElement {
 | 
					    public GetSimpleIcon(tags: Store<OsmTags>): BaseUIElement {
 | 
				
			||||||
        const self = this
 | 
					        const self = this
 | 
				
			||||||
        if (this.icon === undefined) {
 | 
					        if (this.icon === undefined) {
 | 
				
			||||||
            return undefined
 | 
					            return undefined
 | 
				
			||||||
| 
						 | 
					@ -175,7 +176,7 @@ export default class PointRenderingConfig extends WithContextLoader {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public GenerateLeafletStyle(
 | 
					    public GenerateLeafletStyle(
 | 
				
			||||||
        tags: UIEventSource<any>,
 | 
					        tags: Store<OsmTags>,
 | 
				
			||||||
        clickable: boolean,
 | 
					        clickable: boolean,
 | 
				
			||||||
        options?: {
 | 
					        options?: {
 | 
				
			||||||
            noSize?: false | boolean
 | 
					            noSize?: false | boolean
 | 
				
			||||||
| 
						 | 
					@ -183,11 +184,7 @@ export default class PointRenderingConfig extends WithContextLoader {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    ): {
 | 
					    ): {
 | 
				
			||||||
        html: BaseUIElement
 | 
					        html: BaseUIElement
 | 
				
			||||||
        iconSize: [number, number]
 | 
					 | 
				
			||||||
        iconAnchor: [number, number]
 | 
					        iconAnchor: [number, number]
 | 
				
			||||||
        popupAnchor: [number, number]
 | 
					 | 
				
			||||||
        iconUrl: string
 | 
					 | 
				
			||||||
        className: string
 | 
					 | 
				
			||||||
    } {
 | 
					    } {
 | 
				
			||||||
        function num(str, deflt = 40) {
 | 
					        function num(str, deflt = 40) {
 | 
				
			||||||
            const n = Number(str)
 | 
					            const n = Number(str)
 | 
				
			||||||
| 
						 | 
					@ -211,20 +208,21 @@ export default class PointRenderingConfig extends WithContextLoader {
 | 
				
			||||||
        let iconH = num(iconSize[1])
 | 
					        let iconH = num(iconSize[1])
 | 
				
			||||||
        const mode = iconSize[2]?.trim()?.toLowerCase() ?? "center"
 | 
					        const mode = iconSize[2]?.trim()?.toLowerCase() ?? "center"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let anchorW = iconW / 2
 | 
					        // in MapLibre, the offset is relative to the _center_ of the object, with left = [-x, 0] and up = [0,-y]
 | 
				
			||||||
 | 
					        let anchorW = 0
 | 
				
			||||||
        let anchorH = iconH / 2
 | 
					        let anchorH = iconH / 2
 | 
				
			||||||
        if (mode === "left") {
 | 
					        if (mode === "left") {
 | 
				
			||||||
            anchorW = 0
 | 
					            anchorW = -iconW / 2
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (mode === "right") {
 | 
					        if (mode === "right") {
 | 
				
			||||||
            anchorW = iconW
 | 
					            anchorW = iconW / 2
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (mode === "top") {
 | 
					        if (mode === "top") {
 | 
				
			||||||
            anchorH = 0
 | 
					            anchorH = -iconH / 2
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (mode === "bottom") {
 | 
					        if (mode === "bottom") {
 | 
				
			||||||
            anchorH = iconH
 | 
					            anchorH = iconH / 2
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const icon = this.GetSimpleIcon(tags)
 | 
					        const icon = this.GetSimpleIcon(tags)
 | 
				
			||||||
| 
						 | 
					@ -264,15 +262,11 @@ export default class PointRenderingConfig extends WithContextLoader {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
            html: htmlEl,
 | 
					            html: htmlEl,
 | 
				
			||||||
            iconSize: [iconW, iconH],
 | 
					 | 
				
			||||||
            iconAnchor: [anchorW, anchorH],
 | 
					            iconAnchor: [anchorW, anchorH],
 | 
				
			||||||
            popupAnchor: [0, 3 - anchorH],
 | 
					 | 
				
			||||||
            iconUrl: undefined,
 | 
					 | 
				
			||||||
            className: clickable ? "leaflet-div-icon" : "leaflet-div-icon unclickable",
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private GetBadges(tags: UIEventSource<any>): BaseUIElement {
 | 
					    private GetBadges(tags: Store<OsmTags>): BaseUIElement {
 | 
				
			||||||
        if (this.iconBadges.length === 0) {
 | 
					        if (this.iconBadges.length === 0) {
 | 
				
			||||||
            return undefined
 | 
					            return undefined
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -304,7 +298,7 @@ export default class PointRenderingConfig extends WithContextLoader {
 | 
				
			||||||
        ).SetClass("absolute bottom-0 right-1/3 h-1/2 w-0")
 | 
					        ).SetClass("absolute bottom-0 right-1/3 h-1/2 w-0")
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private GetLabel(tags: UIEventSource<any>): BaseUIElement {
 | 
					    private GetLabel(tags: Store<OsmTags>): BaseUIElement {
 | 
				
			||||||
        if (this.label === undefined) {
 | 
					        if (this.label === undefined) {
 | 
				
			||||||
            return undefined
 | 
					            return undefined
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,8 +3,6 @@ import { UIEventSource } from "../../Logic/UIEventSource"
 | 
				
			||||||
import Loc from "../../Models/Loc"
 | 
					import Loc from "../../Models/Loc"
 | 
				
			||||||
import Svg from "../../Svg"
 | 
					import Svg from "../../Svg"
 | 
				
			||||||
import Toggle from "../Input/Toggle"
 | 
					import Toggle from "../Input/Toggle"
 | 
				
			||||||
import BaseLayer from "../../Models/BaseLayer"
 | 
					 | 
				
			||||||
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"
 | 
					 | 
				
			||||||
import BaseUIElement from "../BaseUIElement"
 | 
					import BaseUIElement from "../BaseUIElement"
 | 
				
			||||||
import { GeoOperations } from "../../Logic/GeoOperations"
 | 
					import { GeoOperations } from "../../Logic/GeoOperations"
 | 
				
			||||||
import Hotkeys from "../Base/Hotkeys"
 | 
					import Hotkeys from "../Base/Hotkeys"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,14 +18,12 @@ import { Unit } from "../../Models/Unit"
 | 
				
			||||||
import { FixedInputElement } from "./FixedInputElement"
 | 
					import { FixedInputElement } from "./FixedInputElement"
 | 
				
			||||||
import WikidataSearchBox from "../Wikipedia/WikidataSearchBox"
 | 
					import WikidataSearchBox from "../Wikipedia/WikidataSearchBox"
 | 
				
			||||||
import Wikidata from "../../Logic/Web/Wikidata"
 | 
					import Wikidata from "../../Logic/Web/Wikidata"
 | 
				
			||||||
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"
 | 
					 | 
				
			||||||
import Table from "../Base/Table"
 | 
					import Table from "../Base/Table"
 | 
				
			||||||
import Combine from "../Base/Combine"
 | 
					import Combine from "../Base/Combine"
 | 
				
			||||||
import Title from "../Base/Title"
 | 
					import Title from "../Base/Title"
 | 
				
			||||||
import InputElementMap from "./InputElementMap"
 | 
					import InputElementMap from "./InputElementMap"
 | 
				
			||||||
import Translations from "../i18n/Translations"
 | 
					import Translations from "../i18n/Translations"
 | 
				
			||||||
import { Translation } from "../i18n/Translation"
 | 
					import { Translation } from "../i18n/Translation"
 | 
				
			||||||
import BaseLayer from "../../Models/BaseLayer"
 | 
					 | 
				
			||||||
import Locale from "../i18n/Locale"
 | 
					import Locale from "../i18n/Locale"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class TextFieldDef {
 | 
					export class TextFieldDef {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,65 +1,81 @@
 | 
				
			||||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
 | 
					import { Store, UIEventSource } from "../../Logic/UIEventSource"
 | 
				
			||||||
import type { Map as MLMap } from "maplibre-gl"
 | 
					import type { Map as MLMap } from "maplibre-gl"
 | 
				
			||||||
import {
 | 
					import { RasterLayerPolygon, RasterLayerProperties } from "../../Models/RasterLayers"
 | 
				
			||||||
    EditorLayerIndexProperties,
 | 
					 | 
				
			||||||
    RasterLayerPolygon,
 | 
					 | 
				
			||||||
    RasterLayerProperties,
 | 
					 | 
				
			||||||
} from "../../Models/RasterLayers"
 | 
					 | 
				
			||||||
import { Utils } from "../../Utils"
 | 
					import { Utils } from "../../Utils"
 | 
				
			||||||
import Loc from "../../Models/Loc"
 | 
					import { BBox } from "../../Logic/BBox"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class MapLibreAdaptor {
 | 
					export interface MapState {
 | 
				
			||||||
 | 
					    readonly location: UIEventSource<{ lon: number; lat: number }>
 | 
				
			||||||
 | 
					    readonly zoom: UIEventSource<number>
 | 
				
			||||||
 | 
					    readonly bounds: Store<BBox>
 | 
				
			||||||
 | 
					    readonly rasterLayer: UIEventSource<RasterLayerPolygon | undefined>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export class MapLibreAdaptor implements MapState {
 | 
				
			||||||
    private readonly _maplibreMap: Store<MLMap>
 | 
					    private readonly _maplibreMap: Store<MLMap>
 | 
				
			||||||
    private readonly _backgroundLayer?: Store<RasterLayerPolygon>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private _currentRasterLayer: string = undefined
 | 
					    readonly location: UIEventSource<{ lon: number; lat: number }>
 | 
				
			||||||
 | 
					    readonly zoom: UIEventSource<number>
 | 
				
			||||||
 | 
					    readonly bounds: Store<BBox>
 | 
				
			||||||
 | 
					    readonly rasterLayer: UIEventSource<RasterLayerPolygon | undefined>
 | 
				
			||||||
 | 
					    private readonly _bounds: UIEventSource<BBox>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(
 | 
					    /**
 | 
				
			||||||
        maplibreMap: Store<MLMap>,
 | 
					     * Used for internal bookkeeping (to remove a rasterLayer when done loading)
 | 
				
			||||||
        state?: {
 | 
					     * @private
 | 
				
			||||||
            // availableBackgroundLayers: Store<BaseLayer[]>
 | 
					     */
 | 
				
			||||||
            /**
 | 
					    private _currentRasterLayer: string
 | 
				
			||||||
             * The current background layer
 | 
					    constructor(maplibreMap: Store<MLMap>, state?: Partial<Omit<MapState, "bounds">>) {
 | 
				
			||||||
             */
 | 
					 | 
				
			||||||
            readonly backgroundLayer?: Store<RasterLayerPolygon>
 | 
					 | 
				
			||||||
            readonly locationControl?: UIEventSource<Loc>
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        this._maplibreMap = maplibreMap
 | 
					        this._maplibreMap = maplibreMap
 | 
				
			||||||
        this._backgroundLayer = state.backgroundLayer
 | 
					
 | 
				
			||||||
 | 
					        this.location = state?.location ?? new UIEventSource({ lon: 0, lat: 0 })
 | 
				
			||||||
 | 
					        this.zoom = state?.zoom ?? new UIEventSource(1)
 | 
				
			||||||
 | 
					        this._bounds = new UIEventSource(BBox.global)
 | 
				
			||||||
 | 
					        this.bounds = this._bounds
 | 
				
			||||||
 | 
					        this.rasterLayer =
 | 
				
			||||||
 | 
					            state?.rasterLayer ?? new UIEventSource<RasterLayerPolygon | undefined>(undefined)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const self = this
 | 
					        const self = this
 | 
				
			||||||
        this._backgroundLayer?.addCallback((_) => self.setBackground())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        maplibreMap.addCallbackAndRunD((map) => {
 | 
					        maplibreMap.addCallbackAndRunD((map) => {
 | 
				
			||||||
            map.on("load", () => {
 | 
					            map.on("load", () => {
 | 
				
			||||||
                self.setBackground()
 | 
					                self.setBackground()
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            if (state.locationControl) {
 | 
					            self.MoveMapToCurrentLoc(this.location.data)
 | 
				
			||||||
                self.MoveMapToCurrentLoc(state.locationControl.data)
 | 
					            self.SetZoom(this.zoom.data)
 | 
				
			||||||
                map.on("moveend", () => {
 | 
					            map.on("moveend", () => {
 | 
				
			||||||
                    const dt = state.locationControl.data
 | 
					                const dt = this.location.data
 | 
				
			||||||
                    dt.lon = map.getCenter().lng
 | 
					                dt.lon = map.getCenter().lng
 | 
				
			||||||
                    dt.lat = map.getCenter().lat
 | 
					                dt.lat = map.getCenter().lat
 | 
				
			||||||
                    dt.zoom = map.getZoom()
 | 
					                this.location.ping()
 | 
				
			||||||
                    state.locationControl.ping()
 | 
					                this.zoom.setData(map.getZoom())
 | 
				
			||||||
                })
 | 
					            })
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        state.locationControl.addCallbackAndRunD((loc) => {
 | 
					        this.rasterLayer.addCallback((_) =>
 | 
				
			||||||
 | 
					            self.setBackground().catch((e) => {
 | 
				
			||||||
 | 
					                console.error("Could not set background")
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.location.addCallbackAndRunD((loc) => {
 | 
				
			||||||
            self.MoveMapToCurrentLoc(loc)
 | 
					            self.MoveMapToCurrentLoc(loc)
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
					        this.zoom.addCallbackAndRunD((z) => self.SetZoom(z))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    private SetZoom(z: number) {
 | 
				
			||||||
    private MoveMapToCurrentLoc(loc: Loc) {
 | 
					        const map = this._maplibreMap.data
 | 
				
			||||||
 | 
					        if (map === undefined || z === undefined) {
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (map.getZoom() !== z) {
 | 
				
			||||||
 | 
					            map.setZoom(z)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    private MoveMapToCurrentLoc(loc: { lat: number; lon: number }) {
 | 
				
			||||||
        const map = this._maplibreMap.data
 | 
					        const map = this._maplibreMap.data
 | 
				
			||||||
        if (map === undefined || loc === undefined) {
 | 
					        if (map === undefined || loc === undefined) {
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (map.getZoom() !== loc.zoom) {
 | 
					
 | 
				
			||||||
            map.setZoom(loc.zoom)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        const center = map.getCenter()
 | 
					        const center = map.getCenter()
 | 
				
			||||||
        if (center.lng !== loc.lon || center.lat !== loc.lat) {
 | 
					        if (center.lng !== loc.lon || center.lat !== loc.lat) {
 | 
				
			||||||
            map.setCenter({ lng: loc.lon, lat: loc.lat })
 | 
					            map.setCenter({ lng: loc.lon, lat: loc.lat })
 | 
				
			||||||
| 
						 | 
					@ -120,14 +136,14 @@ export class MapLibreAdaptor {
 | 
				
			||||||
        if (map === undefined) {
 | 
					        if (map === undefined) {
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const background: RasterLayerProperties = this._backgroundLayer?.data?.properties
 | 
					        const background: RasterLayerProperties = this.rasterLayer?.data?.properties
 | 
				
			||||||
        if (background !== undefined && this._currentRasterLayer === background.id) {
 | 
					        if (background !== undefined && this._currentRasterLayer === background.id) {
 | 
				
			||||||
            // already the correct background layer, nothing to do
 | 
					            // already the correct background layer, nothing to do
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        await this.awaitStyleIsLoaded()
 | 
					        await this.awaitStyleIsLoaded()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (background !== this._backgroundLayer?.data?.properties) {
 | 
					        if (background !== this.rasterLayer?.data?.properties) {
 | 
				
			||||||
            // User selected another background in the meantime... abort
 | 
					            // User selected another background in the meantime... abort
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										108
									
								
								UI/Map/ShowDataLayer.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								UI/Map/ShowDataLayer.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,108 @@
 | 
				
			||||||
 | 
					import { ImmutableStore, Store } from "../../Logic/UIEventSource"
 | 
				
			||||||
 | 
					import type { Map as MlMap } from "maplibre-gl"
 | 
				
			||||||
 | 
					import { Marker } from "maplibre-gl"
 | 
				
			||||||
 | 
					import { ShowDataLayerOptions } from "../ShowDataLayer/ShowDataLayerOptions"
 | 
				
			||||||
 | 
					import { GeoOperations } from "../../Logic/GeoOperations"
 | 
				
			||||||
 | 
					import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
 | 
				
			||||||
 | 
					import PointRenderingConfig from "../../Models/ThemeConfig/PointRenderingConfig"
 | 
				
			||||||
 | 
					import { OsmFeature, OsmTags } from "../../Models/OsmFeature"
 | 
				
			||||||
 | 
					import FeatureSource from "../../Logic/FeatureSource/FeatureSource"
 | 
				
			||||||
 | 
					import { BBox } from "../../Logic/BBox"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PointRenderingLayer {
 | 
				
			||||||
 | 
					    private readonly _config: PointRenderingConfig
 | 
				
			||||||
 | 
					    private readonly _fetchStore?: (id: string) => Store<OsmTags>
 | 
				
			||||||
 | 
					    private readonly _map: MlMap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(
 | 
				
			||||||
 | 
					        map: MlMap,
 | 
				
			||||||
 | 
					        features: FeatureSource,
 | 
				
			||||||
 | 
					        config: PointRenderingConfig,
 | 
				
			||||||
 | 
					        fetchStore?: (id: string) => Store<OsmTags>
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        this._config = config
 | 
				
			||||||
 | 
					        this._map = map
 | 
				
			||||||
 | 
					        this._fetchStore = fetchStore
 | 
				
			||||||
 | 
					        const cache: Map<string, Marker> = new Map<string, Marker>()
 | 
				
			||||||
 | 
					        const self = this
 | 
				
			||||||
 | 
					        features.features.addCallbackAndRunD((features) => {
 | 
				
			||||||
 | 
					            const unseenKeys = new Set(cache.keys())
 | 
				
			||||||
 | 
					            for (const { feature } of features) {
 | 
				
			||||||
 | 
					                const id = feature.properties.id
 | 
				
			||||||
 | 
					                unseenKeys.delete(id)
 | 
				
			||||||
 | 
					                const loc = GeoOperations.centerpointCoordinates(feature)
 | 
				
			||||||
 | 
					                if (cache.has(id)) {
 | 
				
			||||||
 | 
					                    console.log("Not creating a marker for ", id)
 | 
				
			||||||
 | 
					                    const cached = cache.get(id)
 | 
				
			||||||
 | 
					                    const oldLoc = cached.getLngLat()
 | 
				
			||||||
 | 
					                    console.log("OldLoc vs newLoc", oldLoc, loc)
 | 
				
			||||||
 | 
					                    if (loc[0] !== oldLoc.lng && loc[1] !== oldLoc.lat) {
 | 
				
			||||||
 | 
					                        cached.setLngLat(loc)
 | 
				
			||||||
 | 
					                        console.log("MOVED")
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                console.log("Creating a marker for ", id)
 | 
				
			||||||
 | 
					                const marker = self.addPoint(feature)
 | 
				
			||||||
 | 
					                cache.set(id, marker)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (const unseenKey of unseenKeys) {
 | 
				
			||||||
 | 
					                cache.get(unseenKey).remove()
 | 
				
			||||||
 | 
					                cache.delete(unseenKey)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private addPoint(feature: OsmFeature): Marker {
 | 
				
			||||||
 | 
					        let store: Store<OsmTags>
 | 
				
			||||||
 | 
					        if (this._fetchStore) {
 | 
				
			||||||
 | 
					            store = this._fetchStore(feature.properties.id)
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            store = new ImmutableStore(feature.properties)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const { html, iconAnchor } = this._config.GenerateLeafletStyle(store, true)
 | 
				
			||||||
 | 
					        html.SetClass("marker")
 | 
				
			||||||
 | 
					        const el = html.ConstructElement()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        el.addEventListener("click", function () {
 | 
				
			||||||
 | 
					            window.alert("Hello world!")
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return new Marker(el)
 | 
				
			||||||
 | 
					            .setLngLat(GeoOperations.centerpointCoordinates(feature))
 | 
				
			||||||
 | 
					            .setOffset(iconAnchor)
 | 
				
			||||||
 | 
					            .addTo(this._map)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class ShowDataLayer {
 | 
				
			||||||
 | 
					    private readonly _map: Store<MlMap>
 | 
				
			||||||
 | 
					    private _options: ShowDataLayerOptions & { layer: LayerConfig }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(map: Store<MlMap>, options: ShowDataLayerOptions & { layer: LayerConfig }) {
 | 
				
			||||||
 | 
					        this._map = map
 | 
				
			||||||
 | 
					        this._options = options
 | 
				
			||||||
 | 
					        const self = this
 | 
				
			||||||
 | 
					        map.addCallbackAndRunD((map) => self.initDrawFeatures(map))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private initDrawFeatures(map: MlMap) {
 | 
				
			||||||
 | 
					        for (const pointRenderingConfig of this._options.layer.mapRendering) {
 | 
				
			||||||
 | 
					            new PointRenderingLayer(
 | 
				
			||||||
 | 
					                map,
 | 
				
			||||||
 | 
					                this._options.features,
 | 
				
			||||||
 | 
					                pointRenderingConfig,
 | 
				
			||||||
 | 
					                this._options.fetchStore
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (this._options.zoomToFeatures) {
 | 
				
			||||||
 | 
					            const features = this._options.features.features.data
 | 
				
			||||||
 | 
					            const bbox = BBox.bboxAroundAll(features.map((f) => BBox.get(f.feature)))
 | 
				
			||||||
 | 
					            map.fitBounds(bbox.toLngLat(), {
 | 
				
			||||||
 | 
					                padding: { top: 10, bottom: 10, left: 10, right: 10 },
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -3,13 +3,35 @@ import { Store, UIEventSource } from "../../Logic/UIEventSource"
 | 
				
			||||||
import { ElementStorage } from "../../Logic/ElementStorage"
 | 
					import { ElementStorage } from "../../Logic/ElementStorage"
 | 
				
			||||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
 | 
					import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
 | 
				
			||||||
import ScrollableFullScreen from "../Base/ScrollableFullScreen"
 | 
					import ScrollableFullScreen from "../Base/ScrollableFullScreen"
 | 
				
			||||||
 | 
					import { OsmTags } from "../../Models/OsmFeature"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ShowDataLayerOptions {
 | 
					export interface ShowDataLayerOptions {
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Features to show
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    features: FeatureSource
 | 
					    features: FeatureSource
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Indication of the current selected element; overrides some filters
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    selectedElement?: UIEventSource<any>
 | 
					    selectedElement?: UIEventSource<any>
 | 
				
			||||||
    leafletMap: Store<L.Map>
 | 
					    /**
 | 
				
			||||||
    popup?: undefined | ((tags: UIEventSource<any>, layer: LayerConfig) => ScrollableFullScreen)
 | 
					     * What popup to build when a feature is selected
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    buildPopup?:
 | 
				
			||||||
 | 
					        | undefined
 | 
				
			||||||
 | 
					        | ((tags: UIEventSource<any>, layer: LayerConfig) => ScrollableFullScreen)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * If set, zoom to the features when initially loaded and when they are changed
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    zoomToFeatures?: false | boolean
 | 
					    zoomToFeatures?: false | boolean
 | 
				
			||||||
    doShowLayer?: Store<boolean>
 | 
					    /**
 | 
				
			||||||
    state?: { allElements?: ElementStorage }
 | 
					     * Toggles the layer on/off
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    doShowLayer?: Store<true | boolean>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Function which fetches the relevant store
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    fetchStore?: (id: string) => Store<OsmTags>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										123
									
								
								test.ts
									
										
									
									
									
								
							
							
						
						
									
										123
									
								
								test.ts
									
										
									
									
									
								
							| 
						 | 
					@ -1,24 +1,23 @@
 | 
				
			||||||
import SvelteUIElement from "./UI/Base/SvelteUIElement"
 | 
					import SvelteUIElement from "./UI/Base/SvelteUIElement"
 | 
				
			||||||
import MaplibreMap from "./UI/Map/MaplibreMap.svelte"
 | 
					import MaplibreMap from "./UI/Map/MaplibreMap.svelte"
 | 
				
			||||||
import { Store, Stores, UIEventSource } from "./Logic/UIEventSource"
 | 
					import { ImmutableStore, UIEventSource } from "./Logic/UIEventSource"
 | 
				
			||||||
import { MapLibreAdaptor } from "./UI/Map/MapLibreAdaptor"
 | 
					import { MapLibreAdaptor } from "./UI/Map/MapLibreAdaptor"
 | 
				
			||||||
import {
 | 
					import { AvailableRasterLayers, RasterLayerPolygon } from "./Models/RasterLayers"
 | 
				
			||||||
    EditorLayerIndexProperties,
 | 
					 | 
				
			||||||
    RasterLayerPolygon,
 | 
					 | 
				
			||||||
    RasterLayerProperties,
 | 
					 | 
				
			||||||
} from "./Models/RasterLayers"
 | 
					 | 
				
			||||||
import type { Map as MlMap } from "maplibre-gl"
 | 
					import type { Map as MlMap } from "maplibre-gl"
 | 
				
			||||||
import { AvailableRasterLayers } from "./Models/RasterLayers"
 | 
					 | 
				
			||||||
import Loc from "./Models/Loc"
 | 
					 | 
				
			||||||
import { BBox } from "./Logic/BBox"
 | 
					 | 
				
			||||||
import { GeoOperations } from "./Logic/GeoOperations"
 | 
					 | 
				
			||||||
import RasterLayerPicker from "./UI/Map/RasterLayerPicker.svelte"
 | 
					import RasterLayerPicker from "./UI/Map/RasterLayerPicker.svelte"
 | 
				
			||||||
import BackgroundLayerResetter from "./Logic/Actors/BackgroundLayerResetter"
 | 
					import BackgroundLayerResetter from "./Logic/Actors/BackgroundLayerResetter"
 | 
				
			||||||
 | 
					import { ShowDataLayer } from "./UI/Map/ShowDataLayer"
 | 
				
			||||||
 | 
					import StaticFeatureSource from "./Logic/FeatureSource/Sources/StaticFeatureSource"
 | 
				
			||||||
 | 
					import { Layer } from "leaflet"
 | 
				
			||||||
 | 
					import LayerConfig from "./Models/ThemeConfig/LayerConfig"
 | 
				
			||||||
 | 
					import * as bench from "./assets/generated/layers/bench.json"
 | 
				
			||||||
 | 
					import { Utils } from "./Utils"
 | 
				
			||||||
 | 
					import SimpleFeatureSource from "./Logic/FeatureSource/Sources/SimpleFeatureSource"
 | 
				
			||||||
 | 
					import { FilterState } from "./Models/FilteredLayer"
 | 
				
			||||||
 | 
					import { FixedUiElement } from "./UI/Base/FixedUiElement"
 | 
				
			||||||
async function main() {
 | 
					async function main() {
 | 
				
			||||||
    const mlmap = new UIEventSource<MlMap>(undefined)
 | 
					    const mlmap = new UIEventSource<MlMap>(undefined)
 | 
				
			||||||
    const locationControl = new UIEventSource<Loc>({
 | 
					    const location = new UIEventSource<{ lon: number; lat: number }>({
 | 
				
			||||||
        zoom: 14,
 | 
					 | 
				
			||||||
        lat: 51.1,
 | 
					        lat: 51.1,
 | 
				
			||||||
        lon: 3.1,
 | 
					        lon: 3.1,
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
| 
						 | 
					@ -29,44 +28,70 @@ async function main() {
 | 
				
			||||||
        .SetStyle("height: 50vh; width: 90%; margin: 1%")
 | 
					        .SetStyle("height: 50vh; width: 90%; margin: 1%")
 | 
				
			||||||
        .AttachTo("maindiv")
 | 
					        .AttachTo("maindiv")
 | 
				
			||||||
    const bg = new UIEventSource<RasterLayerPolygon>(undefined)
 | 
					    const bg = new UIEventSource<RasterLayerPolygon>(undefined)
 | 
				
			||||||
    new MapLibreAdaptor(mlmap, {
 | 
					    const mla = new MapLibreAdaptor(mlmap, {
 | 
				
			||||||
        backgroundLayer: bg,
 | 
					        rasterLayer: bg,
 | 
				
			||||||
        locationControl,
 | 
					        location,
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const availableLayersBboxes = Stores.ListStabilized(
 | 
					    const features = new UIEventSource([
 | 
				
			||||||
        locationControl.mapD((loc) => {
 | 
					        {
 | 
				
			||||||
            const lonlat: [number, number] = [loc.lon, loc.lat]
 | 
					            feature: {
 | 
				
			||||||
            return AvailableRasterLayers.EditorLayerIndex.filter((eliPolygon) =>
 | 
					                type: "Feature",
 | 
				
			||||||
                BBox.get(eliPolygon).contains(lonlat)
 | 
					                properties: {
 | 
				
			||||||
            )
 | 
					                    hello: "world",
 | 
				
			||||||
        })
 | 
					                    id: "" + 1,
 | 
				
			||||||
    )
 | 
					                },
 | 
				
			||||||
    const availableLayers: Store<RasterLayerPolygon[]> = Stores.ListStabilized(
 | 
					                geometry: {
 | 
				
			||||||
        availableLayersBboxes.map((eliPolygons) => {
 | 
					                    type: "Point",
 | 
				
			||||||
            const loc = locationControl.data
 | 
					                    coordinates: [3.1, 51.2],
 | 
				
			||||||
            const lonlat: [number, number] = [loc.lon, loc.lat]
 | 
					                },
 | 
				
			||||||
            const matching: RasterLayerPolygon[] = eliPolygons.filter((eliPolygon) => {
 | 
					            },
 | 
				
			||||||
                if (eliPolygon.geometry === null) {
 | 
					            freshness: new Date(),
 | 
				
			||||||
                    return true // global ELI-layer
 | 
					        },
 | 
				
			||||||
                }
 | 
					    ])
 | 
				
			||||||
                return GeoOperations.inside(lonlat, eliPolygon)
 | 
					    const layer = new LayerConfig(bench)
 | 
				
			||||||
            })
 | 
					    const options = {
 | 
				
			||||||
            matching.unshift(AvailableRasterLayers.osmCarto)
 | 
					        zoomToFeatures: false,
 | 
				
			||||||
            matching.push(...AvailableRasterLayers.globalLayers)
 | 
					        features: new SimpleFeatureSource(
 | 
				
			||||||
            return matching
 | 
					            {
 | 
				
			||||||
        })
 | 
					                layerDef: layer,
 | 
				
			||||||
    )
 | 
					                isDisplayed: new UIEventSource<boolean>(true),
 | 
				
			||||||
 | 
					                appliedFilters: new UIEventSource<Map<string, FilterState>>(undefined),
 | 
				
			||||||
    availableLayers.map((a) =>
 | 
					            },
 | 
				
			||||||
        console.log(
 | 
					            0,
 | 
				
			||||||
            "Availabe layers at current location:",
 | 
					            features
 | 
				
			||||||
            a.map((al) => al.properties.id)
 | 
					        ),
 | 
				
			||||||
        )
 | 
					        layer,
 | 
				
			||||||
    )
 | 
					    }
 | 
				
			||||||
 | 
					    new ShowDataLayer(mlmap, options)
 | 
				
			||||||
    new BackgroundLayerResetter(bg, availableLayers)
 | 
					    mla.zoom.set(9)
 | 
				
			||||||
    new SvelteUIElement(RasterLayerPicker, { availableLayers, value: bg }).AttachTo("extradiv")
 | 
					    mla.location.set({ lon: 3.1, lat: 51.1 })
 | 
				
			||||||
 | 
					    const availableLayers = AvailableRasterLayers.layersAvailableAt(location)
 | 
				
			||||||
 | 
					    // new BackgroundLayerResetter(bg, availableLayers)
 | 
				
			||||||
 | 
					    // new SvelteUIElement(RasterLayerPicker, { availableLayers, value: bg }).AttachTo("extradiv")
 | 
				
			||||||
 | 
					    for (let i = 0; i <= 10; i++) {
 | 
				
			||||||
 | 
					        await Utils.waitFor(1000)
 | 
				
			||||||
 | 
					        features.ping()
 | 
				
			||||||
 | 
					        new FixedUiElement("> " + (5 - i)).AttachTo("extradiv")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    options.zoomToFeatures = false
 | 
				
			||||||
 | 
					    features.setData([
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            feature: {
 | 
				
			||||||
 | 
					                type: "Feature",
 | 
				
			||||||
 | 
					                properties: {
 | 
				
			||||||
 | 
					                    hello: "world",
 | 
				
			||||||
 | 
					                    id: "" + 1,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                geometry: {
 | 
				
			||||||
 | 
					                    type: "Point",
 | 
				
			||||||
 | 
					                    coordinates: [3.103, 51.10003],
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            freshness: new Date(),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    ])
 | 
				
			||||||
 | 
					    new FixedUiElement("> OK").AttachTo("extradiv")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
main().then((_) => {})
 | 
					main().then((_) => {})
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue