MapComplete/src/UI/BigComponents/NewPointLocationInput.svelte

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

158 lines
5.6 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 { Tag } from "../../Logic/Tags/Tag"
import { TagUtils } from "../../Logic/Tags/TagUtils"
import type { WayId } from "../../Models/OsmFeature"
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 } = undefined
2024-02-20 13:33:38 +01:00
/**
2024-09-12 21:42:35 +02:00
* Max distance that one is allowed to move, to prevent to stray too much
*/
export let maxDistanceInMeters = 50
/**
* The resulting location; either the map center or the snapped coordinate
2024-02-20 13:33:38 +01:00
* 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 = undefined
export let targetLayer: LayerConfig | undefined = undefined
/**
* If a 'targetLayer' is given, objects of this layer will be shown as well to avoid duplicates
* If you want to hide some of them, blacklist them here
*/
export let dontShow: string[] = []
2024-02-20 13:33:38 +01:00
export let maxSnapDistance: number = undefined
2024-04-18 15:29:07 +02:00
export let presetProperties: Tag[] = []
let presetPropertiesUnpacked = TagUtils.KVtoProperties(presetProperties)
2023-06-15 16:12:46 +02:00
export let snappedTo: UIEventSource<WayId | undefined>
2024-02-20 13:33:38 +01:00
const map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined)
export let mapProperties: Partial<MapProperties> & { location } = {
2024-02-20 13:33:38 +01:00
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
* */
2024-09-12 21:42:35 +02:00
location: new UIEventSource<{ lon: number; lat: number }>(coordinate),
2024-02-20 13:33:38 +01:00
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
let featuresForLayer: FeatureSource = state.perLayer.get(targetLayer.id)
2024-02-20 13:33:38 +01:00
if (featuresForLayer) {
if (dontShow) {
featuresForLayer = new StaticFeatureSource(featuresForLayer.features.map(feats => feats.filter(f => dontShow.indexOf(f.properties.id) < 0)))
}
2024-02-20 13:33:38 +01:00
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
mapProperties.location,
2024-02-20 13:33:38 +01:00
{
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,
}
}),
),
)
// 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
}
2024-09-12 21:42:35 +02:00
</script>
<LocationInput
{map}
on:click
{mapProperties}
2024-09-12 21:42:35 +02:00
value={ snapToLayers?.length > 0 ? new UIEventSource(undefined) : value}
2024-01-31 11:37:09 +01:00
initialCoordinate={coordinate}
2024-09-12 21:42:35 +02:00
{maxDistanceInMeters}
>
<slot name="image" slot="image">
2023-12-01 15:23:28 +01:00
<Move_arrows class="h-full max-h-24" />
</slot>
</LocationInput>