forked from MapComplete/MapComplete
		
	Stabilize adding new points
This commit is contained in:
		
							parent
							
								
									d3c26c4f0e
								
							
						
					
					
						commit
						a3c16d6297
					
				
					 11 changed files with 249 additions and 257 deletions
				
			
		| 
						 | 
					@ -56,8 +56,6 @@ export default class OverpassFeatureSource implements FeatureSource, FeatureSour
 | 
				
			||||||
            readonly overpassTimeout: UIEventSource<number>;
 | 
					            readonly overpassTimeout: UIEventSource<number>;
 | 
				
			||||||
            readonly overpassMaxZoom: UIEventSource<number>
 | 
					            readonly overpassMaxZoom: UIEventSource<number>
 | 
				
			||||||
        }) {
 | 
					        }) {
 | 
				
			||||||
        console.trace("Initializing an overpass FS")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.state = state
 | 
					        this.state = state
 | 
				
			||||||
        this.relationsTracker = new RelationsTracker()
 | 
					        this.relationsTracker = new RelationsTracker()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@ import {OsmNode, OsmRelation, OsmWay} from "../../Osm/OsmObject";
 | 
				
			||||||
import FeatureSource from "../FeatureSource";
 | 
					import FeatureSource from "../FeatureSource";
 | 
				
			||||||
import {UIEventSource} from "../../UIEventSource";
 | 
					import {UIEventSource} from "../../UIEventSource";
 | 
				
			||||||
import {ChangeDescription} from "../../Osm/Actions/ChangeDescription";
 | 
					import {ChangeDescription} from "../../Osm/Actions/ChangeDescription";
 | 
				
			||||||
 | 
					import State from "../../../State";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class NewGeometryFromChangesFeatureSource implements FeatureSource {
 | 
					export class NewGeometryFromChangesFeatureSource implements FeatureSource {
 | 
				
			||||||
    // This class name truly puts the 'Java' into 'Javascript'
 | 
					    // This class name truly puts the 'Java' into 'Javascript'
 | 
				
			||||||
| 
						 | 
					@ -54,6 +55,9 @@ export class NewGeometryFromChangesFeatureSource implements FeatureSource {
 | 
				
			||||||
                            tags[kv.k] = kv.v
 | 
					                            tags[kv.k] = kv.v
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        tags["id"] = change.type+"/"+change.id
 | 
					                        tags["id"] = change.type+"/"+change.id
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        tags["_backend"] = State.state.osmConnection._oauth_config.url
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
                        switch (change.type) {
 | 
					                        switch (change.type) {
 | 
				
			||||||
                            case "node":
 | 
					                            case "node":
 | 
				
			||||||
                                const n = new OsmNode(change.id)
 | 
					                                const n = new OsmNode(change.id)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@ import {LocalStorageSource} from "../Web/LocalStorageSource";
 | 
				
			||||||
export class Changes {
 | 
					export class Changes {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private _nextId : number = -1; // Newly assigned ID's are negative
 | 
					    private _nextId: number = -1; // Newly assigned ID's are negative
 | 
				
			||||||
    public readonly name = "Newly added features"
 | 
					    public readonly name = "Newly added features"
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * All the newly created features as featureSource + all the modified features
 | 
					     * All the newly created features as featureSource + all the modified features
 | 
				
			||||||
| 
						 | 
					@ -31,7 +31,10 @@ export class Changes {
 | 
				
			||||||
        // We keep track of all changes just as well
 | 
					        // We keep track of all changes just as well
 | 
				
			||||||
        this.allChanges.setData([...this.pendingChanges.data])
 | 
					        this.allChanges.setData([...this.pendingChanges.data])
 | 
				
			||||||
        // If a pending change contains a negative ID, we save that
 | 
					        // If a pending change contains a negative ID, we save that
 | 
				
			||||||
       this._nextId = Math.min(-1, ...this.pendingChanges.data?.map(pch => pch.id) ?? [])
 | 
					        this._nextId = Math.min(-1, ...this.pendingChanges.data?.map(pch => pch.id) ?? [])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Note: a changeset might be reused which was opened just before and might have already used some ids
 | 
				
			||||||
 | 
					        // This doesn't matter however, as the '-1' is per piecewise upload, not global per changeset
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static createChangesetFor(csId: string,
 | 
					    private static createChangesetFor(csId: string,
 | 
				
			||||||
| 
						 | 
					@ -90,62 +93,58 @@ export class Changes {
 | 
				
			||||||
        if (this.pendingChanges.data.length === 0) {
 | 
					        if (this.pendingChanges.data.length === 0) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (this.isUploading.data) {
 | 
					        if (this.isUploading.data) {
 | 
				
			||||||
            console.log("Is already uploading... Abort")
 | 
					            console.log("Is already uploading... Abort")
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.isUploading.setData(true)
 | 
					        this.isUploading.setData(true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.flushChangesAsync(flushreason)
 | 
				
			||||||
 | 
					            .then(_ => {
 | 
				
			||||||
 | 
					                this.isUploading.setData(false)
 | 
				
			||||||
 | 
					                console.log("Changes flushed!");
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .catch(e => {
 | 
				
			||||||
 | 
					                this.isUploading.setData(false)
 | 
				
			||||||
 | 
					                console.error("Flushing changes failed due to", e);
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private async flushChangesAsync(flushreason: string = undefined): Promise<void> {
 | 
				
			||||||
        console.log("Beginning upload... " + flushreason ?? "");
 | 
					        console.log("Beginning upload... " + flushreason ?? "");
 | 
				
			||||||
        // At last, we build the changeset and upload
 | 
					        // At last, we build the changeset and upload
 | 
				
			||||||
        const self = this;
 | 
					        const self = this;
 | 
				
			||||||
        const pending = self.pendingChanges.data;
 | 
					        const pending = self.pendingChanges.data;
 | 
				
			||||||
        const neededIds = Changes.GetNeededIds(pending)
 | 
					        const neededIds = Changes.GetNeededIds(pending)
 | 
				
			||||||
        console.log("Needed ids", neededIds)
 | 
					        const osmObjects = await Promise.all(neededIds.map(id => OsmObject.DownloadObjectAsync(id)));
 | 
				
			||||||
        OsmObject.DownloadAll(neededIds, true).addCallbackAndRunD(osmObjects => {
 | 
					        console.log("Got the fresh objects!", osmObjects, "pending: ", pending)
 | 
				
			||||||
            console.log("Got the fresh objects!", osmObjects, "pending: ", pending)
 | 
					        try {
 | 
				
			||||||
            try {
 | 
					            const changes: {
 | 
				
			||||||
 | 
					                newObjects: OsmObject[],
 | 
				
			||||||
 | 
					                modifiedObjects: OsmObject[]
 | 
				
			||||||
                const changes: {
 | 
					                deletedObjects: OsmObject[]
 | 
				
			||||||
                    newObjects: OsmObject[],
 | 
					            } = self.CreateChangesetObjects(pending, osmObjects)
 | 
				
			||||||
                    modifiedObjects: OsmObject[]
 | 
					            if (changes.newObjects.length + changes.deletedObjects.length + changes.modifiedObjects.length === 0) {
 | 
				
			||||||
                    deletedObjects: OsmObject[]
 | 
					                console.log("No changes to be made")
 | 
				
			||||||
 | 
					 | 
				
			||||||
                } = self.CreateChangesetObjects(pending, osmObjects)
 | 
					 | 
				
			||||||
                if (changes.newObjects.length + changes.deletedObjects.length + changes.modifiedObjects.length === 0) {
 | 
					 | 
				
			||||||
                    console.log("No changes to be made")
 | 
					 | 
				
			||||||
                    self.pendingChanges.setData([])
 | 
					 | 
				
			||||||
                    self.isUploading.setData(false)
 | 
					 | 
				
			||||||
                    return true; // Unregister the callback
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                State.state.osmConnection.UploadChangeset(
 | 
					 | 
				
			||||||
                    State.state.layoutToUse.data,
 | 
					 | 
				
			||||||
                    State.state.allElements,
 | 
					 | 
				
			||||||
                    (csId) => Changes.createChangesetFor(csId, changes),
 | 
					 | 
				
			||||||
                    () => {
 | 
					 | 
				
			||||||
                        console.log("Upload successfull!")
 | 
					 | 
				
			||||||
                        self.pendingChanges.setData([]);
 | 
					 | 
				
			||||||
                        self.isUploading.setData(false)
 | 
					 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                    () => {
 | 
					 | 
				
			||||||
                        console.log("Upload failed - trying again later")
 | 
					 | 
				
			||||||
                        return self.isUploading.setData(false);
 | 
					 | 
				
			||||||
                    } // Failed - mark to try again
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            } catch (e) {
 | 
					 | 
				
			||||||
                console.error("Could not handle changes - probably an old, pending changeset in localstorage with an invalid format; erasing those", e)
 | 
					 | 
				
			||||||
                self.pendingChanges.setData([])
 | 
					                self.pendingChanges.setData([])
 | 
				
			||||||
                self.isUploading.setData(false)
 | 
					                self.isUploading.setData(false)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        });
 | 
					            await State.state.osmConnection.UploadChangeset(
 | 
				
			||||||
 | 
					                State.state.layoutToUse.data,
 | 
				
			||||||
 | 
					                State.state.allElements,
 | 
				
			||||||
 | 
					                (csId) => Changes.createChangesetFor(csId, changes),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            console.log("Upload successfull!")
 | 
				
			||||||
 | 
					            this.pendingChanges.setData([]);
 | 
				
			||||||
 | 
					            this.isUploading.setData(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (e) {
 | 
				
			||||||
 | 
					            console.error("Could not handle changes - probably an old, pending changeset in localstorage with an invalid format; erasing those", e)
 | 
				
			||||||
 | 
					            self.pendingChanges.setData([])
 | 
				
			||||||
 | 
					            self.isUploading.setData(false)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -311,4 +310,8 @@ export class Changes {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return result
 | 
					        return result
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public registerIdRewrites(mappings: Map<string, string>): void {
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -8,15 +8,23 @@ import Locale from "../../UI/i18n/Locale";
 | 
				
			||||||
import Constants from "../../Models/Constants";
 | 
					import Constants from "../../Models/Constants";
 | 
				
			||||||
import {OsmObject} from "./OsmObject";
 | 
					import {OsmObject} from "./OsmObject";
 | 
				
			||||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
 | 
					import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
 | 
				
			||||||
 | 
					import {Changes} from "./Changes";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class ChangesetHandler {
 | 
					export class ChangesetHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public readonly currentChangeset: UIEventSource<string>;
 | 
					    public readonly currentChangeset: UIEventSource<string>;
 | 
				
			||||||
 | 
					    private readonly allElements: ElementStorage;
 | 
				
			||||||
 | 
					    private readonly changes: Changes;
 | 
				
			||||||
    private readonly _dryRun: boolean;
 | 
					    private readonly _dryRun: boolean;
 | 
				
			||||||
    private readonly userDetails: UIEventSource<UserDetails>;
 | 
					    private readonly userDetails: UIEventSource<UserDetails>;
 | 
				
			||||||
    private readonly auth: any;
 | 
					    private readonly auth: any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(layoutName: string, dryRun: boolean, osmConnection: OsmConnection, auth) {
 | 
					    constructor(layoutName: string, dryRun: boolean, osmConnection: OsmConnection,
 | 
				
			||||||
 | 
					                allElements: ElementStorage,
 | 
				
			||||||
 | 
					                changes: Changes,
 | 
				
			||||||
 | 
					                auth) {
 | 
				
			||||||
 | 
					        this.allElements = allElements;
 | 
				
			||||||
 | 
					        this.changes = changes;
 | 
				
			||||||
        this._dryRun = dryRun;
 | 
					        this._dryRun = dryRun;
 | 
				
			||||||
        this.userDetails = osmConnection.userDetails;
 | 
					        this.userDetails = osmConnection.userDetails;
 | 
				
			||||||
        this.auth = auth;
 | 
					        this.auth = auth;
 | 
				
			||||||
| 
						 | 
					@ -27,35 +35,55 @@ export class ChangesetHandler {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static parseUploadChangesetResponse(response: XMLDocument, allElements: ElementStorage): void {
 | 
					    private handleIdRewrite(node: any, type: string): [string, string] {
 | 
				
			||||||
 | 
					        const oldId = parseInt(node.attributes.old_id.value);
 | 
				
			||||||
 | 
					        if (node.attributes.new_id === undefined) {
 | 
				
			||||||
 | 
					            // We just removed this point!
 | 
				
			||||||
 | 
					            const element =this. allElements.getEventSourceById("node/" + oldId);
 | 
				
			||||||
 | 
					            element.data._deleted = "yes"
 | 
				
			||||||
 | 
					            element.ping();
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const newId = parseInt(node.attributes.new_id.value);
 | 
				
			||||||
 | 
					        const result: [string, string] = [type + "/" + oldId, type + "/" + newId]
 | 
				
			||||||
 | 
					        if (!(oldId !== undefined && newId !== undefined &&
 | 
				
			||||||
 | 
					            !isNaN(oldId) && !isNaN(newId))) {
 | 
				
			||||||
 | 
					            return undefined;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (oldId == newId) {
 | 
				
			||||||
 | 
					            return undefined;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        console.log("Rewriting id: ", type + "/" + oldId, "-->", type + "/" + newId);
 | 
				
			||||||
 | 
					        const element = this.allElements.getEventSourceById("node/" + oldId);
 | 
				
			||||||
 | 
					        element.data.id = type + "/" + newId;
 | 
				
			||||||
 | 
					        this.allElements.addElementById(type + "/" + newId, element);
 | 
				
			||||||
 | 
					        this.allElements.ContainingFeatures.set(type + "/" + newId, this.allElements.ContainingFeatures.get(type + "/" + oldId))
 | 
				
			||||||
 | 
					        element.ping();
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private parseUploadChangesetResponse(response: XMLDocument): void {
 | 
				
			||||||
        const nodes = response.getElementsByTagName("node");
 | 
					        const nodes = response.getElementsByTagName("node");
 | 
				
			||||||
 | 
					        const mappings = new Map<string, string>()
 | 
				
			||||||
        // @ts-ignore
 | 
					        // @ts-ignore
 | 
				
			||||||
        for (const node of nodes) {
 | 
					        for (const node of nodes) {
 | 
				
			||||||
            const oldId = parseInt(node.attributes.old_id.value);
 | 
					            const mapping = this.handleIdRewrite(node, "node")
 | 
				
			||||||
            if (node.attributes.new_id === undefined) {
 | 
					            if (mapping !== undefined) {
 | 
				
			||||||
                // We just removed this point!
 | 
					                mappings.set(mapping[0], mapping[1])
 | 
				
			||||||
                const element = allElements.getEventSourceById("node/" + oldId);
 | 
					 | 
				
			||||||
                element.data._deleted = "yes"
 | 
					 | 
				
			||||||
                element.ping();
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					 | 
				
			||||||
            const newId = parseInt(node.attributes.new_id.value);
 | 
					 | 
				
			||||||
            if (oldId !== undefined && newId !== undefined &&
 | 
					 | 
				
			||||||
                !isNaN(oldId) && !isNaN(newId)) {
 | 
					 | 
				
			||||||
                if (oldId == newId) {
 | 
					 | 
				
			||||||
                    continue;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                console.log("Rewriting id: ", oldId, "-->", newId);
 | 
					 | 
				
			||||||
                const element = allElements.getEventSourceById("node/" + oldId);
 | 
					 | 
				
			||||||
                element.data.id = "node/" + newId;
 | 
					 | 
				
			||||||
                allElements.addElementById("node/" + newId, element);
 | 
					 | 
				
			||||||
                element.ping();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const ways = response.getElementsByTagName("way");
 | 
				
			||||||
 | 
					        // @ts-ignore
 | 
				
			||||||
 | 
					        for (const way of ways) {
 | 
				
			||||||
 | 
					            const mapping = this.handleIdRewrite(way, "way")
 | 
				
			||||||
 | 
					            if (mapping !== undefined) {
 | 
				
			||||||
 | 
					                mappings.set(mapping[0], mapping[1])
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.changes.registerIdRewrites(mappings)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					@ -68,13 +96,9 @@ export class ChangesetHandler {
 | 
				
			||||||
     * If 'dryrun' is specified, the changeset XML will be printed to console instead of being uploaded
 | 
					     * If 'dryrun' is specified, the changeset XML will be printed to console instead of being uploaded
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public UploadChangeset(
 | 
					    public async UploadChangeset(
 | 
				
			||||||
        layout: LayoutConfig,
 | 
					        layout: LayoutConfig,
 | 
				
			||||||
        allElements: ElementStorage,
 | 
					        generateChangeXML: (csid: string) => string): Promise<void> {
 | 
				
			||||||
        generateChangeXML: (csid: string) => string,
 | 
					 | 
				
			||||||
        whenDone: (csId: string) => void,
 | 
					 | 
				
			||||||
        onFail: () => void) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (this.userDetails.data.csCount == 0) {
 | 
					        if (this.userDetails.data.csCount == 0) {
 | 
				
			||||||
            // The user became a contributor!
 | 
					            // The user became a contributor!
 | 
				
			||||||
            this.userDetails.data.csCount = 1;
 | 
					            this.userDetails.data.csCount = 1;
 | 
				
			||||||
| 
						 | 
					@ -84,46 +108,36 @@ export class ChangesetHandler {
 | 
				
			||||||
        if (this._dryRun) {
 | 
					        if (this._dryRun) {
 | 
				
			||||||
            const changesetXML = generateChangeXML("123456");
 | 
					            const changesetXML = generateChangeXML("123456");
 | 
				
			||||||
            console.log(changesetXML);
 | 
					            console.log(changesetXML);
 | 
				
			||||||
            whenDone("123456")
 | 
					 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const self = this;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (this.currentChangeset.data === undefined || this.currentChangeset.data === "") {
 | 
					        if (this.currentChangeset.data === undefined || this.currentChangeset.data === "") {
 | 
				
			||||||
            // We have to open a new changeset
 | 
					            // We have to open a new changeset
 | 
				
			||||||
            this.OpenChangeset(layout, (csId) => {
 | 
					            try {
 | 
				
			||||||
 | 
					                const csId = await this.OpenChangeset(layout)
 | 
				
			||||||
                this.currentChangeset.setData(csId);
 | 
					                this.currentChangeset.setData(csId);
 | 
				
			||||||
                const changeset = generateChangeXML(csId);
 | 
					                const changeset = generateChangeXML(csId);
 | 
				
			||||||
                console.log(changeset);
 | 
					                console.log("Current changeset is:", changeset);
 | 
				
			||||||
                self.AddChange(csId, changeset,
 | 
					                await this.AddChange(csId, changeset)
 | 
				
			||||||
                    allElements,
 | 
					            } catch (e) {
 | 
				
			||||||
                    whenDone,
 | 
					                console.error("Could not open/upload changeset due to ", e)
 | 
				
			||||||
                    (e) => {
 | 
					                this.currentChangeset.setData("")
 | 
				
			||||||
                        console.error("UPLOADING FAILED!", e)
 | 
					            }
 | 
				
			||||||
                        onFail()
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            }, {
 | 
					 | 
				
			||||||
                onFail: onFail
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            // There still exists an open changeset (or at least we hope so)
 | 
					            // There still exists an open changeset (or at least we hope so)
 | 
				
			||||||
            const csId = this.currentChangeset.data;
 | 
					            const csId = this.currentChangeset.data;
 | 
				
			||||||
            self.AddChange(
 | 
					            try {
 | 
				
			||||||
                csId,
 | 
					 | 
				
			||||||
                generateChangeXML(csId),
 | 
					 | 
				
			||||||
                allElements,
 | 
					 | 
				
			||||||
                whenDone,
 | 
					 | 
				
			||||||
                (e) => {
 | 
					 | 
				
			||||||
                    console.warn("Could not upload, changeset is probably closed: ", e);
 | 
					 | 
				
			||||||
                    // Mark the CS as closed...
 | 
					 | 
				
			||||||
                    this.currentChangeset.setData("");
 | 
					 | 
				
			||||||
                    // ... and try again. As the cs is closed, no recursive loop can exist  
 | 
					 | 
				
			||||||
                    self.UploadChangeset(layout, allElements, generateChangeXML, whenDone, onFail);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                await this.AddChange(
 | 
				
			||||||
 | 
					                    csId,
 | 
				
			||||||
 | 
					                    generateChangeXML(csId))
 | 
				
			||||||
 | 
					            } catch (e) {
 | 
				
			||||||
 | 
					                console.warn("Could not upload, changeset is probably closed: ", e);
 | 
				
			||||||
 | 
					                // Mark the CS as closed...
 | 
				
			||||||
 | 
					                this.currentChangeset.setData("");
 | 
				
			||||||
 | 
					                // ... and try again. As the cs is closed, no recursive loop can exist  
 | 
				
			||||||
 | 
					                await this.UploadChangeset(layout, generateChangeXML)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -143,6 +157,13 @@ export class ChangesetHandler {
 | 
				
			||||||
                         reason: string,
 | 
					                         reason: string,
 | 
				
			||||||
                         allElements: ElementStorage,
 | 
					                         allElements: ElementStorage,
 | 
				
			||||||
                         continuation: () => void) {
 | 
					                         continuation: () => void) {
 | 
				
			||||||
 | 
					        return this.DeleteElementAsync(object, layout, reason, allElements).then(continuation)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async DeleteElementAsync(object: OsmObject,
 | 
				
			||||||
 | 
					                                    layout: LayoutConfig,
 | 
				
			||||||
 | 
					                                    reason: string,
 | 
				
			||||||
 | 
					                                    allElements: ElementStorage): Promise<void> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        function generateChangeXML(csId: string) {
 | 
					        function generateChangeXML(csId: string) {
 | 
				
			||||||
            let [lat, lon] = object.centerpoint();
 | 
					            let [lat, lon] = object.centerpoint();
 | 
				
			||||||
| 
						 | 
					@ -151,9 +172,7 @@ export class ChangesetHandler {
 | 
				
			||||||
            changes +=
 | 
					            changes +=
 | 
				
			||||||
                `<delete><${object.type} id="${object.id}" version="${object.version}" changeset="${csId}" lat="${lat}" lon="${lon}" /></delete>`;
 | 
					                `<delete><${object.type} id="${object.id}" version="${object.version}" changeset="${csId}" lat="${lat}" lon="${lon}" /></delete>`;
 | 
				
			||||||
            changes += "</osmChange>";
 | 
					            changes += "</osmChange>";
 | 
				
			||||||
            continuation()
 | 
					 | 
				
			||||||
            return changes;
 | 
					            return changes;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -163,143 +182,122 @@ export class ChangesetHandler {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const self = this;
 | 
					        const csId = await this.OpenChangeset(layout, {
 | 
				
			||||||
        this.OpenChangeset(layout, (csId: string) => {
 | 
					            isDeletionCS: true,
 | 
				
			||||||
 | 
					            deletionReason: reason
 | 
				
			||||||
                // The cs is open - let us actually upload!
 | 
					        })
 | 
				
			||||||
                const changes = generateChangeXML(csId)
 | 
					        // The cs is open - let us actually upload!
 | 
				
			||||||
 | 
					        const changes = generateChangeXML(csId)
 | 
				
			||||||
                self.AddChange(csId, changes, allElements, (csId) => {
 | 
					        await this.AddChange(csId, changes)
 | 
				
			||||||
                    console.log("Successfully deleted ", object.id)
 | 
					        await this.CloseChangeset(csId)
 | 
				
			||||||
                    self.CloseChangeset(csId, continuation)
 | 
					 | 
				
			||||||
                }, (csId) => {
 | 
					 | 
				
			||||||
                    alert("Deletion failed... Should not happend")
 | 
					 | 
				
			||||||
                    // FAILED
 | 
					 | 
				
			||||||
                    self.CloseChangeset(csId, continuation)
 | 
					 | 
				
			||||||
                })
 | 
					 | 
				
			||||||
            }, {
 | 
					 | 
				
			||||||
                isDeletionCS: true,
 | 
					 | 
				
			||||||
                deletionReason: reason
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private CloseChangeset(changesetId: string = undefined, continuation: (() => void) = () => {
 | 
					    private async CloseChangeset(changesetId: string = undefined): Promise<void> {
 | 
				
			||||||
    }) {
 | 
					        const self = this
 | 
				
			||||||
        if (changesetId === undefined) {
 | 
					        return new Promise<void>(function (resolve, reject) {
 | 
				
			||||||
            changesetId = this.currentChangeset.data;
 | 
					            if (changesetId === undefined) {
 | 
				
			||||||
        }
 | 
					                changesetId = self.currentChangeset.data;
 | 
				
			||||||
        if (changesetId === undefined) {
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        console.log("closing changeset", changesetId);
 | 
					 | 
				
			||||||
        this.currentChangeset.setData("");
 | 
					 | 
				
			||||||
        this.auth.xhr({
 | 
					 | 
				
			||||||
            method: 'PUT',
 | 
					 | 
				
			||||||
            path: '/api/0.6/changeset/' + changesetId + '/close',
 | 
					 | 
				
			||||||
        }, function (err, response) {
 | 
					 | 
				
			||||||
            if (response == null) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                console.log("err", err);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            console.log("Closed changeset ", changesetId)
 | 
					            if (changesetId === undefined) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
            if (continuation !== undefined) {
 | 
					 | 
				
			||||||
                continuation();
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					            console.log("closing changeset", changesetId);
 | 
				
			||||||
 | 
					            self.currentChangeset.setData("");
 | 
				
			||||||
 | 
					            self.auth.xhr({
 | 
				
			||||||
 | 
					                method: 'PUT',
 | 
				
			||||||
 | 
					                path: '/api/0.6/changeset/' + changesetId + '/close',
 | 
				
			||||||
 | 
					            }, function (err, response) {
 | 
				
			||||||
 | 
					                if (response == null) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    console.log("err", err);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                console.log("Closed changeset ", changesetId)
 | 
				
			||||||
 | 
					                resolve()
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private OpenChangeset(
 | 
					    private OpenChangeset(
 | 
				
			||||||
        layout: LayoutConfig,
 | 
					        layout: LayoutConfig,
 | 
				
			||||||
        continuation: (changesetId: string) => void,
 | 
					 | 
				
			||||||
        options?: {
 | 
					        options?: {
 | 
				
			||||||
            isDeletionCS?: boolean,
 | 
					            isDeletionCS?: boolean,
 | 
				
			||||||
            deletionReason?: string,
 | 
					            deletionReason?: string,
 | 
				
			||||||
            onFail?: () => void
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    ) {
 | 
					    ): Promise<string> {
 | 
				
			||||||
        options = options ?? {}
 | 
					        const self = this;
 | 
				
			||||||
        options.isDeletionCS = options.isDeletionCS ?? false
 | 
					        return new Promise<string>(function (resolve, reject) {
 | 
				
			||||||
        const commentExtra = layout.changesetmessage !== undefined ? " - " + layout.changesetmessage : "";
 | 
					            options = options ?? {}
 | 
				
			||||||
        let comment = `Adding data with #MapComplete for theme #${layout.id}${commentExtra}`
 | 
					            options.isDeletionCS = options.isDeletionCS ?? false
 | 
				
			||||||
        if (options.isDeletionCS) {
 | 
					            const commentExtra = layout.changesetmessage !== undefined ? " - " + layout.changesetmessage : "";
 | 
				
			||||||
            comment = `Deleting a point with #MapComplete for theme #${layout.id}${commentExtra}`
 | 
					            let comment = `Adding data with #MapComplete for theme #${layout.id}${commentExtra}`
 | 
				
			||||||
            if (options.deletionReason) {
 | 
					            if (options.isDeletionCS) {
 | 
				
			||||||
                comment += ": " + options.deletionReason;
 | 
					                comment = `Deleting a point with #MapComplete for theme #${layout.id}${commentExtra}`
 | 
				
			||||||
            }
 | 
					                if (options.deletionReason) {
 | 
				
			||||||
        }
 | 
					                    comment += ": " + options.deletionReason;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        let path = window.location.pathname;
 | 
					 | 
				
			||||||
        path = path.substr(1, path.lastIndexOf("/"));
 | 
					 | 
				
			||||||
        const metadata = [
 | 
					 | 
				
			||||||
            ["created_by", `MapComplete ${Constants.vNumber}`],
 | 
					 | 
				
			||||||
            ["comment", comment],
 | 
					 | 
				
			||||||
            ["deletion", options.isDeletionCS ? "yes" : undefined],
 | 
					 | 
				
			||||||
            ["theme", layout.id],
 | 
					 | 
				
			||||||
            ["language", Locale.language.data],
 | 
					 | 
				
			||||||
            ["host", window.location.host],
 | 
					 | 
				
			||||||
            ["path", path],
 | 
					 | 
				
			||||||
            ["source", State.state.currentGPSLocation.data !== undefined ? "survey" : undefined],
 | 
					 | 
				
			||||||
            ["imagery", State.state.backgroundLayer.data.id],
 | 
					 | 
				
			||||||
            ["theme-creator", layout.maintainer]
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
            .filter(kv => (kv[1] ?? "") !== "")
 | 
					 | 
				
			||||||
            .map(kv => `<tag k="${kv[0]}" v="${escapeHtml(kv[1])}"/>`)
 | 
					 | 
				
			||||||
            .join("\n")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.auth.xhr({
 | 
					 | 
				
			||||||
            method: 'PUT',
 | 
					 | 
				
			||||||
            path: '/api/0.6/changeset/create',
 | 
					 | 
				
			||||||
            options: {header: {'Content-Type': 'text/xml'}},
 | 
					 | 
				
			||||||
            content: [`<osm><changeset>`,
 | 
					 | 
				
			||||||
                metadata,
 | 
					 | 
				
			||||||
                `</changeset></osm>`].join("")
 | 
					 | 
				
			||||||
        }, function (err, response) {
 | 
					 | 
				
			||||||
            if (response === undefined) {
 | 
					 | 
				
			||||||
                console.log("err", err);
 | 
					 | 
				
			||||||
                if (options.onFail) {
 | 
					 | 
				
			||||||
                    options.onFail()
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                continuation(response);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					
 | 
				
			||||||
 | 
					            let path = window.location.pathname;
 | 
				
			||||||
 | 
					            path = path.substr(1, path.lastIndexOf("/"));
 | 
				
			||||||
 | 
					            const metadata = [
 | 
				
			||||||
 | 
					                ["created_by", `MapComplete ${Constants.vNumber}`],
 | 
				
			||||||
 | 
					                ["comment", comment],
 | 
				
			||||||
 | 
					                ["deletion", options.isDeletionCS ? "yes" : undefined],
 | 
				
			||||||
 | 
					                ["theme", layout.id],
 | 
				
			||||||
 | 
					                ["language", Locale.language.data],
 | 
				
			||||||
 | 
					                ["host", window.location.host],
 | 
				
			||||||
 | 
					                ["path", path],
 | 
				
			||||||
 | 
					                ["source", State.state.currentGPSLocation.data !== undefined ? "survey" : undefined],
 | 
				
			||||||
 | 
					                ["imagery", State.state.backgroundLayer.data.id],
 | 
				
			||||||
 | 
					                ["theme-creator", layout.maintainer]
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					                .filter(kv => (kv[1] ?? "") !== "")
 | 
				
			||||||
 | 
					                .map(kv => `<tag k="${kv[0]}" v="${escapeHtml(kv[1])}"/>`)
 | 
				
			||||||
 | 
					                .join("\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.auth.xhr({
 | 
				
			||||||
 | 
					                method: 'PUT',
 | 
				
			||||||
 | 
					                path: '/api/0.6/changeset/create',
 | 
				
			||||||
 | 
					                options: {header: {'Content-Type': 'text/xml'}},
 | 
				
			||||||
 | 
					                content: [`<osm><changeset>`,
 | 
				
			||||||
 | 
					                    metadata,
 | 
				
			||||||
 | 
					                    `</changeset></osm>`].join("")
 | 
				
			||||||
 | 
					            }, function (err, response) {
 | 
				
			||||||
 | 
					                if (response === undefined) {
 | 
				
			||||||
 | 
					                    console.log("err", err);
 | 
				
			||||||
 | 
					                    reject(err)
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    resolve(response);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Upload a changesetXML
 | 
					     * Upload a changesetXML
 | 
				
			||||||
     * @param changesetId
 | 
					 | 
				
			||||||
     * @param changesetXML
 | 
					 | 
				
			||||||
     * @param allElements
 | 
					 | 
				
			||||||
     * @param continuation
 | 
					 | 
				
			||||||
     * @param onFail
 | 
					 | 
				
			||||||
     * @constructor
 | 
					 | 
				
			||||||
     * @private
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private AddChange(changesetId: string,
 | 
					    private AddChange(changesetId: string,
 | 
				
			||||||
                      changesetXML: string,
 | 
					                      changesetXML: string): Promise<string> {
 | 
				
			||||||
                      allElements: ElementStorage,
 | 
					        const self = this;
 | 
				
			||||||
                      continuation: ((changesetId: string) => void),
 | 
					        return new Promise(function (resolve, reject) {
 | 
				
			||||||
                      onFail: ((changesetId: string, reason: string) => void) = undefined) {
 | 
					            self.auth.xhr({
 | 
				
			||||||
        this.auth.xhr({
 | 
					                method: 'POST',
 | 
				
			||||||
            method: 'POST',
 | 
					                options: {header: {'Content-Type': 'text/xml'}},
 | 
				
			||||||
            options: {header: {'Content-Type': 'text/xml'}},
 | 
					                path: '/api/0.6/changeset/' + changesetId + '/upload',
 | 
				
			||||||
            path: '/api/0.6/changeset/' + changesetId + '/upload',
 | 
					                content: changesetXML
 | 
				
			||||||
            content: changesetXML
 | 
					            }, function (err, response) {
 | 
				
			||||||
        }, function (err, response) {
 | 
					                if (response == null) {
 | 
				
			||||||
            if (response == null) {
 | 
					                    console.log("err", err);
 | 
				
			||||||
                console.log("err", err);
 | 
					                    reject(err);
 | 
				
			||||||
                if (onFail) {
 | 
					 | 
				
			||||||
                    onFail(changesetId, err);
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                return;
 | 
					                self.parseUploadChangesetResponse(response);
 | 
				
			||||||
            }
 | 
					                console.log("Uploaded changeset ", changesetId);
 | 
				
			||||||
            ChangesetHandler.parseUploadChangesetResponse(response, allElements);
 | 
					                resolve(changesetId);
 | 
				
			||||||
            console.log("Uploaded changeset ", changesetId);
 | 
					            });
 | 
				
			||||||
            continuation(changesetId);
 | 
					        })
 | 
				
			||||||
        });
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,7 @@ import Img from "../../UI/Base/Img";
 | 
				
			||||||
import {Utils} from "../../Utils";
 | 
					import {Utils} from "../../Utils";
 | 
				
			||||||
import {OsmObject} from "./OsmObject";
 | 
					import {OsmObject} from "./OsmObject";
 | 
				
			||||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
 | 
					import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
 | 
				
			||||||
 | 
					import {Changes} from "./Changes";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class UserDetails {
 | 
					export default class UserDetails {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,7 +55,7 @@ export class OsmConnection {
 | 
				
			||||||
    private _onLoggedIn: ((userDetails: UserDetails) => void)[] = [];
 | 
					    private _onLoggedIn: ((userDetails: UserDetails) => void)[] = [];
 | 
				
			||||||
    private readonly _iframeMode: Boolean | boolean;
 | 
					    private readonly _iframeMode: Boolean | boolean;
 | 
				
			||||||
    private readonly _singlePage: boolean;
 | 
					    private readonly _singlePage: boolean;
 | 
				
			||||||
    private readonly _oauth_config: {
 | 
					    public readonly _oauth_config: {
 | 
				
			||||||
        oauth_consumer_key: string,
 | 
					        oauth_consumer_key: string,
 | 
				
			||||||
        oauth_secret: string,
 | 
					        oauth_secret: string,
 | 
				
			||||||
        url: string
 | 
					        url: string
 | 
				
			||||||
| 
						 | 
					@ -63,6 +64,8 @@ export class OsmConnection {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(dryRun: boolean,
 | 
					    constructor(dryRun: boolean,
 | 
				
			||||||
                fakeUser: boolean,
 | 
					                fakeUser: boolean,
 | 
				
			||||||
 | 
					                allElements: ElementStorage,
 | 
				
			||||||
 | 
					                changes: Changes,
 | 
				
			||||||
                oauth_token: UIEventSource<string>,
 | 
					                oauth_token: UIEventSource<string>,
 | 
				
			||||||
                // Used to keep multiple changesets open and to write to the correct changeset
 | 
					                // Used to keep multiple changesets open and to write to the correct changeset
 | 
				
			||||||
                layoutName: string,
 | 
					                layoutName: string,
 | 
				
			||||||
| 
						 | 
					@ -101,7 +104,7 @@ export class OsmConnection {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.preferencesHandler = new OsmPreferences(this.auth, this);
 | 
					        this.preferencesHandler = new OsmPreferences(this.auth, this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.changesetHandler = new ChangesetHandler(layoutName, dryRun, this, this.auth);
 | 
					        this.changesetHandler = new ChangesetHandler(layoutName, dryRun, this, allElements, changes, this.auth);
 | 
				
			||||||
        if (oauth_token.data !== undefined) {
 | 
					        if (oauth_token.data !== undefined) {
 | 
				
			||||||
            console.log(oauth_token.data)
 | 
					            console.log(oauth_token.data)
 | 
				
			||||||
            const self = this;
 | 
					            const self = this;
 | 
				
			||||||
| 
						 | 
					@ -124,10 +127,8 @@ export class OsmConnection {
 | 
				
			||||||
    public UploadChangeset(
 | 
					    public UploadChangeset(
 | 
				
			||||||
        layout: LayoutConfig,
 | 
					        layout: LayoutConfig,
 | 
				
			||||||
        allElements: ElementStorage,
 | 
					        allElements: ElementStorage,
 | 
				
			||||||
        generateChangeXML: (csid: string) => string,
 | 
					        generateChangeXML: (csid: string) => string): Promise<void> {
 | 
				
			||||||
        whenDone: (csId: string) => void,
 | 
					        return this.changesetHandler.UploadChangeset(layout, generateChangeXML);
 | 
				
			||||||
        onFail: () => {}) {
 | 
					 | 
				
			||||||
        this.changesetHandler.UploadChangeset(layout, allElements, generateChangeXML, whenDone, onFail);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public GetPreference(key: string, prefix: string = "mapcomplete-"): UIEventSource<string> {
 | 
					    public GetPreference(key: string, prefix: string = "mapcomplete-"): UIEventSource<string> {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -157,23 +157,6 @@ export abstract class OsmObject {
 | 
				
			||||||
        const elements: any[] = data.elements;
 | 
					        const elements: any[] = data.elements;
 | 
				
			||||||
        return OsmObject.ParseObjects(elements);
 | 
					        return OsmObject.ParseObjects(elements);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static DownloadAll(neededIds, forceRefresh = true): UIEventSource<OsmObject[]> {
 | 
					 | 
				
			||||||
        // local function which downloads all the objects one by one
 | 
					 | 
				
			||||||
        // this is one big loop, running one download, then rerunning the entire function
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const allSources: UIEventSource<OsmObject> [] = neededIds.map(id => OsmObject.DownloadObject(id, forceRefresh))
 | 
					 | 
				
			||||||
        const allCompleted = new UIEventSource(undefined).map(_ => {
 | 
					 | 
				
			||||||
            return !allSources.some(uiEventSource => uiEventSource.data === undefined)
 | 
					 | 
				
			||||||
        }, allSources)
 | 
					 | 
				
			||||||
        return allCompleted.map(completed => {
 | 
					 | 
				
			||||||
            if (completed) {
 | 
					 | 
				
			||||||
                return allSources.map(src => src.data)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return undefined
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected static isPolygon(tags: any): boolean {
 | 
					    protected static isPolygon(tags: any): boolean {
 | 
				
			||||||
        for (const tagsKey in tags) {
 | 
					        for (const tagsKey in tags) {
 | 
				
			||||||
            if (!tags.hasOwnProperty(tagsKey)) {
 | 
					            if (!tags.hasOwnProperty(tagsKey)) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										11
									
								
								State.ts
									
										
									
									
									
								
							
							
						
						
									
										11
									
								
								State.ts
									
										
									
									
									
								
							| 
						 | 
					@ -32,11 +32,11 @@ export default class State {
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     The mapping from id -> UIEventSource<properties>
 | 
					     The mapping from id -> UIEventSource<properties>
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public allElements: ElementStorage;
 | 
					    public allElements: ElementStorage = new ElementStorage();
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     THe change handler
 | 
					     THe change handler
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public changes: Changes;
 | 
					    public changes: Changes = new Changes();
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     The leaflet instance of the big basemap
 | 
					     The leaflet instance of the big basemap
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
| 
						 | 
					@ -155,7 +155,6 @@ export default class State {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(layoutToUse: LayoutConfig) {
 | 
					    constructor(layoutToUse: LayoutConfig) {
 | 
				
			||||||
        const self = this;
 | 
					        const self = this;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.layoutToUse.setData(layoutToUse);
 | 
					        this.layoutToUse.setData(layoutToUse);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // -- Location control initialization
 | 
					        // -- Location control initialization
 | 
				
			||||||
| 
						 | 
					@ -376,6 +375,8 @@ export default class State {
 | 
				
			||||||
        this.osmConnection = new OsmConnection(
 | 
					        this.osmConnection = new OsmConnection(
 | 
				
			||||||
            this.featureSwitchIsTesting.data,
 | 
					            this.featureSwitchIsTesting.data,
 | 
				
			||||||
            this.featureSwitchFakeUser.data,
 | 
					            this.featureSwitchFakeUser.data,
 | 
				
			||||||
 | 
					            this.allElements, 
 | 
				
			||||||
 | 
					            this.changes,
 | 
				
			||||||
            QueryParameters.GetQueryParameter(
 | 
					            QueryParameters.GetQueryParameter(
 | 
				
			||||||
                "oauth_token",
 | 
					                "oauth_token",
 | 
				
			||||||
                undefined,
 | 
					                undefined,
 | 
				
			||||||
| 
						 | 
					@ -387,9 +388,7 @@ export default class State {
 | 
				
			||||||
            this.featureSwitchApiURL.data
 | 
					            this.featureSwitchApiURL.data
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.allElements = new ElementStorage();
 | 
					      
 | 
				
			||||||
        this.changes = new Changes();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        new ChangeToElementsActor(this.changes, this.allElements)
 | 
					        new ChangeToElementsActor(this.changes, this.allElements)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        new PendingChangesUploader(this.changes, this.selectedElement);
 | 
					        new PendingChangesUploader(this.changes, this.selectedElement);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -57,7 +57,6 @@ export default class SimpleAddUI extends Toggle {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        function createNewPoint(tags: any[], location: { lat: number, lon: number }, snapOntoWay?: OsmWay) {
 | 
					        function createNewPoint(tags: any[], location: { lat: number, lon: number }, snapOntoWay?: OsmWay) {
 | 
				
			||||||
            console.trace("Creating a new point")
 | 
					 | 
				
			||||||
            const newElementAction = new CreateNewNodeAction(tags, location.lat, location.lon, {snapOnto: snapOntoWay})
 | 
					            const newElementAction = new CreateNewNodeAction(tags, location.lat, location.lon, {snapOnto: snapOntoWay})
 | 
				
			||||||
            State.state.changes.applyAction(newElementAction)
 | 
					            State.state.changes.applyAction(newElementAction)
 | 
				
			||||||
            selectedPreset.setData(undefined)
 | 
					            selectedPreset.setData(undefined)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,13 +54,16 @@
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      "tagRenderings": [
 | 
					      "tagRenderings": [
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					          "id": "uk_addresses_explanation",
 | 
				
			||||||
          "render": "There probably is an address here"
 | 
					          "render": "There probably is an address here"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					          "id": "uk_addresses_embedding_outline",
 | 
				
			||||||
          "render": "An outline embedding this point with an address already exists in OpenStreetMap.<br>This <a href='https://openstreetmap.org/{_embedding_object:id}' target='blank'>object</a> has address <b>{_embedding_object:addr:street} {_embedding_object:addr:housenumber}</b>",
 | 
					          "render": "An outline embedding this point with an address already exists in OpenStreetMap.<br>This <a href='https://openstreetmap.org/{_embedding_object:id}' target='blank'>object</a> has address <b>{_embedding_object:addr:street} {_embedding_object:addr:housenumber}</b>",
 | 
				
			||||||
          "condition": "_embedding_object:id~*"
 | 
					          "condition": "_embedding_object:id~*"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					          "id": "uk_addresses_import_button",
 | 
				
			||||||
          "render": "{import_button(ref:inspireid=$inspireid, Add this address, ./assets/themes/uk_addresses/housenumber_add.svg)}"
 | 
					          "render": "{import_button(ref:inspireid=$inspireid, Add this address, ./assets/themes/uk_addresses/housenumber_add.svg)}"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "all_tags"
 | 
					        "all_tags"
 | 
				
			||||||
| 
						 | 
					@ -109,11 +112,13 @@
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      "tagRenderings": [
 | 
					      "tagRenderings": [
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					          "id": "uk_addresses_explanation_osm",
 | 
				
			||||||
          "render": {
 | 
					          "render": {
 | 
				
			||||||
            "en": "This address is saved in OpenStreetMap"
 | 
					            "en": "This address is saved in OpenStreetMap"
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					          "id": "uk_addresses_housenumber",
 | 
				
			||||||
          "render": {
 | 
					          "render": {
 | 
				
			||||||
            "en": "The housenumber is <b>{addr:housenumber}</b>"
 | 
					            "en": "The housenumber is <b>{addr:housenumber}</b>"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
| 
						 | 
					@ -137,6 +142,7 @@
 | 
				
			||||||
          ]
 | 
					          ]
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					          "id": "uk_addresses_street",
 | 
				
			||||||
          "render": {
 | 
					          "render": {
 | 
				
			||||||
            "en": "This address is in street <b>{addr:street}</b>"
 | 
					            "en": "This address is in street <b>{addr:street}</b>"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,7 +13,8 @@
 | 
				
			||||||
    "uploadDone": "<span class='thanks'>Your picture has been added. Thanks for helping out!</span>",
 | 
					    "uploadDone": "<span class='thanks'>Your picture has been added. Thanks for helping out!</span>",
 | 
				
			||||||
    "dontDelete": "Cancel",
 | 
					    "dontDelete": "Cancel",
 | 
				
			||||||
    "doDelete": "Remove image",
 | 
					    "doDelete": "Remove image",
 | 
				
			||||||
    "isDeleted": "Deleted"
 | 
					    "isDeleted": "Deleted",
 | 
				
			||||||
 | 
					    "hasBeenImported": "This feature has been imported"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "centerMessage": {
 | 
					  "centerMessage": {
 | 
				
			||||||
    "loadingData": "Loading data…",
 | 
					    "loadingData": "Loading data…",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1326,10 +1326,10 @@
 | 
				
			||||||
                "description": "Addresses",
 | 
					                "description": "Addresses",
 | 
				
			||||||
                "name": "Known addresses in OSM",
 | 
					                "name": "Known addresses in OSM",
 | 
				
			||||||
                "tagRenderings": {
 | 
					                "tagRenderings": {
 | 
				
			||||||
                    "0": {
 | 
					                    "uk_addresses_explanation_osm": {
 | 
				
			||||||
                        "render": "This address is saved in OpenStreetMap"
 | 
					                        "render": "This address is saved in OpenStreetMap"
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    "1": {
 | 
					                    "uk_addresses_housenumber": {
 | 
				
			||||||
                        "mappings": {
 | 
					                        "mappings": {
 | 
				
			||||||
                            "0": {
 | 
					                            "0": {
 | 
				
			||||||
                                "then": "This building has no house number"
 | 
					                                "then": "This building has no house number"
 | 
				
			||||||
| 
						 | 
					@ -1338,7 +1338,7 @@
 | 
				
			||||||
                        "question": "What is the number of this house?",
 | 
					                        "question": "What is the number of this house?",
 | 
				
			||||||
                        "render": "The housenumber is <b>{addr:housenumber}</b>"
 | 
					                        "render": "The housenumber is <b>{addr:housenumber}</b>"
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    "2": {
 | 
					                    "uk_addresses_street": {
 | 
				
			||||||
                        "question": "What street is this address located in?",
 | 
					                        "question": "What street is this address located in?",
 | 
				
			||||||
                        "render": "This address is in street <b>{addr:street}</b>"
 | 
					                        "render": "This address is in street <b>{addr:street}</b>"
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue