From 99a38f2b10583bebbe53f518ed5ec8d9409a3101 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 19 Dec 2021 02:11:22 +0100 Subject: [PATCH] Add intersection function --- Docs/CalculatedTags.md | 16 ++++++++++- Logic/ExtraFunctions.ts | 43 +++++++++++++++++++++++++++++- Logic/GeoOperations.ts | 9 ++++++- Models/ThemeConfig/LayoutConfig.ts | 12 +++++---- scripts/generateLayerOverview.ts | 3 +++ 5 files changed, 75 insertions(+), 8 deletions(-) diff --git a/Docs/CalculatedTags.md b/Docs/CalculatedTags.md index 8e19bdc03..6caf88434 100644 --- a/Docs/CalculatedTags.md +++ b/Docs/CalculatedTags.md @@ -20,6 +20,7 @@ + [sidewalk:left, sidewalk:right, generic_key:left:property, generic_key:right:property](#sidewalkleft,-sidewalk:right,-generic_key:left:property,-generic_key:right:property) + [distanceTo](#distanceto) + [overlapWith](#overlapwith) + + [intersectionsWith](#intersectionswith) + [closest](#closest) + [closestn](#closestn) + [memberships](#memberships) @@ -200,6 +201,7 @@ Some advanced functions are available on **feat** as well: - [distanceTo](#distanceTo) - [overlapWith](#overlapWith) + - [intersectionsWith](#intersectionsWith) - [closest](#closest) - [closestn](#closestn) - [memberships](#memberships) @@ -223,7 +225,19 @@ The resulting list is sorted in descending order by overlap. The feature with th For example to get all objects which overlap or embed from a layer, use `_contained_climbing_routes_properties=feat.overlapWith('climbing_route')` - 0. ...layerIds - one or more layer ids of the layer from which every feature is checked for overlap) + 0. ...layerIds - one or more layer ids of the layer from which every feature is checked for overlap) + + +### intersectionsWith + + Gives the intersection points with selected features. Only works with (Multi)Polygons and LineStrings. + +Returns a `{feat: GeoJson, intersections: [number,number][]}` where `feat` is the full, original feature. This list is in random order. + +If the current feature is a point, this function will return an empty list. +Points from other layers are ignored - even if the points are parts of the current linestring. + + 0. ...layerIds - one or more layer ids of the layer from which every feature is checked for intersection) ### closest diff --git a/Logic/ExtraFunctions.ts b/Logic/ExtraFunctions.ts index 054603e52..47bfc9a4a 100644 --- a/Logic/ExtraFunctions.ts +++ b/Logic/ExtraFunctions.ts @@ -39,7 +39,7 @@ class OverlapFunc implements ExtraFunction { "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')`" - _args = ["...layerIds - one or more layer ids of the layer from which every feature is checked for overlap)"] + _args = ["...layerIds - one or more layer ids of the layer from which every feature is checked for overlap)"] _f(params, feat) { return (...layerIds: string[]) => { @@ -67,6 +67,46 @@ class OverlapFunc implements ExtraFunction { } } + +class IntersectionFunc implements ExtraFunction { + + + _name = "intersectionsWith"; + _doc = "Gives the intersection points with selected features. Only works with (Multi)Polygons and LineStrings.\n\n" + + "Returns a `{feat: GeoJson, intersections: [number,number][]}` where `feat` is the full, original feature. This list is in random order.\n\n" + + "If the current feature is a point, this function will return an empty list.\n" + + "Points from other layers are ignored - even if the points are parts of the current linestring." + _args = ["...layerIds - one or more layer ids of the layer from which every feature is checked for intersection)"] + + _f(params: ExtraFuncParams, feat) { + return (...layerIds: string[]) => { + const result: { feat: any, intersections: [number,number][] }[] = [] + + const bbox = BBox.get(feat) + + for (const layerId of layerIds) { + const otherLayers = params.getFeaturesWithin(layerId, bbox) + if (otherLayers === undefined) { + continue; + } + if (otherLayers.length === 0) { + continue; + } + for (const tile of otherLayers) { + for (const otherFeature of tile) { + + const intersections = GeoOperations.LineIntersections(feat, otherFeature) + result.push({feat, intersections}) + } + } + } + + return result; + } + } +} + + class DistanceToFunc implements ExtraFunction { _name = "distanceTo"; @@ -351,6 +391,7 @@ export class ExtraFunctions { private static readonly allFuncs: ExtraFunction[] = [ new DistanceToFunc(), new OverlapFunc(), + new IntersectionFunc(), new ClosestObjectFunc(), new ClosestNObjectFunc(), new Memberships(), diff --git a/Logic/GeoOperations.ts b/Logic/GeoOperations.ts index e5f556a78..13d9bfdba 100644 --- a/Logic/GeoOperations.ts +++ b/Logic/GeoOperations.ts @@ -356,7 +356,7 @@ export class GeoOperations { * Returns 0 if both are linestrings * Returns null if the features are not intersecting */ - static calculateInstersection(feature, otherFeature, featureBBox: BBox, otherFeatureBBox?: BBox): number { + private static calculateInstersection(feature, otherFeature, featureBBox: BBox, otherFeatureBBox?: BBox): number { try { if (feature.geometry.type === "LineString") { @@ -442,6 +442,13 @@ export class GeoOperations { return undefined; } + /** + * Calculates line intersection between two features. + */ + public static LineIntersections(feature, otherFeature): [number,number][]{ + return turf.lineIntersect(feature, otherFeature).features.map(p =><[number,number]> p.geometry.coordinates) + } + public static AsGpx(feature, generatedWithLayer?: LayerConfig){ const metadata = {} diff --git a/Models/ThemeConfig/LayoutConfig.ts b/Models/ThemeConfig/LayoutConfig.ts index 157e4fd4b..1d9fe8cef 100644 --- a/Models/ThemeConfig/LayoutConfig.ts +++ b/Models/ThemeConfig/LayoutConfig.ts @@ -156,6 +156,9 @@ export default class LayoutConfig { private static ExtractLayers(json: LayoutConfigJson, official: boolean, context: string): { layers: LayerConfig[], extractAllNodes: boolean } { const result: LayerConfig[] = [] let exportAllNodes = false + if(json.layers === undefined){ + throw "Got undefined layers for "+json.id+" at "+context + } json.layers.forEach((layer, i) => { if (typeof layer === "string") { @@ -193,12 +196,11 @@ export default class LayoutConfig { if (typeof names === "string") { names = [names] } + + // This is a very special layer which triggers special behaviour + exportAllNodes = names.some(name => name === "type_node"); + names.forEach(name => { - if (name === "type_node") { - // This is a very special layer which triggers special behaviour - exportAllNodes = true; - } - const shared = AllKnownLayers.sharedLayersJson.get(name); if (shared === undefined) { throw `Unknown shared/builtin layer ${name} at ${context}.layers[${i}]. Available layers are ${Array.from(AllKnownLayers.sharedLayersJson.keys()).join(", ")}`; diff --git a/scripts/generateLayerOverview.ts b/scripts/generateLayerOverview.ts index ceeaf61c9..a2ee4bc70 100644 --- a/scripts/generateLayerOverview.ts +++ b/scripts/generateLayerOverview.ts @@ -88,6 +88,7 @@ class LayerOverviewUtils { return errorCount } + main(args: string[]) { AllKnownLayers.runningGenerateScript = true; @@ -211,6 +212,8 @@ class LayerOverviewUtils { // We load again from disc, as modifications were made above const lt = this.loadThemesAndLayers(); + + this.writeFiles(lt); } else { const errors = layerErrorCount.concat(themeErrorCount).join("\n")