From 7e3241311378cdb939221b74dccd12cc0a605e91 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 29 Jul 2022 20:04:36 +0200 Subject: [PATCH] Add 'steal' as special rendering, update 'multi', add entrance overview to onwheels layer --- Logic/ExtraFunctions.ts | 32 ++- Logic/MetaTagging.ts | 1 + Logic/Osm/Actions/CreateNewNodeAction.ts | 2 +- Logic/Osm/OsmObject.ts | 3 + Models/ThemeConfig/Conversion/PrepareLayer.ts | 36 ++- UI/SpecialVisualizations.ts | 90 ++++++- .../walls_and_buildings.json | 93 +++++-- assets/themes/indoors/indoors.json | 8 +- assets/themes/onwheels/onwheels.json | 26 +- test/Chai.spec.ts | 1 - test/Logic/ExtraFunctions.spec.ts | 243 ++++++++++++++++++ 11 files changed, 462 insertions(+), 73 deletions(-) create mode 100644 test/Logic/ExtraFunctions.spec.ts diff --git a/Logic/ExtraFunctions.ts b/Logic/ExtraFunctions.ts index 450c01cc8..71549df33 100644 --- a/Logic/ExtraFunctions.ts +++ b/Logic/ExtraFunctions.ts @@ -13,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) => Feature[][], + getFeaturesWithin: (layerId: string, bbox: BBox) => Feature[][], memberships: RelationsTracker - getFeatureById: (id: string) => Feature + getFeatureById: (id: string) => Feature } /** @@ -31,10 +31,11 @@ interface ExtraFunction { 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)","", + _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") + "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) { return (...layerIds: string[]) => { const result: { feat: any }[] = [] @@ -51,14 +52,14 @@ class EnclosingFunc implements ExtraFunction { } for (const otherFeatures of otherFeaturess) { for (const otherFeature of otherFeatures) { - if(seenIds.has(otherFeature.properties.id)){ + if (seenIds.has(otherFeature.properties.id)) { continue } seenIds.add(otherFeature.properties.id) - if(otherFeature.geometry.type !== "Polygon" && otherFeature.geometry.type !== "MultiPolygon"){ + if (otherFeature.geometry.type !== "Polygon" && otherFeature.geometry.type !== "MultiPolygon") { continue; } - if(GeoOperations.completelyWithin(feat, > otherFeature)){ + if (GeoOperations.completelyWithin(feat, >otherFeature)) { result.push({feat: otherFeature}) } } @@ -75,10 +76,10 @@ 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." , + "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." , + "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')`", "", @@ -89,6 +90,7 @@ class OverlapFunc implements ExtraFunction { _f(params, feat) { return (...layerIds: string[]) => { const result: { feat: any, overlap: number }[] = [] + const seenIds = new Set() const bbox = BBox.get(feat) for (const layerId of layerIds) { const otherFeaturess = params.getFeaturesWithin(layerId, bbox) @@ -99,12 +101,18 @@ class OverlapFunc implements ExtraFunction { continue; } for (const otherFeatures of otherFeaturess) { - result.push(...GeoOperations.calculateOverlap(feat, otherFeatures)); + const overlap = GeoOperations.calculateOverlap(feat, otherFeatures) + for (const overlappingFeature of overlap) { + if(seenIds.has(overlappingFeature.feat.properties.id)){ + continue + } + seenIds.add(overlappingFeature.feat.properties.id) + result.push(overlappingFeature) + } } } result.sort((a, b) => b.overlap - a.overlap) - return result; } } diff --git a/Logic/MetaTagging.ts b/Logic/MetaTagging.ts index d01af1919..f7224689f 100644 --- a/Logic/MetaTagging.ts +++ b/Logic/MetaTagging.ts @@ -35,6 +35,7 @@ export default class MetaTagging { return; } + console.log("Recalculating metatags...") const metatagsToApply: SimpleMetaTagger[] = [] for (const metatag of SimpleMetaTaggers.metatags) { if (metatag.includesDates) { diff --git a/Logic/Osm/Actions/CreateNewNodeAction.ts b/Logic/Osm/Actions/CreateNewNodeAction.ts index e6755ecd0..9ddc5a23e 100644 --- a/Logic/Osm/Actions/CreateNewNodeAction.ts +++ b/Logic/Osm/Actions/CreateNewNodeAction.ts @@ -93,7 +93,7 @@ export default class CreateNewNodeAction extends OsmCreateAction { // Project the point onto the way - + console.log("Snapping a node onto an existing way...") const geojson = this._snapOnto.asGeoJson() const projected = GeoOperations.nearestPoint(geojson, [this._lon, this._lat]) const projectedCoor= <[number, number]>projected.geometry.coordinates diff --git a/Logic/Osm/OsmObject.ts b/Logic/Osm/OsmObject.ts index 5bee76f45..0dd967bb0 100644 --- a/Logic/Osm/OsmObject.ts +++ b/Logic/Osm/OsmObject.ts @@ -219,6 +219,9 @@ export abstract class OsmObject { /** * Uses the list of polygon features to determine if the given tags are a polygon or not. + * + * OsmObject.isPolygon({"building":"yes"}) // => true + * OsmObject.isPolygon({"highway":"residential"}) // => false * */ protected static isPolygon(tags: any): boolean { for (const tagsKey in tags) { diff --git a/Models/ThemeConfig/Conversion/PrepareLayer.ts b/Models/ThemeConfig/Conversion/PrepareLayer.ts index e436b31b0..ab5f1578e 100644 --- a/Models/ThemeConfig/Conversion/PrepareLayer.ts +++ b/Models/ThemeConfig/Conversion/PrepareLayer.ts @@ -378,6 +378,25 @@ export class RewriteSpecial extends DesugaringStep { * const errors = [] * RewriteSpecial.convertIfNeeded({"special": {}}, errors, "test") // => undefined * errors // => ["A 'special'-block should define 'type' to indicate which visualisation should be used"] + * + * + * // an actual test + * const special = {"special": { + * "type": "multi", + * "before": { + * "en": "

Entrances

This building has {_entrances_count} entrances:" + * }, + * "after": { + * "en": "{_entrances_count_without_width_count} entrances don't have width information yet" + * }, + * "key": "_entrance_properties_with_width", + * "tagrendering": { + * "en": "An entrance of {canonical(width)}" + * } + * }} + * const errors = [] + * RewriteSpecial.convertIfNeeded(special, errors, "test") // => {"en": "

Entrances

This building has {_entrances_count} entrances: {multi(_entrance_properties_with_width,An entrance of &LBRACEcanonical&LPARENSwidth&RPARENS&RBRACE)}An entrance of {canonical(width)}"} + * errors // => [] */ private static convertIfNeeded(input: (object & { special: { type: string } }) | any, errors: string[], context: string): any { const special = input["special"] @@ -385,10 +404,6 @@ export class RewriteSpecial extends DesugaringStep { return input } - for (const wrongKey of Object.keys(input).filter(k => k !== "special" && k !== "before" && k !== "after")) { - errors.push(`At ${context}: Unexpected key in a special block: ${wrongKey}`) - } - const type = special["type"] if (type === undefined) { errors.push("A 'special'-block should define 'type' to indicate which visualisation should be used") @@ -406,10 +421,10 @@ export class RewriteSpecial extends DesugaringStep { // Check for obsolete and misspelled arguments errors.push(...Object.keys(special) .filter(k => !argNames.has(k)) - .filter(k => k !== "type") + .filter(k => k !== "type" && k !== "before" && k !== "after") .map(wrongArg => { const byDistance = Utils.sortedByLevenshteinDistance(wrongArg, argNamesList, x => x) - return `Unexpected argument with name '${wrongArg}'. Did you mean ${byDistance[0]}?\n\tAll known arguments are ${argNamesList.join(", ")}`; + return `Unexpected argument in special block at ${context} with name '${wrongArg}'. Did you mean ${byDistance[0]}?\n\tAll known arguments are ${argNamesList.join(", ")}`; })) // Check that all obligated arguments are present. They are obligated if they don't have a preset value @@ -496,12 +511,21 @@ export class RewriteSpecial extends DesugaringStep { * const expected = {render: {'*': "{image_carousel(image)}"}, mappings: [{if: "other_image_key", then: {'*': "{image_carousel(other_image_key)}"}} ]} * result // => expected * + * // Should put text before if specified * const tr = { * render: {special: {type: "image_carousel", image_key: "image"}, before: {en: "Some introduction"} }, * } * const result = new RewriteSpecial().convert(tr,"test").result * const expected = {render: {'en': "Some introduction{image_carousel(image)}"}} * result // => expected + * + * // Should put text after if specified + * const tr = { + * render: {special: {type: "image_carousel", image_key: "image"}, after: {en: "Some footer"} }, + * } + * const result = new RewriteSpecial().convert(tr,"test").result + * const expected = {render: {'en': "{image_carousel(image)}Some footer"}} + * result // => expected */ convert(json: TagRenderingConfigJson, context: string): { result: TagRenderingConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } { const errors = [] diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index d10485f4a..062563118 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -42,7 +42,6 @@ import NoteCommentElement from "./Popup/NoteCommentElement"; import ImgurUploader from "../Logic/ImageProviders/ImgurUploader"; import FileSelectorButton from "./Input/FileSelectorButton"; import {LoginToggle} from "./Popup/LoginButton"; -import {start} from "repl"; import {SubstitutedTranslation} from "./SubstitutedTranslation"; import {TextField} from "./Input/TextField"; import Wikidata, {WikidataResponse} from "../Logic/Web/Wikidata"; @@ -60,7 +59,8 @@ import Slider from "./Input/Slider"; import List from "./Base/List"; import StatisticsPanel from "./BigComponents/StatisticsPanel"; import {OsmFeature} from "../Models/OsmFeature"; -import Link from "./Base/Link"; +import EditableTagRendering from "./Popup/EditableTagRendering"; +import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig"; export interface SpecialVisualization { funcName: string, @@ -334,6 +334,13 @@ export default class SpecialVisualizations { render: { special: { type: "some_special_visualisation", + before: { + en: "Some text to prefix before the special element (e.g. a title)", + nl: "Een tekst om voor het element te zetten (bv. een titel)" + }, + after: { + en: "Some text to put after the element, e.g. a footer" + }, "argname": "some_arg", "message": { en: "some other really long message", @@ -1206,7 +1213,7 @@ export default class SpecialVisualizations { { funcName: "multi", docs: "Given an embedded tagRendering (read only) and a key, will read the keyname as a JSON-list. Every element of this list will be considered as tags and rendered with the tagRendering", - example: "```json\n"+JSON.stringify({ + example: "```json\n" + JSON.stringify({ render: { special: { type: "multi", @@ -1216,20 +1223,81 @@ export default class SpecialVisualizations { } } } - }, null, " ")+"```", + }, null, " ") + "```", args: [ - {name: "key", - doc: "The property to read and to interpret as a list of properties"}, { - name:"tagrendering", - doc: "An entire tagRenderingConfig" + name: "key", + doc: "The property to read and to interpret as a list of properties", + required: true + }, + { + name: "tagrendering", + doc: "An entire tagRenderingConfig", + required: true } ] -, + , constr(state, featureTags, args) { const [key, tr] = args - console.log("MULTI: ", key, tr) - return undefined + const translation = new Translation({"*": tr}) + return new VariableUiElement(featureTags.map(tags => { + const properties: object[] = JSON.parse(tags[key]) + const elements = [] + for (const property of properties) { + const subsTr = new SubstitutedTranslation(translation, new UIEventSource(property), state) + elements.push(subsTr) + } + return new List(elements) + })) + } + }, + { + funcName: "steal", + docs: "Shows a tagRendering from a different object as if this was the object itself", + args: [{ + name: "featureId", + doc: "The key of the attribute which contains the id of the feature from which to use the tags", + required: true + }, + { + name: "tagRenderingId", + doc: "The layer-id and tagRenderingId to render. Can be multiple value if ';'-separated (in which case every value must also contain the layerId, e.g. `layerId.tagRendering0; layerId.tagRendering1`). Note: this can cause layer injection", + required: true + }], + constr(state, featureTags, args) { + const [featureIdKey, layerAndtagRenderingIds] = args + const tagRenderings: [LayerConfig, TagRenderingConfig][] = [] + for (const layerAndTagRenderingId of layerAndtagRenderingIds.split(";")) { + const [layerId, tagRenderingId] = layerAndTagRenderingId.trim().split(".") + const layer = state.layoutToUse.layers.find(l => l.id === layerId) + const tagRendering = layer.tagRenderings.find(tr => tr.id === tagRenderingId) + tagRenderings.push([layer, tagRendering]) + } + return new VariableUiElement(featureTags.map(tags => { + const featureId = tags[featureIdKey] + if (featureId === undefined) { + return undefined; + } + const otherTags = state.allElements.getEventSourceById(featureId) + const elements: BaseUIElement[] = [] + for (const [layer, tagRendering] of tagRenderings) { + const el = new EditableTagRendering(otherTags, tagRendering, layer.units, state, {}) + elements.push(el) + } + if (elements.length === 1) { + return elements[0] + } + return new Combine(elements).SetClass("flex flex-col"); + })) + }, + + getLayerDependencies(args): string[] { + const [_, tagRenderingId] = args + if (tagRenderingId.indexOf(".") < 0) { + throw "Error: argument 'layerId.tagRenderingId' of special visualisation 'steal' should contain a dot" + } + const [layerId, __] = tagRenderingId.split(".") + return [layerId] } } ] diff --git a/assets/layers/walls_and_buildings/walls_and_buildings.json b/assets/layers/walls_and_buildings/walls_and_buildings.json index cd73c0fa1..333a1e026 100644 --- a/assets/layers/walls_and_buildings/walls_and_buildings.json +++ b/assets/layers/walls_and_buildings/walls_and_buildings.json @@ -47,33 +47,84 @@ } ], "calculatedTags": [ - "_entrance_properties=feat.overlapWith('entrance')?.map(e => e.feat.properties).filter(p => p !== undefined).filter(p => p.width !== undefined)", - "_entrance:id=feat.get('_entrance_properties')?.map(e => e.id)?.at(0)", - "_entrance:width=feat.get('_entrance_properties')?.map(e => e.width)?.at(0)" + "_entrance_properties=feat.overlapWith('entrance')?.map(e => e.feat.properties)?.filter(p => p !== undefined && p.indoor !== 'door')", + "_entrance_properties_with_width=feat.get('_entrance_properties')?.filter(p => p['width'] !== undefined)", + "_entrances_count=feat.get('_entrance_properties').length", + "_entrances_count_without_width_count= feat.get('_entrances_count') - feat.get('_entrance_properties_with_width').length", + "_biggest_width= Math.max( feat.get('_entrance_properties').map(p => p.width))", + "_biggest_width_properties= /* Can be a list! */ feat.get('_entrance_properties').filter(p => p.width === feat.get('_biggest_width'))", + "_biggest_width_id=feat.get('_biggest_width_properties').id" ], - "tagRenderings": [ + "units": [ { - "id": "_entrance:width", - "render": { - "en": "This door has a width of {canonical(_entrance:width)} meters ", - "nl": "Deze deur heeft een breedte van {canonical(_entrance:width)} meter ", - "de": "Diese Tür hat eine Durchgangsbreite von {canonical(_entrance:width)} Meter ", - "es": "Esta puerta tiene una ancho de {canonical(_entrance:width)} metros ", - "fr": "Cette porte a une largeur de {canonical(_entrance:width)} mètres " - }, - "freeform": { - "key": "_entrance:width" - }, - "mappings": [ + "appliesToKey": [ + "width","_biggest_width" + ], + "applicableUnits": [ { - "if": "_entrance:width=", - "then": { - "en": "This entrance has no width information", - "de": "Der Eingang hat keine Informationen zur Durchgangsbreite", - "fr": "Cette entrée n'a pas d'informations sur sa largeur" + "canonicalDenomination": "m", + "alternativeDenomination": [ + "meter" + ], + "human": { + "en": "meter", + "fr": "mètre", + "de": "Meter" + } + }, + { + "default": true, + "canonicalDenomination": "cm", + "alternativeDenomination": [ + "centimeter", + "cms" + ], + "human": { + "en": "centimeter", + "fr": "centimètre", + "de": "Zentimeter" } } ] } + ], + "tagRenderings": [ + { + "id": "entrance_info", + "render": { + "before": { + "en": "

Entrances

This building has {_entrances_count} entrances:" + }, + "after": { + "en": "{_entrances_count_without_width_count} entrances don't have width information yet" + }, + "special": { + "type": "multi", + "key": "_entrance_properties_with_width", + "tagrendering": { + "en": "An entrance of {canonical(width)}" + } + } + }, + "mappings": [ + { + "if": "_entrances_count=0", + "then": { + "en": "No entrance has been marked" + } + }, + { + "if": "_entrances_count_without_width:=_entrances_count", + "then": { + "en": "None of the {_entrance_count} entrances have width information yet" + } + } + ] + }, + { + "id": "biggest_width", + "render": "The entrance with the biggest width is {canonical(_biggest_width)} wide", + "condition": "_biggest_width_id~*" + } ] } \ No newline at end of file diff --git a/assets/themes/indoors/indoors.json b/assets/themes/indoors/indoors.json index 529ee0807..7c3e5fa78 100644 --- a/assets/themes/indoors/indoors.json +++ b/assets/themes/indoors/indoors.json @@ -17,6 +17,12 @@ "startZoom": 14, "widenFactor": 2, "layers": [ - "indoors" + "indoors", + { + "builtin": ["walls_and_buildings"], + "override": { + "shownByDefault": true + } + } ] } \ No newline at end of file diff --git a/assets/themes/onwheels/onwheels.json b/assets/themes/onwheels/onwheels.json index 4301c2823..84bf7688a 100644 --- a/assets/themes/onwheels/onwheels.json +++ b/assets/themes/onwheels/onwheels.json @@ -366,15 +366,11 @@ ], "overrideAll": { "+calculatedTags": [ - "_poi_walls_and_buildings_entrance_properties=[].concat(...feat.closestn('walls_and_buildings',1, undefined, 500).map(w => ({id: w.feat.properties.id, width: w.feat.properties['_entrance_properties']})))", - "_poi_walls_and_buildings_entrance_count=[].concat(...feat.overlapWith('walls_and_buildings').map(w => ({id: w.feat.properties.id, width: w.feat.properties['_entrance_properties']})))", - "_poi_walls_and_buildings_entrance_properties_with_width=feat.get('_poi_walls_and_buildings_entrance_properties').filter(p => p['width'] !== undefined)", - "_poi_entrance:id=JSON.parse(feat.properties._poi_walls_and_buildings_entrance_properteis)?.id", - "_poi_entrance:width=JSON.parse(feat.properties._poi_walls_and_buildings_entrance_properties)?.width" + "_enclosing_building=feat.enclosingFeatures('walls_and_buildings')?.map(f => f.feat.properties.id)?.at(0)" ], - "+tagRenderings": [ + "tagRenderings+": [ { - "id": "_containing_poi_entrance:width", + "id": "_stolen_entrances", "condition": { "and": [ "entrance=", @@ -383,21 +379,11 @@ "door=" ] }, - "mappings": [{ - "if": "_poi_walls_and_buildings_entrance_properties_with_width=[]", - "then": { - "en": "The containing building has {}" - } - }], "render": { "special": { - "type": "multi", - "key": "_poi_walls_and_buildings_entrance_properties", - "tagrendering": { - "en": "The containing building can be entered via a door of {canonical(_poi_entrance:width)}", - "fr": "On peut entrer dans ce batiment via une porte de {canonical(_poi_entrance:width)}", - "de": "Das Gebäude kann über durch eine Tür von {canonical(_poi_entrance:width)} betreten werden." - } + "type": "steal", + "featureId": "_enclosing_building", + "tagRenderingId": "walls_and_buildings.entrance_info; walls_and_buildings.biggest_width" } } } diff --git a/test/Chai.spec.ts b/test/Chai.spec.ts index b7a2f54db..9a434dee5 100644 --- a/test/Chai.spec.ts +++ b/test/Chai.spec.ts @@ -1,6 +1,5 @@ import {describe} from 'mocha' import {expect} from 'chai' -import {Utils} from "../Utils"; describe("TestSuite", () => { diff --git a/test/Logic/ExtraFunctions.spec.ts b/test/Logic/ExtraFunctions.spec.ts new file mode 100644 index 000000000..0f3bb60d6 --- /dev/null +++ b/test/Logic/ExtraFunctions.spec.ts @@ -0,0 +1,243 @@ +import {describe} from 'mocha' +import {expect} from 'chai' +import {ExtraFuncParams, ExtraFunctions} from "../../Logic/ExtraFunctions"; +import {OsmFeature} from "../../Models/OsmFeature"; + + +describe("OverlapFunc", () => { + + it("should give doors on the edge", () => { + const door: OsmFeature = { + "type": "Feature", + "id": "node/9909268725", + "properties": { + "automatic_door": "no", + "door": "hinged", + "indoor": "door", + "kerb:height": "0 cm", + "width": "1", + "id": "node/9909268725", + }, + "geometry": { + "type": "Point", + "coordinates": [ + 4.3494436, + 50.8657928 + ] + }, + } + + const hermanTeirlinck = { + "type": "Feature", + "id": "way/444059131", + "properties": { + "timestamp": "2022-07-27T15:15:01Z", + "version": 27, + "changeset": 124146283, + "user": "Pieter Vander Vennet", + "uid": 3818858, + "addr:city": "Bruxelles - Brussel", + "addr:housenumber": "88", + "addr:postcode": "1000", + "addr:street": "Avenue du Port - Havenlaan", + "building": "government", + "building:levels": "5", + "name": "Herman Teirlinckgebouw", + "operator": "Vlaamse overheid", + "wikidata": "Q47457146", + "wikipedia": "nl:Herman Teirlinckgebouw", + "id": "way/444059131", + "_backend": "https://www.openstreetmap.org", + "_lat": "50.86622355", + "_lon": "4.3501212", + "_layer": "walls_and_buildings", + "_length": "380.5933566256343", + "_length:km": "0.4", + "_now:date": "2022-07-29", + "_now:datetime": "2022-07-29 14:19:25", + "_loaded:date": "2022-07-29", + "_loaded:datetime": "2022-07-29 14:19:25", + "_last_edit:contributor": "Pieter Vander Vennet", + "_last_edit:contributor:uid": 3818858, + "_last_edit:changeset": 124146283, + "_last_edit:timestamp": "2022-07-27T15:15:01Z", + "_version_number": 27, + "_geometry:type": "Polygon", + "_surface": "7461.252251355437", + "_surface:ha": "0.7", + "_country": "be" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 4.3493369, + 50.8658274 + ], + [ + 4.3493393, + 50.8658266 + ], + [ + 4.3494436, + 50.8657928 + ], + [ + 4.3495272, + 50.8657658 + ], + [ + 4.349623, + 50.8657348 + ], + [ + 4.3497442, + 50.8656956 + ], + [ + 4.3498441, + 50.8656632 + ], + [ + 4.3500768, + 50.8655878 + ], + [ + 4.3501619, + 50.8656934 + ], + [ + 4.3502113, + 50.8657551 + ], + [ + 4.3502729, + 50.8658321 + ], + [ + 4.3503063, + 50.8658737 + ], + [ + 4.3503397, + 50.8659153 + ], + [ + 4.3504159, + 50.8660101 + ], + [ + 4.3504177, + 50.8660123 + ], + [ + 4.3504354, + 50.8660345 + ], + [ + 4.3505348, + 50.8661584 + ], + [ + 4.3504935, + 50.866172 + ], + [ + 4.3506286, + 50.8663405 + ], + [ + 4.3506701, + 50.8663271 + ], + [ + 4.3508563, + 50.8665592 + ], + [ + 4.3509055, + 50.8666206 + ], + [ + 4.3506278, + 50.8667104 + ], + [ + 4.3504502, + 50.8667675 + ], + [ + 4.3503132, + 50.8668115 + ], + [ + 4.3502162, + 50.8668427 + ], + [ + 4.3501645, + 50.8668593 + ], + [ + 4.3499296, + 50.8665664 + ], + [ + 4.3498821, + 50.8665073 + ], + [ + 4.3498383, + 50.8664527 + ], + [ + 4.3498126, + 50.8664207 + ], + [ + 4.3497459, + 50.8663376 + ], + [ + 4.3497227, + 50.8663086 + ], + [ + 4.3496517, + 50.8662201 + ], + [ + 4.3495158, + 50.8660507 + ], + [ + 4.3493369, + 50.8658274 + ] + ] + ] + }, + "bbox": { + "maxLat": 50.8668593, + "maxLon": 4.3509055, + "minLat": 50.8655878, + "minLon": 4.3493369 + } + } + + const params: ExtraFuncParams = { + getFeatureById: id => undefined, + getFeaturesWithin: () => [[door]], + memberships: undefined + } + + + ExtraFunctions.FullPatchFeature(params, hermanTeirlinck) + const overlap = (hermanTeirlinck).overlapWith("*") + console.log(JSON.stringify(overlap)) + expect(overlap[0].feat == door).true + + }) + +}) \ No newline at end of file