More work on splitting roads, WIP; refactoring tests

This commit is contained in:
Pieter Vander Vennet 2021-09-22 05:02:09 +02:00
parent e374bb355c
commit 1f93923820
62 changed files with 1163 additions and 823 deletions

View file

@ -3,12 +3,12 @@
* Uses the freshest feature available in the case multiple sources offer data with the same identifier
*/
import {UIEventSource} from "../../UIEventSource";
import FeatureSource, {FeatureSourceForLayer, Tiled} from "../FeatureSource";
import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource, Tiled} from "../FeatureSource";
import FilteredLayer from "../../../Models/FilteredLayer";
import {BBox} from "../../GeoOperations";
import {Utils} from "../../../Utils";
export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled {
export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled, IndexedFeatureSource {
public features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]);
public readonly name;
@ -16,6 +16,7 @@ export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled
private readonly _sources: UIEventSource<FeatureSource[]>;
public readonly tileIndex: number;
public readonly bbox: BBox;
public readonly containedIds: UIEventSource<Set<string>> = new UIEventSource<Set<string>>(new Set())
constructor(layer: FilteredLayer, tileIndex: number, bbox: BBox, sources: UIEventSource<FeatureSource[]>) {
this.tileIndex = tileIndex;
@ -54,7 +55,7 @@ export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled
// We seed the dictionary with the previously loaded features
const oldValues = this.features.data ?? [];
for (const oldValue of oldValues) {
all.set(oldValue.feature.id + oldValue.feature._matching_layer_id, oldValue)
all.set(oldValue.feature.id, oldValue)
}
for (const source of this._sources.data) {
@ -62,7 +63,7 @@ export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled
continue;
}
for (const f of source.features.data) {
const id = f.feature.properties.id + f.feature._matching_layer_id;
const id = f.feature.properties.id;
if (!all.has(id)) {
// This is a new feature
somethingChanged = true;
@ -90,6 +91,7 @@ export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled
all.forEach((value, _) => {
newList.push(value)
})
this.containedIds.setData(new Set(all.keys()))
this.features.setData(newList);
}

View file

@ -0,0 +1,90 @@
import {Changes} from "../../Osm/Changes";
import {OsmNode, OsmRelation, OsmWay} from "../../Osm/OsmObject";
import FeatureSource from "../FeatureSource";
import {UIEventSource} from "../../UIEventSource";
import {ChangeDescription} from "../../Osm/Actions/ChangeDescription";
export class NewGeometryFromChangesFeatureSource implements FeatureSource {
// This class name truly puts the 'Java' into 'Javascript'
/**
* A feature source containing exclusively new elements
*/
public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]);
public readonly name: string = "newFeatures";
constructor(changes: Changes) {
const seenChanges = new Set<ChangeDescription>();
const features = this.features.data;
const self = this;
changes.pendingChanges
.map(changes => changes.filter(ch =>
// only new objects allowed
ch.id < 0 &&
// The change is an update to the object (e.g. tags or geometry) - not the actual create
ch.changes !== undefined &&
// If tags is undefined, this is probably a new point that is part of a split road
ch.tags !== undefined &&
// Already handled
!seenChanges.has(ch)))
.addCallbackAndRunD(changes => {
if (changes.length === 0) {
return;
}
const now = new Date();
function add(feature) {
feature.id = feature.properties.id
features.push({
feature: feature,
freshness: now
})
console.warn("Added a new feature: ", JSON.stringify(feature))
}
for (const change of changes) {
seenChanges.add(change)
try {
const tags = {}
for (const kv of change.tags) {
tags[kv.k] = kv.v
}
tags["id"] = change.type+"/"+change.id
switch (change.type) {
case "node":
const n = new OsmNode(change.id)
n.tags = tags
n.lat = change.changes["lat"]
n.lon = change.changes["lon"]
const geojson = n.asGeoJson()
add(geojson)
break;
case "way":
const w = new OsmWay(change.id)
w.tags = tags
w.nodes = change.changes["nodes"]
w.coordinates = change.changes["coordinates"].map(coor => coor.reverse())
add(w.asGeoJson())
break;
case "relation":
const r = new OsmRelation(change.id)
r.tags = tags
r.members = change.changes["members"]
add(r.asGeoJson())
break;
}
} catch (e) {
console.error("Could not generate a new geometry to render on screen for:", e)
}
}
self.features.ping()
})
}
}

View file

@ -1,16 +1,19 @@
import {UIEventSource} from "../../UIEventSource";
import FilteredLayer from "../../../Models/FilteredLayer";
import {FeatureSourceForLayer} from "../FeatureSource";
import {FeatureSourceForLayer, Tiled} from "../FeatureSource";
import {BBox} from "../../GeoOperations";
import {Utils} from "../../../Utils";
export default class SimpleFeatureSource implements FeatureSourceForLayer {
export default class SimpleFeatureSource implements FeatureSourceForLayer, Tiled {
public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]);
public readonly name: string = "SimpleFeatureSource";
public readonly layer: FilteredLayer;
public readonly bbox: BBox = BBox.global;
public readonly tileIndex: number = Utils.tile_index(0, 0, 0);
constructor(layer: FilteredLayer) {
this.name = "SimpleFeatureSource("+layer.layerDef.id+")"
this.name = "SimpleFeatureSource(" + layer.layerDef.id + ")"
this.layer = layer
}
}