forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			117 lines
		
	
	
	
		
			3.9 KiB
		
	
	
	
		
			Svelte
		
	
	
	
	
	
			
		
		
	
	
			117 lines
		
	
	
	
		
			3.9 KiB
		
	
	
	
		
			Svelte
		
	
	
	
	
	
<script lang="ts">
 | 
						|
  import type { SpecialVisualizationState } from "../SpecialVisualization"
 | 
						|
  import LocationInput from "../InputElement/Helpers/LocationInput.svelte"
 | 
						|
  import { UIEventSource } from "../../Logic/UIEventSource"
 | 
						|
  import { Tiles } from "../../Models/TileRange"
 | 
						|
  import { Map as MlMap } from "maplibre-gl"
 | 
						|
  import { BBox } from "../../Logic/BBox"
 | 
						|
  import type { MapProperties } from "../../Models/MapProperties"
 | 
						|
  import ShowDataLayer from "../Map/ShowDataLayer"
 | 
						|
  import type {
 | 
						|
    FeatureSource,
 | 
						|
    FeatureSourceForLayer,
 | 
						|
  } from "../../Logic/FeatureSource/FeatureSource"
 | 
						|
  import SnappingFeatureSource from "../../Logic/FeatureSource/Sources/SnappingFeatureSource"
 | 
						|
  import FeatureSourceMerger from "../../Logic/FeatureSource/Sources/FeatureSourceMerger"
 | 
						|
  import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
 | 
						|
  import { Utils } from "../../Utils"
 | 
						|
  import { createEventDispatcher } from "svelte"
 | 
						|
 | 
						|
  /**
 | 
						|
   * An advanced location input, which has support to:
 | 
						|
   * - Show more layers
 | 
						|
   * - Snap to layers
 | 
						|
   *
 | 
						|
   * This one is mostly used to insert new points, including when importing
 | 
						|
   */
 | 
						|
  export let state: SpecialVisualizationState
 | 
						|
  /**
 | 
						|
   * The start coordinate
 | 
						|
   */
 | 
						|
  export let coordinate: { lon: number; lat: number }
 | 
						|
  export let snapToLayers: string[] | undefined
 | 
						|
  export let targetLayer: LayerConfig
 | 
						|
  export let maxSnapDistance: number = undefined
 | 
						|
 | 
						|
  export let snappedTo: UIEventSource<string | undefined>
 | 
						|
 | 
						|
  export let value: UIEventSource<{ lon: number; lat: number }>
 | 
						|
  if (value.data === undefined) {
 | 
						|
    value.setData(coordinate)
 | 
						|
  }
 | 
						|
 | 
						|
  let preciseLocation: UIEventSource<{ lon: number; lat: number }> = new UIEventSource<{
 | 
						|
    lon: number
 | 
						|
    lat: number
 | 
						|
  }>(undefined)
 | 
						|
 | 
						|
  const dispatch = createEventDispatcher<{ click: { lon: number; lat: number } }>()
 | 
						|
 | 
						|
  const xyz = Tiles.embedded_tile(coordinate.lat, coordinate.lon, 16)
 | 
						|
  const map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined)
 | 
						|
  let initialMapProperties: Partial<MapProperties> = {
 | 
						|
    zoom: new UIEventSource<number>(19),
 | 
						|
    maxbounds: new UIEventSource(undefined),
 | 
						|
    /*If no snapping needed: the value is simply the map location;
 | 
						|
     * If snapping is needed: the value will be set later on by the snapping feature source
 | 
						|
     * */
 | 
						|
    location:
 | 
						|
      snapToLayers?.length > 0
 | 
						|
        ? new UIEventSource<{ lon: number; lat: number }>(coordinate)
 | 
						|
        : value,
 | 
						|
    bounds: new UIEventSource<BBox>(undefined),
 | 
						|
    allowMoving: new UIEventSource<boolean>(true),
 | 
						|
    allowZooming: new UIEventSource<boolean>(true),
 | 
						|
    minzoom: new UIEventSource<number>(18),
 | 
						|
    rasterLayer: UIEventSource.feedFrom(state.mapProperties.rasterLayer),
 | 
						|
  }
 | 
						|
 | 
						|
  const featuresForLayer = state.perLayer.get(targetLayer.id)
 | 
						|
  if (featuresForLayer) {
 | 
						|
    new ShowDataLayer(map, {
 | 
						|
      layer: targetLayer,
 | 
						|
      features: featuresForLayer,
 | 
						|
    })
 | 
						|
  }
 | 
						|
 | 
						|
  if (snapToLayers?.length > 0) {
 | 
						|
    const snapSources: FeatureSource[] = []
 | 
						|
    for (const layerId of snapToLayers ?? []) {
 | 
						|
      const layer: FeatureSourceForLayer = state.perLayer.get(layerId)
 | 
						|
      snapSources.push(layer)
 | 
						|
      if (layer.features === undefined) {
 | 
						|
        continue
 | 
						|
      }
 | 
						|
      new ShowDataLayer(map, {
 | 
						|
        layer: layer.layer.layerDef,
 | 
						|
        zoomToFeatures: false,
 | 
						|
        features: layer,
 | 
						|
      })
 | 
						|
    }
 | 
						|
    const snappedLocation = new SnappingFeatureSource(
 | 
						|
      new FeatureSourceMerger(...Utils.NoNull(snapSources)),
 | 
						|
      // We snap to the (constantly updating) map location
 | 
						|
      initialMapProperties.location,
 | 
						|
      {
 | 
						|
        maxDistance: maxSnapDistance ?? 15,
 | 
						|
        allowUnsnapped: true,
 | 
						|
        snappedTo,
 | 
						|
        snapLocation: value,
 | 
						|
      }
 | 
						|
    )
 | 
						|
 | 
						|
    new ShowDataLayer(map, {
 | 
						|
      layer: targetLayer,
 | 
						|
      features: snappedLocation,
 | 
						|
    })
 | 
						|
  }
 | 
						|
</script>
 | 
						|
 | 
						|
<LocationInput
 | 
						|
  {map}
 | 
						|
  on:click={(data) => dispatch("click", data)}
 | 
						|
  mapProperties={initialMapProperties}
 | 
						|
  value={preciseLocation}
 | 
						|
  initialCoordinate={coordinate}
 | 
						|
  maxDistanceInMeters="50"
 | 
						|
/>
 |