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