forked from MapComplete/MapComplete
88 lines
3 KiB
Svelte
88 lines
3 KiB
Svelte
<script lang="ts">
|
|
import { 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 Direction_stroke from "../../../assets/svg/Direction_stroke.svelte"
|
|
import type { SpecialVisualizationState } from "../../SpecialVisualization"
|
|
import type Feature from "geojson"
|
|
import { GeoOperations } from "../../../Logic/GeoOperations"
|
|
/**
|
|
* A visualisation to pick a direction on a map background.
|
|
*/
|
|
export let value: UIEventSource<undefined | string>
|
|
export let state: SpecialVisualizationState = undefined
|
|
export let args: any[] = []
|
|
export let feature: Feature
|
|
let [lon, lat] = GeoOperations.centerpointCoordinates(feature)
|
|
let mapProperties: MapProperties = {
|
|
location: new UIEventSource({ lon, lat }),
|
|
zoom: new UIEventSource(args[0] !== undefined ? Number(args[0]) : 17),
|
|
rasterLayer: state.mapProperties.rasterLayer,
|
|
rotation: state.mapProperties.rotation,
|
|
}
|
|
let map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined)
|
|
let mla = new MapLibreAdaptor(map, mapProperties)
|
|
mla.allowMoving.setData(false)
|
|
mla.allowZooming.setData(false)
|
|
let rotation = new UIEventSource(value.data)
|
|
rotation.addCallbackD(rotation => {
|
|
const r = (rotation + mapProperties.rotation.data + 360) % 360
|
|
console.log("Setting value to", r)
|
|
value.setData(""+Math.floor(r))
|
|
}, [mapProperties.rotation])
|
|
let directionElem: HTMLElement | undefined
|
|
$: rotation.addCallbackAndRunD((degrees) => {
|
|
if (!directionElem?.style) {
|
|
return
|
|
}
|
|
directionElem.style.rotate = degrees + "deg"
|
|
})
|
|
|
|
let mainElem: HTMLElement
|
|
|
|
|
|
function onPosChange(x: number, y: number) {
|
|
const rect = mainElem.getBoundingClientRect()
|
|
const dx = -(rect.left + rect.right) / 2 + x
|
|
const dy = (rect.top + rect.bottom) / 2 - y
|
|
const angle = (180 * Math.atan2(dy, dx)) / Math.PI
|
|
const angleGeo = Math.floor((450 - angle) % 360)
|
|
rotation.setData(angleGeo)
|
|
}
|
|
|
|
let isDown = false
|
|
</script>
|
|
|
|
<div
|
|
bind:this={mainElem}
|
|
class="relative h-48 min-w-48 w-full cursor-pointer overflow-hidden rounded-xl"
|
|
on:click={(e) => onPosChange(e.x, e.y)}
|
|
on:mousedown={(e) => {
|
|
isDown = true
|
|
onPosChange(e.clientX, e.clientY)
|
|
}}
|
|
on:mousemove={(e) => {
|
|
if (isDown) {
|
|
onPosChange(e.clientX, e.clientY)
|
|
e.preventDefault()
|
|
}
|
|
}}
|
|
on:mouseup={() => {
|
|
isDown = false
|
|
}}
|
|
on:touchmove={(e) => {
|
|
onPosChange(e.touches[0].clientX, e.touches[0].clientY)
|
|
e.preventDefault()
|
|
}}
|
|
on:touchstart={(e) => onPosChange(e.touches[0].clientX, e.touches[0].clientY)}
|
|
>
|
|
<div class="absolute left-0 top-0 h-full w-full cursor-pointer">
|
|
<MaplibreMap mapProperties={mla} {map} />
|
|
</div>
|
|
|
|
<div bind:this={directionElem} class="absolute left-0 top-0 h-full w-full p-1">
|
|
<Direction_stroke />
|
|
</div>
|
|
</div>
|