forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			96 lines
		
	
	
	
		
			3.5 KiB
		
	
	
	
		
			Svelte
		
	
	
	
	
	
			
		
		
	
	
			96 lines
		
	
	
	
		
			3.5 KiB
		
	
	
	
		
			Svelte
		
	
	
	
	
	
<script lang="ts">
 | 
						|
  import { Store, UIEventSource } from "../../../Logic/UIEventSource"
 | 
						|
  import type { MapProperties } from "../../../Models/MapProperties"
 | 
						|
  import { Map as MlMap } from "maplibre-gl"
 | 
						|
  import { MapLibreAdaptor } from "../../Map/MapLibreAdaptor"
 | 
						|
  import MaplibreMap from "../../Map/MaplibreMap.svelte"
 | 
						|
  import DragInvitation from "../../Base/DragInvitation.svelte"
 | 
						|
  import { GeoOperations } from "../../../Logic/GeoOperations"
 | 
						|
  import ShowDataLayer from "../../Map/ShowDataLayer"
 | 
						|
  import * as boundsdisplay from "../../../assets/layers/range/range.json"
 | 
						|
  import StaticFeatureSource from "../../../Logic/FeatureSource/Sources/StaticFeatureSource"
 | 
						|
  import * as turf from "@turf/turf"
 | 
						|
  import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
 | 
						|
  import { createEventDispatcher, onDestroy } from "svelte"
 | 
						|
 | 
						|
  /**
 | 
						|
   * A visualisation to pick a location on a map background
 | 
						|
   */
 | 
						|
  export let value: UIEventSource<{ lon: number; lat: number }>
 | 
						|
  export let initialCoordinate: { lon: number; lat: number }
 | 
						|
  initialCoordinate = initialCoordinate ?? value.data
 | 
						|
  export let maxDistanceInMeters: number = undefined
 | 
						|
  export let mapProperties: Partial<MapProperties> & {
 | 
						|
    readonly location: UIEventSource<{ lon: number; lat: number }>
 | 
						|
  } = undefined
 | 
						|
  /**
 | 
						|
   * Called when setup is done, can be used to add more layers to the map
 | 
						|
   */
 | 
						|
  export let onCreated: (
 | 
						|
    value: Store<{
 | 
						|
      lon: number
 | 
						|
      lat: number
 | 
						|
    }>,
 | 
						|
    map: Store<MlMap>,
 | 
						|
    mapProperties: MapProperties
 | 
						|
  ) => void = undefined
 | 
						|
 | 
						|
  const dispatch = createEventDispatcher<{ click: { lon: number; lat: number } }>()
 | 
						|
 | 
						|
  export let map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined)
 | 
						|
  let mla = new MapLibreAdaptor(map, mapProperties)
 | 
						|
  mla.lastClickLocation.addCallbackAndRunD((lastClick) => {
 | 
						|
    dispatch("click", lastClick)
 | 
						|
  })
 | 
						|
  mapProperties.location.syncWith(value)
 | 
						|
  if (onCreated) {
 | 
						|
    onCreated(value, map, mla)
 | 
						|
  }
 | 
						|
 | 
						|
  let rangeIsShown = false
 | 
						|
  if (maxDistanceInMeters) {
 | 
						|
    onDestroy(
 | 
						|
      mla.location.addCallbackD((newLocation) => {
 | 
						|
        const l = [newLocation.lon, newLocation.lat]
 | 
						|
        const c: [number, number] = [initialCoordinate.lon, initialCoordinate.lat]
 | 
						|
        const d = GeoOperations.distanceBetween(l, c)
 | 
						|
        console.log("distance is", d, l, c)
 | 
						|
        if (d <= maxDistanceInMeters) {
 | 
						|
          return
 | 
						|
        }
 | 
						|
        // This is too far away - let's move back
 | 
						|
        const correctLocation = GeoOperations.along(c, l, maxDistanceInMeters - 10)
 | 
						|
        window.setTimeout(() => {
 | 
						|
          mla.location.setData({ lon: correctLocation[0], lat: correctLocation[1] })
 | 
						|
        }, 25)
 | 
						|
 | 
						|
        if (!rangeIsShown) {
 | 
						|
          new ShowDataLayer(map, {
 | 
						|
            layer: new LayerConfig(boundsdisplay),
 | 
						|
            features: new StaticFeatureSource([
 | 
						|
              turf.circle(c, maxDistanceInMeters, {
 | 
						|
                units: "meters",
 | 
						|
                properties: { range: "yes", id: "0" },
 | 
						|
              }),
 | 
						|
            ]),
 | 
						|
          })
 | 
						|
          rangeIsShown = true
 | 
						|
        }
 | 
						|
      })
 | 
						|
    )
 | 
						|
  }
 | 
						|
</script>
 | 
						|
 | 
						|
<div class="min-h-32 relative h-full cursor-pointer overflow-hidden">
 | 
						|
  <div class="absolute top-0 left-0 h-full w-full cursor-pointer">
 | 
						|
    <MaplibreMap center={({lng: initialCoordinate.lon, lat: initialCoordinate.lat})}} {map} />
 | 
						|
  </div>
 | 
						|
 | 
						|
  <div
 | 
						|
    class="pointer-events-none absolute top-0 left-0 flex h-full w-full items-center p-8 opacity-50"
 | 
						|
  >
 | 
						|
    <img class="h-full max-h-24" src="./assets/svg/move-arrows.svg" />
 | 
						|
  </div>
 | 
						|
 | 
						|
  <DragInvitation hideSignal={mla.location} />
 | 
						|
</div>
 |