forked from MapComplete/MapComplete
Stabilize personal theme, textfield now correctly appears if it is an option in the freeform too
This commit is contained in:
parent
79fc3f54e5
commit
416a76ae4f
22 changed files with 278 additions and 149 deletions
|
@ -13,7 +13,7 @@ export default class InstalledThemes {
|
|||
return installedThemes;
|
||||
}
|
||||
const invalidThemes = []
|
||||
for (var allPreferencesKey in allPreferences) {
|
||||
for (const allPreferencesKey in allPreferences) {
|
||||
const themename = allPreferencesKey.match(/^mapcomplete-installed-theme-(.*)-combined-length$/);
|
||||
if (themename && themename[1] !== "") {
|
||||
const customLayout = osmConnection.GetLongPreference("installed-theme-" + themename[1]);
|
||||
|
@ -37,7 +37,7 @@ export default class InstalledThemes {
|
|||
}
|
||||
|
||||
InstalledThemes.DeleteInvalid(osmConnection, invalidThemes);
|
||||
|
||||
|
||||
return installedThemes;
|
||||
|
||||
});
|
||||
|
|
|
@ -44,10 +44,15 @@ export default class SelectedFeatureHandler {
|
|||
// Feature already selected
|
||||
return;
|
||||
}
|
||||
|
||||
const hash = this._hash.data;
|
||||
if(hash === undefined || hash === "" || hash === "#"){
|
||||
return;
|
||||
}
|
||||
console.log("Selecting a feature from the hash...")
|
||||
for (const feature of features) {
|
||||
const id = feature.feature?.properties?.id;
|
||||
if(id === this._hash.data){
|
||||
if(id === hash){
|
||||
this._selectedFeature.setData(feature.feature);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -7,18 +7,18 @@ import Bounds from "../../Models/Bounds";
|
|||
import FeatureSource from "../FeatureSource/FeatureSource";
|
||||
|
||||
|
||||
export default class UpdateFromOverpass implements FeatureSource{
|
||||
export default class UpdateFromOverpass implements FeatureSource {
|
||||
|
||||
/**
|
||||
* The last loaded features of the geojson
|
||||
*/
|
||||
public readonly features: UIEventSource<{feature:any, freshness: Date}[]> = new UIEventSource<any[]>(undefined);
|
||||
public readonly features: UIEventSource<{ feature: any, freshness: Date }[]> = new UIEventSource<any[]>(undefined);
|
||||
|
||||
|
||||
public readonly sufficientlyZoomed: UIEventSource<boolean>;
|
||||
public readonly runningQuery: UIEventSource<boolean> = new UIEventSource<boolean>(false);
|
||||
public readonly retries: UIEventSource<number> = new UIEventSource<number>(0);
|
||||
|
||||
public readonly timeout: UIEventSource<number> = new UIEventSource<number>(0);
|
||||
private readonly retries: UIEventSource<number> = new UIEventSource<number>(0);
|
||||
/**
|
||||
* The previous bounds for which the query has been run at the given zoom level
|
||||
*
|
||||
|
@ -44,7 +44,7 @@ export default class UpdateFromOverpass implements FeatureSource{
|
|||
const self = this;
|
||||
|
||||
this.sufficientlyZoomed = location.map(location => {
|
||||
if(location?.zoom === undefined){
|
||||
if (location?.zoom === undefined) {
|
||||
return false;
|
||||
}
|
||||
let minzoom = Math.min(...layoutToUse.data.layers.map(layer => layer.minzoom ?? 18));
|
||||
|
@ -55,11 +55,11 @@ export default class UpdateFromOverpass implements FeatureSource{
|
|||
// This update removes all data on all layers -> erase the map on lower levels too
|
||||
this._previousBounds.set(i, []);
|
||||
}
|
||||
|
||||
|
||||
layoutToUse.addCallback(() => {
|
||||
self.update()
|
||||
});
|
||||
location.addCallbackAndRun(() => {
|
||||
location.addCallback(() => {
|
||||
self.update()
|
||||
});
|
||||
}
|
||||
|
@ -74,17 +74,17 @@ export default class UpdateFromOverpass implements FeatureSource{
|
|||
private GetFilter() {
|
||||
const filters: TagsFilter[] = [];
|
||||
for (const layer of this._layoutToUse.data.layers) {
|
||||
if(typeof(layer) === "string"){
|
||||
if (typeof (layer) === "string") {
|
||||
continue;
|
||||
}
|
||||
if (this._location.data.zoom < layer.minzoom) {
|
||||
continue;
|
||||
}
|
||||
if(layer.doNotDownload){
|
||||
if (layer.doNotDownload) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Check if data for this layer has already been loaded
|
||||
let previouslyLoaded = false;
|
||||
for (let z = layer.minzoom; z < 25 && !previouslyLoaded; z++) {
|
||||
|
@ -94,7 +94,7 @@ export default class UpdateFromOverpass implements FeatureSource{
|
|||
}
|
||||
for (const previousLoadedBound of previousLoadedBounds) {
|
||||
previouslyLoaded = previouslyLoaded || this.IsInBounds(previousLoadedBound);
|
||||
if(previouslyLoaded){
|
||||
if (previouslyLoaded) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -109,6 +109,7 @@ export default class UpdateFromOverpass implements FeatureSource{
|
|||
}
|
||||
return new Or(filters);
|
||||
}
|
||||
|
||||
private update(): void {
|
||||
const filter = this.GetFilter();
|
||||
if (filter === undefined) {
|
||||
|
@ -145,21 +146,36 @@ export default class UpdateFromOverpass implements FeatureSource{
|
|||
function (reason) {
|
||||
self.retries.data++;
|
||||
self.ForceRefresh();
|
||||
console.log(`QUERY FAILED (retrying in ${5 * self.retries.data} sec)`, undefined);
|
||||
self.timeout.setData(self.retries.data * 5);
|
||||
console.log(`QUERY FAILED (retrying in ${5 * self.retries.data} sec)`, reason);
|
||||
self.retries.ping();
|
||||
self.runningQuery.setData(false)
|
||||
window?.setTimeout(
|
||||
function () {
|
||||
self.update()
|
||||
}, self.retries.data * 5000
|
||||
)
|
||||
self.runningQuery.setData(false);
|
||||
|
||||
function countDown() {
|
||||
window?.setTimeout(
|
||||
function () {
|
||||
console.log("Countdown: ", self.timeout.data)
|
||||
if (self.timeout.data > 1) {
|
||||
self.timeout.setData(self.timeout.data - 1);
|
||||
window.setTimeout(
|
||||
countDown,
|
||||
1000
|
||||
)
|
||||
} else {
|
||||
self.timeout.setData(0);
|
||||
self.update()
|
||||
}
|
||||
}, 1000
|
||||
)
|
||||
}
|
||||
countDown();
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private IsInBounds(bounds: Bounds): boolean {
|
||||
if (this._previousBounds === undefined) {
|
||||
return false;
|
||||
|
@ -171,8 +187,6 @@ export default class UpdateFromOverpass implements FeatureSource{
|
|||
b.getEast() <= bounds.east &&
|
||||
b.getWest() >= bounds.west;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -12,7 +12,7 @@ export default class FeatureDuplicatorPerLayer implements FeatureSource {
|
|||
public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>;
|
||||
|
||||
|
||||
constructor(layers: { layerDef: LayerConfig }[], upstream: FeatureSource) {
|
||||
constructor(layers: UIEventSource<{ layerDef: LayerConfig }[]>, upstream: FeatureSource) {
|
||||
this.features = upstream.features.map(features => {
|
||||
const newFeatures: { feature: any, freshness: Date }[] = [];
|
||||
if(features === undefined){
|
||||
|
@ -29,7 +29,7 @@ export default class FeatureDuplicatorPerLayer implements FeatureSource {
|
|||
|
||||
|
||||
let foundALayer = false;
|
||||
for (const layer of layers) {
|
||||
for (const layer of layers.data) {
|
||||
if (layer.layerDef.overpassTags.matchesProperties(f.feature.properties)) {
|
||||
foundALayer = true;
|
||||
if (layer.layerDef.passAllFeatures) {
|
||||
|
|
|
@ -16,7 +16,7 @@ export default class FeaturePipeline implements FeatureSource {
|
|||
|
||||
public features: UIEventSource<{ feature: any; freshness: Date }[]>;
|
||||
|
||||
constructor(flayers: { isDisplayed: UIEventSource<boolean>, layerDef: LayerConfig }[],
|
||||
constructor(flayers: UIEventSource<{ isDisplayed: UIEventSource<boolean>, layerDef: LayerConfig }[]>,
|
||||
updater: FeatureSource,
|
||||
layout: UIEventSource<LayoutConfig>,
|
||||
newPoints: FeatureSource,
|
||||
|
|
|
@ -6,26 +6,27 @@ import Loc from "../../Models/Loc";
|
|||
export default class FilteringFeatureSource implements FeatureSource {
|
||||
public features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]);
|
||||
|
||||
constructor(layers: {
|
||||
constructor(layers: UIEventSource<{
|
||||
isDisplayed: UIEventSource<boolean>,
|
||||
layerDef: LayerConfig
|
||||
}[],
|
||||
}[]>,
|
||||
location: UIEventSource<Loc>,
|
||||
upstream: FeatureSource) {
|
||||
|
||||
const self = this;
|
||||
|
||||
const layerDict = {};
|
||||
for (const layer of layers) {
|
||||
layerDict[layer.layerDef.id] = layer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function update() {
|
||||
console.log("Updating the filtering layer")
|
||||
|
||||
const layerDict = {};
|
||||
for (const layer of layers.data) {
|
||||
layerDict[layer.layerDef.id] = layer;
|
||||
}
|
||||
|
||||
console.log("Updating the filtering layer, input ", upstream.features.data.length, "features")
|
||||
const features: { feature: any, freshness: Date }[] = upstream.features.data;
|
||||
|
||||
|
||||
|
||||
|
||||
const newFeatures = features.filter(f => {
|
||||
const layerId = f.feature._matching_layer_id;
|
||||
if (layerId !== undefined) {
|
||||
|
@ -42,7 +43,7 @@ export default class FilteringFeatureSource implements FeatureSource {
|
|||
}
|
||||
}
|
||||
// Does it match any other layer - e.g. because of a switch?
|
||||
for (const toCheck of layers) {
|
||||
for (const toCheck of layers.data) {
|
||||
if (!FilteringFeatureSource.showLayer(toCheck, location)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -53,6 +54,8 @@ export default class FilteringFeatureSource implements FeatureSource {
|
|||
return false;
|
||||
|
||||
});
|
||||
console.log("Updating the filtering layer, output ", newFeatures.length, "features")
|
||||
|
||||
self.features.setData(newFeatures);
|
||||
}
|
||||
|
||||
|
@ -63,20 +66,33 @@ export default class FilteringFeatureSource implements FeatureSource {
|
|||
location.map(l => {
|
||||
// We want something that is stable for the shown layers
|
||||
const displayedLayerIndexes = [];
|
||||
for (let i = 0; i < layers.length; i++) {
|
||||
if (l.zoom < layers[i].layerDef.minzoom) {
|
||||
for (let i = 0; i < layers.data.length; i++) {
|
||||
const layer = layers.data[i];
|
||||
if (l.zoom < layer.layerDef.minzoom) {
|
||||
continue;
|
||||
}
|
||||
if (!layers[i].isDisplayed.data) {
|
||||
if (!layer.isDisplayed.data) {
|
||||
continue;
|
||||
}
|
||||
displayedLayerIndexes.push(i);
|
||||
}
|
||||
return displayedLayerIndexes.join(",")
|
||||
}, layers.map(l => l.isDisplayed))
|
||||
.addCallback(() => {
|
||||
update();
|
||||
});
|
||||
}).addCallback(() => {
|
||||
update();
|
||||
});
|
||||
|
||||
layers.addCallback(update);
|
||||
|
||||
const registered = new Set<UIEventSource<boolean>>();
|
||||
layers.addCallback(layers => {
|
||||
for (const layer of layers) {
|
||||
if(registered.has(layer.isDisplayed)){
|
||||
continue;
|
||||
}
|
||||
registered.add(layer.isDisplayed);
|
||||
layer.isDisplayed.addCallback(update);
|
||||
}
|
||||
})
|
||||
|
||||
update();
|
||||
|
||||
|
|
|
@ -9,18 +9,21 @@ export default class LocalStorageSource implements FeatureSource {
|
|||
constructor(layout: UIEventSource<LayoutConfig>) {
|
||||
this.features = new UIEventSource<{ feature: any; freshness: Date }[]>([])
|
||||
const key = LocalStorageSaver.storageKey + layout.data.id
|
||||
try {
|
||||
const fromStorage = localStorage.getItem(key);
|
||||
if (fromStorage == null) {
|
||||
return;
|
||||
}
|
||||
const loaded = JSON.parse(fromStorage);
|
||||
this.features.setData(loaded);
|
||||
console.log("Loaded ",loaded.length," features from localstorage as cache")
|
||||
} catch (e) {
|
||||
console.log("Could not load features from localStorage:", e)
|
||||
localStorage.removeItem(key)
|
||||
}
|
||||
layout.addCallbackAndRun(_ => {
|
||||
|
||||
|
||||
try {
|
||||
const fromStorage = localStorage.getItem(key);
|
||||
if (fromStorage == null) {
|
||||
return;
|
||||
}
|
||||
const loaded = JSON.parse(fromStorage);
|
||||
this.features.setData(loaded);
|
||||
console.log("Loaded ", loaded.length, " features from localstorage as cache")
|
||||
} catch (e) {
|
||||
console.log("Could not load features from localStorage:", e)
|
||||
localStorage.removeItem(key)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -12,16 +12,12 @@ export default class NoOverlapSource {
|
|||
|
||||
features: UIEventSource<{ feature: any, freshness: Date }[]> = new UIEventSource<{ feature: any, freshness: Date }[]>([]);
|
||||
|
||||
constructor(layers: {
|
||||
constructor(layers: UIEventSource<{
|
||||
layerDef: LayerConfig
|
||||
}[],
|
||||
}[]>,
|
||||
upstream: FeatureSource) {
|
||||
const layerDict = {};
|
||||
let noOverlapRemoval = true;
|
||||
const layerIds = []
|
||||
for (const layer of layers) {
|
||||
layerDict[layer.layerDef.id] = layer;
|
||||
layerIds.push(layer.layerDef.id);
|
||||
for (const layer of layers.data) {
|
||||
if ((layer.layerDef.hideUnderlayingFeaturesMinPercentage ?? 0) !== 0) {
|
||||
noOverlapRemoval = false;
|
||||
}
|
||||
|
@ -31,13 +27,22 @@ export default class NoOverlapSource {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
this.features = upstream.features.map(
|
||||
features => {
|
||||
if (features === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const layerIds = []
|
||||
const layerDict = {};
|
||||
for (const layer of layers.data) {
|
||||
layerDict[layer.layerDef.id] = layer;
|
||||
layerIds.push(layer.layerDef.id);
|
||||
if ((layer.layerDef.hideUnderlayingFeaturesMinPercentage ?? 0) !== 0) {
|
||||
noOverlapRemoval = false;
|
||||
}
|
||||
}
|
||||
|
||||
// There is overlap removal active
|
||||
// We partition all the features with their respective layerIDs
|
||||
const partitions = {};
|
||||
|
@ -67,7 +72,7 @@ export default class NoOverlapSource {
|
|||
guardPartition.map(f => f.feature),
|
||||
percentage
|
||||
);
|
||||
if(!doesOverlap){
|
||||
if (!doesOverlap) {
|
||||
newPartition.push(mightBeDeleted);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,29 +6,26 @@ import {GeoOperations} from "../GeoOperations";
|
|||
export default class WayHandlingApplyingFeatureSource implements FeatureSource {
|
||||
features: UIEventSource<{ feature: any; freshness: Date }[]>;
|
||||
|
||||
constructor(layers: {
|
||||
constructor(layers: UIEventSource<{
|
||||
layerDef: LayerConfig
|
||||
}[],
|
||||
}[]>,
|
||||
upstream: FeatureSource) {
|
||||
const layerDict = {};
|
||||
let allDefaultWayHandling = true;
|
||||
for (const layer of layers) {
|
||||
layerDict[layer.layerDef.id] = layer;
|
||||
if (layer.layerDef.wayHandling !== LayerConfig.WAYHANDLING_DEFAULT) {
|
||||
allDefaultWayHandling = false;
|
||||
}
|
||||
}
|
||||
if (allDefaultWayHandling) {
|
||||
this.features = upstream.features;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.features = upstream.features.map(
|
||||
features => {
|
||||
if(features === undefined){
|
||||
return;
|
||||
}
|
||||
|
||||
const layerDict = {};
|
||||
let allDefaultWayHandling = true;
|
||||
for (const layer of layers.data) {
|
||||
layerDict[layer.layerDef.id] = layer;
|
||||
if (layer.layerDef.wayHandling !== LayerConfig.WAYHANDLING_DEFAULT) {
|
||||
allDefaultWayHandling = false;
|
||||
}
|
||||
}
|
||||
|
||||
const newFeatures: { feature: any, freshness: Date }[] = [];
|
||||
for (const f of features) {
|
||||
const feat = f.feature;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue