Some cleanup of changesetHandler, add import source with reference to the note

This commit is contained in:
pietervdvn 2022-02-01 00:09:28 +01:00
parent 7f222bce11
commit 5cefc4d25f
4 changed files with 129 additions and 55 deletions

View file

@ -23,6 +23,7 @@ export default class CreateNewNodeAction extends OsmCreateAction {
private meta: { changeType: "create" | "import"; theme: string; specialMotivation?: string };
private readonly _reusePreviouslyCreatedPoint: boolean;
constructor(basicTags: Tag[],
lat: number, lon: number,
options: {
@ -46,7 +47,7 @@ export default class CreateNewNodeAction extends OsmCreateAction {
this.meta = {
theme: options.theme,
changeType: options.changeType,
specialMotivation: options.specialMotivation
}
}
@ -67,6 +68,7 @@ export default class CreateNewNodeAction extends OsmCreateAction {
}
async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> {
if (this._reusePreviouslyCreatedPoint) {
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])
ids.splice(index + 1, 0, id)
// Allright, we have to insert a new point in the way
return [
newPointChange,

View file

@ -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.
*
@ -81,7 +105,13 @@ export class ChangesetHandler {
openChangeset.setData(csId);
const changeset = generateChangeXML(csId);
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) {
console.error("Could not open/upload changeset due to ", e)
openChangeset.setData(undefined)
@ -91,6 +121,7 @@ export class ChangesetHandler {
// Let's check!
const csId = openChangeset.data;
try {
const oldChangesetMeta = await this.GetChangesetMeta(csId)
if (!oldChangesetMeta.open) {
// Mark the CS as closed...
@ -101,41 +132,11 @@ export class ChangesetHandler {
return;
}
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)
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(
const rewritings = await this.AddChange(
csId,
generateChangeXML(csId))
await this.RewriteTagsOf(extraMetaTags, rewritings, oldChangesetMeta)
} catch (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] {
const oldId = parseInt(node.attributes.old_id.value);
if (node.attributes.new_id === undefined) {
@ -163,7 +232,6 @@ export class ChangesetHandler {
if (oldId == newId) {
return undefined;
}
console.log("Rewriting id: ", type + "/" + oldId, "-->", type + "/" + newId);
const element = this.allElements.getEventSourceById("node/" + oldId);
if (element === undefined) {
// Element to rewrite not found, probably a node or relation that is not rendered
@ -176,7 +244,7 @@ export class ChangesetHandler {
return result;
}
private parseUploadChangesetResponse(response: XMLDocument): void {
private parseUploadChangesetResponse(response: XMLDocument): Map<string, string> {
const nodes = response.getElementsByTagName("node");
const mappings = new Map<string, string>()
// @ts-ignore
@ -196,6 +264,7 @@ export class ChangesetHandler {
}
}
this.changes.registerIdRewrites(mappings)
return mappings
}
@ -205,7 +274,6 @@ export class ChangesetHandler {
if (changesetId === undefined) {
return;
}
console.log("closing changeset", changesetId);
self.auth.xhr({
method: 'PUT',
path: '/api/0.6/changeset/' + changesetId + '/close',
@ -232,15 +300,21 @@ export class ChangesetHandler {
return csData.elements[0]
}
/**
* Puts the specified tags onto the changesets as they are.
* This method will erase previously set tags
*/
private async UpdateTags(
csId: number,
tags: [string, string][]) {
tags: ChangesetTag[]) {
console.trace("Updating tags of " + csId)
const self = this;
return new Promise<string>(function (resolve, reject) {
tags = Utils.NoNull(tags).filter(([k, v]) => k !== undefined && v !== undefined && k !== "" && v !== "")
const metadata = tags.map(kv => `<tag k="${kv[0]}" v="${escapeHtml(kv[1])}"/>`)
tags = Utils.NoNull(tags).filter(tag => tag.key !== undefined && tag.value !== undefined && tag.key !== "" && tag.value !== "")
const metadata = tags.map(kv => `<tag k="${kv.key}" v="${escapeHtml(kv.value)}"/>`)
self.auth.xhr({
method: 'PUT',
@ -258,7 +332,6 @@ export class ChangesetHandler {
}
});
})
}
private OpenChangeset(
@ -306,7 +379,7 @@ export class ChangesetHandler {
* Upload a changesetXML
*/
private AddChange(changesetId: number,
changesetXML: string): Promise<number> {
changesetXML: string): Promise<Map<string, string>> {
const self = this;
return new Promise(function (resolve, reject) {
self.auth.xhr({
@ -319,9 +392,9 @@ export class ChangesetHandler {
console.log("err", err);
reject(err);
}
self.parseUploadChangesetResponse(response);
const changes = self.parseUploadChangesetResponse(response);
console.log("Uploaded changeset ", changesetId);
resolve(changesetId);
resolve(changes);
});
})

View file

@ -117,7 +117,7 @@ export default class FilterView extends VariableUiElement {
const style =
"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 layerChecked = new Combine([icon, layerIcon, styledNameChecked, zoomStatus])

View file

@ -523,26 +523,25 @@ export class ImportPointButton extends AbstractImportButton {
snapOnto = await OsmObject.DownloadObjectAsync(snapOntoWayId)
}
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, {
theme: state.layoutToUse.id,
changeType: "import",
snapOnto: <OsmWay>snapOnto,
specialMotivation
specialMotivation: specialMotivation
})
await state.changes.applyAction(newElementAction)
state.selectedElement.setData(state.allElements.ContainingFeatures.get(
newElementAction.newElementId
))
if (args.note_id !== undefined) {
let note_id = args.note_id
if (isNaN(Number(args.note_id))) {
note_id = originalFeatureTags.data[args.note_id]
}
if (note_id !== undefined) {
state.osmConnection.closeNote(note_id, "imported")
originalFeatureTags.data["closed_at"] = new Date().toISOString()
originalFeatureTags.ping()