forked from MapComplete/MapComplete
		
	Some cleanup of changesetHandler, add import source with reference to the note
This commit is contained in:
		
							parent
							
								
									7f222bce11
								
							
						
					
					
						commit
						5cefc4d25f
					
				
					 4 changed files with 129 additions and 55 deletions
				
			
		|  | @ -23,6 +23,7 @@ export default class CreateNewNodeAction extends OsmCreateAction { | ||||||
|     private meta: { changeType: "create" | "import"; theme: string; specialMotivation?: string }; |     private meta: { changeType: "create" | "import"; theme: string; specialMotivation?: string }; | ||||||
|     private readonly _reusePreviouslyCreatedPoint: boolean; |     private readonly _reusePreviouslyCreatedPoint: boolean; | ||||||
| 
 | 
 | ||||||
|  |      | ||||||
|     constructor(basicTags: Tag[], |     constructor(basicTags: Tag[], | ||||||
|                 lat: number, lon: number, |                 lat: number, lon: number, | ||||||
|                 options: { |                 options: { | ||||||
|  | @ -46,7 +47,7 @@ export default class CreateNewNodeAction extends OsmCreateAction { | ||||||
|         this.meta = { |         this.meta = { | ||||||
|             theme: options.theme, |             theme: options.theme, | ||||||
|             changeType: options.changeType, |             changeType: options.changeType, | ||||||
| 
 |             specialMotivation: options.specialMotivation | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -67,6 +68,7 @@ export default class CreateNewNodeAction extends OsmCreateAction { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> { |     async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> { | ||||||
|  | 
 | ||||||
|         if (this._reusePreviouslyCreatedPoint) { |         if (this._reusePreviouslyCreatedPoint) { | ||||||
| 
 | 
 | ||||||
|             const key = this._lat + "," + this._lon |             const key = this._lat + "," + this._lon | ||||||
|  | @ -139,7 +141,7 @@ export default class CreateNewNodeAction extends OsmCreateAction { | ||||||
| 
 | 
 | ||||||
|         locations.splice(index + 1, 0, [this._lon, this._lat]) |         locations.splice(index + 1, 0, [this._lon, this._lat]) | ||||||
|         ids.splice(index + 1, 0, id) |         ids.splice(index + 1, 0, id) | ||||||
| 
 |          | ||||||
|         // Allright, we have to insert a new point in the way
 |         // Allright, we have to insert a new point in the way
 | ||||||
|         return [ |         return [ | ||||||
|             newPointChange, |             newPointChange, | ||||||
|  |  | ||||||
|  | @ -43,6 +43,30 @@ export class ChangesetHandler { | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * If the metatags contain a special motivation of the format "<change-type>:node/-<number>", this method will rewrite this negative number to the actual ID | ||||||
|  |      * The key is changed _in place_; true will be returned if a change has been applied | ||||||
|  |      * @param extraMetaTags | ||||||
|  |      * @param rewriteIds | ||||||
|  |      * @private | ||||||
|  |      */ | ||||||
|  |     private static rewriteMetaTags(extraMetaTags: ChangesetTag[], rewriteIds: Map<string, string>) { | ||||||
|  |         let hasChange = false; | ||||||
|  |         for (const tag of extraMetaTags) { | ||||||
|  |             const match = tag.key.match(/^([a-zA-Z0-9_]+):(node\/-[0-9])$/) | ||||||
|  |             if (match == null) { | ||||||
|  |                 continue | ||||||
|  |             } | ||||||
|  |             // This is a special motivation which has a negative ID -> we check for rewrites
 | ||||||
|  |             const [_, reason, id] = match | ||||||
|  |             if (rewriteIds.has(id)) { | ||||||
|  |                 tag.key = reason + ":" + rewriteIds.get(id) | ||||||
|  |                 hasChange = true | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return hasChange | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * The full logic to upload a change to one or more elements. |      * The full logic to upload a change to one or more elements. | ||||||
|      * |      * | ||||||
|  | @ -81,7 +105,13 @@ export class ChangesetHandler { | ||||||
|                 openChangeset.setData(csId); |                 openChangeset.setData(csId); | ||||||
|                 const changeset = generateChangeXML(csId); |                 const changeset = generateChangeXML(csId); | ||||||
|                 console.trace("Opened a new changeset (openChangeset.data is undefined):", changeset); |                 console.trace("Opened a new changeset (openChangeset.data is undefined):", changeset); | ||||||
|                 await this.AddChange(csId, changeset) |                 const changes = await this.AddChange(csId, changeset) | ||||||
|  |                 const hasSpecialMotivationChanges = ChangesetHandler.rewriteMetaTags(extraMetaTags, changes) | ||||||
|  |                 if(hasSpecialMotivationChanges){ | ||||||
|  |                     // At this point, 'extraMetaTags' will have changed - we need to set the tags again
 | ||||||
|  |                     this.UpdateTags(csId, extraMetaTags) | ||||||
|  |                 } | ||||||
|  |                  | ||||||
|             } catch (e) { |             } catch (e) { | ||||||
|                 console.error("Could not open/upload changeset due to ", e) |                 console.error("Could not open/upload changeset due to ", e) | ||||||
|                 openChangeset.setData(undefined) |                 openChangeset.setData(undefined) | ||||||
|  | @ -91,6 +121,7 @@ export class ChangesetHandler { | ||||||
|             // Let's check!
 |             // Let's check!
 | ||||||
|             const csId = openChangeset.data; |             const csId = openChangeset.data; | ||||||
|             try { |             try { | ||||||
|  | 
 | ||||||
|                 const oldChangesetMeta = await this.GetChangesetMeta(csId) |                 const oldChangesetMeta = await this.GetChangesetMeta(csId) | ||||||
|                 if (!oldChangesetMeta.open) { |                 if (!oldChangesetMeta.open) { | ||||||
|                     // Mark the CS as closed...
 |                     // Mark the CS as closed...
 | ||||||
|  | @ -101,41 +132,11 @@ export class ChangesetHandler { | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 const extraTagsById = new Map<string, ChangesetTag>() |                 const rewritings = await this.AddChange( | ||||||
|                 for (const extraMetaTag of extraMetaTags) { |  | ||||||
|                     extraTagsById.set(extraMetaTag.key, extraMetaTag) |  | ||||||
|                 } |  | ||||||
|                 const oldCsTags = oldChangesetMeta.tags |  | ||||||
|                 for (const key in oldCsTags) { |  | ||||||
|                     const newMetaTag = extraTagsById.get(key) |  | ||||||
|                     if (newMetaTag === undefined) { |  | ||||||
|                         extraMetaTags.push({ |  | ||||||
|                             key: key, |  | ||||||
|                             value: oldCsTags[key] |  | ||||||
|                         }) |  | ||||||
|                     } else if (newMetaTag.aggregate) { |  | ||||||
|                         let n = Number(newMetaTag.value) |  | ||||||
|                         if (isNaN(n)) { |  | ||||||
|                             n = 0 |  | ||||||
|                         } |  | ||||||
|                         let o = Number(oldCsTags[key]) |  | ||||||
|                         if (isNaN(o)) { |  | ||||||
|                             o = 0 |  | ||||||
|                         } |  | ||||||
|                         // We _update_ the tag itself, as it'll be updated in 'extraMetaTags' straight away
 |  | ||||||
|                         newMetaTag.value = "" + (n + o) |  | ||||||
|                     } else { |  | ||||||
|                         // The old value is overwritten, thus we drop
 |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 await this.UpdateTags(csId, extraMetaTags.map(csTag => <[string, string]>[csTag.key, csTag.value])) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|                 await this.AddChange( |  | ||||||
|                     csId, |                     csId, | ||||||
|                     generateChangeXML(csId)) |                     generateChangeXML(csId)) | ||||||
| 
 | 
 | ||||||
|  |                 await this.RewriteTagsOf(extraMetaTags, rewritings, oldChangesetMeta) | ||||||
| 
 | 
 | ||||||
|             } catch (e) { |             } catch (e) { | ||||||
|                 console.warn("Could not upload, changeset is probably closed: ", e); |                 console.warn("Could not upload, changeset is probably closed: ", e); | ||||||
|  | @ -144,6 +145,74 @@ export class ChangesetHandler { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Updates the metatag of a changeset - | ||||||
|  |      * @param extraMetaTags: new changeset tags to add/fuse with this changeset | ||||||
|  |      * @param oldChangesetMeta: the metadata-object of the already existing changeset | ||||||
|  |      * @constructor | ||||||
|  |      * @private | ||||||
|  |      */ | ||||||
|  |     private async RewriteTagsOf(extraMetaTags: ChangesetTag[], | ||||||
|  |                                 rewriteIds: Map<string, string>, | ||||||
|  |                                 oldChangesetMeta: { | ||||||
|  |                                     open: boolean, | ||||||
|  |                                     id: number | ||||||
|  |                                     uid: number, // User ID
 | ||||||
|  |                                     changes_count: number, | ||||||
|  |                                     tags: any | ||||||
|  |                                 }) { | ||||||
|  | 
 | ||||||
|  |         const csId = oldChangesetMeta.id | ||||||
|  | 
 | ||||||
|  |         // Note: extraMetaTags is where all the tags are collected into
 | ||||||
|  | 
 | ||||||
|  |         // same as 'extraMetaTag', but indexed
 | ||||||
|  |         // Note that updates to 'extraTagsById.get(<key>).value = XYZ' is shared with extraMetatags
 | ||||||
|  |         const extraTagsById = new Map<string, ChangesetTag>() | ||||||
|  |         for (const extraMetaTag of extraMetaTags) { | ||||||
|  |             extraTagsById.set(extraMetaTag.key, extraMetaTag) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const oldCsTags = oldChangesetMeta.tags | ||||||
|  |         for (const key in oldCsTags) { | ||||||
|  |             const newMetaTag = extraTagsById.get(key) | ||||||
|  |             const existingValue = oldCsTags[key] | ||||||
|  | 
 | ||||||
|  |             if (newMetaTag !== undefined && newMetaTag.value === existingValue) { | ||||||
|  |                 continue | ||||||
|  |             } | ||||||
|  |             if (newMetaTag === undefined) { | ||||||
|  |                 extraMetaTags.push({ | ||||||
|  |                     key: key, | ||||||
|  |                     value: oldCsTags[key] | ||||||
|  |                 }) | ||||||
|  |                 continue | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (newMetaTag.aggregate) { | ||||||
|  |                 let n = Number(newMetaTag.value) | ||||||
|  |                 if (isNaN(n)) { | ||||||
|  |                     n = 0 | ||||||
|  |                 } | ||||||
|  |                 let o = Number(oldCsTags[key]) | ||||||
|  |                 if (isNaN(o)) { | ||||||
|  |                     o = 0 | ||||||
|  |                 } | ||||||
|  |                 // We _update_ the tag itself, as it'll be updated in 'extraMetaTags' straight away
 | ||||||
|  |                 newMetaTag.value = "" + (n + o) | ||||||
|  |             } else { | ||||||
|  |                 // The old value is overwritten, thus we drop this old key
 | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         ChangesetHandler.rewriteMetaTags(extraMetaTags, rewriteIds) | ||||||
|  | 
 | ||||||
|  |         await this.UpdateTags(csId, extraMetaTags) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private handleIdRewrite(node: any, type: string): [string, string] { |     private handleIdRewrite(node: any, type: string): [string, string] { | ||||||
|         const oldId = parseInt(node.attributes.old_id.value); |         const oldId = parseInt(node.attributes.old_id.value); | ||||||
|         if (node.attributes.new_id === undefined) { |         if (node.attributes.new_id === undefined) { | ||||||
|  | @ -163,7 +232,6 @@ export class ChangesetHandler { | ||||||
|         if (oldId == newId) { |         if (oldId == newId) { | ||||||
|             return undefined; |             return undefined; | ||||||
|         } |         } | ||||||
|         console.log("Rewriting id: ", type + "/" + oldId, "-->", type + "/" + newId); |  | ||||||
|         const element = this.allElements.getEventSourceById("node/" + oldId); |         const element = this.allElements.getEventSourceById("node/" + oldId); | ||||||
|         if (element === undefined) { |         if (element === undefined) { | ||||||
|             // Element to rewrite not found, probably a node or relation that is not rendered
 |             // Element to rewrite not found, probably a node or relation that is not rendered
 | ||||||
|  | @ -176,7 +244,7 @@ export class ChangesetHandler { | ||||||
|         return result; |         return result; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private parseUploadChangesetResponse(response: XMLDocument): void { |     private parseUploadChangesetResponse(response: XMLDocument): Map<string, string> { | ||||||
|         const nodes = response.getElementsByTagName("node"); |         const nodes = response.getElementsByTagName("node"); | ||||||
|         const mappings = new Map<string, string>() |         const mappings = new Map<string, string>() | ||||||
|         // @ts-ignore
 |         // @ts-ignore
 | ||||||
|  | @ -196,6 +264,7 @@ export class ChangesetHandler { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         this.changes.registerIdRewrites(mappings) |         this.changes.registerIdRewrites(mappings) | ||||||
|  |         return mappings | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -205,7 +274,6 @@ export class ChangesetHandler { | ||||||
|             if (changesetId === undefined) { |             if (changesetId === undefined) { | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|             console.log("closing changeset", changesetId); |  | ||||||
|             self.auth.xhr({ |             self.auth.xhr({ | ||||||
|                 method: 'PUT', |                 method: 'PUT', | ||||||
|                 path: '/api/0.6/changeset/' + changesetId + '/close', |                 path: '/api/0.6/changeset/' + changesetId + '/close', | ||||||
|  | @ -232,15 +300,21 @@ export class ChangesetHandler { | ||||||
|         return csData.elements[0] |         return csData.elements[0] | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Puts the specified tags onto the changesets as they are. | ||||||
|  |      * This method will erase previously set tags | ||||||
|  |      */ | ||||||
|     private async UpdateTags( |     private async UpdateTags( | ||||||
|         csId: number, |         csId: number, | ||||||
|         tags: [string, string][]) { |         tags: ChangesetTag[]) { | ||||||
| 
 | 
 | ||||||
|  |         console.trace("Updating tags of " + csId) | ||||||
|         const self = this; |         const self = this; | ||||||
|         return new Promise<string>(function (resolve, reject) { |         return new Promise<string>(function (resolve, reject) { | ||||||
| 
 | 
 | ||||||
|             tags = Utils.NoNull(tags).filter(([k, v]) => k !== undefined && v !== undefined && k !== "" && v !== "") |             tags = Utils.NoNull(tags).filter(tag => tag.key !== undefined && tag.value !== undefined && tag.key !== "" && tag.value !== "") | ||||||
|             const metadata = tags.map(kv => `<tag k="${kv[0]}" v="${escapeHtml(kv[1])}"/>`) |             const metadata = tags.map(kv => `<tag k="${kv.key}" v="${escapeHtml(kv.value)}"/>`) | ||||||
| 
 | 
 | ||||||
|             self.auth.xhr({ |             self.auth.xhr({ | ||||||
|                 method: 'PUT', |                 method: 'PUT', | ||||||
|  | @ -258,7 +332,6 @@ export class ChangesetHandler { | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|         }) |         }) | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private OpenChangeset( |     private OpenChangeset( | ||||||
|  | @ -306,7 +379,7 @@ export class ChangesetHandler { | ||||||
|      * Upload a changesetXML |      * Upload a changesetXML | ||||||
|      */ |      */ | ||||||
|     private AddChange(changesetId: number, |     private AddChange(changesetId: number, | ||||||
|                       changesetXML: string): Promise<number> { |                       changesetXML: string): Promise<Map<string, string>> { | ||||||
|         const self = this; |         const self = this; | ||||||
|         return new Promise(function (resolve, reject) { |         return new Promise(function (resolve, reject) { | ||||||
|             self.auth.xhr({ |             self.auth.xhr({ | ||||||
|  | @ -319,9 +392,9 @@ export class ChangesetHandler { | ||||||
|                     console.log("err", err); |                     console.log("err", err); | ||||||
|                     reject(err); |                     reject(err); | ||||||
|                 } |                 } | ||||||
|                 self.parseUploadChangesetResponse(response); |                 const changes = self.parseUploadChangesetResponse(response); | ||||||
|                 console.log("Uploaded changeset ", changesetId); |                 console.log("Uploaded changeset ", changesetId); | ||||||
|                 resolve(changesetId); |                 resolve(changes); | ||||||
|             }); |             }); | ||||||
|         }) |         }) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -117,7 +117,7 @@ export default class FilterView extends VariableUiElement { | ||||||
| 
 | 
 | ||||||
|         const style = |         const style = | ||||||
|             "display:flex;align-items:center;padding:0.5rem 0;"; |             "display:flex;align-items:center;padding:0.5rem 0;"; | ||||||
|         const layerIcon = layer.defaultIcon()?.SetClass("w-8 h-8 ml-2") |         const layerIcon = layer.defaultIcon()?.SetClass("w-8 h-8 ml-2 shrink-0") | ||||||
|         const layerIconUnchecked = layer.defaultIcon()?.SetClass("opacity-50  w-8 h-8 ml-2") |         const layerIconUnchecked = layer.defaultIcon()?.SetClass("opacity-50  w-8 h-8 ml-2") | ||||||
| 
 | 
 | ||||||
|         const layerChecked = new Combine([icon, layerIcon, styledNameChecked, zoomStatus]) |         const layerChecked = new Combine([icon, layerIcon, styledNameChecked, zoomStatus]) | ||||||
|  |  | ||||||
|  | @ -523,26 +523,25 @@ export class ImportPointButton extends AbstractImportButton { | ||||||
|                 snapOnto = await OsmObject.DownloadObjectAsync(snapOntoWayId) |                 snapOnto = await OsmObject.DownloadObjectAsync(snapOntoWayId) | ||||||
|             } |             } | ||||||
|             let specialMotivation = undefined |             let specialMotivation = undefined | ||||||
|             if (args.note_id !== undefined) { | 
 | ||||||
|                 specialMotivation = "source: https://osm.org/note/" + args.note_id |             let note_id = args.note_id | ||||||
|  |             if (args.note_id !== undefined &&  isNaN(Number(args.note_id))) { | ||||||
|  |                 note_id = originalFeatureTags.data[args.note_id] | ||||||
|  |                 specialMotivation = "source: https://osm.org/note/" + note_id | ||||||
|             } |             } | ||||||
|  |              | ||||||
|             const newElementAction = new CreateNewNodeAction(tags, location.lat, location.lon, { |             const newElementAction = new CreateNewNodeAction(tags, location.lat, location.lon, { | ||||||
|                 theme: state.layoutToUse.id, |                 theme: state.layoutToUse.id, | ||||||
|                 changeType: "import", |                 changeType: "import", | ||||||
|                 snapOnto: <OsmWay>snapOnto, |                 snapOnto: <OsmWay>snapOnto, | ||||||
|                 specialMotivation |                 specialMotivation: specialMotivation | ||||||
|             }) |             }) | ||||||
| 
 | 
 | ||||||
|             await state.changes.applyAction(newElementAction) |             await state.changes.applyAction(newElementAction) | ||||||
|             state.selectedElement.setData(state.allElements.ContainingFeatures.get( |             state.selectedElement.setData(state.allElements.ContainingFeatures.get( | ||||||
|                 newElementAction.newElementId |                 newElementAction.newElementId | ||||||
|             )) |             )) | ||||||
|             if (args.note_id !== undefined) { |             if (note_id !== undefined) { | ||||||
|                 let note_id = args.note_id |  | ||||||
|                 if (isNaN(Number(args.note_id))) { |  | ||||||
|                     note_id = originalFeatureTags.data[args.note_id] |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 state.osmConnection.closeNote(note_id, "imported") |                 state.osmConnection.closeNote(note_id, "imported") | ||||||
|                 originalFeatureTags.data["closed_at"] = new Date().toISOString() |                 originalFeatureTags.data["closed_at"] = new Date().toISOString() | ||||||
|                 originalFeatureTags.ping() |                 originalFeatureTags.ping() | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue