2024-09-04 00:07:23 +02:00
|
|
|
import { ChangeDescription } from "./ChangeDescription"
|
|
|
|
import { GeoOperations } from "../../GeoOperations"
|
|
|
|
import { OsmWay } from "../OsmObject"
|
|
|
|
|
2024-10-19 14:44:55 +02:00
|
|
|
export default class InsertPointIntoWayAction {
|
2024-09-04 00:07:23 +02:00
|
|
|
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
|
|
|
) {
|
2024-09-04 00:07:23 +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 {
|
2024-09-04 00:07:23 +02:00
|
|
|
// 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
|
|
|
|
) {
|
2024-09-04 00:07:23 +02:00
|
|
|
// 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
|
|
|
|
) {
|
2024-09-04 00:07:23 +02:00
|
|
|
// 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 {
|
2024-09-04 00:07:23 +02:00
|
|
|
type: "way",
|
|
|
|
id: this._snapOnto.id,
|
|
|
|
changes: {
|
|
|
|
coordinates: locations,
|
|
|
|
nodes: ids,
|
2024-10-19 14:44:55 +02:00
|
|
|
},
|
2024-09-04 00:07:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|