2023-04-06 01:33:08 +02:00
|
|
|
<script lang="ts">
|
2024-02-20 13:33:38 +01:00
|
|
|
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
|
|
|
import LocationInput from "../InputElement/Helpers/LocationInput.svelte"
|
|
|
|
import { UIEventSource } from "../../Logic/UIEventSource"
|
|
|
|
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,
|
2024-04-13 02:40:21 +02:00
|
|
|
FeatureSourceForLayer,
|
2024-02-20 13:33:38 +01:00
|
|
|
} 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 Move_arrows from "../../assets/svg/Move_arrows.svelte"
|
2024-04-12 02:04:33 +02:00
|
|
|
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
|
|
|
|
import { Tag } from "../../Logic/Tags/Tag"
|
|
|
|
import { TagUtils } from "../../Logic/Tags/TagUtils"
|
2023-04-06 01:33:08 +02:00
|
|
|
|
2024-02-20 13:33:38 +01:00
|
|
|
/**
|
|
|
|
* 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 }
|
2023-04-06 01:33:08 +02:00
|
|
|
|
2024-02-20 13:33:38 +01:00
|
|
|
/**
|
|
|
|
* The center of the map at all times
|
|
|
|
* If undefined at the beginning, 'coordinate' will be used
|
|
|
|
*/
|
|
|
|
export let value: UIEventSource<{ lon: number; lat: number }>
|
|
|
|
if (value.data === undefined) {
|
|
|
|
value.setData(coordinate)
|
|
|
|
}
|
|
|
|
if (coordinate === undefined) {
|
|
|
|
coordinate = value.data
|
|
|
|
}
|
|
|
|
export let snapToLayers: string[] | undefined
|
|
|
|
export let targetLayer: LayerConfig | undefined
|
|
|
|
export let maxSnapDistance: number = undefined
|
2024-04-18 15:29:07 +02:00
|
|
|
export let presetProperties: Tag[] = []
|
2024-04-12 02:04:33 +02:00
|
|
|
let presetPropertiesUnpacked = TagUtils.KVtoProperties(presetProperties)
|
2023-06-15 16:12:46 +02:00
|
|
|
|
2024-02-20 13:33:38 +01:00
|
|
|
export let snappedTo: UIEventSource<string | undefined>
|
2023-04-06 01:33:08 +02:00
|
|
|
|
2024-02-20 13:33:38 +01:00
|
|
|
let preciseLocation: UIEventSource<{ lon: number; lat: number }> = new UIEventSource<{
|
|
|
|
lon: number
|
|
|
|
lat: number
|
|
|
|
}>(undefined)
|
2023-04-06 01:33:08 +02:00
|
|
|
|
2024-02-20 13:33:38 +01:00
|
|
|
const map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined)
|
|
|
|
let initialMapProperties: Partial<MapProperties> & { location } = {
|
|
|
|
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),
|
2024-04-13 02:40:21 +02:00
|
|
|
rasterLayer: UIEventSource.feedFrom(state.mapProperties.rasterLayer),
|
2024-02-20 13:33:38 +01:00
|
|
|
}
|
2024-03-04 15:31:09 +01:00
|
|
|
state?.showCurrentLocationOn(map)
|
2023-04-06 01:33:08 +02:00
|
|
|
|
2024-02-20 13:33:38 +01:00
|
|
|
if (targetLayer) {
|
2024-04-12 02:04:33 +02:00
|
|
|
// Show already existing items
|
2024-02-20 13:33:38 +01:00
|
|
|
const featuresForLayer = state.perLayer.get(targetLayer.id)
|
|
|
|
if (featuresForLayer) {
|
|
|
|
new ShowDataLayer(map, {
|
|
|
|
layer: targetLayer,
|
2024-04-13 02:40:21 +02:00
|
|
|
features: featuresForLayer,
|
2024-02-20 13:33:38 +01:00
|
|
|
})
|
2024-01-31 11:37:09 +01:00
|
|
|
}
|
2024-02-20 13:33:38 +01:00
|
|
|
}
|
2024-01-16 04:20:25 +01:00
|
|
|
|
2024-02-20 13:33:38 +01:00
|
|
|
if (snapToLayers?.length > 0) {
|
|
|
|
const snapSources: FeatureSource[] = []
|
|
|
|
for (const layerId of snapToLayers ?? []) {
|
2024-02-24 21:05:46 +01:00
|
|
|
// We assume that the layer contains the data, as the OSM-API-Feature-source should have loaded them, even though the layer might not be displayed
|
2024-02-20 13:33:38 +01:00
|
|
|
const layer: FeatureSourceForLayer = state.perLayer.get(layerId)
|
|
|
|
snapSources.push(layer)
|
|
|
|
if (layer.features === undefined) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
new ShowDataLayer(map, {
|
|
|
|
layer: layer.layer.layerDef,
|
|
|
|
zoomToFeatures: false,
|
2024-04-13 02:40:21 +02:00
|
|
|
features: layer,
|
2024-02-20 13:33:38 +01:00
|
|
|
})
|
2024-01-16 04:20:25 +01:00
|
|
|
}
|
2024-02-20 13:33:38 +01:00
|
|
|
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,
|
2024-04-13 02:40:21 +02:00
|
|
|
snapLocation: value,
|
2024-02-20 13:33:38 +01:00
|
|
|
}
|
|
|
|
)
|
2024-04-12 02:04:33 +02:00
|
|
|
const withCorrectedAttributes = new StaticFeatureSource(
|
2024-04-13 02:40:21 +02:00
|
|
|
snappedLocation.features.mapD((feats) =>
|
|
|
|
feats.map((f) => {
|
|
|
|
const properties = {
|
|
|
|
...f.properties,
|
|
|
|
...presetPropertiesUnpacked,
|
|
|
|
}
|
|
|
|
properties["_referencing_ways"] = f.properties["snapped-to"]
|
|
|
|
return {
|
|
|
|
...f,
|
|
|
|
properties,
|
|
|
|
}
|
2024-04-12 02:04:33 +02:00
|
|
|
})
|
2024-04-13 02:40:21 +02:00
|
|
|
)
|
2024-04-12 02:04:33 +02:00
|
|
|
)
|
|
|
|
// The actual point to be created, snapped at the new location
|
2024-02-20 13:33:38 +01:00
|
|
|
new ShowDataLayer(map, {
|
|
|
|
layer: targetLayer,
|
2024-04-13 02:40:21 +02:00
|
|
|
features: withCorrectedAttributes,
|
2024-02-20 13:33:38 +01:00
|
|
|
})
|
2024-04-13 02:40:21 +02:00
|
|
|
withCorrectedAttributes.features.addCallbackAndRunD((f) => console.log("Snapped point is", f))
|
2024-02-20 13:33:38 +01:00
|
|
|
}
|
2023-04-06 01:33:08 +02:00
|
|
|
</script>
|
|
|
|
|
2023-06-14 20:39:36 +02:00
|
|
|
<LocationInput
|
|
|
|
{map}
|
2024-03-04 15:31:09 +01:00
|
|
|
on:click
|
2024-01-31 11:37:09 +01:00
|
|
|
mapProperties={initialMapProperties}
|
2023-06-14 20:39:36 +02:00
|
|
|
value={preciseLocation}
|
2024-01-31 11:37:09 +01:00
|
|
|
initialCoordinate={coordinate}
|
|
|
|
maxDistanceInMeters={50}
|
2023-10-15 10:55:56 +02:00
|
|
|
>
|
|
|
|
<slot name="image" slot="image">
|
2023-12-01 15:23:28 +01:00
|
|
|
<Move_arrows class="h-full max-h-24" />
|
2023-10-15 10:55:56 +02:00
|
|
|
</slot>
|
|
|
|
</LocationInput>
|