Merge branch 'feature/schools'
This commit is contained in:
commit
dd483fc42b
30 changed files with 1551 additions and 135 deletions
|
@ -5,6 +5,7 @@ import BaseUIElement from "../UI/BaseUIElement";
|
|||
import List from "../UI/Base/List";
|
||||
import Title from "../UI/Base/Title";
|
||||
import {BBox} from "./BBox";
|
||||
import {Feature, Geometry, MultiPolygon, Polygon} from "@turf/turf";
|
||||
|
||||
export interface ExtraFuncParams {
|
||||
/**
|
||||
|
@ -12,9 +13,9 @@ export interface ExtraFuncParams {
|
|||
* Note that more features then requested can be given back.
|
||||
* Format: [ [ geojson, geojson, geojson, ... ], [geojson, ...], ...]
|
||||
*/
|
||||
getFeaturesWithin: (layerId: string, bbox: BBox) => any[][],
|
||||
getFeaturesWithin: (layerId: string, bbox: BBox) => Feature<Geometry, {id: string}>[][],
|
||||
memberships: RelationsTracker
|
||||
getFeatureById: (id: string) => any
|
||||
getFeatureById: (id: string) => Feature<Geometry, {id: string}>
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -24,21 +25,65 @@ interface ExtraFunction {
|
|||
readonly _name: string;
|
||||
readonly _args: string[];
|
||||
readonly _doc: string;
|
||||
readonly _f: (params: ExtraFuncParams, feat: any) => any;
|
||||
readonly _f: (params: ExtraFuncParams, feat: Feature<Geometry, any>) => any;
|
||||
|
||||
}
|
||||
|
||||
class EnclosingFunc implements ExtraFunction {
|
||||
_name = "enclosingFeatures"
|
||||
_doc = ["Gives a list of all features in the specified layers which fully contain this object. Returned features will always be (multi)polygons. (LineStrings and Points from the other layers are ignored)","",
|
||||
"The result is a list of features: `{feat: Polygon}[]`",
|
||||
"This function will never return the feature itself."].join("\n")
|
||||
_args = ["...layerIds - one or more layer ids of the layer from which every feature is checked for overlap)"]
|
||||
_f(params: ExtraFuncParams, feat: Feature<Geometry, any>) {
|
||||
return (...layerIds: string[]) => {
|
||||
const result: { feat: any }[] = []
|
||||
const bbox = BBox.get(feat)
|
||||
const seenIds = new Set<string>()
|
||||
seenIds.add(feat.properties.id)
|
||||
for (const layerId of layerIds) {
|
||||
const otherFeaturess = params.getFeaturesWithin(layerId, bbox)
|
||||
if (otherFeaturess === undefined) {
|
||||
continue;
|
||||
}
|
||||
if (otherFeaturess.length === 0) {
|
||||
continue;
|
||||
}
|
||||
for (const otherFeatures of otherFeaturess) {
|
||||
for (const otherFeature of otherFeatures) {
|
||||
if(seenIds.has(otherFeature.properties.id)){
|
||||
continue
|
||||
}
|
||||
seenIds.add(otherFeature.properties.id)
|
||||
if(otherFeature.geometry.type !== "Polygon" && otherFeature.geometry.type !== "MultiPolygon"){
|
||||
continue;
|
||||
}
|
||||
if(GeoOperations.completelyWithin(feat, <Feature<Polygon | MultiPolygon, any>> otherFeature)){
|
||||
result.push({feat: otherFeature})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class OverlapFunc implements ExtraFunction {
|
||||
|
||||
|
||||
_name = "overlapWith";
|
||||
_doc = "Gives a list of features from the specified layer which this feature (partly) overlaps with. A point which is embedded in the feature is detected as well." +
|
||||
"If the current feature is a point, all features that this point is embeded in are given.\n\n" +
|
||||
"The returned value is `{ feat: GeoJSONFeature, overlap: number}[]` where `overlap` is the overlapping surface are (in m²) for areas, the overlapping length (in meter) if the current feature is a line or `undefined` if the current feature is a point.\n" +
|
||||
"The resulting list is sorted in descending order by overlap. The feature with the most overlap will thus be the first in the list\n" +
|
||||
"\n" +
|
||||
"For example to get all objects which overlap or embed from a layer, use `_contained_climbing_routes_properties=feat.overlapWith('climbing_route')`"
|
||||
_doc = ["Gives a list of features from the specified layer which this feature (partly) overlaps with. A point which is embedded in the feature is detected as well.",
|
||||
"If the current feature is a point, all features that this point is embeded in are given." ,
|
||||
"",
|
||||
"The returned value is `{ feat: GeoJSONFeature, overlap: number}[]` where `overlap` is the overlapping surface are (in m²) for areas, the overlapping length (in meter) if the current feature is a line or `undefined` if the current feature is a point." ,
|
||||
"The resulting list is sorted in descending order by overlap. The feature with the most overlap will thus be the first in the list." ,
|
||||
"",
|
||||
"For example to get all objects which overlap or embed from a layer, use `_contained_climbing_routes_properties=feat.overlapWith('climbing_route')`",
|
||||
"",
|
||||
"Also see [enclosingFeatures](#enclosingFeatures) which can be used to get all objects which fully contain this feature"
|
||||
].join("\n")
|
||||
_args = ["...layerIds - one or more layer ids of the layer from which every feature is checked for overlap)"]
|
||||
|
||||
_f(params, feat) {
|
||||
|
@ -46,15 +91,15 @@ class OverlapFunc implements ExtraFunction {
|
|||
const result: { feat: any, overlap: number }[] = []
|
||||
const bbox = BBox.get(feat)
|
||||
for (const layerId of layerIds) {
|
||||
const otherLayers = params.getFeaturesWithin(layerId, bbox)
|
||||
if (otherLayers === undefined) {
|
||||
const otherFeaturess = params.getFeaturesWithin(layerId, bbox)
|
||||
if (otherFeaturess === undefined) {
|
||||
continue;
|
||||
}
|
||||
if (otherLayers.length === 0) {
|
||||
if (otherFeaturess.length === 0) {
|
||||
continue;
|
||||
}
|
||||
for (const otherLayer of otherLayers) {
|
||||
result.push(...GeoOperations.calculateOverlap(feat, otherLayer));
|
||||
for (const otherFeatures of otherFeaturess) {
|
||||
result.push(...GeoOperations.calculateOverlap(feat, otherFeatures));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -392,6 +437,7 @@ export class ExtraFunctions {
|
|||
private static readonly allFuncs: ExtraFunction[] = [
|
||||
new DistanceToFunc(),
|
||||
new OverlapFunc(),
|
||||
new EnclosingFunc(),
|
||||
new IntersectionFunc(),
|
||||
new ClosestObjectFunc(),
|
||||
new ClosestNObjectFunc(),
|
||||
|
|
|
@ -79,6 +79,9 @@ export default class SaveTileToLocalStorageActor {
|
|||
}
|
||||
loadedTiles.add(key)
|
||||
this.GetIdb(key).then((features: { feature: any, freshness: Date }[]) => {
|
||||
if(features === undefined){
|
||||
return;
|
||||
}
|
||||
console.debug("Loaded tile " + self._layer.id + "_" + key + " from disk")
|
||||
const src = new SimpleFeatureSource(self._flayer, key, new UIEventSource<{ feature: any; freshness: Date }[]>(features))
|
||||
registerTile(src)
|
||||
|
|
|
@ -23,6 +23,7 @@ import TileFreshnessCalculator from "./TileFreshnessCalculator";
|
|||
import FullNodeDatabaseSource from "./TiledFeatureSource/FullNodeDatabaseSource";
|
||||
import MapState from "../State/MapState";
|
||||
import {ElementStorage} from "../ElementStorage";
|
||||
import {Feature, Geometry} from "@turf/turf";
|
||||
|
||||
|
||||
/**
|
||||
|
@ -337,7 +338,7 @@ export default class FeaturePipeline {
|
|||
|
||||
}
|
||||
|
||||
public GetAllFeaturesWithin(bbox: BBox): any[][] {
|
||||
public GetAllFeaturesWithin(bbox: BBox): Feature<Geometry, {id: string}>[][] {
|
||||
const self = this
|
||||
const tiles = []
|
||||
Array.from(this.perLayerHierarchy.keys())
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
/**
|
||||
* Merges features from different featureSources for a single layer
|
||||
* Uses the freshest feature available in the case multiple sources offer data with the same identifier
|
||||
*/
|
||||
import {UIEventSource} from "../../UIEventSource";
|
||||
import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource, Tiled} from "../FeatureSource";
|
||||
import FilteredLayer from "../../../Models/FilteredLayer";
|
||||
import {Tiles} from "../../../Models/TileRange";
|
||||
import {BBox} from "../../BBox";
|
||||
|
||||
|
||||
export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled, IndexedFeatureSource {
|
||||
|
||||
public features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]);
|
||||
|
@ -17,7 +14,10 @@ export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled
|
|||
public readonly bbox: BBox;
|
||||
public readonly containedIds: UIEventSource<Set<string>> = new UIEventSource<Set<string>>(new Set())
|
||||
private readonly _sources: UIEventSource<FeatureSource[]>;
|
||||
|
||||
/**
|
||||
* Merges features from different featureSources for a single layer
|
||||
* Uses the freshest feature available in the case multiple sources offer data with the same identifier
|
||||
*/
|
||||
constructor(layer: FilteredLayer, tileIndex: number, bbox: BBox, sources: UIEventSource<FeatureSource[]>) {
|
||||
this.tileIndex = tileIndex;
|
||||
this.bbox = bbox;
|
||||
|
|
|
@ -6,23 +6,90 @@ import {GeoOperations} from "../../GeoOperations";
|
|||
import FeatureSource from "../FeatureSource";
|
||||
import PointRenderingConfig from "../../../Models/ThemeConfig/PointRenderingConfig";
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig";
|
||||
import LineRenderingConfig from "../../../Models/ThemeConfig/LineRenderingConfig";
|
||||
import {within} from "@turf/turf";
|
||||
|
||||
export default class RenderingMultiPlexerFeatureSource {
|
||||
public readonly features: UIEventSource<(any & { pointRenderingIndex: number | undefined, lineRenderingIndex: number | undefined })[]>;
|
||||
private readonly pointRenderings: { rendering: PointRenderingConfig; index: number }[];
|
||||
private centroidRenderings: { rendering: PointRenderingConfig; index: number }[];
|
||||
private projectedCentroidRenderings: { rendering: PointRenderingConfig; index: number }[];
|
||||
private startRenderings: { rendering: PointRenderingConfig; index: number }[];
|
||||
private endRenderings: { rendering: PointRenderingConfig; index: number }[];
|
||||
private hasCentroid: boolean;
|
||||
private lineRenderObjects: LineRenderingConfig[];
|
||||
|
||||
|
||||
private inspectFeature(feat, addAsPoint: (feat, rendering, centerpoint: [number, number]) => void, withIndex: any[]){
|
||||
if (feat.geometry.type === "Point") {
|
||||
|
||||
for (const rendering of this.pointRenderings) {
|
||||
withIndex.push({
|
||||
...feat,
|
||||
pointRenderingIndex: rendering.index
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// This is a a line: add the centroids
|
||||
let centerpoint: [number, number] = undefined;
|
||||
let projectedCenterPoint : [number, number] = undefined
|
||||
if(this.hasCentroid){
|
||||
centerpoint = GeoOperations.centerpointCoordinates(feat)
|
||||
if(this.projectedCentroidRenderings.length > 0){
|
||||
projectedCenterPoint = <[number,number]> GeoOperations.nearestPoint(feat, centerpoint).geometry.coordinates
|
||||
}
|
||||
}
|
||||
for (const rendering of this.centroidRenderings) {
|
||||
addAsPoint(feat, rendering, centerpoint)
|
||||
}
|
||||
|
||||
|
||||
if (feat.geometry.type === "LineString") {
|
||||
|
||||
for (const rendering of this.projectedCentroidRenderings) {
|
||||
addAsPoint(feat, rendering, projectedCenterPoint)
|
||||
}
|
||||
|
||||
// Add start- and endpoints
|
||||
const coordinates = feat.geometry.coordinates
|
||||
for (const rendering of this.startRenderings) {
|
||||
addAsPoint(feat, rendering, coordinates[0])
|
||||
}
|
||||
for (const rendering of this.endRenderings) {
|
||||
const coordinate = coordinates[coordinates.length - 1]
|
||||
addAsPoint(feat, rendering, coordinate)
|
||||
}
|
||||
|
||||
}else{
|
||||
for (const rendering of this.projectedCentroidRenderings) {
|
||||
addAsPoint(feat, rendering, centerpoint)
|
||||
}
|
||||
}
|
||||
|
||||
// AT last, add it 'as is' to what we should render
|
||||
for (let i = 0; i < this.lineRenderObjects.length; i++) {
|
||||
withIndex.push({
|
||||
...feat,
|
||||
lineRenderingIndex: i
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
constructor(upstream: FeatureSource, layer: LayerConfig) {
|
||||
|
||||
const pointRenderObjects: { rendering: PointRenderingConfig, index: number }[] = layer.mapRendering.map((r, i) => ({
|
||||
rendering: r,
|
||||
index: i
|
||||
}))
|
||||
const pointRenderings = pointRenderObjects.filter(r => r.rendering.location.has("point"))
|
||||
const centroidRenderings = pointRenderObjects.filter(r => r.rendering.location.has("centroid"))
|
||||
const projectedCentroidRenderings = pointRenderObjects.filter(r => r.rendering.location.has("projected_centerpoint"))
|
||||
const startRenderings = pointRenderObjects.filter(r => r.rendering.location.has("start"))
|
||||
const endRenderings = pointRenderObjects.filter(r => r.rendering.location.has("end"))
|
||||
const hasCentroid = centroidRenderings.length > 0 || projectedCentroidRenderings.length > 0
|
||||
const lineRenderObjects = layer.lineRendering
|
||||
this.pointRenderings = pointRenderObjects.filter(r => r.rendering.location.has("point"))
|
||||
this.centroidRenderings = pointRenderObjects.filter(r => r.rendering.location.has("centroid"))
|
||||
this.projectedCentroidRenderings = pointRenderObjects.filter(r => r.rendering.location.has("projected_centerpoint"))
|
||||
this.startRenderings = pointRenderObjects.filter(r => r.rendering.location.has("start"))
|
||||
this.endRenderings = pointRenderObjects.filter(r => r.rendering.location.has("end"))
|
||||
this.hasCentroid = this.centroidRenderings.length > 0 || this.projectedCentroidRenderings.length > 0
|
||||
this.lineRenderObjects = layer.lineRendering
|
||||
|
||||
this.features = upstream.features.map(
|
||||
features => {
|
||||
|
@ -31,8 +98,7 @@ export default class RenderingMultiPlexerFeatureSource {
|
|||
}
|
||||
|
||||
|
||||
const withIndex: (any & { pointRenderingIndex: number | undefined, lineRenderingIndex: number | undefined, multiLineStringIndex: number | undefined })[] = [];
|
||||
|
||||
const withIndex: any[] = [];
|
||||
|
||||
function addAsPoint(feat, rendering, coordinate) {
|
||||
const patched = {
|
||||
|
@ -45,63 +111,10 @@ export default class RenderingMultiPlexerFeatureSource {
|
|||
}
|
||||
withIndex.push(patched)
|
||||
}
|
||||
|
||||
|
||||
for (const f of features) {
|
||||
const feat = f.feature;
|
||||
if (feat.geometry.type === "Point") {
|
||||
|
||||
for (const rendering of pointRenderings) {
|
||||
withIndex.push({
|
||||
...feat,
|
||||
pointRenderingIndex: rendering.index
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// This is a a line: add the centroids
|
||||
let centerpoint: [number, number] = undefined;
|
||||
let projectedCenterPoint : [number, number] = undefined
|
||||
if(hasCentroid){
|
||||
centerpoint = GeoOperations.centerpointCoordinates(feat)
|
||||
if(projectedCentroidRenderings.length > 0){
|
||||
projectedCenterPoint = <[number,number]> GeoOperations.nearestPoint(feat, centerpoint).geometry.coordinates
|
||||
}
|
||||
}
|
||||
for (const rendering of centroidRenderings) {
|
||||
addAsPoint(feat, rendering, centerpoint)
|
||||
}
|
||||
|
||||
|
||||
if (feat.geometry.type === "LineString") {
|
||||
|
||||
for (const rendering of projectedCentroidRenderings) {
|
||||
addAsPoint(feat, rendering, projectedCenterPoint)
|
||||
}
|
||||
|
||||
// Add start- and endpoints
|
||||
const coordinates = feat.geometry.coordinates
|
||||
for (const rendering of startRenderings) {
|
||||
addAsPoint(feat, rendering, coordinates[0])
|
||||
}
|
||||
for (const rendering of endRenderings) {
|
||||
const coordinate = coordinates[coordinates.length - 1]
|
||||
addAsPoint(feat, rendering, coordinate)
|
||||
}
|
||||
|
||||
}else{
|
||||
for (const rendering of projectedCentroidRenderings) {
|
||||
addAsPoint(feat, rendering, centerpoint)
|
||||
}
|
||||
}
|
||||
|
||||
// AT last, add it 'as is' to what we should render
|
||||
for (let i = 0; i < lineRenderObjects.length; i++) {
|
||||
withIndex.push({
|
||||
...feat,
|
||||
lineRenderingIndex: i
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
this.inspectFeature(f.feature, addAsPoint, withIndex)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import FilteredLayer from "../../../Models/FilteredLayer";
|
|||
import {FeatureSourceForLayer, Tiled} from "../FeatureSource";
|
||||
import {Tiles} from "../../../Models/TileRange";
|
||||
import {BBox} from "../../BBox";
|
||||
import {OsmConnection} from "../../Osm/OsmConnection";
|
||||
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig";
|
||||
import {Or} from "../../Tags/Or";
|
||||
import {TagsFilter} from "../../Tags/TagsFilter";
|
||||
|
@ -27,65 +26,71 @@ export default class OsmFeatureSource {
|
|||
handleTile: (tile: FeatureSourceForLayer & Tiled) => void;
|
||||
isActive: UIEventSource<boolean>,
|
||||
neededTiles: UIEventSource<number[]>,
|
||||
state: {
|
||||
readonly osmConnection: OsmConnection;
|
||||
},
|
||||
markTileVisited?: (tileId: number) => void
|
||||
};
|
||||
private readonly allowedTags: TagsFilter;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param options: allowedFeatures is normally calculated from the layoutToUse
|
||||
*/
|
||||
constructor(options: {
|
||||
handleTile: (tile: FeatureSourceForLayer & Tiled) => void;
|
||||
isActive: UIEventSource<boolean>,
|
||||
neededTiles: UIEventSource<number[]>,
|
||||
state: {
|
||||
readonly filteredLayers: UIEventSource<FilteredLayer[]>;
|
||||
readonly osmConnection: OsmConnection;
|
||||
readonly layoutToUse: LayoutConfig
|
||||
readonly osmConnection: {
|
||||
Backend(): string
|
||||
};
|
||||
readonly layoutToUse?: LayoutConfig
|
||||
},
|
||||
readonly allowedFeatures?: TagsFilter,
|
||||
markTileVisited?: (tileId: number) => void
|
||||
}) {
|
||||
this.options = options;
|
||||
this._backend = options.state.osmConnection._oauth_config.url;
|
||||
this._backend = options.state.osmConnection.Backend();
|
||||
this.filteredLayers = options.state.filteredLayers.map(layers => layers.filter(layer => layer.layerDef.source.geojsonSource === undefined))
|
||||
this.handleTile = options.handleTile
|
||||
this.isActive = options.isActive
|
||||
const self = this
|
||||
options.neededTiles.addCallbackAndRunD(neededTiles => {
|
||||
if (options.isActive?.data === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
neededTiles = neededTiles.filter(tile => !self.downloadedTiles.has(tile))
|
||||
|
||||
if (neededTiles.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.isRunning.setData(true)
|
||||
try {
|
||||
|
||||
for (const neededTile of neededTiles) {
|
||||
self.downloadedTiles.add(neededTile)
|
||||
self.LoadTile(...Tiles.tile_from_index(neededTile)).then(_ => {
|
||||
console.debug("Tile ", Tiles.tile_from_index(neededTile).join("/"), "loaded from OSM")
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
self.isRunning.setData(false)
|
||||
}
|
||||
self.Update(neededTiles)
|
||||
})
|
||||
|
||||
|
||||
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))
|
||||
this.allowedTags = options.allowedFeatures ?? new Or(neededLayers.map(l => l.source.osmTags))
|
||||
}
|
||||
|
||||
private async LoadTile(z, x, y): Promise<void> {
|
||||
private async Update(neededTiles: number[]) {
|
||||
if (this.options.isActive?.data === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
neededTiles = neededTiles.filter(tile => !this.downloadedTiles.has(tile))
|
||||
|
||||
if (neededTiles.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isRunning.setData(true)
|
||||
try {
|
||||
|
||||
for (const neededTile of neededTiles) {
|
||||
this.downloadedTiles.add(neededTile)
|
||||
this.LoadTile(...Tiles.tile_from_index(neededTile))
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
this.isRunning.setData(false)
|
||||
}
|
||||
}
|
||||
|
||||
private LoadTile(z, x, y): void {
|
||||
if (z > 25) {
|
||||
throw "This is an absurd high zoom level"
|
||||
}
|
||||
|
@ -96,11 +101,10 @@ export default class OsmFeatureSource {
|
|||
|
||||
const bbox = BBox.fromTile(z, x, y)
|
||||
const url = `${this._backend}/api/0.6/map?bbox=${bbox.minLon},${bbox.minLat},${bbox.maxLon},${bbox.maxLat}`
|
||||
try {
|
||||
|
||||
const osmJson = await Utils.downloadJson(url)
|
||||
Utils.downloadJson(url).then(osmJson => {
|
||||
try {
|
||||
console.debug("Got tile", z, x, y, "from the osm api")
|
||||
console.log("Got tile", z, x, y, "from the osm api")
|
||||
this.rawDataHandlers.forEach(handler => handler(osmJson, Tiles.tile_index(z, x, y)))
|
||||
const geojson = OsmToGeoJson.default(osmJson,
|
||||
// @ts-ignore
|
||||
|
@ -130,17 +134,18 @@ export default class OsmFeatureSource {
|
|||
} catch (e) {
|
||||
console.error("Weird error: ", e)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Could not download tile", z, x, y, "due to", e, "; retrying with smaller bounds")
|
||||
if (e === "rate limited") {
|
||||
})
|
||||
.catch(e => {
|
||||
console.error("Could not download tile", z, x, y, "due to", e, "; retrying with smaller bounds")
|
||||
if (e === "rate limited") {
|
||||
return;
|
||||
}
|
||||
this.LoadTile(z + 1, x * 2, y * 2)
|
||||
this.LoadTile(z + 1, 1 + x * 2, y * 2)
|
||||
this.LoadTile(z + 1, x * 2, 1 + y * 2)
|
||||
this.LoadTile(z + 1, 1 + x * 2, 1 + y * 2)
|
||||
return;
|
||||
}
|
||||
await this.LoadTile(z + 1, x * 2, y * 2)
|
||||
await this.LoadTile(z + 1, 1 + x * 2, y * 2)
|
||||
await this.LoadTile(z + 1, x * 2, 1 + y * 2)
|
||||
await this.LoadTile(z + 1, 1 + x * 2, 1 + y * 2)
|
||||
return;
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import {BBox} from "./BBox";
|
|||
import togpx from "togpx"
|
||||
import Constants from "../Models/Constants";
|
||||
import LayerConfig from "../Models/ThemeConfig/LayerConfig";
|
||||
import {Coord} from "@turf/turf";
|
||||
import {booleanWithin, Coord, Feature, Geometry, MultiPolygon, Polygon, Properties} from "@turf/turf";
|
||||
|
||||
export class GeoOperations {
|
||||
|
||||
|
@ -142,7 +142,10 @@ export class GeoOperations {
|
|||
return result;
|
||||
}
|
||||
|
||||
public static pointInPolygonCoordinates(x: number, y: number, coordinates: [number, number][][]) {
|
||||
/**
|
||||
* Helper function which does the heavy lifting for 'inside'
|
||||
*/
|
||||
private static pointInPolygonCoordinates(x: number, y: number, coordinates: [number, number][][]) {
|
||||
const inside = GeoOperations.pointWithinRing(x, y, /*This is the outer ring of the polygon */coordinates[0])
|
||||
if (!inside) {
|
||||
return false;
|
||||
|
@ -737,6 +740,38 @@ export class GeoOperations {
|
|||
return turf.bearing(a, b)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if one feature contains the other feature
|
||||
*
|
||||
* const pond: Feature<Polygon, any> = {
|
||||
* "type": "Feature",
|
||||
* "properties": {"natural":"water","water":"pond"},
|
||||
* "geometry": {
|
||||
* "type": "Polygon",
|
||||
* "coordinates": [[
|
||||
* [4.362924098968506,50.8435422298544 ],
|
||||
* [4.363272786140442,50.8435219059949 ],
|
||||
* [4.363213777542114,50.8437420806679 ],
|
||||
* [4.362924098968506,50.8435422298544 ]
|
||||
* ]]}}
|
||||
* const park: Feature<Polygon, any> = {
|
||||
* "type": "Feature",
|
||||
* "properties": {"leisure":"park"},
|
||||
* "geometry": {
|
||||
* "type": "Polygon",
|
||||
* "coordinates": [[
|
||||
* [ 4.36073541641235,50.84323737103244 ],
|
||||
* [ 4.36469435691833, 50.8423905305197 ],
|
||||
* [ 4.36659336090087, 50.8458997374786 ],
|
||||
* [ 4.36254858970642, 50.8468007074916 ],
|
||||
* [ 4.36073541641235, 50.8432373710324 ]
|
||||
* ]]}}
|
||||
* GeoOperations.completelyWithin(pond, park) // => true
|
||||
* GeoOperations.completelyWithin(park, pond) // => false
|
||||
*/
|
||||
static completelyWithin(feature: Feature<Geometry, any>, possiblyEncloingFeature: Feature<Polygon | MultiPolygon, any>) : boolean {
|
||||
return booleanWithin(feature, possiblyEncloingFeature);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -155,7 +155,6 @@ export default class MetaTagging {
|
|||
|
||||
// Lazy function
|
||||
const f = (feature: any) => {
|
||||
const oldValue = feature.properties[key]
|
||||
delete feature.properties[key]
|
||||
Object.defineProperty(feature.properties, key, {
|
||||
configurable: true,
|
||||
|
|
|
@ -143,6 +143,10 @@ export class OsmConnection {
|
|||
console.log("Logged out")
|
||||
this.loadingStatus.setData("not-attempted")
|
||||
}
|
||||
|
||||
public Backend(): string {
|
||||
return this._oauth_config.url;
|
||||
}
|
||||
|
||||
public AttemptLogin() {
|
||||
this.loadingStatus.setData("loading")
|
||||
|
|
|
@ -486,7 +486,7 @@ export default class SimpleMetaTaggers {
|
|||
const subElements: (string | BaseUIElement)[] = [
|
||||
new Combine([
|
||||
"Metatags are extra tags available, in order to display more data or to give better questions.",
|
||||
"The are calculated automatically on every feature when the data arrives in the webbrowser. This document gives an overview of the available metatags.",
|
||||
"They are calculated automatically on every feature when the data arrives in the webbrowser. This document gives an overview of the available metatags.",
|
||||
"**Hint:** when using metatags, add the [query parameter](URL_Parameters.md) `debug=true` to the URL. This will include a box in the popup for features which shows all the properties of the object"
|
||||
]).SetClass("flex-col")
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue