| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  | import { Tag } from "../../Tags/Tag" | 
					
						
							| 
									
										
										
										
											2022-01-18 18:52:42 +01:00
										 |  |  | import { OsmCreateAction } from "./OsmChangeAction" | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  | import { Changes } from "../Changes" | 
					
						
							|  |  |  | import { ChangeDescription } from "./ChangeDescription" | 
					
						
							|  |  |  | import { And } from "../../Tags/And" | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  | import { OsmWay } from "../OsmObject" | 
					
						
							|  |  |  | import { GeoOperations } from "../../GeoOperations" | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-10 04:00:02 +01:00
										 |  |  | export default class CreateNewNodeAction extends OsmCreateAction { | 
					
						
							| 
									
										
										
										
											2021-10-29 16:38:33 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Maps previously created points onto their assigned ID, to reuse the point if uplaoded | 
					
						
							|  |  |  |      * "lat,lon" --> id | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     private static readonly previouslyCreatedPoints = new Map<string, number>() | 
					
						
							| 
									
										
										
										
											2021-09-09 00:05:51 +02:00
										 |  |  |     public newElementId: string = undefined | 
					
						
							| 
									
										
										
										
											2021-10-29 16:38:33 +02:00
										 |  |  |     public newElementIdNumber: number = undefined | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  |     private readonly _basicTags: Tag[] | 
					
						
							|  |  |  |     private readonly _lat: number | 
					
						
							|  |  |  |     private readonly _lon: number | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |     private readonly _snapOnto: OsmWay | 
					
						
							|  |  |  |     private readonly _reusePointDistance: number | 
					
						
							| 
									
										
										
										
											2022-10-27 01:50:41 +02:00
										 |  |  |     private readonly meta: { | 
					
						
							|  |  |  |         changeType: "create" | "import" | 
					
						
							|  |  |  |         theme: string | 
					
						
							|  |  |  |         specialMotivation?: string | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-10-29 16:38:33 +02:00
										 |  |  |     private readonly _reusePreviouslyCreatedPoint: boolean | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |     constructor( | 
					
						
							|  |  |  |         basicTags: Tag[], | 
					
						
							|  |  |  |         lat: number, | 
					
						
							|  |  |  |         lon: number, | 
					
						
							|  |  |  |         options: { | 
					
						
							| 
									
										
										
										
											2021-10-29 16:38:33 +02:00
										 |  |  |             allowReuseOfPreviouslyCreatedPoints?: boolean | 
					
						
							|  |  |  |             snapOnto?: OsmWay | 
					
						
							|  |  |  |             reusePointWithinMeters?: number | 
					
						
							| 
									
										
										
										
											2022-01-25 00:48:05 +01:00
										 |  |  |             theme: string | 
					
						
							|  |  |  |             changeType: "create" | "import" | null | 
					
						
							|  |  |  |             specialMotivation?: string | 
					
						
							| 
									
										
										
										
											2021-10-29 16:38:33 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |     ) { | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  |         super(null, basicTags !== undefined && basicTags.length > 0) | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  |         this._basicTags = basicTags | 
					
						
							|  |  |  |         this._lat = lat | 
					
						
							|  |  |  |         this._lon = lon | 
					
						
							| 
									
										
										
										
											2021-09-09 00:05:51 +02:00
										 |  |  |         if (lat === undefined || lon === undefined) { | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |             throw "Lat or lon are undefined!" | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         this._snapOnto = options?.snapOnto | 
					
						
							| 
									
										
										
										
											2021-09-18 02:34:13 +02:00
										 |  |  |         this._reusePointDistance = options?.reusePointWithinMeters ?? 1 | 
					
						
							| 
									
										
										
										
											2021-10-29 16:38:33 +02:00
										 |  |  |         this._reusePreviouslyCreatedPoint = | 
					
						
							|  |  |  |             options?.allowReuseOfPreviouslyCreatedPoints ?? basicTags.length === 0 | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |         this.meta = { | 
					
						
							|  |  |  |             theme: options.theme, | 
					
						
							| 
									
										
										
										
											2022-01-25 00:48:05 +01:00
										 |  |  |             changeType: options.changeType, | 
					
						
							| 
									
										
										
										
											2022-02-01 00:09:28 +01:00
										 |  |  |             specialMotivation: options.specialMotivation, | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |     async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> { | 
					
						
							| 
									
										
										
										
											2021-10-29 16:38:33 +02:00
										 |  |  |         if (this._reusePreviouslyCreatedPoint) { | 
					
						
							|  |  |  |             const key = this._lat + "," + this._lon | 
					
						
							|  |  |  |             const prev = CreateNewNodeAction.previouslyCreatedPoints | 
					
						
							|  |  |  |             if (prev.has(key)) { | 
					
						
							|  |  |  |                 this.newElementIdNumber = prev.get(key) | 
					
						
							|  |  |  |                 this.newElementId = "node/" + this.newElementIdNumber | 
					
						
							|  |  |  |                 return [] | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  |         const id = changes.getNewID() | 
					
						
							|  |  |  |         const properties = { | 
					
						
							|  |  |  |             id: "node/" + id, | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-10-29 16:38:33 +02:00
										 |  |  |         this.setElementId(id) | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  |         for (const kv of this._basicTags) { | 
					
						
							|  |  |  |             if (typeof kv.value !== "string") { | 
					
						
							| 
									
										
										
										
											2022-07-25 16:55:44 +02:00
										 |  |  |                 throw ( | 
					
						
							|  |  |  |                     "Invalid value: don't use non-string value in a preset. The tag " + | 
					
						
							|  |  |  |                     kv.key + | 
					
						
							|  |  |  |                     "=" + | 
					
						
							|  |  |  |                     kv.value + | 
					
						
							|  |  |  |                     " is not a string, the value is a " + | 
					
						
							|  |  |  |                     typeof kv.value | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 ) | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |             properties[kv.key] = kv.value | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |         const newPointChange: ChangeDescription = { | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  |             tags: new And(this._basicTags).asChange(properties), | 
					
						
							|  |  |  |             type: "node", | 
					
						
							|  |  |  |             id: id, | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |             changes: { | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  |                 lat: this._lat, | 
					
						
							|  |  |  |                 lon: this._lon, | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |             }, | 
					
						
							|  |  |  |             meta: this.meta, | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |         if (this._snapOnto === undefined) { | 
					
						
							|  |  |  |             return [newPointChange] | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Project the point onto the way
 | 
					
						
							| 
									
										
										
										
											2022-07-29 20:04:36 +02:00
										 |  |  |         console.log("Snapping a node onto an existing way...") | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |         const geojson = this._snapOnto.asGeoJson() | 
					
						
							|  |  |  |         const projected = GeoOperations.nearestPoint(geojson, [this._lon, this._lat]) | 
					
						
							| 
									
										
										
										
											2022-02-22 14:13:41 +01:00
										 |  |  |         const projectedCoor = <[number, number]>projected.geometry.coordinates | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |         const index = projected.properties.index | 
					
						
							|  |  |  |         // We check that it isn't close to an already existing point
 | 
					
						
							|  |  |  |         let reusedPointId = undefined | 
					
						
							| 
									
										
										
										
											2022-02-22 14:13:41 +01:00
										 |  |  |         let outerring: [number, number][] | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-22 14:13:41 +01:00
										 |  |  |         if (geojson.geometry.type === "LineString") { | 
					
						
							|  |  |  |             outerring = <[number, number][]>geojson.geometry.coordinates | 
					
						
							|  |  |  |         } else if (geojson.geometry.type === "Polygon") { | 
					
						
							|  |  |  |             outerring = <[number, number][]>geojson.geometry.coordinates[0] | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-22 14:13:41 +01:00
										 |  |  |         const prev = outerring[index] | 
					
						
							|  |  |  |         if (GeoOperations.distanceBetween(prev, projectedCoor) < this._reusePointDistance) { | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |             // We reuse this point instead!
 | 
					
						
							|  |  |  |             reusedPointId = this._snapOnto.nodes[index] | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-02-22 14:13:41 +01:00
										 |  |  |         const next = outerring[index + 1] | 
					
						
							|  |  |  |         if (GeoOperations.distanceBetween(next, projectedCoor) < this._reusePointDistance) { | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |             // We reuse this point instead!
 | 
					
						
							|  |  |  |             reusedPointId = this._snapOnto.nodes[index + 1] | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (reusedPointId !== undefined) { | 
					
						
							| 
									
										
										
										
											2021-10-29 16:38:33 +02:00
										 |  |  |             this.setElementId(reusedPointId) | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |             return [ | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     tags: new And(this._basicTags).asChange(properties), | 
					
						
							|  |  |  |                     type: "node", | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |                     id: reusedPointId, | 
					
						
							|  |  |  |                     meta: this.meta, | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |                 }, | 
					
						
							|  |  |  |             ] | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-09-09 00:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-22 14:13:41 +01:00
										 |  |  |         const locations = [ | 
					
						
							|  |  |  |             ...this._snapOnto.coordinates.map(([lat, lon]) => <[number, number]>[lon, lat]), | 
					
						
							|  |  |  |         ] | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |         const ids = [...this._snapOnto.nodes] | 
					
						
							| 
									
										
										
										
											2021-09-09 00:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |         locations.splice(index + 1, 0, [this._lon, this._lat]) | 
					
						
							|  |  |  |         ids.splice(index + 1, 0, id) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |         // Allright, we have to insert a new point in the way
 | 
					
						
							|  |  |  |         return [ | 
					
						
							|  |  |  |             newPointChange, | 
					
						
							|  |  |  |             { | 
					
						
							| 
									
										
										
										
											2021-09-09 00:05:51 +02:00
										 |  |  |                 type: "way", | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |                 id: this._snapOnto.id, | 
					
						
							|  |  |  |                 changes: { | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |                     coordinates: locations, | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |                     nodes: ids, | 
					
						
							| 
									
										
										
										
											2021-10-04 03:12:42 +02:00
										 |  |  |                 }, | 
					
						
							| 
									
										
										
										
											2021-10-29 16:38:33 +02:00
										 |  |  |                 meta: this.meta, | 
					
						
							| 
									
										
										
										
											2021-08-07 21:19:01 +02:00
										 |  |  |             }, | 
					
						
							|  |  |  |         ] | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-29 16:38:33 +02:00
										 |  |  |     private setElementId(id: number) { | 
					
						
							|  |  |  |         this.newElementIdNumber = id | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |         this.newElementId = "node/" + id | 
					
						
							| 
									
										
										
										
											2021-10-29 16:38:33 +02:00
										 |  |  |         if (!this._reusePreviouslyCreatedPoint) { | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         const key = this._lat + "," + this._lon | 
					
						
							|  |  |  |         CreateNewNodeAction.previouslyCreatedPoints.set(key, id) | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  | } |