forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			108 lines
		
	
	
	
		
			4.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			108 lines
		
	
	
	
		
			4.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { Store, UIEventSource } from "../../Logic/UIEventSource"
 | |
| import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
 | |
| import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization"
 | |
| import { Feature } from "geojson"
 | |
| import { MapLibreAdaptor } from "../Map/MapLibreAdaptor"
 | |
| import SvelteUIElement from "../Base/SvelteUIElement"
 | |
| import MaplibreMap from "../Map/MaplibreMap.svelte"
 | |
| import ShowDataLayer from "../Map/ShowDataLayer"
 | |
| import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
 | |
| import { GeoOperations } from "../../Logic/GeoOperations"
 | |
| import { BBox } from "../../Logic/BBox"
 | |
| 
 | |
| export class MinimapViz implements SpecialVisualization {
 | |
|     funcName = "minimap"
 | |
|     docs = "A small map showing the selected feature."
 | |
|     args = [
 | |
|         {
 | |
|             doc: "The (maximum) zoomlevel: the target zoomlevel after fitting the entire feature. The minimap will fit the entire feature, then zoom out to this zoom level. The higher, the more zoomed in with 1 being the entire world and 19 being really close",
 | |
|             name: "zoomlevel",
 | |
|             defaultValue: "18",
 | |
|         },
 | |
|         {
 | |
|             doc: "(Matches all resting arguments) This argument should be the key of a property of the feature. The corresponding value is interpreted as either the id or the a list of ID's. The features with these ID's will be shown on this minimap. (Note: if the key is 'id', list interpration is disabled)",
 | |
|             name: "idKey",
 | |
|             defaultValue: "id",
 | |
|         },
 | |
|     ]
 | |
|     example: "`{minimap()}`, `{minimap(17, id, _list_of_embedded_feature_ids_calculated_by_calculated_tag):height:10rem; border: 2px solid black}`"
 | |
| 
 | |
|     constr(
 | |
|         state: SpecialVisualizationState,
 | |
|         tagSource: UIEventSource<Record<string, string>>,
 | |
|         args: string[],
 | |
|         feature: Feature,
 | |
|         layer: LayerConfig
 | |
|     ) {
 | |
|         if (state === undefined || feature === undefined || layer.source === undefined) {
 | |
|             return undefined
 | |
|         }
 | |
|         const keys = [...args]
 | |
|         keys.splice(0, 1)
 | |
|         const featuresToShow: Store<Feature[]> = state.indexedFeatures.featuresById.map(
 | |
|             (featuresById) => {
 | |
|                 if (featuresById === undefined) {
 | |
|                     return []
 | |
|                 }
 | |
|                 const properties = tagSource.data
 | |
|                 const features: Feature[] = []
 | |
|                 for (const key of keys) {
 | |
|                     const value = properties[key]
 | |
|                     if (value === undefined || value === null) {
 | |
|                         continue
 | |
|                     }
 | |
| 
 | |
|                     let idList = [value]
 | |
|                     if (key !== "id" && value.startsWith("[")) {
 | |
|                         // This is a list of values
 | |
|                         idList = JSON.parse(value)
 | |
|                     }
 | |
| 
 | |
|                     for (const id of idList) {
 | |
|                         const feature = featuresById.get(id)
 | |
|                         if (feature === undefined) {
 | |
|                             console.warn("No feature found for id ", id)
 | |
|                             continue
 | |
|                         }
 | |
|                         features.push(feature)
 | |
|                     }
 | |
|                 }
 | |
|                 return features
 | |
|             },
 | |
|             [tagSource]
 | |
|         )
 | |
| 
 | |
|         const mlmap = new UIEventSource(undefined)
 | |
|         const mla = new MapLibreAdaptor(mlmap)
 | |
| 
 | |
|         let zoom = 18
 | |
|         if (args[0]) {
 | |
|             const parsed = Number(args[0])
 | |
|             if (!isNaN(parsed) && parsed > 0 && parsed < 25) {
 | |
|                 zoom = parsed
 | |
|             }
 | |
|         }
 | |
|         featuresToShow.addCallbackAndRunD((features) => {
 | |
|             if (features.length === 0) {
 | |
|                 return
 | |
|             }
 | |
|             const bboxGeojson = GeoOperations.bbox({ features, type: "FeatureCollection" })
 | |
|             const [lon, lat] = GeoOperations.centerpointCoordinates(bboxGeojson)
 | |
|             mla.bounds.setData(BBox.get(bboxGeojson))
 | |
|             mla.location.setData({ lon, lat })
 | |
|         })
 | |
|         mla.zoom.setData(zoom)
 | |
|         mla.allowMoving.setData(false)
 | |
|         mla.allowZooming.setData(false)
 | |
| 
 | |
|         ShowDataLayer.showMultipleLayers(
 | |
|             mlmap,
 | |
|             new StaticFeatureSource(featuresToShow),
 | |
|             state.layout.layers
 | |
|         )
 | |
| 
 | |
|         return new SvelteUIElement(MaplibreMap, { map: mlmap })
 | |
|             .SetClass("h-40 rounded")
 | |
|             .SetStyle("overflow: hidden; pointer-events: none;")
 | |
|     }
 | |
| }
 |