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…
Reference in a new issue