Fix various bugs

This commit is contained in:
Pieter Vander Vennet 2022-02-22 14:13:41 +01:00
parent 30f4be183e
commit 5284f198d8
26 changed files with 339 additions and 119 deletions

View file

@ -239,7 +239,7 @@ export default class AvailableBaseLayersImplementation implements AvailableBaseL
prefered = preferedCategory.data;
}
prefered.reverse();
prefered.reverse(/*New list, inplace reverse is fine*/);
for (const category of prefered) {
//Then sort all 'photo'-layers to the top. Stability of the sorting will force a 'best' photo layer on top
available.sort((a, b) => {

View file

@ -75,7 +75,7 @@ export default class FeaturePipeline {
this.state = state;
const self = this
const expiryInSeconds = Math.min(...state.layoutToUse.layers.map(l => l.maxAgeOfCache))
const expiryInSeconds = Math.min(...state.layoutToUse?.layers?.map(l => l.maxAgeOfCache) ?? [])
this.oldestAllowedDate = new Date(new Date().getTime() - expiryInSeconds);
this.osmSourceZoomLevel = state.osmApiTileSize.data;
const useOsmApi = state.locationControl.map(l => l.zoom > (state.overpassMaxZoom.data ?? 12))

View file

@ -74,7 +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)
console.log("Applying a geometry change onto:", feature,"The change is:", change,"which becomes:", copy)
newFeatures.push(copy)
}
this.features.setData(newFeatures)

View file

@ -79,7 +79,7 @@ export default class OsmFeatureSource {
})
const neededLayers = options.state.layoutToUse.layers
const neededLayers = (options.state.layoutToUse?.layers ?? [])
.filter(layer => !layer.doNotDownload)
.filter(layer => layer.source.geojsonSource === undefined || layer.source.isOsmCacheLayer)
this.allowedTags = new Or(neededLayers.map(l => l.source.osmTags))

View file

@ -81,7 +81,7 @@ export class ChangeDescriptionTools {
case "way":
const w = new OsmWay(change.id)
w.nodes = change.changes["nodes"]
w.coordinates = change.changes["coordinates"].map(coor => coor.reverse())
w.coordinates = change.changes["coordinates"].map(([lon, lat]) => [lat, lon])
return w.asGeoJson().geometry
case "relation":
const r = new OsmRelation(change.id)

View file

@ -33,12 +33,12 @@ export default class CreateMultiPolygonWithPointReuseAction extends OsmCreateAct
super(null, true);
this._tags = [...tags, new Tag("type", "multipolygon")];
this.changeType = changeType;
this.theme = state.layoutToUse.id
this.theme = state?.layoutToUse?.id ?? ""
this.createOuterWay = new CreateWayWithPointReuseAction([], outerRingCoordinates, state, config)
this.createInnerWays = innerRingsCoordinates.map(ringCoordinates =>
new CreateNewWayAction([],
ringCoordinates.map(([lon, lat]) => ({lat, lon})),
{theme: state.layoutToUse.id}))
{theme: state?.layoutToUse?.id}))
this.geojsonPreview = {
type: "Feature",

View file

@ -112,16 +112,25 @@ export default class CreateNewNodeAction extends OsmCreateAction {
const geojson = this._snapOnto.asGeoJson()
const projected = GeoOperations.nearestPoint(geojson, [this._lon, this._lat])
const projectedCoor= <[number, number]>projected.geometry.coordinates
const index = projected.properties.index
// We check that it isn't close to an already existing point
let reusedPointId = undefined;
const prev = <[number, number]>geojson.geometry.coordinates[index]
if (GeoOperations.distanceBetween(prev, <[number, number]>projected.geometry.coordinates) < this._reusePointDistance) {
let outerring : [number,number][];
if(geojson.geometry.type === "LineString"){
outerring = <[number, number][]> geojson.geometry.coordinates
}else if(geojson.geometry.type === "Polygon"){
outerring =<[number, number][]> geojson.geometry.coordinates[0]
}
const prev= outerring[index]
if (GeoOperations.distanceBetween(prev, projectedCoor) < this._reusePointDistance) {
// We reuse this point instead!
reusedPointId = this._snapOnto.nodes[index]
}
const next = <[number, number]>geojson.geometry.coordinates[index + 1]
if (GeoOperations.distanceBetween(next, <[number, number]>projected.geometry.coordinates) < this._reusePointDistance) {
const next = outerring[index + 1]
if (GeoOperations.distanceBetween(next, projectedCoor) < this._reusePointDistance) {
// We reuse this point instead!
reusedPointId = this._snapOnto.nodes[index + 1]
}
@ -135,8 +144,7 @@ export default class CreateNewNodeAction extends OsmCreateAction {
}]
}
const locations = [...this._snapOnto.coordinates]
locations.forEach(coor => coor.reverse())
const locations = [...this._snapOnto.coordinates.map(([lat, lon]) =><[number,number]> [lon, lat])]
const ids = [...this._snapOnto.nodes]
locations.splice(index + 1, 0, [this._lon, this._lat])

View file

@ -33,7 +33,7 @@ export default class CreateNewWayAction extends OsmCreateAction {
We filter those here, as the CreateWayWithPointReuseAction delegates the actual creation to here.
Filtering here also prevents similar bugs in other actions
*/
if(this.coordinates.length > 0 && this.coordinates[this.coordinates.length - 1].nodeId === coordinate.nodeId){
if(this.coordinates.length > 0 && coordinate.nodeId !== undefined && this.coordinates[this.coordinates.length - 1].nodeId === coordinate.nodeId){
// This is a duplicate id
console.warn("Skipping a node in createWay to avoid a duplicate node:", coordinate,"\nThe previous coordinates are: ", this.coordinates)
continue

View file

@ -186,7 +186,7 @@ export default class CreateWayWithPointReuseAction extends OsmCreateAction {
}
public async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> {
const theme = this._state.layoutToUse.id
const theme = this._state?.layoutToUse?.id
const allChanges: ChangeDescription[] = []
const nodeIdsToUse: { lat: number, lon: number, nodeId?: number }[] = []
for (let i = 0; i < this._coordinateInfo.length; i++) {
@ -251,7 +251,7 @@ export default class CreateWayWithPointReuseAction extends OsmCreateAction {
const bbox = new BBox(coordinates)
const state = this._state
const allNodes = [].concat(...state.featurePipeline.GetFeaturesWithin("type_node", bbox.pad(1.2)))
const allNodes = [].concat(...state?.featurePipeline?.GetFeaturesWithin("type_node", bbox.pad(1.2))??[])
const maxDistance = Math.max(...this._config.map(c => c.withinRangeOfM))
// Init coordianteinfo with undefined but the same length as coordinates

View file

@ -28,6 +28,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction {
/**
* The target coordinates that should end up in OpenStreetMap.
* This is identical to either this.feature.geometry.coordinates or -in case of a polygon- feature.geometry.coordinates[0]
* Format: [lon, lat]
*/
private readonly targetCoordinates: [number, number][];
/**
@ -540,8 +541,6 @@ export default class ReplaceGeometryAction extends OsmChangeAction {
id: nodeId,
})
})
}
return allChanges

View file

@ -55,8 +55,8 @@ export class Changes {
// This doesn't matter however, as the '-1' is per piecewise upload, not global per changeset
}
private static createChangesetFor(csId: string,
allChanges: {
static createChangesetFor(csId: string,
allChanges: {
modifiedObjects: OsmObject[],
newObjects: OsmObject[],
deletedObjects: OsmObject[]

View file

@ -207,27 +207,36 @@ export abstract class OsmObject {
return objects;
}
/**
* Uses the list of polygon features to determine if the given tags are a polygon or not.
* */
protected static isPolygon(tags: any): boolean {
for (const tagsKey in tags) {
if (!tags.hasOwnProperty(tagsKey)) {
continue
}
const polyGuide = OsmObject.polygonFeatures.get(tagsKey)
const polyGuide : { values: Set<string>; blacklist: boolean } = OsmObject.polygonFeatures.get(tagsKey)
if (polyGuide === undefined) {
continue
}
if ((polyGuide.values === null)) {
// We match all
// .values is null, thus merely _having_ this key is enough to be a polygon (or if blacklist, being a line)
return !polyGuide.blacklist
}
// is the key contained?
return polyGuide.values.has(tags[tagsKey])
// is the key contained? Then we have a match if the value is contained
const doesMatch = polyGuide.values.has(tags[tagsKey])
if(polyGuide.blacklist){
return !doesMatch
}
return doesMatch
}
return false;
}
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) {
for (const polygonFeature of (polygon_features["default"] ?? polygon_features)) {
const key = polygonFeature.key;
if (polygonFeature.polygon === "all") {
@ -381,7 +390,7 @@ export class OsmWay extends OsmObject {
}
if (element.nodes === undefined) {
console.log("PANIC")
console.error("PANIC: no nodes!")
}
for (const nodeId of element.nodes) {
@ -417,7 +426,9 @@ export class OsmWay extends OsmObject {
}
private isPolygon(): boolean {
if (this.coordinates[0] !== this.coordinates[this.coordinates.length - 1]) {
// Compare lat and lon seperately, as the coordinate array might not be a reference to the same object
if (this.coordinates[0][0] !== this.coordinates[this.coordinates.length - 1][0] ||
this.coordinates[0][1] !== this.coordinates[this.coordinates.length - 1][1] ) {
return false; // Not closed
}
return OsmObject.isPolygon(this.tags)

View file

@ -25,7 +25,7 @@ export default class FeaturePipelineState extends MapState {
constructor(layoutToUse: LayoutConfig) {
super(layoutToUse);
const clustering = layoutToUse.clustering
const clustering = layoutToUse?.clustering
this.featureAggregator = TileHierarchyAggregator.createHierarchy(this);
const clusterCounter = this.featureAggregator
const self = this;

View file

@ -117,10 +117,12 @@ export default class MapState extends UserRelatedState {
})
this.overlayToggles = this.layoutToUse.tileLayerSources.filter(c => c.name !== undefined).map(c => ({
this.overlayToggles = this.layoutToUse?.tileLayerSources
?.filter(c => c.name !== undefined)
?.map(c => ({
config: c,
isDisplayed: QueryParameters.GetBooleanQueryParameter("overlay-" + c.id, c.defaultState, "Wether or not the overlay " + c.id + " is shown")
}))
})) ?? []
this.filteredLayers = this.InitializeFilteredLayers()
@ -142,7 +144,7 @@ export default class MapState extends UserRelatedState {
initialized.add(overlayToggle.config)
}
for (const tileLayerSource of this.layoutToUse.tileLayerSources) {
for (const tileLayerSource of this.layoutToUse?.tileLayerSources ?? []) {
if (initialized.has(tileLayerSource)) {
continue
}
@ -153,28 +155,14 @@ export default class MapState extends UserRelatedState {
private lockBounds() {
const layout = this.layoutToUse;
if (layout.lockLocation) {
if (layout.lockLocation === true) {
const tile = Tiles.embedded_tile(
layout.startLat,
layout.startLon,
layout.startZoom - 1
);
const bounds = Tiles.tile_bounds(tile.z, tile.x, tile.y);
// We use the bounds to get a sense of distance for this zoom level
const latDiff = bounds[0][0] - bounds[1][0];
const lonDiff = bounds[0][1] - bounds[1][1];
layout.lockLocation = [
[layout.startLat - latDiff, layout.startLon - lonDiff],
[layout.startLat + latDiff, layout.startLon + lonDiff],
];
}
console.warn("Locking the bounds to ", layout.lockLocation);
this.mainMapObject.installBounds(
new BBox(layout.lockLocation),
this.featureSwitchIsTesting.data
)
if (!layout?.lockLocation) {
return;
}
console.warn("Locking the bounds to ", layout.lockLocation);
this.mainMapObject.installBounds(
new BBox(layout.lockLocation),
this.featureSwitchIsTesting.data
)
}
private initCurrentView() {
@ -364,8 +352,10 @@ export default class MapState extends UserRelatedState {
}
private InitializeFilteredLayers() {
const layoutToUse = this.layoutToUse;
if(layoutToUse === undefined){
return new UIEventSource<FilteredLayer[]>([])
}
const flayers: FilteredLayer[] = [];
for (const layer of layoutToUse.layers) {
let isDisplayed: UIEventSource<boolean>