Merge master
This commit is contained in:
commit
5e5528eb7d
13 changed files with 160 additions and 126 deletions
|
@ -489,7 +489,7 @@ export class InitUiElements {
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}, [State.state.currentBounds]
|
}, [State.state.currentBounds, source.layer.isDisplayed]
|
||||||
)
|
)
|
||||||
|
|
||||||
new ShowDataLayer(
|
new ShowDataLayer(
|
||||||
|
|
|
@ -38,8 +38,7 @@ export default class OverpassFeatureSource implements FeatureSource {
|
||||||
readonly currentBounds: UIEventSource<BBox>
|
readonly currentBounds: UIEventSource<BBox>
|
||||||
}
|
}
|
||||||
private readonly _isActive: UIEventSource<boolean>;
|
private readonly _isActive: UIEventSource<boolean>;
|
||||||
private readonly onBboxLoaded: (bbox: BBox, date: Date, layers: LayerConfig[]) => void;
|
private readonly onBboxLoaded: (bbox: BBox, date: Date, layers: LayerConfig[], zoomlevel: number) => void;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
state: {
|
state: {
|
||||||
readonly locationControl: UIEventSource<Loc>,
|
readonly locationControl: UIEventSource<Loc>,
|
||||||
|
@ -49,10 +48,11 @@ export default class OverpassFeatureSource implements FeatureSource {
|
||||||
readonly overpassMaxZoom: UIEventSource<number>,
|
readonly overpassMaxZoom: UIEventSource<number>,
|
||||||
readonly currentBounds: UIEventSource<BBox>
|
readonly currentBounds: UIEventSource<BBox>
|
||||||
},
|
},
|
||||||
options?: {
|
options: {
|
||||||
|
padToTiles: UIEventSource<number>,
|
||||||
isActive?: UIEventSource<boolean>,
|
isActive?: UIEventSource<boolean>,
|
||||||
relationTracker: RelationsTracker,
|
relationTracker: RelationsTracker,
|
||||||
onBboxLoaded?: (bbox: BBox, date: Date, layers: LayerConfig[]) => void
|
onBboxLoaded?: (bbox: BBox, date: Date, layers: LayerConfig[], zoomlevel: number) => void
|
||||||
}) {
|
}) {
|
||||||
|
|
||||||
this.state = state
|
this.state = state
|
||||||
|
@ -61,7 +61,7 @@ export default class OverpassFeatureSource implements FeatureSource {
|
||||||
this.relationsTracker = options.relationTracker
|
this.relationsTracker = options.relationTracker
|
||||||
const self = this;
|
const self = this;
|
||||||
state.currentBounds.addCallback(_ => {
|
state.currentBounds.addCallback(_ => {
|
||||||
self.update()
|
self.update(options.padToTiles.data)
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -84,21 +84,21 @@ export default class OverpassFeatureSource implements FeatureSource {
|
||||||
return new Overpass(new Or(filters), extraScripts, interpreterUrl, this.state.overpassTimeout, this.relationsTracker);
|
return new Overpass(new Or(filters), extraScripts, interpreterUrl, this.state.overpassTimeout, this.relationsTracker);
|
||||||
}
|
}
|
||||||
|
|
||||||
private update() {
|
private update(paddedZoomLevel: number) {
|
||||||
if (!this._isActive.data) {
|
if (!this._isActive.data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const self = this;
|
const self = this;
|
||||||
this.updateAsync().then(bboxDate => {
|
this.updateAsync(paddedZoomLevel).then(bboxDate => {
|
||||||
if(bboxDate === undefined || self.onBboxLoaded === undefined){
|
if(bboxDate === undefined || self.onBboxLoaded === undefined){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const [bbox, date, layers] = bboxDate
|
const [bbox, date, layers] = bboxDate
|
||||||
self.onBboxLoaded(bbox, date, layers)
|
self.onBboxLoaded(bbox, date, layers, paddedZoomLevel)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateAsync(): Promise<[BBox, Date, LayerConfig[]]> {
|
private async updateAsync(padToZoomLevel: number): Promise<[BBox, Date, LayerConfig[]]> {
|
||||||
if (this.runningQuery.data) {
|
if (this.runningQuery.data) {
|
||||||
console.log("Still running a query, not updating");
|
console.log("Still running a query, not updating");
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -109,7 +109,7 @@ export default class OverpassFeatureSource implements FeatureSource {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bounds = this.state.currentBounds.data?.pad(this.state.layoutToUse.widenFactor)?.expandToTileBounds(14);
|
const bounds = this.state.currentBounds.data?.pad(this.state.layoutToUse.widenFactor)?.expandToTileBounds(padToZoomLevel);
|
||||||
|
|
||||||
if (bounds === undefined) {
|
if (bounds === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
|
@ -116,16 +116,15 @@ export class BBox {
|
||||||
return this.minLat
|
return this.minLat
|
||||||
}
|
}
|
||||||
|
|
||||||
pad(factor: number): BBox {
|
pad(factor: number, maxIncrease = 2): BBox {
|
||||||
const latDiff = this.maxLat - this.minLat
|
|
||||||
const lat = (this.maxLat + this.minLat) / 2
|
const latDiff = Math.min(maxIncrease / 2, Math.abs(this.maxLat - this.minLat) * factor)
|
||||||
const lonDiff = this.maxLon - this.minLon
|
const lonDiff =Math.min(maxIncrease / 2, Math.abs(this.maxLon - this.minLon) * factor)
|
||||||
const lon = (this.maxLon + this.minLon) / 2
|
|
||||||
return new BBox([[
|
return new BBox([[
|
||||||
lon - lonDiff * factor,
|
this.minLon - lonDiff,
|
||||||
lat - latDiff * factor
|
this.minLat - latDiff
|
||||||
], [lon + lonDiff * factor,
|
], [this.maxLon + lonDiff,
|
||||||
lat + latDiff * factor]])
|
this.maxLat + latDiff]])
|
||||||
}
|
}
|
||||||
|
|
||||||
toLeaflet() {
|
toLeaflet() {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import SimpleMetaTagger from "../../SimpleMetaTagger";
|
||||||
|
|
||||||
export default class SaveTileToLocalStorageActor {
|
export default class SaveTileToLocalStorageActor {
|
||||||
public static readonly storageKey: string = "cached-features";
|
public static readonly storageKey: string = "cached-features";
|
||||||
public static readonly formatVersion: string = "1"
|
public static readonly formatVersion: string = "2"
|
||||||
|
|
||||||
constructor(source: FeatureSourceForLayer, tileIndex: number) {
|
constructor(source: FeatureSourceForLayer, tileIndex: number) {
|
||||||
|
|
||||||
|
@ -37,6 +37,5 @@ export default class SaveTileToLocalStorageActor {
|
||||||
}catch(e){
|
}catch(e){
|
||||||
console.error("Could not mark tile ", key, "as visited")
|
console.error("Could not mark tile ", key, "as visited")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -58,7 +58,7 @@ export default class FeaturePipeline {
|
||||||
private readonly freshnesses = new Map<string, TileFreshnessCalculator>();
|
private readonly freshnesses = new Map<string, TileFreshnessCalculator>();
|
||||||
|
|
||||||
private readonly oldestAllowedDate: Date = new Date(new Date().getTime() - 60 * 60 * 24 * 30 * 1000);
|
private readonly oldestAllowedDate: Date = new Date(new Date().getTime() - 60 * 60 * 24 * 30 * 1000);
|
||||||
private readonly osmSourceZoomLevel = 14
|
private readonly osmSourceZoomLevel = 15
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
handleFeatureSource: (source: FeatureSourceForLayer & Tiled) => void,
|
handleFeatureSource: (source: FeatureSourceForLayer & Tiled) => void,
|
||||||
|
@ -147,7 +147,7 @@ export default class FeaturePipeline {
|
||||||
// We split them up into tiles anyway as it is an OSM source
|
// We split them up into tiles anyway as it is an OSM source
|
||||||
TiledFeatureSource.createHierarchy(src, {
|
TiledFeatureSource.createHierarchy(src, {
|
||||||
layer: src.layer,
|
layer: src.layer,
|
||||||
minZoomLevel: 14,
|
minZoomLevel: this.osmSourceZoomLevel,
|
||||||
dontEnforceMinZoom: true,
|
dontEnforceMinZoom: true,
|
||||||
registerTile: (tile) => {
|
registerTile: (tile) => {
|
||||||
new RegisteringAllFromFeatureSourceActor(tile)
|
new RegisteringAllFromFeatureSourceActor(tile)
|
||||||
|
@ -155,7 +155,7 @@ export default class FeaturePipeline {
|
||||||
tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile))
|
tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}else{
|
} else {
|
||||||
new RegisteringAllFromFeatureSourceActor(src)
|
new RegisteringAllFromFeatureSourceActor(src)
|
||||||
perLayerHierarchy.get(id).registerTile(src)
|
perLayerHierarchy.get(id).registerTile(src)
|
||||||
src.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(src))
|
src.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(src))
|
||||||
|
@ -200,7 +200,7 @@ export default class FeaturePipeline {
|
||||||
new PerLayerFeatureSourceSplitter(state.filteredLayers,
|
new PerLayerFeatureSourceSplitter(state.filteredLayers,
|
||||||
(source) => TiledFeatureSource.createHierarchy(source, {
|
(source) => TiledFeatureSource.createHierarchy(source, {
|
||||||
layer: source.layer,
|
layer: source.layer,
|
||||||
minZoomLevel: 14,
|
minZoomLevel: source.layer.layerDef.minzoom,
|
||||||
dontEnforceMinZoom: true,
|
dontEnforceMinZoom: true,
|
||||||
maxFeatureCount: state.layoutToUse.clustering.minNeededElements,
|
maxFeatureCount: state.layoutToUse.clustering.minNeededElements,
|
||||||
maxZoomLevel: state.layoutToUse.clustering.maxZoom,
|
maxZoomLevel: state.layoutToUse.clustering.maxZoom,
|
||||||
|
@ -235,7 +235,7 @@ export default class FeaturePipeline {
|
||||||
|
|
||||||
|
|
||||||
// Whenever fresh data comes in, we need to update the metatagging
|
// Whenever fresh data comes in, we need to update the metatagging
|
||||||
self.newDataLoadedSignal.stabilized(1000).addCallback(src => {
|
self.newDataLoadedSignal.stabilized(1000).addCallback(_ => {
|
||||||
self.updateAllMetaTagging()
|
self.updateAllMetaTagging()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -276,15 +276,15 @@ export default class FeaturePipeline {
|
||||||
const self = this
|
const self = this
|
||||||
return this.state.currentBounds.map(bbox => {
|
return this.state.currentBounds.map(bbox => {
|
||||||
if (bbox === undefined) {
|
if (bbox === undefined) {
|
||||||
return
|
return undefined
|
||||||
}
|
}
|
||||||
if (!isSufficientlyZoomed.data) {
|
if (!isSufficientlyZoomed.data) {
|
||||||
return;
|
return undefined;
|
||||||
}
|
}
|
||||||
const osmSourceZoomLevel = self.osmSourceZoomLevel
|
const osmSourceZoomLevel = self.osmSourceZoomLevel
|
||||||
const range = bbox.containingTileRange(osmSourceZoomLevel)
|
const range = bbox.containingTileRange(osmSourceZoomLevel)
|
||||||
const tileIndexes = []
|
const tileIndexes = []
|
||||||
if (range.total > 100) {
|
if (range.total >= 100) {
|
||||||
// Too much tiles!
|
// Too much tiles!
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@ -294,7 +294,7 @@ export default class FeaturePipeline {
|
||||||
if (oldestDate !== undefined && oldestDate > this.oldestAllowedDate) {
|
if (oldestDate !== undefined && oldestDate > this.oldestAllowedDate) {
|
||||||
console.debug("Skipping tile", osmSourceZoomLevel, x, y, "as a decently fresh one is available")
|
console.debug("Skipping tile", osmSourceZoomLevel, x, y, "as a decently fresh one is available")
|
||||||
// The cached tiles contain decently fresh data
|
// The cached tiles contain decently fresh data
|
||||||
return;
|
return undefined;
|
||||||
}
|
}
|
||||||
tileIndexes.push(i)
|
tileIndexes.push(i)
|
||||||
})
|
})
|
||||||
|
@ -327,28 +327,30 @@ export default class FeaturePipeline {
|
||||||
}
|
}
|
||||||
|
|
||||||
const range = bbox.containingTileRange(zoom)
|
const range = bbox.containingTileRange(zoom)
|
||||||
if (range.total > 100) {
|
if (range.total >= 5000) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const self = this;
|
const self = this;
|
||||||
const allFreshnesses = Tiles.MapRange(range, (x, y) => self.freshnessForVisibleLayers(zoom, x, y))
|
const allFreshnesses = Tiles.MapRange(range, (x, y) => self.freshnessForVisibleLayers(zoom, x, y))
|
||||||
return allFreshnesses.some(freshness => freshness === undefined || freshness < this.oldestAllowedDate)
|
return allFreshnesses.some(freshness => freshness === undefined || freshness < this.oldestAllowedDate)
|
||||||
|
|
||||||
}, [state.locationControl])
|
}, [state.locationControl])
|
||||||
|
|
||||||
const self = this;
|
const self = this;
|
||||||
const updater = new OverpassFeatureSource(state,
|
const updater = new OverpassFeatureSource(state,
|
||||||
{
|
{
|
||||||
|
padToTiles: state.locationControl.map(l => Math.min(15, l.zoom + 1)),
|
||||||
relationTracker: this.relationTracker,
|
relationTracker: this.relationTracker,
|
||||||
isActive: useOsmApi.map(b => !b && overpassIsActive.data, [overpassIsActive]),
|
isActive: useOsmApi.map(b => !b && overpassIsActive.data, [overpassIsActive]),
|
||||||
onBboxLoaded: ((bbox, date, downloadedLayers) => {
|
onBboxLoaded: (bbox, date, downloadedLayers, paddedToZoomLevel) => {
|
||||||
Tiles.MapRange(bbox.containingTileRange(self.osmSourceZoomLevel), (x, y) => {
|
Tiles.MapRange(bbox.containingTileRange(paddedToZoomLevel), (x, y) => {
|
||||||
|
const tileIndex = Tiles.tile_index(paddedToZoomLevel, x, y)
|
||||||
downloadedLayers.forEach(layer => {
|
downloadedLayers.forEach(layer => {
|
||||||
SaveTileToLocalStorageActor.MarkVisited(layer.id, Tiles.tile_index(this.osmSourceZoomLevel, x, y), date)
|
self.freshnesses.get(layer.id).addTileLoad(tileIndex, date)
|
||||||
|
SaveTileToLocalStorageActor.MarkVisited(layer.id, tileIndex, date)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
import FilteredLayer from "../../../Models/FilteredLayer";
|
import FilteredLayer from "../../../Models/FilteredLayer";
|
||||||
import {FeatureSourceForLayer, Tiled} from "../FeatureSource";
|
import {FeatureSourceForLayer, Tiled} from "../FeatureSource";
|
||||||
import {UIEventSource} from "../../UIEventSource";
|
import {UIEventSource} from "../../UIEventSource";
|
||||||
import Loc from "../../../Models/Loc";
|
|
||||||
import TileHierarchy from "./TileHierarchy";
|
import TileHierarchy from "./TileHierarchy";
|
||||||
import SaveTileToLocalStorageActor from "../Actors/SaveTileToLocalStorageActor";
|
import SaveTileToLocalStorageActor from "../Actors/SaveTileToLocalStorageActor";
|
||||||
import {Tiles} from "../../../Models/TileRange";
|
import {Tiles} from "../../../Models/TileRange";
|
||||||
import {BBox} from "../../BBox";
|
import {BBox} from "../../BBox";
|
||||||
|
|
||||||
export default class TiledFromLocalStorageSource implements TileHierarchy<FeatureSourceForLayer & Tiled> {
|
export default class TiledFromLocalStorageSource implements TileHierarchy<FeatureSourceForLayer & Tiled> {
|
||||||
public loadedTiles: Map<number, FeatureSourceForLayer & Tiled> = new Map<number, FeatureSourceForLayer & Tiled>();
|
public readonly loadedTiles: Map<number, FeatureSourceForLayer & Tiled> = new Map<number, FeatureSourceForLayer & Tiled>();
|
||||||
|
private readonly layer: FilteredLayer;
|
||||||
|
private readonly handleFeatureSource: (src: FeatureSourceForLayer & Tiled, index: number) => void;
|
||||||
|
private readonly undefinedTiles: Set<number>;
|
||||||
|
|
||||||
public static GetFreshnesses(layerId: string): Map<number, Date> {
|
public static GetFreshnesses(layerId: string): Map<number, Date> {
|
||||||
const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layerId + "-"
|
const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layerId + "-"
|
||||||
|
@ -29,14 +31,15 @@ export default class TiledFromLocalStorageSource implements TileHierarchy<Featur
|
||||||
constructor(layer: FilteredLayer,
|
constructor(layer: FilteredLayer,
|
||||||
handleFeatureSource: (src: FeatureSourceForLayer & Tiled, index: number) => void,
|
handleFeatureSource: (src: FeatureSourceForLayer & Tiled, index: number) => void,
|
||||||
state: {
|
state: {
|
||||||
locationControl: UIEventSource<Loc>
|
currentBounds: UIEventSource<BBox>
|
||||||
leafletMap: any
|
|
||||||
}) {
|
}) {
|
||||||
|
this.layer = layer;
|
||||||
|
this.handleFeatureSource = handleFeatureSource;
|
||||||
|
|
||||||
const undefinedTiles = new Set<number>()
|
|
||||||
|
this.undefinedTiles = new Set<number>()
|
||||||
const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layer.layerDef.id + "-"
|
const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layer.layerDef.id + "-"
|
||||||
// @ts-ignore
|
const knownTiles: number[] = Object.keys(localStorage)
|
||||||
const indexes: number[] = Object.keys(localStorage)
|
|
||||||
.filter(key => {
|
.filter(key => {
|
||||||
return key.startsWith(prefix) && !key.endsWith("-time") && !key.endsWith("-format");
|
return key.startsWith(prefix) && !key.endsWith("-time") && !key.endsWith("-format");
|
||||||
})
|
})
|
||||||
|
@ -45,8 +48,8 @@ export default class TiledFromLocalStorageSource implements TileHierarchy<Featur
|
||||||
})
|
})
|
||||||
.filter(i => !isNaN(i))
|
.filter(i => !isNaN(i))
|
||||||
|
|
||||||
console.debug("Layer", layer.layerDef.id, "has following tiles in available in localstorage", indexes.map(i => Tiles.tile_from_index(i).join("/")).join(", "))
|
console.debug("Layer", layer.layerDef.id, "has following tiles in available in localstorage", knownTiles.map(i => Tiles.tile_from_index(i).join("/")).join(", "))
|
||||||
for (const index of indexes) {
|
for (const index of knownTiles) {
|
||||||
|
|
||||||
const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layer.layerDef.id + "-" + index;
|
const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layer.layerDef.id + "-" + index;
|
||||||
const version = localStorage.getItem(prefix + "-format")
|
const version = localStorage.getItem(prefix + "-format")
|
||||||
|
@ -55,78 +58,54 @@ export default class TiledFromLocalStorageSource implements TileHierarchy<Featur
|
||||||
localStorage.removeItem(prefix)
|
localStorage.removeItem(prefix)
|
||||||
localStorage.removeItem(prefix+"-time")
|
localStorage.removeItem(prefix+"-time")
|
||||||
localStorage.removeItem(prefix+"-format")
|
localStorage.removeItem(prefix+"-format")
|
||||||
undefinedTiles.add(index)
|
this. undefinedTiles.add(index)
|
||||||
console.log("Dropped old format tile", prefix)
|
console.log("Dropped old format tile", prefix)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const zLevels = indexes.map(i => i % 100)
|
const self = this
|
||||||
const indexesSet = new Set(indexes)
|
state.currentBounds.map(bounds => {
|
||||||
const maxZoom = Math.max(...zLevels)
|
|
||||||
const minZoom = Math.min(...zLevels)
|
|
||||||
const self = this;
|
|
||||||
|
|
||||||
const neededTiles = state.locationControl.map(
|
if(bounds === undefined){
|
||||||
location => {
|
return;
|
||||||
if (!layer.isDisplayed.data) {
|
|
||||||
// No need to download! - the layer is disabled
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (location.zoom < layer.layerDef.minzoom) {
|
|
||||||
// No need to download! - the layer is disabled
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Yup, this is cheating to just get the bounds here
|
|
||||||
const bounds = state.leafletMap.data?.getBounds()
|
|
||||||
if (bounds === undefined) {
|
|
||||||
// We'll retry later
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
const needed = []
|
|
||||||
for (let z = minZoom; z <= maxZoom; z++) {
|
|
||||||
|
|
||||||
const tileRange = Tiles.TileRangeBetween(z, bounds.getNorth(), bounds.getEast(), bounds.getSouth(), bounds.getWest())
|
|
||||||
const neededZ = Tiles.MapRange(tileRange, (x, y) => Tiles.tile_index(z, x, y))
|
|
||||||
.filter(i => !self.loadedTiles.has(i) && !undefinedTiles.has(i) && indexesSet.has(i))
|
|
||||||
needed.push(...neededZ)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needed.length === 0) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
return needed
|
|
||||||
}
|
}
|
||||||
, [layer.isDisplayed, state.leafletMap]).stabilized(50);
|
for (const knownTile of knownTiles) {
|
||||||
|
|
||||||
neededTiles.addCallbackAndRunD(neededIndexes => {
|
if(this.loadedTiles.has(knownTile)){
|
||||||
for (const neededIndex of neededIndexes) {
|
continue;
|
||||||
// We load the features from localStorage
|
|
||||||
try {
|
|
||||||
const key = SaveTileToLocalStorageActor.storageKey + "-" + layer.layerDef.id + "-" + neededIndex
|
|
||||||
const data = localStorage.getItem(key)
|
|
||||||
const features = JSON.parse(data)
|
|
||||||
const src = {
|
|
||||||
layer: layer,
|
|
||||||
features: new UIEventSource<{ feature: any; freshness: Date }[]>(features),
|
|
||||||
name: "FromLocalStorage(" + key + ")",
|
|
||||||
tileIndex: neededIndex,
|
|
||||||
bbox: BBox.fromTileIndex(neededIndex)
|
|
||||||
}
|
|
||||||
handleFeatureSource(src, neededIndex)
|
|
||||||
self.loadedTiles.set(neededIndex, src)
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Could not load data tile from local storage due to", e)
|
|
||||||
undefinedTiles.add(neededIndex)
|
|
||||||
}
|
}
|
||||||
|
if(this.undefinedTiles.has(knownTile)){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!bounds.overlapsWith(BBox.fromTileIndex(knownTile))){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
self.loadTile(knownTile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private loadTile( neededIndex: number){
|
||||||
|
try {
|
||||||
|
const key = SaveTileToLocalStorageActor.storageKey + "-" + this.layer.layerDef.id + "-" + neededIndex
|
||||||
|
const data = localStorage.getItem(key)
|
||||||
|
const features = JSON.parse(data)
|
||||||
|
const src = {
|
||||||
|
layer: this.layer,
|
||||||
|
features: new UIEventSource<{ feature: any; freshness: Date }[]>(features),
|
||||||
|
name: "FromLocalStorage(" + key + ")",
|
||||||
|
tileIndex: neededIndex,
|
||||||
|
bbox: BBox.fromTileIndex(neededIndex)
|
||||||
|
}
|
||||||
|
this.handleFeatureSource(src, neededIndex)
|
||||||
|
this.loadedTiles.set(neededIndex, src)
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Could not load data tile from local storage due to", e)
|
||||||
|
this.undefinedTiles.add(neededIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@ import {Utils} from "../Utils";
|
||||||
|
|
||||||
export default class Constants {
|
export default class Constants {
|
||||||
|
|
||||||
public static vNumber = "0.10.3";
|
public static vNumber = "0.10.5";
|
||||||
public static ImgurApiKey = '7070e7167f0a25a'
|
public static ImgurApiKey = '7070e7167f0a25a'
|
||||||
public static readonly mapillary_client_token_v3 = 'TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2'
|
public static readonly mapillary_client_token_v3 = 'TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2'
|
||||||
public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85"
|
public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85"
|
||||||
|
|
|
@ -92,7 +92,7 @@ export default class LayoutConfig {
|
||||||
throw "Widenfactor too small"
|
throw "Widenfactor too small"
|
||||||
}else{
|
}else{
|
||||||
// Unofficial themes get away with this
|
// Unofficial themes get away with this
|
||||||
console.warn("Detected a very small widenfactor, bumping this above 1.")
|
console.warn("Detected a very small widenfactor for theme ", this.id ,", bumping this above 1.")
|
||||||
json.widenFactor = json.widenFactor + 1
|
json.widenFactor = json.widenFactor + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ export class Tiles {
|
||||||
public static MapRange<T>(tileRange: TileRange, f: (x: number, y: number) => T): T[] {
|
public static MapRange<T>(tileRange: TileRange, f: (x: number, y: number) => T): T[] {
|
||||||
const result: T[] = []
|
const result: T[] = []
|
||||||
const total = tileRange.total
|
const total = tileRange.total
|
||||||
if(total > 5000){
|
if(total > 100000){
|
||||||
throw "Tilerange too big"
|
throw "Tilerange too big"
|
||||||
}
|
}
|
||||||
for (let x = tileRange.xstart; x <= tileRange.xend; x++) {
|
for (let x = tileRange.xstart; x <= tileRange.xend; x++) {
|
||||||
|
|
|
@ -50,13 +50,11 @@ export default class ThemeIntroductionPanel extends Combine {
|
||||||
)
|
)
|
||||||
|
|
||||||
super([
|
super([
|
||||||
layout.description.Clone(),
|
layout.description.Clone().SetClass("blcok mb-4"),
|
||||||
"<br/><br/>",
|
|
||||||
toTheMap,
|
toTheMap,
|
||||||
loginStatus,
|
loginStatus.SetClass("block"),
|
||||||
layout.descriptionTail?.Clone(),
|
layout.descriptionTail?.Clone().SetClass("block mt-4"),
|
||||||
"<br/>",
|
languagePicker.SetClass("block mt-4"),
|
||||||
languagePicker,
|
|
||||||
...layout.CustomCodeSnippets()
|
...layout.CustomCodeSnippets()
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,10 @@
|
||||||
"nl": "Een kaart om toeristisch relevante info op aan te duiden"
|
"nl": "Een kaart om toeristisch relevante info op aan te duiden"
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"nl": "Op deze kaart kan je info zien die relevant is voor toerisme, zoals:<br/><ul><li>Eetgelegenheden</li><li>Cafés en bars</li><li>(Fiets)oplaadpunten</li><li>Fietspompen, fietserverhuur en fietswinkels</li><li>Uitkijktorens</li><li>...</li></ul> Zie je fouten op de kaart? Dan kan je zelf makkelijk aanpasingen maken, die zichtbaar zijn voor iedereen. Hiervoor dien je een gratis OpenStreetMap account voor te maken.<br/><br/>Met de steun van Toerisme Vlaanderen<img src='./assets/themes/toerisme_vlaanderen/logo.png' />"
|
"nl": "Op deze kaart kan je info zien die relevant is voor toerisme, zoals:<br/><ul><li>Eetgelegenheden</li><li>Cafés en bars</li><li>(Fiets)oplaadpunten</li><li>Fietspompen, fietserverhuur en fietswinkels</li><li>Uitkijktorens</li><li>...</li></ul> Zie je fouten op de kaart? Dan kan je zelf makkelijk aanpasingen maken, die zichtbaar zijn voor iedereen. Hiervoor dien je een gratis OpenStreetMap account voor te maken."
|
||||||
|
},
|
||||||
|
"descriptionTail": {
|
||||||
|
"nl": "Met de steun van Toerisme Vlaanderen<img style='height:5rem; width: auto;' src='./assets/themes/toerisme_vlaanderen/logo.png' />"
|
||||||
},
|
},
|
||||||
"icon": "./assets/svg/star.svg",
|
"icon": "./assets/svg/star.svg",
|
||||||
"startZoom": 8,
|
"startZoom": 8,
|
||||||
|
@ -28,16 +31,38 @@
|
||||||
"cafe_pub"
|
"cafe_pub"
|
||||||
],
|
],
|
||||||
"override": {
|
"override": {
|
||||||
"minzoom": 16
|
"minzoom": 17
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"charging_station",
|
{
|
||||||
"toilet",
|
"builtin": [
|
||||||
"bench",
|
"bench",
|
||||||
"waste_basket",
|
"picnic_table",
|
||||||
"bike_repair_station",
|
"waste_basket"
|
||||||
"binocular",
|
],
|
||||||
"observation_tower"
|
"override": {
|
||||||
|
"minzoom": 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"builtin": [
|
||||||
|
"charging_station",
|
||||||
|
"toilet",
|
||||||
|
"bike_repair_station"
|
||||||
|
],
|
||||||
|
"override": {
|
||||||
|
"minzoom": 14
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"builtin": [
|
||||||
|
"binocular",
|
||||||
|
"observation_tower"
|
||||||
|
],
|
||||||
|
"override": {
|
||||||
|
"minzoom": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"hideFromOverview": true
|
"hideFromOverview": true
|
||||||
}
|
}
|
|
@ -20,5 +20,20 @@
|
||||||
"uploadingMultiple": "A enviar {count} imagens…",
|
"uploadingMultiple": "A enviar {count} imagens…",
|
||||||
"uploadingPicture": "A enviar a sua imagem…",
|
"uploadingPicture": "A enviar a sua imagem…",
|
||||||
"addPicture": "Adicionar imagem"
|
"addPicture": "Adicionar imagem"
|
||||||
|
},
|
||||||
|
"index": {
|
||||||
|
"#": "Estes textos são mostrados acima dos botões do tema quando nenhum tema é carregado",
|
||||||
|
"title": "Bem-vindo(a) ao MapComplete",
|
||||||
|
"intro": "O MapComplete é um visualizador e editor do OpenStreetMap, que mostra informações sobre um tema específico.",
|
||||||
|
"pickTheme": "Escolha um tema abaixo para começar."
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"reasons": {
|
||||||
|
"notFound": "Não foi possível encontrar este elemento"
|
||||||
|
},
|
||||||
|
"explanations": {
|
||||||
|
"selectReason": "Por favor, selecione a razão porque este elemento deve ser eliminado",
|
||||||
|
"hardDelete": "Este ponto será eliminado no OpenStreetMap. Pode ser recuperado por um contribuidor com experiência"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,23 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"question": "Este lugar é acessível a utilizadores de cadeiras de rodas?"
|
"question": "Este lugar é acessível a utilizadores de cadeiras de rodas?"
|
||||||
|
},
|
||||||
|
"dog-access": {
|
||||||
|
"mappings": {
|
||||||
|
"0": {
|
||||||
|
"then": "Os cães são permitidos"
|
||||||
|
},
|
||||||
|
"1": {
|
||||||
|
"then": "Os cães <b>não</b> são permitidos"
|
||||||
|
},
|
||||||
|
"2": {
|
||||||
|
"then": "Os cães são permitidos, mas têm de ser presos pela trela"
|
||||||
|
},
|
||||||
|
"3": {
|
||||||
|
"then": "Os cães são permitidos e podem correr livremente"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"question": "Os cães são permitidos neste estabelecimento?"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue