198 lines
7.5 KiB
TypeScript
198 lines
7.5 KiB
TypeScript
import { UIEventSource } from "../../Logic/UIEventSource"
|
|
import Translations from "../i18n/Translations"
|
|
import { Translation } from "../i18n/Translation"
|
|
import BaseUIElement from "../BaseUIElement"
|
|
import ChangeLocationAction from "../../Logic/Osm/Actions/ChangeLocationAction"
|
|
import MoveConfig from "../../Models/ThemeConfig/MoveConfig"
|
|
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"
|
|
import { And } from "../../Logic/Tags/And"
|
|
import { Tag } from "../../Logic/Tags/Tag"
|
|
import { SpecialVisualizationState } from "../SpecialVisualization"
|
|
import { Feature, Point } from "geojson"
|
|
import SvelteUIElement from "../Base/SvelteUIElement"
|
|
import Relocation from "../../assets/svg/Relocation.svelte"
|
|
import Location from "../../assets/svg/Location.svelte"
|
|
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
|
import { WayId } from "../../Models/OsmFeature"
|
|
|
|
export interface MoveReason {
|
|
text: Translation | string
|
|
invitingText: Translation | string
|
|
icon: string
|
|
changesetCommentValue: string
|
|
lockBounds: true | boolean
|
|
includeSearch: false | boolean
|
|
background: undefined | "map" | "photo" | string | string[]
|
|
startZoom: number
|
|
minZoom: number
|
|
eraseAddressFields: false | boolean
|
|
/**
|
|
* Snap to these layers
|
|
*/
|
|
snapTo?: string[]
|
|
maxSnapDistance?: number
|
|
}
|
|
|
|
export class MoveWizardState {
|
|
public readonly reasons: ReadonlyArray<MoveReason>
|
|
|
|
public readonly moveDisallowedReason = new UIEventSource<Translation>(undefined)
|
|
private readonly layer: LayerConfig
|
|
private readonly _state: SpecialVisualizationState
|
|
private readonly featureToMoveId: string
|
|
|
|
/**
|
|
* Initialize the movestate for the feature of the given ID
|
|
* @param id of the feature that should be moved
|
|
* @param options
|
|
* @param layer
|
|
* @param state
|
|
*/
|
|
constructor(id: string, options: MoveConfig, layer: LayerConfig, state: SpecialVisualizationState) {
|
|
this.layer = layer
|
|
this._state = state
|
|
this.featureToMoveId = id
|
|
this.reasons = this.initReasons(options)
|
|
if (this.reasons.length > 0) {
|
|
this.checkIsAllowed(id)
|
|
}
|
|
}
|
|
|
|
private initReasons(options: MoveConfig): MoveReason[] {
|
|
const t = Translations.t.move
|
|
const reasons: MoveReason[] = []
|
|
if (options.enableRelocation) {
|
|
reasons.push({
|
|
text: t.reasons.reasonRelocation,
|
|
invitingText: t.inviteToMove.reasonRelocation,
|
|
icon: "relocation",
|
|
changesetCommentValue: "relocated",
|
|
lockBounds: false,
|
|
background: undefined,
|
|
includeSearch: true,
|
|
startZoom: 12,
|
|
minZoom: 6,
|
|
eraseAddressFields: true,
|
|
})
|
|
}
|
|
if (options.enableImproveAccuracy) {
|
|
reasons.push({
|
|
text: t.reasons.reasonInaccurate,
|
|
invitingText: t.inviteToMove.reasonInaccurate,
|
|
icon: "location",
|
|
changesetCommentValue: "improve_accuracy",
|
|
lockBounds: true,
|
|
includeSearch: false,
|
|
background: "photo",
|
|
startZoom: 18,
|
|
minZoom: 16,
|
|
eraseAddressFields: false,
|
|
})
|
|
}
|
|
|
|
const tags = this._state.featureProperties.getStore(this.featureToMoveId).data
|
|
const matchingPresets = this.layer.presets.filter(preset => preset.preciseInput.snapToLayers && new And(preset.tags).matchesProperties(tags))
|
|
const matchingPreset = matchingPresets.flatMap(pr => pr.preciseInput?.snapToLayers)
|
|
for (const layerId of matchingPreset) {
|
|
const snapOntoLayer = this._state.layout.getLayer(layerId)
|
|
const text = <Translation> t.reasons.reasonSnapTo.PartialSubsTr("name", snapOntoLayer.snapName)
|
|
reasons.push({
|
|
text,
|
|
invitingText: text,
|
|
icon: "snap",
|
|
changesetCommentValue: "snap",
|
|
lockBounds: true,
|
|
includeSearch: false,
|
|
background: "photo",
|
|
startZoom: 19,
|
|
minZoom: 16,
|
|
eraseAddressFields: false,
|
|
snapTo: [snapOntoLayer.id],
|
|
maxSnapDistance: 5,
|
|
})
|
|
}
|
|
|
|
|
|
return reasons
|
|
}
|
|
|
|
public async moveFeature(
|
|
loc: { lon: number; lat: number },
|
|
snappedTo: WayId,
|
|
reason: MoveReason,
|
|
featureToMove: Feature<Point>,
|
|
) {
|
|
const state = this._state
|
|
if(snappedTo !== undefined){
|
|
this.moveDisallowedReason.set(Translations.t.move.partOfAWay)
|
|
}
|
|
await state.changes.applyAction(
|
|
new ChangeLocationAction(state,
|
|
featureToMove.properties.id,
|
|
[loc.lon, loc.lat],
|
|
snappedTo,
|
|
{
|
|
reason: reason.changesetCommentValue,
|
|
theme: state.layout.id,
|
|
}),
|
|
)
|
|
featureToMove.properties._lat = loc.lat
|
|
featureToMove.properties._lon = loc.lon
|
|
featureToMove.geometry.coordinates = [loc.lon, loc.lat]
|
|
if (reason.eraseAddressFields) {
|
|
await state.changes.applyAction(
|
|
new ChangeTagAction(
|
|
featureToMove.properties.id,
|
|
new And([
|
|
new Tag("addr:housenumber", ""),
|
|
new Tag("addr:street", ""),
|
|
new Tag("addr:city", ""),
|
|
new Tag("addr:postcode", ""),
|
|
]),
|
|
featureToMove.properties,
|
|
{
|
|
changeType: "relocated",
|
|
theme: state.layout.id,
|
|
},
|
|
),
|
|
)
|
|
}
|
|
|
|
state.featureProperties.getStore(featureToMove.properties.id)?.ping()
|
|
state.mapProperties.location.setData(loc)
|
|
}
|
|
|
|
private checkIsAllowed(id: string) {
|
|
const t = Translations.t.move
|
|
if (id.startsWith("way")) {
|
|
this.moveDisallowedReason.setData(t.isWay)
|
|
} else if (id.startsWith("relation")) {
|
|
this.moveDisallowedReason.setData(t.isRelation)
|
|
} else if (id.indexOf("-") < 0) {
|
|
this._state.osmObjectDownloader.DownloadReferencingWays(id).then((referencing) => {
|
|
if (referencing.length > 0) {
|
|
this.moveDisallowedReason.setData(t.partOfAWay)
|
|
}
|
|
})
|
|
this._state.osmObjectDownloader.DownloadReferencingRelations(id).then((partOf) => {
|
|
if (partOf.length > 0) {
|
|
this.moveDisallowedReason.setData(t.partOfRelation)
|
|
}
|
|
})
|
|
} else {
|
|
// This is a new point. Check if it was snapped to an existing way due to the '_referencing_ways'-tag
|
|
const store = this._state.featureProperties.getStore(id)
|
|
store?.addCallbackAndRunD((tags) => {
|
|
try {
|
|
if (tags._referencing_ways !== undefined && tags._referencing_ways !== "[]") {
|
|
console.log("Got referencing ways according to the tags")
|
|
this.moveDisallowedReason.setData(t.partOfAWay)
|
|
return true // unregister
|
|
}
|
|
} catch (e) {
|
|
console.error("Could not get '_referencing_ways'-attribute of tags due to", e)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|