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> { | ||||
|         return { | ||||
|             type: "Feature", | ||||
|  |  | |||
|  | @ -1,8 +1,6 @@ | |||
| import UserRelatedState from "./UserRelatedState" | ||||
| import { Store, Stores, UIEventSource } from "../UIEventSource" | ||||
| import BaseLayer from "../../Models/BaseLayer" | ||||
| import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" | ||||
| import AvailableBaseLayers from "../Actors/AvailableBaseLayers" | ||||
| import Attribution from "../../UI/BigComponents/Attribution" | ||||
| import Minimap, { MinimapObj } from "../../UI/Base/Minimap" | ||||
| import { Tiles } from "../../Models/TileRange" | ||||
|  | @ -43,10 +41,6 @@ export default class MapState extends UserRelatedState { | |||
|      The leaflet instance of the big basemap | ||||
|      */ | ||||
|     public leafletMap = new UIEventSource<any /*L.Map*/>(undefined, "leafletmap") | ||||
|     /** | ||||
|      * A list of currently available background layers | ||||
|      */ | ||||
|     public availableBackgroundLayers: Store<BaseLayer[]> | ||||
| 
 | ||||
|     /** | ||||
|      * The current background layer | ||||
|  |  | |||
|  | @ -2,6 +2,8 @@ import { Feature, Polygon } from "geojson" | |||
| import * as editorlayerindex from "../assets/editor-layer-index.json" | ||||
| import * as globallayers from "../assets/global-raster-layers.json" | ||||
| import { BBox } from "../Logic/BBox" | ||||
| import { Store, Stores } from "../Logic/UIEventSource" | ||||
| import { GeoOperations } from "../Logic/GeoOperations" | ||||
| 
 | ||||
| export class AvailableRasterLayers { | ||||
|     public static EditorLayerIndex: (Feature<Polygon, EditorLayerIndexProperties> & | ||||
|  | @ -33,6 +35,35 @@ export class AvailableRasterLayers { | |||
|         properties: AvailableRasterLayers.osmCartoProperties, | ||||
|         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 { | ||||
|  |  | |||
|  | @ -5,12 +5,13 @@ import { TagUtils } from "../../Logic/Tags/TagUtils" | |||
| import { Utils } from "../../Utils" | ||||
| import Svg from "../../Svg" | ||||
| import WithContextLoader from "./WithContextLoader" | ||||
| import { UIEventSource } from "../../Logic/UIEventSource" | ||||
| import { Store } from "../../Logic/UIEventSource" | ||||
| import BaseUIElement from "../../UI/BaseUIElement" | ||||
| import { FixedUiElement } from "../../UI/Base/FixedUiElement" | ||||
| import Img from "../../UI/Base/Img" | ||||
| import Combine from "../../UI/Base/Combine" | ||||
| import { VariableUiElement } from "../../UI/Base/VariableUIElement" | ||||
| import { OsmTags } from "../OsmFeature" | ||||
| 
 | ||||
| export default class PointRenderingConfig extends WithContextLoader { | ||||
|     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) | ||||
|     } | ||||
| 
 | ||||
|     public GetSimpleIcon(tags: UIEventSource<any>): BaseUIElement { | ||||
|     public GetSimpleIcon(tags: Store<OsmTags>): BaseUIElement { | ||||
|         const self = this | ||||
|         if (this.icon === undefined) { | ||||
|             return undefined | ||||
|  | @ -175,7 +176,7 @@ export default class PointRenderingConfig extends WithContextLoader { | |||
|     } | ||||
| 
 | ||||
|     public GenerateLeafletStyle( | ||||
|         tags: UIEventSource<any>, | ||||
|         tags: Store<OsmTags>, | ||||
|         clickable: boolean, | ||||
|         options?: { | ||||
|             noSize?: false | boolean | ||||
|  | @ -183,11 +184,7 @@ export default class PointRenderingConfig extends WithContextLoader { | |||
|         } | ||||
|     ): { | ||||
|         html: BaseUIElement | ||||
|         iconSize: [number, number] | ||||
|         iconAnchor: [number, number] | ||||
|         popupAnchor: [number, number] | ||||
|         iconUrl: string | ||||
|         className: string | ||||
|     } { | ||||
|         function num(str, deflt = 40) { | ||||
|             const n = Number(str) | ||||
|  | @ -211,20 +208,21 @@ export default class PointRenderingConfig extends WithContextLoader { | |||
|         let iconH = num(iconSize[1]) | ||||
|         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 | ||||
|         if (mode === "left") { | ||||
|             anchorW = 0 | ||||
|             anchorW = -iconW / 2 | ||||
|         } | ||||
|         if (mode === "right") { | ||||
|             anchorW = iconW | ||||
|             anchorW = iconW / 2 | ||||
|         } | ||||
| 
 | ||||
|         if (mode === "top") { | ||||
|             anchorH = 0 | ||||
|             anchorH = -iconH / 2 | ||||
|         } | ||||
|         if (mode === "bottom") { | ||||
|             anchorH = iconH | ||||
|             anchorH = iconH / 2 | ||||
|         } | ||||
| 
 | ||||
|         const icon = this.GetSimpleIcon(tags) | ||||
|  | @ -264,15 +262,11 @@ export default class PointRenderingConfig extends WithContextLoader { | |||
|         } | ||||
|         return { | ||||
|             html: htmlEl, | ||||
|             iconSize: [iconW, iconH], | ||||
|             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) { | ||||
|             return undefined | ||||
|         } | ||||
|  | @ -304,7 +298,7 @@ export default class PointRenderingConfig extends WithContextLoader { | |||
|         ).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) { | ||||
|             return undefined | ||||
|         } | ||||
|  |  | |||
|  | @ -3,8 +3,6 @@ import { UIEventSource } from "../../Logic/UIEventSource" | |||
| import Loc from "../../Models/Loc" | ||||
| import Svg from "../../Svg" | ||||
| import Toggle from "../Input/Toggle" | ||||
| import BaseLayer from "../../Models/BaseLayer" | ||||
| import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers" | ||||
| import BaseUIElement from "../BaseUIElement" | ||||
| import { GeoOperations } from "../../Logic/GeoOperations" | ||||
| import Hotkeys from "../Base/Hotkeys" | ||||
|  |  | |||
|  | @ -18,14 +18,12 @@ import { Unit } from "../../Models/Unit" | |||
| import { FixedInputElement } from "./FixedInputElement" | ||||
| import WikidataSearchBox from "../Wikipedia/WikidataSearchBox" | ||||
| import Wikidata from "../../Logic/Web/Wikidata" | ||||
| import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers" | ||||
| import Table from "../Base/Table" | ||||
| import Combine from "../Base/Combine" | ||||
| import Title from "../Base/Title" | ||||
| import InputElementMap from "./InputElementMap" | ||||
| import Translations from "../i18n/Translations" | ||||
| import { Translation } from "../i18n/Translation" | ||||
| import BaseLayer from "../../Models/BaseLayer" | ||||
| import Locale from "../i18n/Locale" | ||||
| 
 | ||||
| export class TextFieldDef { | ||||
|  |  | |||
|  | @ -1,65 +1,81 @@ | |||
| import { Store, UIEventSource } from "../../Logic/UIEventSource" | ||||
| import type { Map as MLMap } from "maplibre-gl" | ||||
| import { | ||||
|     EditorLayerIndexProperties, | ||||
|     RasterLayerPolygon, | ||||
|     RasterLayerProperties, | ||||
| } from "../../Models/RasterLayers" | ||||
| import { RasterLayerPolygon, RasterLayerProperties } from "../../Models/RasterLayers" | ||||
| 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 _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>, | ||||
|         state?: { | ||||
|             // availableBackgroundLayers: Store<BaseLayer[]>
 | ||||
|     /** | ||||
|              * The current background layer | ||||
|      * Used for internal bookkeeping (to remove a rasterLayer when done loading) | ||||
|      * @private | ||||
|      */ | ||||
|             readonly backgroundLayer?: Store<RasterLayerPolygon> | ||||
|             readonly locationControl?: UIEventSource<Loc> | ||||
|         } | ||||
|     ) { | ||||
|     private _currentRasterLayer: string | ||||
|     constructor(maplibreMap: Store<MLMap>, state?: Partial<Omit<MapState, "bounds">>) { | ||||
|         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 | ||||
|         this._backgroundLayer?.addCallback((_) => self.setBackground()) | ||||
| 
 | ||||
|         maplibreMap.addCallbackAndRunD((map) => { | ||||
|             map.on("load", () => { | ||||
|                 self.setBackground() | ||||
|             }) | ||||
|             if (state.locationControl) { | ||||
|                 self.MoveMapToCurrentLoc(state.locationControl.data) | ||||
|             self.MoveMapToCurrentLoc(this.location.data) | ||||
|             self.SetZoom(this.zoom.data) | ||||
|             map.on("moveend", () => { | ||||
|                     const dt = state.locationControl.data | ||||
|                 const dt = this.location.data | ||||
|                 dt.lon = map.getCenter().lng | ||||
|                 dt.lat = map.getCenter().lat | ||||
|                     dt.zoom = map.getZoom() | ||||
|                     state.locationControl.ping() | ||||
|                 this.location.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) | ||||
|         }) | ||||
|         this.zoom.addCallbackAndRunD((z) => self.SetZoom(z)) | ||||
|     } | ||||
| 
 | ||||
|     private MoveMapToCurrentLoc(loc: Loc) { | ||||
|     private SetZoom(z: number) { | ||||
|         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 | ||||
|         if (map === undefined || loc === undefined) { | ||||
|             return | ||||
|         } | ||||
|         if (map.getZoom() !== loc.zoom) { | ||||
|             map.setZoom(loc.zoom) | ||||
|         } | ||||
| 
 | ||||
|         const center = map.getCenter() | ||||
|         if (center.lng !== loc.lon || center.lat !== loc.lat) { | ||||
|             map.setCenter({ lng: loc.lon, lat: loc.lat }) | ||||
|  | @ -120,14 +136,14 @@ export class MapLibreAdaptor { | |||
|         if (map === undefined) { | ||||
|             return | ||||
|         } | ||||
|         const background: RasterLayerProperties = this._backgroundLayer?.data?.properties | ||||
|         const background: RasterLayerProperties = this.rasterLayer?.data?.properties | ||||
|         if (background !== undefined && this._currentRasterLayer === background.id) { | ||||
|             // already the correct background layer, nothing to do
 | ||||
|             return | ||||
|         } | ||||
|         await this.awaitStyleIsLoaded() | ||||
| 
 | ||||
|         if (background !== this._backgroundLayer?.data?.properties) { | ||||
|         if (background !== this.rasterLayer?.data?.properties) { | ||||
|             // User selected another background in the meantime... abort
 | ||||
|             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 LayerConfig from "../../Models/ThemeConfig/LayerConfig" | ||||
| import ScrollableFullScreen from "../Base/ScrollableFullScreen" | ||||
| import { OsmTags } from "../../Models/OsmFeature" | ||||
| 
 | ||||
| export interface ShowDataLayerOptions { | ||||
|     /** | ||||
|      * Features to show | ||||
|      */ | ||||
|     features: FeatureSource | ||||
|     /** | ||||
|      * Indication of the current selected element; overrides some filters | ||||
|      */ | ||||
|     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 | ||||
|     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> | ||||
| } | ||||
|  |  | |||
							
								
								
									
										121
									
								
								test.ts
									
										
									
									
									
								
							
							
						
						
									
										121
									
								
								test.ts
									
										
									
									
									
								
							|  | @ -1,24 +1,23 @@ | |||
| import SvelteUIElement from "./UI/Base/SvelteUIElement" | ||||
| 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 { | ||||
|     EditorLayerIndexProperties, | ||||
|     RasterLayerPolygon, | ||||
|     RasterLayerProperties, | ||||
| } from "./Models/RasterLayers" | ||||
| import { AvailableRasterLayers, RasterLayerPolygon } from "./Models/RasterLayers" | ||||
| 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 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() { | ||||
|     const mlmap = new UIEventSource<MlMap>(undefined) | ||||
|     const locationControl = new UIEventSource<Loc>({ | ||||
|         zoom: 14, | ||||
|     const location = new UIEventSource<{ lon: number; lat: number }>({ | ||||
|         lat: 51.1, | ||||
|         lon: 3.1, | ||||
|     }) | ||||
|  | @ -29,44 +28,70 @@ async function main() { | |||
|         .SetStyle("height: 50vh; width: 90%; margin: 1%") | ||||
|         .AttachTo("maindiv") | ||||
|     const bg = new UIEventSource<RasterLayerPolygon>(undefined) | ||||
|     new MapLibreAdaptor(mlmap, { | ||||
|         backgroundLayer: bg, | ||||
|         locationControl, | ||||
|     const mla = new MapLibreAdaptor(mlmap, { | ||||
|         rasterLayer: bg, | ||||
|         location, | ||||
|     }) | ||||
| 
 | ||||
|     const availableLayersBboxes = Stores.ListStabilized( | ||||
|         locationControl.mapD((loc) => { | ||||
|             const lonlat: [number, number] = [loc.lon, loc.lat] | ||||
|             return AvailableRasterLayers.EditorLayerIndex.filter((eliPolygon) => | ||||
|                 BBox.get(eliPolygon).contains(lonlat) | ||||
|             ) | ||||
|         }) | ||||
|     ) | ||||
|     const availableLayers: Store<RasterLayerPolygon[]> = Stores.ListStabilized( | ||||
|         availableLayersBboxes.map((eliPolygons) => { | ||||
|             const loc = locationControl.data | ||||
|             const lonlat: [number, number] = [loc.lon, loc.lat] | ||||
|             const matching: RasterLayerPolygon[] = eliPolygons.filter((eliPolygon) => { | ||||
|                 if (eliPolygon.geometry === null) { | ||||
|                     return true // global ELI-layer
 | ||||
|     const features = new UIEventSource([ | ||||
|         { | ||||
|             feature: { | ||||
|                 type: "Feature", | ||||
|                 properties: { | ||||
|                     hello: "world", | ||||
|                     id: "" + 1, | ||||
|                 }, | ||||
|                 geometry: { | ||||
|                     type: "Point", | ||||
|                     coordinates: [3.1, 51.2], | ||||
|                 }, | ||||
|             }, | ||||
|             freshness: new Date(), | ||||
|         }, | ||||
|     ]) | ||||
|     const layer = new LayerConfig(bench) | ||||
|     const options = { | ||||
|         zoomToFeatures: false, | ||||
|         features: new SimpleFeatureSource( | ||||
|             { | ||||
|                 layerDef: layer, | ||||
|                 isDisplayed: new UIEventSource<boolean>(true), | ||||
|                 appliedFilters: new UIEventSource<Map<string, FilterState>>(undefined), | ||||
|             }, | ||||
|             0, | ||||
|             features | ||||
|         ), | ||||
|         layer, | ||||
|     } | ||||
|                 return GeoOperations.inside(lonlat, eliPolygon) | ||||
|             }) | ||||
|             matching.unshift(AvailableRasterLayers.osmCarto) | ||||
|             matching.push(...AvailableRasterLayers.globalLayers) | ||||
|             return matching | ||||
|         }) | ||||
|     ) | ||||
| 
 | ||||
|     availableLayers.map((a) => | ||||
|         console.log( | ||||
|             "Availabe layers at current location:", | ||||
|             a.map((al) => al.properties.id) | ||||
|         ) | ||||
|     ) | ||||
| 
 | ||||
|     new BackgroundLayerResetter(bg, availableLayers) | ||||
|     new SvelteUIElement(RasterLayerPicker, { availableLayers, value: bg }).AttachTo("extradiv") | ||||
|     new ShowDataLayer(mlmap, options) | ||||
|     mla.zoom.set(9) | ||||
|     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((_) => {}) | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue