Add possibility to load external data as mercator tiles, add bbox tile possibilities, add CRAB and GRB as datasources in the GRB theme

This commit is contained in:
Pieter Vander Vennet 2021-10-27 03:52:19 +02:00
parent e777776531
commit d5f4572e9a
12 changed files with 254 additions and 61 deletions

View file

@ -1,5 +1,6 @@
import * as turf from "@turf/turf";
import {TileRange, Tiles} from "../Models/TileRange";
import {GeoOperations} from "./GeoOperations";
export class BBox {
@ -22,7 +23,7 @@ export class BBox {
this.minLon = Math.min(this.minLon, coordinate[0]);
this.minLat = Math.min(this.minLat, coordinate[1]);
}
this.maxLon = Math.min(this.maxLon, 180)
this.maxLat = Math.min(this.maxLat, 90)
this.minLon = Math.max(this.minLon, -180)
@ -117,12 +118,12 @@ export class BBox {
}
pad(factor: number, maxIncrease = 2): BBox {
const latDiff = Math.min(maxIncrease / 2, Math.abs(this.maxLat - this.minLat) * factor)
const lonDiff =Math.min(maxIncrease / 2, Math.abs(this.maxLon - this.minLon) * factor)
const lonDiff = Math.min(maxIncrease / 2, Math.abs(this.maxLon - this.minLon) * factor)
return new BBox([[
this.minLon - lonDiff,
this.minLat - latDiff
this.minLat - latDiff
], [this.maxLon + lonDiff,
this.maxLat + latDiff]])
}
@ -161,4 +162,16 @@ export class BBox {
const boundslr = Tiles.tile_bounds_lon_lat(lr.z, lr.x, lr.y)
return new BBox([].concat(boundsul, boundslr))
}
toMercator(): { minLat: number, maxLat: number, minLon: number, maxLon: number } {
const [minLon, minLat] = GeoOperations.ConvertWgs84To900913([this.minLon, this.minLat])
const [maxLon, maxLat] = GeoOperations.ConvertWgs84To900913([this.maxLon, this.maxLat])
return {
minLon, maxLon,
minLat, maxLat
}
}
}

View file

@ -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)
@ -205,7 +205,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))
@ -213,7 +215,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())
})
})

View file

@ -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 } [] = []

View file

@ -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,

View file

@ -226,7 +226,7 @@ export class GeoOperations {
/**
* Generates the closest point on a way from a given point
*
*
* The properties object will contain three values:
// - `index`: closest point was found on nth line part,
// - `dist`: distance between pt and the closest point (in kilometer),
@ -283,6 +283,34 @@ export class GeoOperations {
return headerValuesOrdered.map(v => JSON.stringify(v)).join(",") + "\n" + lines.join("\n")
}
private static readonly _earthRadius = 6378137;
private static readonly _originShift = 2 * Math.PI * GeoOperations._earthRadius / 2;
//Converts given lat/lon in WGS84 Datum to XY in Spherical Mercator EPSG:900913
public static ConvertWgs84To900913(lonLat: [number, number]): [number, number] {
const lon = lonLat[0];
const lat = lonLat[1];
const x = lon * GeoOperations._originShift / 180;
let y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);
y = y * GeoOperations._originShift / 180;
return [x, y];
}
//Converts XY point from (Spherical) Web Mercator EPSG:3785 (unofficially EPSG:900913) to lat/lon in WGS84 Datum
public static Convert900913ToWgs84(lonLat: [number, number]): [number, number] {
const lon = lonLat[0]
const lat = lonLat[1]
const x = 180 * lon / GeoOperations._originShift;
let y = 180 * lat / GeoOperations._originShift;
y = 180 / Math.PI * (2 * Math.atan(Math.exp(y * Math.PI / 180)) - Math.PI / 2);
return [x, y];
}
public static GeoJsonToWGS84(geojson){
return turf.toWgs84(geojson)
}
/**
* Calculates the intersection between two features.
* Returns the length if intersecting a linestring and a (multi)polygon (in meters), returns a surface area (in m²) if intersecting two (multi)polygons