forked from MapComplete/MapComplete
		
	Merge feature branch
This commit is contained in:
		
						commit
						d5d11d48b5
					
				
					 12 changed files with 487 additions and 69 deletions
				
			
		|  | @ -1,5 +1,6 @@ | |||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  Metatags  | ||||
| ========== | ||||
| 
 | ||||
|  |  | |||
|  | @ -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"] | ||||
| 
 | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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 = [] | ||||
|  |  | |||
|  | @ -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] | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
| 
 | ||||
|  |  | |||
|  | @ -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~*" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
|  | @ -17,6 +17,12 @@ | |||
|   "startZoom": 14, | ||||
|   "widenFactor": 2, | ||||
|   "layers": [ | ||||
|     "indoors" | ||||
|     "indoors", | ||||
|     { | ||||
|       "builtin": ["walls_and_buildings"], | ||||
|       "override": { | ||||
|         "shownByDefault": true | ||||
|       } | ||||
|     } | ||||
|   ] | ||||
| } | ||||
|  | @ -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" | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| import {describe} from 'mocha' | ||||
| import {expect} from 'chai' | ||||
| import {Utils} from "../Utils"; | ||||
| 
 | ||||
| describe("TestSuite", () => { | ||||
|      | ||||
|  |  | |||
							
								
								
									
										243
									
								
								test/Logic/ExtraFunctions.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								test/Logic/ExtraFunctions.spec.ts
									
										
									
									
									
										Normal 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 | ||||
| 
 | ||||
|     }) | ||||
| 
 | ||||
| }) | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue