forked from MapComplete/MapComplete
Merge develop
This commit is contained in:
commit
897c59f97a
35 changed files with 1792 additions and 1172 deletions
|
@ -98,7 +98,7 @@ export default class FeaturePipeline {
|
|||
this.osmSourceZoomLevel = state.osmApiTileSize.data;
|
||||
const useOsmApi = state.locationControl.map(l => l.zoom > (state.overpassMaxZoom.data ?? 12))
|
||||
this.relationTracker = new RelationsTracker()
|
||||
|
||||
|
||||
state.changes.allChanges.addCallbackAndRun(allChanges => {
|
||||
allChanges.filter(ch => ch.id < 0 && ch.changes !== undefined)
|
||||
.map(ch => ch.changes)
|
||||
|
@ -203,7 +203,9 @@ export default class FeaturePipeline {
|
|||
neededTiles: neededTilesFromOsm,
|
||||
handleTile: tile => {
|
||||
new RegisteringAllFromFeatureSourceActor(tile)
|
||||
new SaveTileToLocalStorageActor(tile, tile.tileIndex)
|
||||
if (tile.layer.layerDef.maxAgeOfCache > 0) {
|
||||
new SaveTileToLocalStorageActor(tile, tile.tileIndex)
|
||||
}
|
||||
perLayerHierarchy.get(tile.layer.layerDef.id).registerTile(tile)
|
||||
tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile))
|
||||
|
||||
|
@ -211,7 +213,9 @@ export default class FeaturePipeline {
|
|||
state: state,
|
||||
markTileVisited: (tileId) =>
|
||||
state.filteredLayers.data.forEach(flayer => {
|
||||
SaveTileToLocalStorageActor.MarkVisited(flayer.layerDef.id, tileId, new Date())
|
||||
if (flayer.layerDef.maxAgeOfCache > 0) {
|
||||
SaveTileToLocalStorageActor.MarkVisited(flayer.layerDef.id, tileId, new Date())
|
||||
}
|
||||
self.freshnesses.get(flayer.layerDef.id).addTileLoad(tileId, new Date())
|
||||
})
|
||||
})
|
||||
|
@ -260,7 +264,7 @@ export default class FeaturePipeline {
|
|||
|
||||
|
||||
// Whenever fresh data comes in, we need to update the metatagging
|
||||
self.newDataLoadedSignal.stabilized(1000).addCallback(_ => {
|
||||
self.newDataLoadedSignal.stabilized(250).addCallback(src => {
|
||||
self.updateAllMetaTagging()
|
||||
})
|
||||
|
||||
|
@ -385,7 +389,7 @@ export default class FeaturePipeline {
|
|||
window.setTimeout(
|
||||
() => {
|
||||
const layerDef = src.layer.layerDef;
|
||||
MetaTagging.addMetatags(
|
||||
const somethingChanged = MetaTagging.addMetatags(
|
||||
src.features.data,
|
||||
{
|
||||
memberships: this.relationTracker,
|
||||
|
@ -406,9 +410,10 @@ export default class FeaturePipeline {
|
|||
|
||||
private updateAllMetaTagging() {
|
||||
const self = this;
|
||||
console.debug("Updating the meta tagging of all tiles as new data got loaded")
|
||||
this.perLayerHierarchy.forEach(hierarchy => {
|
||||
hierarchy.loadedTiles.forEach(src => {
|
||||
self.applyMetaTags(src)
|
||||
hierarchy.loadedTiles.forEach(tile => {
|
||||
self.applyMetaTags(tile)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import {UIEventSource} from "../../UIEventSource";
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig";
|
||||
import FilteredLayer from "../../../Models/FilteredLayer";
|
||||
import {FeatureSourceForLayer, Tiled} from "../FeatureSource";
|
||||
import Hash from "../../Web/Hash";
|
||||
|
@ -12,6 +11,8 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
|
|||
public readonly layer: FilteredLayer;
|
||||
public readonly tileIndex: number
|
||||
public readonly bbox: BBox
|
||||
private readonly upstream: FeatureSourceForLayer;
|
||||
private readonly state: { locationControl: UIEventSource<{ zoom: number }>; selectedElement: UIEventSource<any> };
|
||||
|
||||
constructor(
|
||||
state: {
|
||||
|
@ -21,70 +22,63 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
|
|||
tileIndex,
|
||||
upstream: FeatureSourceForLayer
|
||||
) {
|
||||
const self = this;
|
||||
this.name = "FilteringFeatureSource(" + upstream.name + ")"
|
||||
this.tileIndex = tileIndex
|
||||
this.bbox = BBox.fromTileIndex(tileIndex)
|
||||
this.upstream = upstream
|
||||
this.state = state
|
||||
|
||||
this.layer = upstream.layer;
|
||||
const layer = upstream.layer;
|
||||
|
||||
function update() {
|
||||
|
||||
const features: { feature: any; freshness: Date }[] = upstream.features.data;
|
||||
const newFeatures = features.filter((f) => {
|
||||
if (
|
||||
state.selectedElement.data?.id === f.feature.id ||
|
||||
f.feature.id === Hash.hash.data) {
|
||||
// This is the selected object - it gets a free pass even if zoom is not sufficient or it is filtered away
|
||||
return true;
|
||||
}
|
||||
|
||||
const isShown = layer.layerDef.isShown;
|
||||
const tags = f.feature.properties;
|
||||
if (isShown.IsKnown(tags)) {
|
||||
const result = layer.layerDef.isShown.GetRenderValue(
|
||||
f.feature.properties
|
||||
).txt;
|
||||
if (result !== "yes") {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const tagsFilter = layer.appliedFilters.data;
|
||||
for (const filter of tagsFilter ?? []) {
|
||||
const neededTags = filter.filter.options[filter.selected].osmTags
|
||||
if (!neededTags.matchesProperties(f.feature.properties)) {
|
||||
// Hidden by the filter on the layer itself - we want to hide it no matter wat
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
self.features.setData(newFeatures);
|
||||
}
|
||||
|
||||
|
||||
upstream.features.addCallback(() => {
|
||||
update();
|
||||
this. update();
|
||||
});
|
||||
|
||||
|
||||
layer.appliedFilters.addCallback(_ => {
|
||||
update()
|
||||
this.update()
|
||||
})
|
||||
|
||||
update();
|
||||
this.update();
|
||||
}
|
||||
public update() {
|
||||
|
||||
const layer = this.upstream.layer;
|
||||
const features: { feature: any; freshness: Date }[] = this.upstream.features.data;
|
||||
const newFeatures = features.filter((f) => {
|
||||
if (
|
||||
this.state.selectedElement.data?.id === f.feature.id ||
|
||||
f.feature.id === Hash.hash.data) {
|
||||
// This is the selected object - it gets a free pass even if zoom is not sufficient or it is filtered away
|
||||
return true;
|
||||
}
|
||||
|
||||
const isShown = layer.layerDef.isShown;
|
||||
const tags = f.feature.properties;
|
||||
if (isShown.IsKnown(tags)) {
|
||||
const result = layer.layerDef.isShown.GetRenderValue(
|
||||
f.feature.properties
|
||||
).txt;
|
||||
if (result !== "yes") {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const tagsFilter = layer.appliedFilters.data;
|
||||
for (const filter of tagsFilter ?? []) {
|
||||
const neededTags = filter.filter.options[filter.selected].osmTags
|
||||
if (!neededTags.matchesProperties(f.feature.properties)) {
|
||||
// Hidden by the filter on the layer itself - we want to hide it no matter wat
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
this.features.setData(newFeatures);
|
||||
}
|
||||
|
||||
private static showLayer(
|
||||
layer: {
|
||||
isDisplayed: UIEventSource<boolean>;
|
||||
layerDef: LayerConfig;
|
||||
}) {
|
||||
return layer.isDisplayed.data;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import {Utils} from "../../../Utils";
|
|||
import {FeatureSourceForLayer, Tiled} from "../FeatureSource";
|
||||
import {Tiles} from "../../../Models/TileRange";
|
||||
import {BBox} from "../../BBox";
|
||||
import {GeoOperations} from "../../GeoOperations";
|
||||
|
||||
|
||||
export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
|
||||
|
@ -14,7 +15,6 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
|
|||
public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>;
|
||||
public readonly name;
|
||||
public readonly isOsmCache: boolean
|
||||
private onFail: ((errorMsg: any, url: string) => void) = undefined;
|
||||
private readonly seenids: Set<string> = new Set<string>()
|
||||
public readonly layer: FilteredLayer;
|
||||
|
||||
|
@ -44,10 +44,20 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
|
|||
let url = flayer.layerDef.source.geojsonSource.replace("{layer}", flayer.layerDef.id);
|
||||
if (zxy !== undefined) {
|
||||
const [z, x, y] = zxy;
|
||||
let tile_bbox = BBox.fromTile(z, x, y)
|
||||
let bounds : { minLat: number, maxLat: number, minLon: number, maxLon: number } = tile_bbox
|
||||
if(this.layer.layerDef.source.mercatorCrs){
|
||||
bounds = tile_bbox.toMercator()
|
||||
}
|
||||
url = url
|
||||
.replace('{z}', "" + z)
|
||||
.replace('{x}', "" + x)
|
||||
.replace('{y}', "" + y)
|
||||
.replace('{y_min}',""+bounds.minLat)
|
||||
.replace('{y_max}',""+bounds.maxLat)
|
||||
.replace('{x_min}',""+bounds.minLon)
|
||||
.replace('{x_max}',""+bounds.maxLon)
|
||||
|
||||
this.tileIndex = Tiles.tile_index(z, x, y)
|
||||
this.bbox = BBox.fromTile(z, x, y)
|
||||
} else {
|
||||
|
@ -71,6 +81,10 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
|
|||
if(json.features === undefined || json.features === null){
|
||||
return;
|
||||
}
|
||||
|
||||
if(self.layer.layerDef.source.mercatorCrs){
|
||||
json = GeoOperations.GeoJsonToWGS84(json)
|
||||
}
|
||||
|
||||
const time = new Date();
|
||||
const newFeatures: { feature: any, freshness: Date } [] = []
|
||||
|
|
|
@ -31,7 +31,6 @@ export class NewGeometryFromChangesFeatureSource implements FeatureSource {
|
|||
// Already handled
|
||||
!seenChanges.has(ch)))
|
||||
.addCallbackAndRunD(changes => {
|
||||
|
||||
if (changes.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -20,24 +20,28 @@ export default class DynamicGeoJsonTileSource extends DynamicTileSource {
|
|||
if (source.geojsonSource === undefined) {
|
||||
throw "Invalid layer: geojsonSource expected"
|
||||
}
|
||||
|
||||
const whitelistUrl = source.geojsonSource
|
||||
.replace("{z}", ""+source.geojsonZoomLevel)
|
||||
.replace("{x}_{y}.geojson", "overview.json")
|
||||
.replace("{layer}",layer.layerDef.id)
|
||||
|
||||
|
||||
let whitelist = undefined
|
||||
Utils.downloadJson(whitelistUrl).then(
|
||||
json => {
|
||||
const data = new Map<number, Set<number>>();
|
||||
for (const x in json) {
|
||||
data.set(Number(x), new Set(json[x]))
|
||||
if (source.geojsonSource.indexOf("{x}_{y}.geojson") > 0) {
|
||||
|
||||
const whitelistUrl = source.geojsonSource
|
||||
.replace("{z}", "" + source.geojsonZoomLevel)
|
||||
.replace("{x}_{y}.geojson", "overview.json")
|
||||
.replace("{layer}", layer.layerDef.id)
|
||||
|
||||
Utils.downloadJson(whitelistUrl).then(
|
||||
json => {
|
||||
const data = new Map<number, Set<number>>();
|
||||
for (const x in json) {
|
||||
data.set(Number(x), new Set(json[x]))
|
||||
}
|
||||
console.log("The whitelist is", data, "based on ", json, "from", whitelistUrl)
|
||||
whitelist = data
|
||||
}
|
||||
whitelist = data
|
||||
}
|
||||
).catch(err => {
|
||||
console.warn("No whitelist found for ", layer.layerDef.id, err)
|
||||
})
|
||||
).catch(err => {
|
||||
console.warn("No whitelist found for ", layer.layerDef.id, err)
|
||||
})
|
||||
}
|
||||
|
||||
const seenIds = new Set<string>();
|
||||
const blackList = new UIEventSource(seenIds)
|
||||
|
@ -45,14 +49,14 @@ export default class DynamicGeoJsonTileSource extends DynamicTileSource {
|
|||
layer,
|
||||
source.geojsonZoomLevel,
|
||||
(zxy) => {
|
||||
if(whitelist !== undefined){
|
||||
if (whitelist !== undefined) {
|
||||
const isWhiteListed = whitelist.get(zxy[1])?.has(zxy[2])
|
||||
if(!isWhiteListed){
|
||||
if (!isWhiteListed) {
|
||||
console.log("Not downloading tile", ...zxy, "as it is not on the whitelist")
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const src = new GeoJsonSource(
|
||||
layer,
|
||||
zxy,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue