| 
									
										
										
										
											2021-12-12 17:35:08 +01:00
										 |  |  | import SimpleMetaTaggers, { SimpleMetaTagger } from "./SimpleMetaTagger" | 
					
						
							| 
									
										
										
										
											2021-12-05 02:06:14 +01:00
										 |  |  | import { ExtraFuncParams, ExtraFunctions } from "./ExtraFunctions" | 
					
						
							| 
									
										
										
										
											2021-08-07 23:11:34 +02:00
										 |  |  | import LayerConfig from "../Models/ThemeConfig/LayerConfig" | 
					
						
							| 
									
										
										
										
											2022-01-26 20:47:08 +01:00
										 |  |  | import { ElementStorage } from "./ElementStorage" | 
					
						
							| 
									
										
										
										
											2021-04-22 13:30:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-19 12:08:42 +02:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Metatagging adds various tags to the elements, e.g. lat, lon, surface area, ... | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * All metatags start with an underscore | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export default class MetaTagging { | 
					
						
							| 
									
										
										
										
											2021-09-09 00:05:51 +02:00
										 |  |  |     private static errorPrintCount = 0 | 
					
						
							|  |  |  |     private static readonly stopErrorOutputAt = 10 | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  |     private static retaggingFuncCache = new Map<string, ((feature: any) => void)[]>() | 
					
						
							| 
									
										
										
										
											2021-09-09 00:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-24 01:25:57 +01:00
										 |  |  |     /** | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |      * This method (re)calculates all metatags and calculated tags on every given object. | 
					
						
							|  |  |  |      * The given features should be part of the given layer | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2021-10-27 20:19:45 +02:00
										 |  |  |      * Returns true if at least one feature has changed properties | 
					
						
							| 
									
										
										
										
											2021-03-24 01:25:57 +01:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |     public static addMetatags( | 
					
						
							|  |  |  |         features: { feature: any; freshness: Date }[], | 
					
						
							| 
									
										
										
										
											2021-09-26 17:36:39 +02:00
										 |  |  |         params: ExtraFuncParams, | 
					
						
							|  |  |  |         layer: LayerConfig, | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  |         state?: { allElements?: ElementStorage }, | 
					
						
							| 
									
										
										
										
											2021-09-26 17:36:39 +02:00
										 |  |  |         options?: { | 
					
						
							|  |  |  |             includeDates?: true | boolean | 
					
						
							| 
									
										
										
										
											2022-02-02 02:36:49 +01:00
										 |  |  |             includeNonDates?: true | boolean | 
					
						
							|  |  |  |             evaluateStrict?: false | boolean | 
					
						
							| 
									
										
										
										
											2021-10-27 20:19:45 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |     ): boolean { | 
					
						
							| 
									
										
										
										
											2021-07-26 20:21:05 +02:00
										 |  |  |         if (features === undefined || features.length === 0) { | 
					
						
							| 
									
										
										
										
											2021-06-21 03:13:49 +02:00
										 |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-10-30 00:56:46 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-29 20:04:36 +02:00
										 |  |  |         console.log("Recalculating metatags...") | 
					
						
							| 
									
										
										
										
											2021-12-12 17:35:08 +01:00
										 |  |  |         const metatagsToApply: SimpleMetaTagger[] = [] | 
					
						
							| 
									
										
										
										
											2021-12-07 02:22:56 +01:00
										 |  |  |         for (const metatag of SimpleMetaTaggers.metatags) { | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |             if (metatag.includesDates) { | 
					
						
							| 
									
										
										
										
											2022-01-26 20:47:08 +01:00
										 |  |  |                 if (options?.includeDates ?? true) { | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |                     metatagsToApply.push(metatag) | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2022-01-26 20:47:08 +01:00
										 |  |  |                 if (options?.includeNonDates ?? true) { | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |                     metatagsToApply.push(metatag) | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2020-10-30 00:56:46 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-10-30 00:56:46 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-26 17:36:39 +02:00
										 |  |  |         // The calculated functions - per layer - which add the new keys
 | 
					
						
							| 
									
										
										
										
											2021-12-12 17:35:08 +01:00
										 |  |  |         const layerFuncs = this.createRetaggingFunc(layer, state) | 
					
						
							| 
									
										
										
										
											2021-05-13 12:40:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-27 20:19:45 +02:00
										 |  |  |         let atLeastOneFeatureChanged = false | 
					
						
							| 
									
										
										
										
											2020-10-30 00:56:46 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-26 17:36:39 +02:00
										 |  |  |         for (let i = 0; i < features.length; i++) { | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |             const ff = features[i] | 
					
						
							|  |  |  |             const feature = ff.feature | 
					
						
							|  |  |  |             const freshness = ff.freshness | 
					
						
							|  |  |  |             let somethingChanged = false | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  |             let definedTags = new Set(Object.getOwnPropertyNames(feature.properties)) | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |             for (const metatag of metatagsToApply) { | 
					
						
							|  |  |  |                 try { | 
					
						
							|  |  |  |                     if (!metatag.keys.some((key) => feature.properties[key] === undefined)) { | 
					
						
							|  |  |  |                         // All keys are already defined, we probably already ran this one
 | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |                     if (metatag.isLazy) { | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  |                         if (!metatag.keys.some((key) => !definedTags.has(key))) { | 
					
						
							| 
									
										
										
										
											2022-01-06 18:51:52 +01:00
										 |  |  |                             // All keys are defined - lets skip!
 | 
					
						
							|  |  |  |                             continue | 
					
						
							|  |  |  |                         } | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |                         somethingChanged = true | 
					
						
							| 
									
										
										
										
											2021-12-12 17:35:08 +01:00
										 |  |  |                         metatag.applyMetaTagsOnFeature(feature, freshness, layer, state) | 
					
						
							| 
									
										
										
										
											2022-02-02 02:36:49 +01:00
										 |  |  |                         if (options?.evaluateStrict) { | 
					
						
							|  |  |  |                             for (const key of metatag.keys) { | 
					
						
							|  |  |  |                                 feature.properties[key] | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                         } | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |                     } else { | 
					
						
							| 
									
										
										
										
											2021-12-12 17:35:08 +01:00
										 |  |  |                         const newValueAdded = metatag.applyMetaTagsOnFeature( | 
					
						
							|  |  |  |                             feature, | 
					
						
							|  |  |  |                             freshness, | 
					
						
							|  |  |  |                             layer, | 
					
						
							|  |  |  |                             state | 
					
						
							|  |  |  |                         ) | 
					
						
							| 
									
										
										
										
											2021-10-04 00:18:08 +02:00
										 |  |  |                         /* Note that the expression: | 
					
						
							|  |  |  |                          * `somethingChanged = newValueAdded || metatag.applyMetaTagsOnFeature(feature, freshness)` | 
					
						
							|  |  |  |                          * Is WRONG | 
					
						
							|  |  |  |                          * | 
					
						
							|  |  |  |                          * IF something changed is `true` due to an earlier run, it will short-circuit and _not_ evaluate the right hand of the OR, | 
					
						
							|  |  |  |                          * thus not running an update! | 
					
						
							|  |  |  |                          */ | 
					
						
							|  |  |  |                         somethingChanged = newValueAdded || somethingChanged | 
					
						
							| 
									
										
										
										
											2021-09-26 17:36:39 +02:00
										 |  |  |                     } | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |                 } catch (e) { | 
					
						
							|  |  |  |                     console.error( | 
					
						
							|  |  |  |                         "Could not calculate metatag for ", | 
					
						
							|  |  |  |                         metatag.keys.join(","), | 
					
						
							|  |  |  |                         ":", | 
					
						
							|  |  |  |                         e, | 
					
						
							|  |  |  |                         e.stack | 
					
						
							|  |  |  |                     ) | 
					
						
							| 
									
										
										
										
											2021-06-20 03:09:26 +02:00
										 |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2020-10-30 00:56:46 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |             if (layerFuncs !== undefined) { | 
					
						
							| 
									
										
										
										
											2022-01-06 18:51:52 +01:00
										 |  |  |                 let retaggingChanged = false | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |                 try { | 
					
						
							| 
									
										
										
										
											2022-01-06 18:51:52 +01:00
										 |  |  |                     retaggingChanged = layerFuncs(params, feature) | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |                 } catch (e) { | 
					
						
							|  |  |  |                     console.error(e) | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2022-01-06 18:51:52 +01:00
										 |  |  |                 somethingChanged = somethingChanged || retaggingChanged | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-09-26 17:36:39 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |             if (somethingChanged) { | 
					
						
							| 
									
										
										
										
											2021-12-12 17:35:08 +01:00
										 |  |  |                 state?.allElements?.getEventSourceById(feature.properties.id)?.ping() | 
					
						
							| 
									
										
										
										
											2021-10-27 20:19:45 +02:00
										 |  |  |                 atLeastOneFeatureChanged = true | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2020-10-30 00:56:46 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-10-27 20:19:45 +02:00
										 |  |  |         return atLeastOneFeatureChanged | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-26 20:47:08 +01:00
										 |  |  |     private static createFunctionsForFeature( | 
					
						
							|  |  |  |         layerId: string, | 
					
						
							|  |  |  |         calculatedTags: [string, string, boolean][] | 
					
						
							|  |  |  |     ): ((feature: any) => void)[] { | 
					
						
							| 
									
										
										
										
											2022-01-07 04:14:53 +01:00
										 |  |  |         const functions: ((feature: any) => any)[] = [] | 
					
						
							| 
									
										
										
										
											2021-03-24 01:25:57 +01:00
										 |  |  |         for (const entry of calculatedTags) { | 
					
						
							|  |  |  |             const key = entry[0] | 
					
						
							|  |  |  |             const code = entry[1] | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  |             const isStrict = entry[2] | 
					
						
							| 
									
										
										
										
											2021-03-24 01:25:57 +01:00
										 |  |  |             if (code === undefined) { | 
					
						
							|  |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2021-03-22 01:04:25 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-07 04:14:53 +01:00
										 |  |  |             const calculateAndAssign: (feat: any) => any = (feat) => { | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  |                 try { | 
					
						
							|  |  |  |                     let result = new Function("feat", "return " + code + ";")(feat) | 
					
						
							|  |  |  |                     if (result === "") { | 
					
						
							|  |  |  |                         result === undefined | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     if (result !== undefined && typeof result !== "string") { | 
					
						
							|  |  |  |                         // Make sure it is a string!
 | 
					
						
							|  |  |  |                         result = JSON.stringify(result) | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     delete feat.properties[key] | 
					
						
							|  |  |  |                     feat.properties[key] = result | 
					
						
							| 
									
										
										
										
											2022-01-07 04:14:53 +01:00
										 |  |  |                     return result | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  |                 } catch (e) { | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  |                     if (MetaTagging.errorPrintCount < MetaTagging.stopErrorOutputAt) { | 
					
						
							|  |  |  |                         console.warn( | 
					
						
							|  |  |  |                             "Could not calculate a " + | 
					
						
							|  |  |  |                                 (isStrict ? "strict " : "") + | 
					
						
							|  |  |  |                                 " calculated tag for key " + | 
					
						
							|  |  |  |                                 key + | 
					
						
							|  |  |  |                                 " defined by " + | 
					
						
							|  |  |  |                                 code + | 
					
						
							|  |  |  |                                 " (in layer" + | 
					
						
							|  |  |  |                                 layerId + | 
					
						
							|  |  |  |                                 ") due to \n" + | 
					
						
							|  |  |  |                                 e + | 
					
						
							|  |  |  |                                 "\n. Are you the theme creator? Doublecheck your code. Note that the metatags might not be stable on new features", | 
					
						
							|  |  |  |                             e, | 
					
						
							|  |  |  |                             e.stack | 
					
						
							|  |  |  |                         ) | 
					
						
							|  |  |  |                         MetaTagging.errorPrintCount++ | 
					
						
							|  |  |  |                         if (MetaTagging.errorPrintCount == MetaTagging.stopErrorOutputAt) { | 
					
						
							|  |  |  |                             console.error( | 
					
						
							|  |  |  |                                 "Got ", | 
					
						
							|  |  |  |                                 MetaTagging.stopErrorOutputAt, | 
					
						
							|  |  |  |                                 " errors calculating this metatagging - stopping output now" | 
					
						
							|  |  |  |                             ) | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2022-01-07 04:14:53 +01:00
										 |  |  |                     return undefined | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (isStrict) { | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  |                 functions.push(calculateAndAssign) | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-12-11 02:19:28 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  |             // Lazy function
 | 
					
						
							|  |  |  |             const f = (feature: any) => { | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |                 delete feature.properties[key] | 
					
						
							|  |  |  |                 Object.defineProperty(feature.properties, key, { | 
					
						
							|  |  |  |                     configurable: true, | 
					
						
							|  |  |  |                     enumerable: false, // By setting this as not enumerable, the localTileSaver will _not_ calculate this
 | 
					
						
							|  |  |  |                     get: function () { | 
					
						
							| 
									
										
										
										
											2022-01-07 04:14:53 +01:00
										 |  |  |                         return calculateAndAssign(feature) | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |                     }, | 
					
						
							|  |  |  |                 }) | 
					
						
							| 
									
										
										
										
											2022-01-07 04:14:53 +01:00
										 |  |  |                 return undefined | 
					
						
							| 
									
										
										
										
											2021-05-10 23:51:03 +02:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |             functions.push(f) | 
					
						
							| 
									
										
										
										
											2021-02-20 03:29:55 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |         return functions | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-06 18:51:52 +01:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Creates the function which adds all the calculated tags to a feature. Called once per layer | 
					
						
							|  |  |  |      * @param layer | 
					
						
							|  |  |  |      * @param state | 
					
						
							|  |  |  |      * @private | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-12-12 17:35:08 +01:00
										 |  |  |     private static createRetaggingFunc( | 
					
						
							|  |  |  |         layer: LayerConfig, | 
					
						
							|  |  |  |         state | 
					
						
							| 
									
										
										
										
											2022-01-06 18:51:52 +01:00
										 |  |  |     ): (params: ExtraFuncParams, feature: any) => boolean { | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  |         const calculatedTags: [string, string, boolean][] = layer.calculatedTags | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |         if (calculatedTags === undefined || calculatedTags.length === 0) { | 
					
						
							|  |  |  |             return undefined | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  |         let functions: ((feature: any) => void)[] = MetaTagging.retaggingFuncCache.get(layer.id) | 
					
						
							| 
									
										
										
										
											2021-12-11 02:19:28 +01:00
										 |  |  |         if (functions === undefined) { | 
					
						
							|  |  |  |             functions = MetaTagging.createFunctionsForFeature(layer.id, calculatedTags) | 
					
						
							|  |  |  |             MetaTagging.retaggingFuncCache.set(layer.id, functions) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |         return (params: ExtraFuncParams, feature) => { | 
					
						
							| 
									
										
										
										
											2021-03-24 01:25:57 +01:00
										 |  |  |             const tags = feature.properties | 
					
						
							|  |  |  |             if (tags === undefined) { | 
					
						
							|  |  |  |                 return | 
					
						
							| 
									
										
										
										
											2020-11-11 16:23:49 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-03-22 02:45:22 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-26 03:24:58 +01:00
										 |  |  |             try { | 
					
						
							| 
									
										
										
										
											2021-12-05 02:06:14 +01:00
										 |  |  |                 ExtraFunctions.FullPatchFeature(params, feature) | 
					
						
							| 
									
										
										
										
											2021-03-26 03:24:58 +01:00
										 |  |  |                 for (const f of functions) { | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |                     f(feature) | 
					
						
							| 
									
										
										
										
											2021-03-24 01:25:57 +01:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2021-03-26 03:24:58 +01:00
										 |  |  |             } catch (e) { | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |                 console.error("Invalid syntax in calculated tags or some other error: ", e) | 
					
						
							| 
									
										
										
										
											2021-03-24 01:25:57 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-01-06 18:51:52 +01:00
										 |  |  |             return true // Something changed
 | 
					
						
							| 
									
										
										
										
											2021-03-22 01:04:25 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-10-19 12:08:42 +02:00
										 |  |  | } |