2023-12-21 17:36:43 +01:00
|
|
|
<script lang="ts">
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An A11Y feature which indicates how far away and in what direction the feature lies.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
import { GeoOperations } from "../../Logic/GeoOperations"
|
|
|
|
import { Store } from "../../Logic/UIEventSource"
|
|
|
|
import type { Feature } from "geojson"
|
|
|
|
import Compass_arrow from "../../assets/svg/Compass_arrow.svelte"
|
|
|
|
import { twMerge } from "tailwind-merge"
|
|
|
|
import { Orientation } from "../../Sensors/Orientation"
|
2023-12-24 05:01:10 +01:00
|
|
|
import Translations from "../i18n/Translations"
|
|
|
|
import Constants from "../../Models/Constants"
|
|
|
|
import Locale from "../i18n/Locale"
|
|
|
|
import { ariaLabelStore } from "../../Utils/ariaLabel"
|
|
|
|
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
2023-12-21 17:36:43 +01:00
|
|
|
|
2023-12-24 05:01:10 +01:00
|
|
|
export let state: SpecialVisualizationState
|
2023-12-21 17:36:43 +01:00
|
|
|
export let feature: Feature
|
2023-12-24 05:01:10 +01:00
|
|
|
export let size = "w-8 h-8"
|
2023-12-21 17:36:43 +01:00
|
|
|
|
|
|
|
let fcenter = GeoOperations.centerpointCoordinates(feature)
|
|
|
|
// Bearing and distance relative to the map center
|
|
|
|
let bearingAndDist: Store<{ bearing: number; dist: number }> = state.mapProperties.location.map(
|
|
|
|
(l) => {
|
|
|
|
let mapCenter = [l.lon, l.lat]
|
2023-12-24 05:01:10 +01:00
|
|
|
let bearing = Math.round(GeoOperations.bearing(mapCenter, fcenter))
|
2023-12-21 17:36:43 +01:00
|
|
|
let dist = Math.round(GeoOperations.distanceBetween(fcenter, mapCenter))
|
|
|
|
return { bearing, dist }
|
|
|
|
},
|
|
|
|
)
|
|
|
|
let bearingFromGps = state.geolocation.geolocationState.currentGPSLocation.mapD(coordinate => {
|
|
|
|
return GeoOperations.bearing([coordinate.longitude, coordinate.latitude], fcenter)
|
|
|
|
})
|
2023-12-24 05:01:10 +01:00
|
|
|
let compass = Orientation.singleton.alpha
|
|
|
|
|
|
|
|
let relativeDirections = Translations.t.general.visualFeedback.directionsRelative
|
|
|
|
let absoluteDirections = Translations.t.general.visualFeedback.directionsAbsolute
|
|
|
|
|
|
|
|
let closeToCurrentLocation = state.geolocation.geolocationState.currentGPSLocation.map(gps => {
|
|
|
|
if (!gps) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
let l = state.mapProperties.location.data
|
|
|
|
let mapCenter = [l.lon, l.lat]
|
|
|
|
const dist = GeoOperations.distanceBetween([gps.longitude, gps.latitude], mapCenter)
|
|
|
|
return dist < Constants.viewportCenterCloseToGpsCutoff
|
|
|
|
},
|
|
|
|
[state.mapProperties.location],
|
|
|
|
)
|
|
|
|
let labelFromCenter: Store<string> = bearingAndDist.mapD(({ bearing, dist }) => {
|
|
|
|
const distHuman = GeoOperations.distanceToHuman(dist)
|
|
|
|
const lang = Locale.language.data
|
|
|
|
const t = absoluteDirections[GeoOperations.bearingToHuman(bearing)]
|
|
|
|
const mainTr = Translations.t.general.visualFeedback.fromMapCenter.Subs({
|
|
|
|
distance: distHuman,
|
|
|
|
direction: t.textFor(lang),
|
|
|
|
})
|
|
|
|
return mainTr.textFor(lang)
|
|
|
|
}, [compass, Locale.language])
|
|
|
|
|
|
|
|
|
|
|
|
// Bearing and distance relative to the map center
|
|
|
|
let bearingAndDistGps: Store<{
|
|
|
|
bearing: number;
|
|
|
|
dist: number
|
|
|
|
} | undefined> = state.geolocation.geolocationState.currentGPSLocation.mapD(
|
|
|
|
({ longitude, latitude }) => {
|
|
|
|
let gps = [longitude, latitude]
|
|
|
|
let bearing = Math.round(GeoOperations.bearing(gps, fcenter))
|
|
|
|
let dist = Math.round(GeoOperations.distanceBetween(fcenter, gps))
|
|
|
|
return { bearing, dist }
|
|
|
|
},
|
|
|
|
)
|
|
|
|
let labelFromGps: Store<string | undefined> = bearingAndDistGps.mapD(({ bearing, dist }) => {
|
|
|
|
const distHuman = GeoOperations.distanceToHuman(dist)
|
|
|
|
const lang = Locale.language.data
|
|
|
|
let bearingHuman: string
|
|
|
|
if (compass.data !== undefined) {
|
|
|
|
console.log("compass:", compass.data)
|
|
|
|
const bearingRelative = bearing - compass.data
|
|
|
|
const t = relativeDirections[GeoOperations.bearingToHumanRelative(bearingRelative)]
|
|
|
|
bearingHuman = t.textFor(lang)
|
|
|
|
} else {
|
|
|
|
bearingHuman = absoluteDirections[GeoOperations.bearingToHuman(bearing)].textFor(lang)
|
|
|
|
}
|
|
|
|
const mainTr = Translations.t.general.visualFeedback.fromGps.Subs({
|
|
|
|
distance: distHuman,
|
|
|
|
direction: bearingHuman,
|
|
|
|
})
|
|
|
|
return mainTr.textFor(lang)
|
|
|
|
}, [compass, Locale.language])
|
|
|
|
|
|
|
|
let label = labelFromCenter.map(labelFromCenter => {
|
|
|
|
if (labelFromGps.data !== undefined) {
|
|
|
|
if(closeToCurrentLocation.data){
|
|
|
|
return labelFromGps.data
|
|
|
|
}
|
|
|
|
return labelFromCenter + ", " + labelFromGps.data
|
|
|
|
}
|
|
|
|
return labelFromCenter
|
|
|
|
}, [labelFromGps])
|
|
|
|
function focusMap(){
|
|
|
|
state.mapProperties.location.setData({ lon: fcenter[0], lat: fcenter[1] })
|
|
|
|
}
|
2023-12-21 17:36:43 +01:00
|
|
|
</script>
|
|
|
|
|
2023-12-24 05:01:10 +01:00
|
|
|
<button class={twMerge("relative rounded-full soft", size)} use:ariaLabelStore={label} on:click={() => focusMap()}>
|
|
|
|
<div class={twMerge("absolute top-0 left-0 flex items-center justify-center text-sm break-words",size)}>
|
2023-12-26 12:35:51 +01:00
|
|
|
{GeoOperations.distanceToHuman($bearingAndDistGps?.dist)}
|
2023-12-21 17:36:43 +01:00
|
|
|
</div>
|
|
|
|
{#if $bearingFromGps !== undefined}
|
2023-12-24 05:01:10 +01:00
|
|
|
<div class={twMerge("absolute top-0 left-0 rounded-full", size)}>
|
2023-12-21 17:36:43 +01:00
|
|
|
<Compass_arrow class={size}
|
2023-12-24 05:01:10 +01:00
|
|
|
style={`transform: rotate( calc( 45deg + ${$bearingFromGps - ($compass ?? 0)}deg) );`} />
|
2023-12-21 17:36:43 +01:00
|
|
|
</div>
|
|
|
|
{/if}
|
2023-12-24 05:01:10 +01:00
|
|
|
</button>
|