forked from MapComplete/MapComplete
		
	Refactor OsmObject to use eventsources, add first version of the delete button
This commit is contained in:
		
							parent
							
								
									ec7833b2ee
								
							
						
					
					
						commit
						bbfcee686f
					
				
					 15 changed files with 553 additions and 229 deletions
				
			
		|  | @ -1,10 +1,17 @@ | |||
| import * as $ from "jquery" | ||||
| import {Utils} from "../../Utils"; | ||||
| import * as polygon_features from "../../assets/polygon-features.json"; | ||||
| import {UIEventSource} from "../UIEventSource"; | ||||
| 
 | ||||
| 
 | ||||
| export abstract class OsmObject { | ||||
| 
 | ||||
|     protected static backendURL = "https://www.openstreetmap.org/" | ||||
|     private static polygonFeatures = OsmObject.constructPolygonFeatures() | ||||
|     private static objectCache = new Map<string, UIEventSource<OsmObject>>(); | ||||
|     private static referencingWaysCache = new Map<string, UIEventSource<OsmWay[]>>(); | ||||
|     private static referencingRelationsCache = new Map<string, UIEventSource<OsmRelation[]>>(); | ||||
|     private static historyCache = new Map<string, UIEventSource<OsmObject[]>>(); | ||||
|     type: string; | ||||
|     id: number; | ||||
|     tags: {} = {}; | ||||
|  | @ -12,9 +19,6 @@ export abstract class OsmObject { | |||
|     public changed: boolean = false; | ||||
|     timestamp: Date; | ||||
| 
 | ||||
| 
 | ||||
|     private static polygonFeatures = OsmObject.constructPolygonFeatures() | ||||
| 
 | ||||
|     protected constructor(type: string, id: number) { | ||||
|         this.id = id; | ||||
|         this.type = type; | ||||
|  | @ -23,67 +27,99 @@ export abstract class OsmObject { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     static DownloadObject(id, continuation: ((element: OsmObject, meta: OsmObjectMeta) => void)) { | ||||
|     public static SetBackendUrl(url: string) { | ||||
|         if (!url.endsWith("/")) { | ||||
|             throw "Backend URL must end with a '/'" | ||||
|         } | ||||
|         if (!url.startsWith("http")) { | ||||
|             throw "Backend URL must begin with http" | ||||
|         } | ||||
|         this.backendURL = url; | ||||
|     } | ||||
| 
 | ||||
|     static DownloadObject(id): UIEventSource<OsmObject> { | ||||
|         if (OsmObject.objectCache.has(id)) { | ||||
|             return OsmObject.objectCache.get(id) | ||||
|         } | ||||
|         const splitted = id.split("/"); | ||||
|         const type = splitted[0]; | ||||
|         const idN = splitted[1]; | ||||
| 
 | ||||
|         const newContinuation = (element: OsmObject, meta: OsmObjectMeta) => { | ||||
|             continuation(element, meta); | ||||
|         const src = new UIEventSource<OsmObject>(undefined) | ||||
|         OsmObject.objectCache.set(id, src); | ||||
|         const newContinuation = (element: OsmObject) => { | ||||
|             src.setData(element) | ||||
|         } | ||||
| 
 | ||||
|         switch (type) { | ||||
|             case("node"): | ||||
|                 return new OsmNode(idN).Download(newContinuation); | ||||
|                 new OsmNode(idN).Download(newContinuation); | ||||
|                 break; | ||||
|             case("way"): | ||||
|                 return new OsmWay(idN).Download(newContinuation); | ||||
|                 new OsmWay(idN).Download(newContinuation); | ||||
|                 break; | ||||
|             case("relation"): | ||||
|                 return new OsmRelation(idN).Download(newContinuation); | ||||
|                 new OsmRelation(idN).Download(newContinuation); | ||||
|                 break; | ||||
| 
 | ||||
|         } | ||||
|         return src; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Downloads the ways that are using this node. | ||||
|      * Beware: their geometry will be incomplete! | ||||
|      * @param id | ||||
|      * @param continuation | ||||
|      * @constructor | ||||
|      */ | ||||
|     public static DownloadReferencingWays(id: string, continuation: (referencingWays: OsmWay[]) => void){ | ||||
|         Utils.downloadJson(`https://www.openStreetMap.org/api/0.6/${id}/ways`) | ||||
|     public static DownloadReferencingWays(id: string): UIEventSource<OsmWay[]> { | ||||
|         if (OsmObject.referencingWaysCache.has(id)) { | ||||
|             return OsmObject.referencingWaysCache.get(id); | ||||
|         } | ||||
|         const waysSrc = new UIEventSource<OsmWay[]>([]) | ||||
|         OsmObject.referencingWaysCache.set(id, waysSrc); | ||||
|         Utils.downloadJson(`${OsmObject.backendURL}api/0.6/${id}/ways`) | ||||
|             .then(data => { | ||||
|                const ways = data.elements.map(wayInfo => { | ||||
|                 const ways = data.elements.map(wayInfo => { | ||||
|                     const way = new OsmWay(wayInfo.id) | ||||
|                     way.LoadData(wayInfo) | ||||
|                     return way | ||||
|                 }) | ||||
|                 continuation(ways) | ||||
|                 waysSrc.setData(ways) | ||||
|             }) | ||||
|         return waysSrc; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Downloads the relations that are using this feature. | ||||
|      * Beware: their geometry will be incomplete! | ||||
|      * @param id | ||||
|      * @param continuation | ||||
|      * @constructor | ||||
|      */ | ||||
|     public static DownloadReferencingRelations(id: string, continuation: (referencingRelations: OsmRelation[]) => void){ | ||||
|         Utils.downloadJson(`https://www.openStreetMap.org/api/0.6/${id}/relations`) | ||||
|     public static DownloadReferencingRelations(id: string): UIEventSource<OsmRelation[]> { | ||||
|         if (OsmObject.referencingRelationsCache.has(id)) { | ||||
|             return OsmObject.referencingRelationsCache.get(id); | ||||
|         } | ||||
|         const relsSrc = new UIEventSource<OsmRelation[]>([]) | ||||
|         OsmObject.referencingRelationsCache.set(id, relsSrc); | ||||
|         Utils.downloadJson(`${OsmObject.backendURL}api/0.6/${id}/relations`) | ||||
|             .then(data => { | ||||
|                 const rels = data.elements.map(wayInfo => { | ||||
|                     const rel = new OsmRelation(wayInfo.id) | ||||
|                     rel.LoadData(wayInfo) | ||||
|                     return rel | ||||
|                 }) | ||||
|                 continuation(rels) | ||||
|                 relsSrc.setData(rels) | ||||
|             }) | ||||
|         return relsSrc; | ||||
|     } | ||||
|     public static DownloadHistory(id: string, continuation: (versions: OsmObject[]) => void): void{ | ||||
| 
 | ||||
|     public static DownloadHistory(id: string): UIEventSource<OsmObject []> { | ||||
|         if (OsmObject.historyCache.has(id)) { | ||||
|             return OsmObject.historyCache.get(id) | ||||
|         } | ||||
|         const splitted = id.split("/"); | ||||
|         const type = splitted[0]; | ||||
|         const idN = splitted[1]; | ||||
|         $.getJSON("https://www.openStreetMap.org/api/0.6/" + type + "/" + idN + "/history", data => { | ||||
|         const src = new UIEventSource<OsmObject[]>([]); | ||||
|         OsmObject.historyCache.set(id, src); | ||||
|         Utils.downloadJson(`${OsmObject.backendURL}api/0.6/${type}/${idN}/history`).then(data => { | ||||
|             const elements: any[] = data.elements; | ||||
|             const osmObjects: OsmObject[] = [] | ||||
|             for (const element of elements) { | ||||
|  | @ -103,30 +139,42 @@ export abstract class OsmObject { | |||
|                 osmObject?.SaveExtraData(element, []); | ||||
|                 osmObjects.push(osmObject) | ||||
|             } | ||||
|             continuation(osmObjects) | ||||
|             src.setData(osmObjects) | ||||
|         }) | ||||
|         return src; | ||||
|     } | ||||
| 
 | ||||
|     // bounds should be: [[maxlat, minlon], [minlat, maxlon]] (same as Utils.tile_bounds)
 | ||||
|     public static LoadArea(bounds: [[number, number], [number, number]], callback: (objects: OsmObject[]) => void) { | ||||
|         const minlon = bounds[0][1] | ||||
|         const maxlon = bounds[1][1] | ||||
|         const minlat = bounds[1][0] | ||||
|         const maxlat = bounds[0][0]; | ||||
|         const url = `${OsmObject.backendURL}api/0.6/map.json?bbox=${minlon},${minlat},${maxlon},${maxlat}` | ||||
|         $.getJSON(url, data => { | ||||
|             const elements: any[] = data.elements; | ||||
|             const objects = OsmObject.ParseObjects(elements) | ||||
|             callback(objects); | ||||
| 
 | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     private static constructPolygonFeatures(): Map<string, { values: Set<string>, blacklist: boolean }> { | ||||
|         const result = new Map<string, { values: Set<string>, blacklist: boolean }>(); | ||||
|     public static DownloadAll(neededIds): 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
 | ||||
| 
 | ||||
|         for (const polygonFeature of polygon_features) { | ||||
|             const key = polygonFeature.key; | ||||
| 
 | ||||
|             if (polygonFeature.polygon === "all") { | ||||
|                 result.set(key, {values: null, blacklist: false}) | ||||
|                 continue | ||||
|         const allSources: UIEventSource<OsmObject> [] = neededIds.map(id => OsmObject.DownloadObject(id)) | ||||
|         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) | ||||
|             } | ||||
| 
 | ||||
|             const blacklist = polygonFeature.polygon === "blacklist" | ||||
|             result.set(key, {values: new Set<string>(polygonFeature.values), blacklist: blacklist}) | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         return result; | ||||
|             return [] | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     protected static isPolygon(tags: any): boolean { | ||||
|         for (const tagsKey in tags) { | ||||
|             if (!tags.hasOwnProperty(tagsKey)) { | ||||
|  | @ -145,43 +193,23 @@ export abstract class OsmObject { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // bounds should be: [[maxlat, minlon], [minlat, maxlon]] (same as Utils.tile_bounds)
 | ||||
|     public static LoadArea(bounds: [[number, number], [number, number]], callback: (objects: OsmObject[]) => void) { | ||||
|         const minlon = bounds[0][1] | ||||
|         const maxlon = bounds[1][1] | ||||
|         const minlat = bounds[1][0] | ||||
|         const maxlat = bounds[0][0]; | ||||
|         const url = `https://www.openstreetmap.org/api/0.6/map.json?bbox=${minlon},${minlat},${maxlon},${maxlat}` | ||||
|         $.getJSON(url, data => { | ||||
|             const elements: any[] = data.elements; | ||||
|             const objects = OsmObject.ParseObjects(elements) | ||||
|             callback(objects); | ||||
|     private static constructPolygonFeatures(): Map<string, { values: Set<string>, blacklist: boolean }> { | ||||
|         const result = new Map<string, { values: Set<string>, blacklist: boolean }>(); | ||||
| 
 | ||||
|         }) | ||||
|     } | ||||
|         for (const polygonFeature of polygon_features) { | ||||
|             const key = polygonFeature.key; | ||||
| 
 | ||||
|     //Loads an area from the OSM-api.
 | ||||
| 
 | ||||
|     public static DownloadAll(neededIds, knownElements: any = {}, continuation: ((knownObjects: any) => void)) { | ||||
|         // local function which downloads all the objects one by one
 | ||||
|         // this is one big loop, running one download, then rerunning the entire function
 | ||||
|         if (neededIds.length == 0) { | ||||
|             continuation(knownElements); | ||||
|             return; | ||||
|         } | ||||
|         const neededId = neededIds.pop(); | ||||
| 
 | ||||
|         if (neededId in knownElements) { | ||||
|             OsmObject.DownloadAll(neededIds, knownElements, continuation); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         OsmObject.DownloadObject(neededId, | ||||
|             function (element) { | ||||
|                 knownElements[neededId] = element; // assign the element for later, continue downloading the next element
 | ||||
|                 OsmObject.DownloadAll(neededIds, knownElements, continuation); | ||||
|             if (polygonFeature.polygon === "all") { | ||||
|                 result.set(key, {values: null, blacklist: false}) | ||||
|                 continue | ||||
|             } | ||||
|         ); | ||||
| 
 | ||||
|             const blacklist = polygonFeature.polygon === "blacklist" | ||||
|             result.set(key, {values: new Set<string>(polygonFeature.values), blacklist: blacklist}) | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     private static ParseObjects(elements: any[]): OsmObject[] { | ||||
|  | @ -245,7 +273,7 @@ export abstract class OsmObject { | |||
|     Download(continuation: ((element: OsmObject, meta: OsmObjectMeta) => void)) { | ||||
|         const self = this; | ||||
|         const full = this.type !== "way" ? "" : "/full"; | ||||
|         const url = "https://www.openstreetmap.org/api/0.6/" + this.type + "/" + this.id + full; | ||||
|         const url = `${OsmObject.backendURL}api/0.6/${this.type}/${this.id}${full}`; | ||||
|         $.getJSON(url, function (data) { | ||||
| 
 | ||||
|                 const element = data.elements.pop(); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue