MapComplete/src/Logic/Osm/Actions/InsertPointIntoWayAction.ts

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

98 lines
3.4 KiB
TypeScript
Raw Normal View History

import { ChangeDescription } from "./ChangeDescription"
import { GeoOperations } from "../../GeoOperations"
import { OsmWay } from "../OsmObject"
2024-10-19 14:44:55 +02:00
export default class InsertPointIntoWayAction {
private readonly _lat: number
private readonly _lon: number
private readonly _idToInsert: number
private readonly _snapOnto: OsmWay
private readonly _options: {
allowReuseOfPreviouslyCreatedPoints?: boolean
reusePointWithinMeters?: number
}
constructor(
lat: number,
lon: number,
idToInsert: number,
snapOnto: OsmWay,
options: {
allowReuseOfPreviouslyCreatedPoints?: boolean
reusePointWithinMeters?: number
}
2024-10-19 14:44:55 +02:00
) {
this._lat = lat
this._lon = lon
this._idToInsert = idToInsert
this._snapOnto = snapOnto
this._options = options
}
/**
* Tries to create the changedescription of the way where the point is inserted
* Returns `undefined` if inserting failed
*/
2024-10-19 14:44:55 +02:00
public prepareChangeDescription(): Omit<ChangeDescription, "meta"> | undefined {
// Project the point onto the way
console.log("Snapping a node onto an existing way...")
const geojson = this._snapOnto.asGeoJson()
const projected = GeoOperations.nearestPoint(GeoOperations.outerRing(geojson), [
this._lon,
this._lat,
])
const projectedCoor = <[number, number]>projected.geometry.coordinates
const index = projected.properties.index
console.log("Attempting to snap:", { geojson, projected, projectedCoor, index })
// We check that it isn't close to an already existing point
let reusedPointId = undefined
let reusedPointCoordinates: [number, number] = undefined
let outerring: [number, number][]
if (geojson.geometry.type === "LineString") {
outerring = <[number, number][]>geojson.geometry.coordinates
} else if (geojson.geometry.type === "Polygon") {
outerring = <[number, number][]>geojson.geometry.coordinates[0]
}
const prev = outerring[index]
2024-10-19 14:44:55 +02:00
if (
GeoOperations.distanceBetween(prev, projectedCoor) <
this._options.reusePointWithinMeters
) {
// We reuse this point instead!
reusedPointId = this._snapOnto.nodes[index]
reusedPointCoordinates = this._snapOnto.coordinates[index]
}
const next = outerring[index + 1]
2024-10-19 14:44:55 +02:00
if (
GeoOperations.distanceBetween(next, projectedCoor) <
this._options.reusePointWithinMeters
) {
// We reuse this point instead!
reusedPointId = this._snapOnto.nodes[index + 1]
reusedPointCoordinates = this._snapOnto.coordinates[index + 1]
}
if (reusedPointId !== undefined) {
return undefined
}
const locations = [
...this._snapOnto.coordinates?.map(([lat, lon]) => <[number, number]>[lon, lat]),
]
const ids = [...this._snapOnto.nodes]
locations.splice(index + 1, 0, [this._lon, this._lat])
ids.splice(index + 1, 0, this._idToInsert)
2024-10-19 14:44:55 +02:00
return {
type: "way",
id: this._snapOnto.id,
changes: {
coordinates: locations,
nodes: ids,
2024-10-19 14:44:55 +02:00
},
}
}
}