mapcomplete/Logic/Osm/Actions/CreateNewWayAction.ts

101 lines
3.5 KiB
TypeScript

import { ChangeDescription } from "./ChangeDescription"
import { OsmCreateAction } from "./OsmChangeAction"
import { Changes } from "../Changes"
import { Tag } from "../../Tags/Tag"
import CreateNewNodeAction from "./CreateNewNodeAction"
import { And } from "../../Tags/And"
export default class CreateNewWayAction extends OsmCreateAction {
public newElementId: string = undefined
public newElementIdNumber: number = undefined
private readonly coordinates: { nodeId?: number; lat: number; lon: number }[]
private readonly tags: Tag[]
private readonly _options: {
theme: string
}
/***
* Creates a new way to upload to OSM
* @param tags: the tags to apply to the way
* @param coordinates: the coordinates. Might have a nodeId, in this case, this node will be used
* @param options
*/
constructor(
tags: Tag[],
coordinates: { nodeId?: number; lat: number; lon: number }[],
options: {
theme: string
}
) {
super(null, true)
this.coordinates = []
for (const coordinate of coordinates) {
/* The 'PointReuseAction' is a bit buggy and might generate duplicate ids.
We filter those here, as the CreateWayWithPointReuseAction delegates the actual creation to here.
Filtering here also prevents similar bugs in other actions
*/
if (
this.coordinates.length > 0 &&
coordinate.nodeId !== undefined &&
this.coordinates[this.coordinates.length - 1].nodeId === coordinate.nodeId
) {
// This is a duplicate id
console.warn(
"Skipping a node in createWay to avoid a duplicate node:",
coordinate,
"\nThe previous coordinates are: ",
this.coordinates
)
continue
}
this.coordinates.push(coordinate)
}
this.tags = tags
this._options = options
}
public async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> {
const newElements: ChangeDescription[] = []
const pointIds: number[] = []
for (const coordinate of this.coordinates) {
if (coordinate.nodeId !== undefined) {
pointIds.push(coordinate.nodeId)
continue
}
const newPoint = new CreateNewNodeAction([], coordinate.lat, coordinate.lon, {
allowReuseOfPreviouslyCreatedPoints: true,
changeType: null,
theme: this._options.theme,
})
newElements.push(...(await newPoint.CreateChangeDescriptions(changes)))
pointIds.push(newPoint.newElementIdNumber)
}
// We have all created (or reused) all the points!
// Time to create the actual way
const id = changes.getNewID()
this.newElementIdNumber = id
const newWay = <ChangeDescription>{
id,
type: "way",
meta: {
theme: this._options.theme,
changeType: "import",
},
tags: new And(this.tags).asChange({}),
changes: {
nodes: pointIds,
coordinates: this.coordinates.map((c) => [c.lon, c.lat]),
},
}
newElements.push(newWay)
this.newElementId = "way/" + id
return newElements
}
}