More cleanup, first somewhat working version of #171

This commit is contained in:
Pieter Vander Vennet 2021-09-22 16:07:56 +02:00
parent 1f93923820
commit bef684aec7
19 changed files with 439 additions and 135 deletions

View file

@ -230,7 +230,7 @@ export default class GeoLocationHandler extends VariableUiElement {
navigator?.permissions
?.query({name: "geolocation"})
?.then(function (status) {
console.log("Geolocation is already", status);
console.log("Geolocation permission is ", status.state);
if (status.state === "granted") {
self.StartGeolocating(forceZoom);
}
@ -289,7 +289,6 @@ export default class GeoLocationHandler extends VariableUiElement {
private StartGeolocating(zoomToGPS = true) {
const self = this;
console.log("Starting geolocation");
this._lastUserRequest = zoomToGPS ? new Date() : new Date(0);
if (self._permission.data === "denied") {
@ -301,8 +300,6 @@ export default class GeoLocationHandler extends VariableUiElement {
this.MoveToCurrentLoction(16);
}
console.log("Searching location using GPS");
if (self._isActive.data) {
return;
}

View file

@ -184,8 +184,8 @@ export default class OverpassFeatureSource implements FeatureSource, FeatureSour
return;
}
this.runningQuery.setData(true);
overpass.queryGeoJson(queryBounds,
function (data, date) {
overpass.queryGeoJson(queryBounds).
then(([data, date]) => {
self._previousBounds.get(z).push(queryBounds);
self.retries.setData(0);
const features = data.features.map(f => ({feature: f, freshness: date}));
@ -197,12 +197,12 @@ export default class OverpassFeatureSource implements FeatureSource, FeatureSour
console.error("Got the overpass response, but could not process it: ", e, e.stack)
}
self.runningQuery.setData(false);
},
function (reason) {
})
.catch((reason) => {
self.retries.data++;
self.ForceRefresh();
self.timeout.setData(self.retries.data * 5);
console.error(`QUERY FAILED (retrying in ${5 * self.retries.data} sec) due to ${reason}`);
console.error(`QUERY FAILED (retrying in ${5 * self.retries.data} sec) due to`, reason);
self.retries.ping();
self.runningQuery.setData(false);

View file

@ -8,8 +8,6 @@ export default class TitleHandler {
constructor(state) {
const currentTitle: UIEventSource<string> = state.selectedElement.map(
selected => {
console.log("UPdating title")
const layout = state.layoutToUse.data
const defaultTitle = Translations.WT(layout?.title)?.txt ?? "MapComplete"

View file

@ -21,7 +21,7 @@ import {BBox} from "../GeoOperations";
import {TileHierarchyMerger} from "./TiledFeatureSource/TileHierarchyMerger";
import RelationsTracker from "../Osm/RelationsTracker";
import {NewGeometryFromChangesFeatureSource} from "./Sources/NewGeometryFromChangesFeatureSource";
import ChangeGeometryApplicator from "./ChangeApplicator";
import ChangeGeometryApplicator from "./Sources/ChangeGeometryApplicator";
export default class FeaturePipeline implements FeatureSourceState {
@ -159,7 +159,6 @@ export default class FeaturePipeline implements FeatureSourceState {
// Whenever fresh data comes in, we need to update the metatagging
self.newDataLoadedSignal.stabilized(1000).addCallback(src => {
console.log("Got an update from ", src.name)
self.updateAllMetaTagging()
})
@ -183,7 +182,6 @@ export default class FeaturePipeline implements FeatureSourceState {
}
private updateAllMetaTagging() {
console.log("Updating the meta tagging")
const self = this;
this.perLayerHierarchy.forEach(hierarchy => {
hierarchy.loadedTiles.forEach(src => {

View file

@ -1,15 +1,13 @@
import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource} from "./FeatureSource";
import {UIEventSource} from "../UIEventSource";
import {Changes} from "../Osm/Changes";
import {ChangeDescription, ChangeDescriptionTools} from "../Osm/Actions/ChangeDescription";
import {Utils} from "../../Utils";
import FilteredLayer from "../../Models/FilteredLayer";
import {OsmNode, OsmRelation, OsmWay} from "../Osm/OsmObject";
/**
* Applies geometry changes from 'Changes' onto every feature of a featureSource
*/
import {Changes} from "../../Osm/Changes";
import {UIEventSource} from "../../UIEventSource";
import {FeatureSourceForLayer, IndexedFeatureSource} from "../FeatureSource";
import FilteredLayer from "../../../Models/FilteredLayer";
import {ChangeDescription, ChangeDescriptionTools} from "../../Osm/Actions/ChangeDescription";
export default class ChangeGeometryApplicator implements FeatureSourceForLayer {
public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]);
public readonly name: string;
@ -76,6 +74,7 @@ export default class ChangeGeometryApplicator implements FeatureSourceForLayer {
// We only apply the last change as that one'll have the latest geometry
const change = changesForFeature[changesForFeature.length - 1]
copy.feature.geometry = ChangeDescriptionTools.getGeojsonGeometry(change)
console.log("Applying a geometry change onto ", feature, change, copy)
newFeatures.push(copy)
}
this.features.setData(newFeatures)

View file

@ -27,7 +27,7 @@ export default class TiledFromLocalStorageSource implements TileHierarchy<Featur
return Number(key.substring(prefix.length));
})
console.log("Avaible datasets in local storage:", indexes)
console.log("Layer", layer.layerDef.id, "has following tiles in available in localstorage", indexes.map(i => Utils.tile_from_index(i).join("/")).join(", "))
const zLevels = indexes.map(i => i % 100)
const indexesSet = new Set(indexes)

View file

@ -15,13 +15,15 @@ export interface RelationSplitInput {
* When a way is split and this way is part of a relation, the relation should be updated too to have the new segment if relevant.
*/
export default class RelationSplitHandler extends OsmChangeAction {
private readonly _input: RelationSplitInput;
constructor(input: RelationSplitInput) {
super()
this._input = input;
}
async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> {
return [];
return new InPlaceReplacedmentRTSH(this._input).CreateChangeDescriptions(changes)
}

View file

@ -15,6 +15,11 @@ export default class SplitAction extends OsmChangeAction {
private readonly wayId: string;
private readonly _splitPointsCoordinates: [number, number] []// lon, lat
/**
*
* @param wayId
* @param splitPointCoordinates: lon, lat
*/
constructor(wayId: string, splitPointCoordinates: [number, number][]) {
super()
this.wayId = wayId;
@ -39,12 +44,11 @@ export default class SplitAction extends OsmChangeAction {
}
async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> {
const self = this;
const originalElement = <OsmWay>await OsmObject.DownloadObjectAsync(this.wayId)
const originalElement = <OsmWay> await OsmObject.DownloadObjectAsync(this.wayId)
const originalNodes = originalElement.nodes;
// First, calculate splitpoints and remove points close to one another
const splitInfo = self.CalculateSplitCoordinates(originalElement)
const splitInfo = this.CalculateSplitCoordinates(originalElement)
// Now we have a list with e.g.
// [ { originalIndex: 0}, {originalIndex: 1, doSplit: true}, {originalIndex: 2}, {originalIndex: undefined, doSplit: true}, {originalIndex: 3}]
@ -166,8 +170,9 @@ export default class SplitAction extends OsmChangeAction {
private CalculateSplitCoordinates(osmWay: OsmWay, toleranceInM = 5): SplitInfo[] {
const wayGeoJson = osmWay.asGeoJson()
// Should be [lon, lat][]
const originalPoints = osmWay.coordinates.map(c => <[number, number]>c.reverse())
const originalPoints : [number, number][] = osmWay.coordinates.map(c => [c[1], c[0]])
const allPoints: {
// lon, lat
coordinates: [number, number],
isSplitPoint: boolean,
originalIndex?: number, // Original index
@ -180,6 +185,7 @@ export default class SplitAction extends OsmChangeAction {
// - `dist`: distance between pt and the closest point,
// `location`: distance along the line between start and the closest point.
let projected = GeoOperations.nearestPoint(wayGeoJson, c)
// c is lon lat
return ({
coordinates: c,
isSplitPoint: true,
@ -233,8 +239,12 @@ export default class SplitAction extends OsmChangeAction {
if (distToNext > distToPrev) {
closest = prevPoint
}
// Ok, we have a closest point!
if(closest.originalIndex === 0 || closest.originalIndex === originalPoints.length){
// We can not split on the first or last points...
continue
}
closest.isSplitPoint = true;
allPoints.splice(i, 1)

View file

@ -14,7 +14,7 @@ import {LocalStorageSource} from "../Web/LocalStorageSource";
export class Changes {
private static _nextId = -1; // Newly assigned ID's are negative
private _nextId : number = -1; // Newly assigned ID's are negative
public readonly name = "Newly added features"
/**
* All the newly created features as featureSource + all the modified features
@ -30,6 +30,8 @@ export class Changes {
constructor() {
// We keep track of all changes just as well
this.allChanges.setData([...this.pendingChanges.data])
// If a pending change contains a negative ID, we save that
this._nextId = Math.min(-1, ...this.pendingChanges.data?.map(pch => pch.id) ?? [])
}
private static createChangesetFor(csId: string,
@ -77,7 +79,7 @@ export class Changes {
* Returns a new ID and updates the value for the next ID
*/
public getNewID() {
return Changes._nextId--;
return this._nextId--;
}
/**

View file

@ -63,17 +63,26 @@ export abstract class OsmObject {
if (idN < 0) {
return undefined;
}
switch (type) {
case("node"):
return await new OsmNode(idN).Download();
case("way"):
return await new OsmWay(idN).Download();
case("relation"):
return await new OsmRelation(idN).Download();
default:
throw ("Invalid object type:" + type + id);
const full = !id.startsWith("way") ? "" : "/full";
const url = `${OsmObject.backendURL}api/0.6/${id}${full}`;
const rawData = await Utils.downloadJson(url)
// A full query might contain more then just the requested object (e.g. nodes that are part of a way, where we only want the way)
const parsed = OsmObject.ParseObjects(rawData.elements);
// Lets fetch the object we need
for (const osmObject of parsed) {
if(osmObject.type !== type){
continue;
}
if(osmObject.id !== idN){
continue
}
// Found the one!
return osmObject
}
throw "PANIC: requested object is not part of the response"
}
@ -205,6 +214,7 @@ export abstract class OsmObject {
private static ParseObjects(elements: any[]): OsmObject[] {
const objects: OsmObject[] = [];
const allNodes: Map<number, OsmNode> = new Map<number, OsmNode>()
for (const element of elements) {
const type = element.type;
const idN = element.id;
@ -226,6 +236,11 @@ export abstract class OsmObject {
osmObject.SaveExtraData(element, [])
break;
}
if (osmObject !== undefined && OsmObject.backendURL !== OsmObject.defaultBackend) {
osmObject.tags["_backend"] = OsmObject.backendURL
}
osmObject?.LoadData(element)
objects.push(osmObject)
}
@ -237,7 +252,7 @@ export abstract class OsmObject {
public abstract asGeoJson(): any;
abstract SaveExtraData(element: any, allElements: any[]);
abstract SaveExtraData(element: any, allElements: OsmObject[]);
/**
* Generates the changeset-XML for tags
@ -260,37 +275,6 @@ export abstract class OsmObject {
return tags;
}
/**
* Downloads the object, a full download for ways and relations
* @constructor
*/
async Download(): Promise<OsmObject> {
const self = this;
const full = this.type !== "way" ? "" : "/full";
const url = `${OsmObject.backendURL}api/0.6/${this.type}/${this.id}${full}`;
return await Utils.downloadJson(url).then(data => {
const element = data.elements.pop();
let nodes = []
if (self.type === "way" && data.elements.length >= 0) {
nodes = OsmObject.ParseObjects(data.elements)
}
if (self.type === "rellation") {
throw "We should save some extra data"
}
self.LoadData(element)
self.SaveExtraData(element, nodes);
if (OsmObject.backendURL !== OsmObject.defaultBackend) {
self.tags["_backend"] = OsmObject.backendURL
}
return this;
}
);
}
abstract ChangesetXML(changesetId: string): string;
protected VersionXML() {
@ -363,7 +347,7 @@ export class OsmNode extends OsmObject {
export class OsmWay extends OsmObject {
nodes: number[];
nodes: number[] = [];
// The coordinates of the way, [lat, lon][]
coordinates: [number, number][] = []
lat: number;
@ -400,6 +384,10 @@ export class OsmWay extends OsmObject {
nodeDict.set(node.id, node)
}
if (element.nodes === undefined) {
console.log("PANIC")
}
for (const nodeId of element.nodes) {
const node = nodeDict.get(nodeId)
if (node === undefined) {
@ -419,8 +407,8 @@ export class OsmWay extends OsmObject {
}
public asGeoJson() {
let coordinates : ([number, number][] | [number, number][][]) = this.coordinates.map(c => <[number, number]>c.reverse());
if(this.isPolygon()){
let coordinates: ([number, number][] | [number, number][][]) = this.coordinates.map(c => [c[1], c[0]]);
if (this.isPolygon()) {
coordinates = [coordinates]
}
return {

View file

@ -145,14 +145,14 @@ export class OsmPreferences {
private SetPreference(k: string, v: string) {
if (!this.userDetails.data.loggedIn) {
console.log(`Not saving preference ${k}: user not logged in`);
console.debug(`Not saving preference ${k}: user not logged in`);
return;
}
if (this.preferences.data[k] === v) {
return;
}
console.log("Updating preference", k, " to ", Utils.EllipsesAfter(v, 15));
console.debug("Updating preference", k, " to ", Utils.EllipsesAfter(v, 15));
if (v === undefined || v === "") {
this.auth.xhr({
@ -161,10 +161,10 @@ export class OsmPreferences {
options: {header: {'Content-Type': 'text/plain'}},
}, function (error) {
if (error) {
console.log("Could not remove preference", error);
console.warn("Could not remove preference", error);
return;
}
console.log("Preference ", k, "removed!");
console.debug("Preference ", k, "removed!");
});
return;

View file

@ -31,7 +31,7 @@ export class Overpass {
this._relationTracker = relationTracker
}
queryGeoJson(bounds: Bounds, continuation: ((any, date: Date) => void), onFail: ((reason) => void)): void {
public async queryGeoJson(bounds: Bounds): Promise<[any, Date]> {
let query = this.buildQuery("[bbox:" + bounds.south + "," + bounds.west + "," + bounds.north + "," + bounds.east + "]")
@ -40,24 +40,18 @@ export class Overpass {
query = Overpass.testUrl;
}
const self = this;
Utils.downloadJson(query)
.then(json => {
if (json.elements === [] && ((json.remarks ?? json.remark).indexOf("runtime error") >= 0)) {
console.log("Timeout or other runtime error");
onFail("Runtime error (timeout)")
return;
}
const json = await Utils.downloadJson(query)
if (json.elements === [] && ((json.remarks ?? json.remark).indexOf("runtime error") >= 0)) {
console.log("Timeout or other runtime error");
throw("Runtime error (timeout)")
}
self._relationTracker.RegisterRelations(json)
// @ts-ignore
const geojson = OsmToGeoJson.default(json);
const osmTime = new Date(json.osm3s.timestamp_osm_base);
continuation(geojson, osmTime);
}).catch(e => {
onFail(e);
})
self._relationTracker.RegisterRelations(json)
// @ts-ignore
const geojson = OsmToGeoJson.default(json);
const osmTime = new Date(json.osm3s.timestamp_osm_base);
return [geojson, osmTime];
}
buildQuery(bbox: string): string {