Merge feature branch

This commit is contained in:
Pieter Vander Vennet 2022-07-29 20:05:55 +02:00
commit d5d11d48b5
12 changed files with 487 additions and 69 deletions

View file

@ -1,5 +1,6 @@
Metatags
==========

View file

@ -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<Geometry, {id: string}>[][],
getFeaturesWithin: (layerId: string, bbox: BBox) => Feature<Geometry, { id: string }>[][],
memberships: RelationsTracker
getFeatureById: (id: string) => Feature<Geometry, {id: string}>
getFeatureById: (id: string) => Feature<Geometry, { id: string }>
}
/**
@ -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<Geometry, any>) {
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, <Feature<Polygon | MultiPolygon, any>> otherFeature)){
if (GeoOperations.completelyWithin(feat, <Feature<Polygon | MultiPolygon, any>>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<string>()
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;
}
}
@ -187,7 +195,7 @@ class DistanceToFunc implements ExtraFunction {
class ClosestObjectFunc implements ExtraFunction {
_name = "closest"
_doc = "Given either a list of geojson features or a single layer name, gives the single object which is nearest to the feature. In the case of ways/polygons, only the centerpoint is considered. Returns a single geojson feature or undefined if nothing is found (or not yet laoded)"
_doc = "Given either a list of geojson features or a single layer name, gives the single object which is nearest to the feature. In the case of ways/polygons, only the centerpoint is considered. Returns a single geojson feature or undefined if nothing is found (or not yet loaded)"
_args = ["list of features or a layer name or '*' to get all features"]

View file

@ -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) {

View file

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

View file

@ -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) {

View file

@ -378,6 +378,25 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
* 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": "<h3>Entrances</h3>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 <a href='#{id}'>entrance</a> of {canonical(width)}"
* }
* }}
* const errors = []
* RewriteSpecial.convertIfNeeded(special, errors, "test") // => {"en": "<h3>Entrances</h3>This building has {_entrances_count} entrances: {multi(_entrance_properties_with_width,An <a href='#&LBRACEid&RBRACE'>entrance</a> of &LBRACEcanonical&LPARENSwidth&RPARENS&RBRACE)}An <a href='#{id}'>entrance</a> 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<TagRenderingConfigJson> {
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<TagRenderingConfigJson> {
// 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
@ -469,6 +484,8 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
.replace(/\{/g, "&LBRACE")
.replace(/}/g, "&RBRACE")
args.push(txt)
} else if(typeof v === "object"){
args.push(JSON.stringify(v))
} else {
args.push(v)
}
@ -494,12 +511,21 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
* 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 = []

View file

@ -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",
@ -1202,6 +1209,96 @@ 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({
render: {
special: {
type: "multi",
key: "_doors_from_building_properties",
tagRendering: {
render: "The building containing this feature has a <a href='#{id}'>door</a> of width {entrance:width}"
}
}
}
}, null, " ") + "```",
args: [
{
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
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<any>(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]
}
}
]

View file

@ -47,34 +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": "<a href ='#{_entrance:id} '>This door has a width of {canonical(_entrance:width)} meters </a>",
"nl": "<a href ='#{_entrance:id} '>Deze deur heeft een breedte van {canonical(_entrance:width)} meter </a>",
"de": "<a href ='#{_entrance:id} '>Diese Tür hat eine Durchgangsbreite von {canonical(_entrance:width)} Meter </a>",
"es": "<a href ='#{_entrance:id} '>Esta puerta tiene una ancho de {canonical(_entrance:width)} metros </a>",
"fr": "<a href ='#{_entrance:id} '>Cette porte a une largeur de {canonical(_entrance:width)} mètres </a>"
},
"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",
"nl": "Deze toegang heeft geen informatie over deurbreedte"
"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": "<h3>Entrances</h3>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 <a href='#{id}'>entrance</a> 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 <a href='#{_biggest_width_id}'>entrance with the biggest width</a> is {canonical(_biggest_width)} wide",
"condition": "_biggest_width_id~*"
}
]
}

View file

@ -17,6 +17,12 @@
"startZoom": 14,
"widenFactor": 2,
"layers": [
"indoors"
"indoors",
{
"builtin": ["walls_and_buildings"],
"override": {
"shownByDefault": true
}
}
]
}

View file

@ -369,13 +369,11 @@
],
"overrideAll": {
"+calculatedTags": [
"_poi_walls_and_buildings_entrance_properties=feat.closestn('walls_and_buildings', 1, undefined, 1000).map(w => ({id: w.feat.properties.id, width: w.feat.properties['_entrance:width']}))[0]",
"_poi_entrance:id=JSON.parce(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=",
@ -385,26 +383,12 @@
]
},
"render": {
"en": "The containing building can be entered via <a href='#{_poi_entrance:id}'>a door of {canonical(_poi_entrance:width)}</a>",
"nl": "Het gebouw waarin dit zich bevindt kan binnengegaan worden <a href='#{_poi_entrance:id}'>via een deur</a> die {canonical(_poi_entrance:width)} breed is",
"fr": "On peut entrer dans ce batiment via <a href='#{_poi_entrance:id}'>une porte de {canonical(_poi_entrance:width)}</a>",
"de": "Das Gebäude kann über <a href='#{_poi_entrance:id}'>durch eine Tür von {canonical(_poi_entrance:width)} betreten werden.</a>"
},
"freeform": {
"key": "_poi_entrance:width",
"type": "distance"
},
"mappings": [
{
"if": "_poi_entrance:width=",
"then": {
"nl": "Het omvattende gebouw heeft geen gekende deurbreedtes. Voeg een deur en breedte toe.",
"en": "The containing building has no information on door widths. Add a door and measure the width to get information",
"fr": "Ce bâtiment n'a aucune information sur les largeurs de portes. Ajoutez une porte et mesurez la largeur pour obtenir des informations",
"de": "Das Gebäude hat keine Informationen über Türbreiten. Fügen Sie eine Tür hinzu und messen Sie die Breite, um Informationen zu erhalten"
"special": {
"type": "steal",
"featureId": "_enclosing_building",
"tagRenderingId": "walls_and_buildings.entrance_info; walls_and_buildings.biggest_width"
}
}
]
}
]
},

View file

@ -1,6 +1,5 @@
import {describe} from 'mocha'
import {expect} from 'chai'
import {Utils} from "../Utils";
describe("TestSuite", () => {

View file

@ -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 = (<any>hermanTeirlinck).overlapWith("*")
console.log(JSON.stringify(overlap))
expect(overlap[0].feat == door).true
})
})