MapComplete/src/UI/BigComponents/NewPointLocationInput.svelte

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

152 lines
5.1 KiB
Svelte
Raw Normal View History

<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"
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
import { prop_dev } from "svelte/internal"
import { Tag } from "../../Logic/Tags/Tag"
import { TagUtils } from "../../Logic/Tags/TagUtils"
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 }
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
export let presetProperties: Tag[] = undefined
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>
2024-02-20 13:33:38 +01:00
let preciseLocation: UIEventSource<{ lon: number; lat: number }> = new UIEventSource<{
lon: number
lat: number
}>(undefined)
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
}
state?.showCurrentLocationOn(map)
2024-02-20 13:33:38 +01:00
if (targetLayer) {
// 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-02-20 13:33:38 +01:00
if (snapToLayers?.length > 0) {
const snapSources: FeatureSource[] = []
for (const layerId of snapToLayers ?? []) {
// 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-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
}
)
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-13 02:40:21 +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
}
</script>
<LocationInput
{map}
on:click
2024-01-31 11:37:09 +01:00
mapProperties={initialMapProperties}
value={preciseLocation}
2024-01-31 11:37:09 +01:00
initialCoordinate={coordinate}
maxDistanceInMeters={50}
>
<slot name="image" slot="image">
2023-12-01 15:23:28 +01:00
<Move_arrows class="h-full max-h-24" />
</slot>
</LocationInput>