| 
									
										
										
										
											2021-07-16 01:42:09 +02:00
										 |  |  | import {SubtleButton} from "../Base/SubtleButton"; | 
					
						
							|  |  |  | import Svg from "../../Svg"; | 
					
						
							|  |  |  | import Translations from "../i18n/Translations"; | 
					
						
							|  |  |  | import State from "../../State"; | 
					
						
							|  |  |  | import {Utils} from "../../Utils"; | 
					
						
							|  |  |  | import Combine from "../Base/Combine"; | 
					
						
							| 
									
										
										
										
											2021-07-26 20:59:55 +02:00
										 |  |  | import CheckBoxes from "../Input/Checkboxes"; | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  | import {GeoOperations} from "../../Logic/GeoOperations"; | 
					
						
							| 
									
										
										
										
											2021-07-26 20:59:55 +02:00
										 |  |  | import Toggle from "../Input/Toggle"; | 
					
						
							|  |  |  | import Title from "../Base/Title"; | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  | import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline"; | 
					
						
							|  |  |  | import {UIEventSource} from "../../Logic/UIEventSource"; | 
					
						
							|  |  |  | import SimpleMetaTagger from "../../Logic/SimpleMetaTagger"; | 
					
						
							|  |  |  | import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  | import {BBox} from "../../Logic/BBox"; | 
					
						
							| 
									
										
										
										
											2022-02-16 01:34:28 +01:00
										 |  |  | import FilteredLayer, {FilterState} from "../../Models/FilteredLayer"; | 
					
						
							| 
									
										
										
										
											2022-03-18 01:21:00 +01:00
										 |  |  | import geojson2svg from "geojson2svg" | 
					
						
							|  |  |  | import Constants from "../../Models/Constants"; | 
					
						
							|  |  |  | import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; | 
					
						
							| 
									
										
										
										
											2021-07-16 01:42:09 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-26 21:03:27 +02:00
										 |  |  | export class DownloadPanel extends Toggle { | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-16 01:34:28 +01:00
										 |  |  |     constructor(state: { | 
					
						
							|  |  |  |         filteredLayers: UIEventSource<FilteredLayer[]> | 
					
						
							|  |  |  |         featurePipeline: FeaturePipeline, | 
					
						
							|  |  |  |         layoutToUse: LayoutConfig, | 
					
						
							| 
									
										
										
										
											2022-03-18 01:21:00 +01:00
										 |  |  |         currentBounds: UIEventSource<BBox>, | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-18 01:21:00 +01:00
										 |  |  |     }) { | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-16 01:42:09 +02:00
										 |  |  |         const t = Translations.t.general.download | 
					
						
							| 
									
										
										
										
											2021-09-28 18:00:44 +02:00
										 |  |  |         const name = State.state.layoutToUse.id; | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-17 23:19:20 +01:00
										 |  |  |         const includeMetaToggle = new CheckBoxes([t.includeMetaData]) | 
					
						
							| 
									
										
										
										
											2021-07-26 20:59:55 +02:00
										 |  |  |         const metaisIncluded = includeMetaToggle.GetValue().map(selected => selected.length > 0) | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-26 21:03:27 +02:00
										 |  |  |         const buttonGeoJson = new SubtleButton(Svg.floppy_ui(), | 
					
						
							| 
									
										
										
										
											2022-03-17 23:19:20 +01:00
										 |  |  |             new Combine([t.downloadGeojson.SetClass("font-bold"), | 
					
						
							|  |  |  |                 t.downloadGeoJsonHelper]).SetClass("flex flex-col")) | 
					
						
							| 
									
										
										
										
											2022-03-18 01:21:00 +01:00
										 |  |  |             .OnClickWithLoading(t.exporting, async () => { | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |                 const geojson = DownloadPanel.getCleanGeoJson(state, metaisIncluded.data) | 
					
						
							|  |  |  |                 Utils.offerContentsAsDownloadableFile(JSON.stringify(geojson, null, "  "), | 
					
						
							| 
									
										
										
										
											2021-07-26 21:03:27 +02:00
										 |  |  |                     `MapComplete_${name}_export_${new Date().toISOString().substr(0, 19)}.geojson`, { | 
					
						
							|  |  |  |                         mimetype: "application/vnd.geo+json" | 
					
						
							|  |  |  |                     }); | 
					
						
							| 
									
										
										
										
											2021-07-16 01:42:09 +02:00
										 |  |  |             }) | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-26 21:03:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         const buttonCSV = new SubtleButton(Svg.floppy_ui(), new Combine( | 
					
						
							| 
									
										
										
										
											2022-03-17 23:19:20 +01:00
										 |  |  |             [t.downloadCSV.SetClass("font-bold"), | 
					
						
							|  |  |  |                 t.downloadCSVHelper]).SetClass("flex flex-col")) | 
					
						
							| 
									
										
										
										
											2022-02-16 01:34:28 +01:00
										 |  |  |             .OnClickWithLoading(t.exporting, async () => { | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |                 const geojson = DownloadPanel.getCleanGeoJson(state, metaisIncluded.data) | 
					
						
							| 
									
										
										
										
											2021-07-26 20:59:55 +02:00
										 |  |  |                 const csv = GeoOperations.toCSV(geojson.features) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 Utils.offerContentsAsDownloadableFile(csv, | 
					
						
							| 
									
										
										
										
											2021-07-26 21:03:27 +02:00
										 |  |  |                     `MapComplete_${name}_export_${new Date().toISOString().substr(0, 19)}.csv`, { | 
					
						
							|  |  |  |                         mimetype: "text/csv" | 
					
						
							| 
									
										
										
										
											2021-07-26 20:59:55 +02:00
										 |  |  |                     }); | 
					
						
							|  |  |  |             }) | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-18 01:21:00 +01:00
										 |  |  |         const buttonSvg = new SubtleButton(Svg.floppy_ui(), new Combine( | 
					
						
							|  |  |  |             [t.downloadAsSvg.SetClass("font-bold"), | 
					
						
							|  |  |  |                 t.downloadAsSvgHelper]).SetClass("flex flex-col")) | 
					
						
							|  |  |  |             .OnClickWithLoading(t.exporting, async () => { | 
					
						
							|  |  |  |                 const geojson = DownloadPanel.getCleanGeoJsonPerLayer(state, metaisIncluded.data) | 
					
						
							|  |  |  |                 const leafletdiv = document.getElementById("leafletDiv") | 
					
						
							|  |  |  |                 const csv = DownloadPanel.asSvg(geojson,  | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         layers: state.filteredLayers.data.map(l => l.layerDef), | 
					
						
							|  |  |  |                     mapExtent: state.currentBounds.data, | 
					
						
							|  |  |  |                     width: leafletdiv.offsetWidth, | 
					
						
							|  |  |  |                     height: leafletdiv.offsetHeight | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 Utils.offerContentsAsDownloadableFile(csv, | 
					
						
							|  |  |  |                     `MapComplete_${name}_export_${new Date().toISOString().substr(0, 19)}.svg`, { | 
					
						
							|  |  |  |                         mimetype: "image/svg+xml" | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-26 20:59:55 +02:00
										 |  |  |         const downloadButtons = new Combine( | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |             [new Title(t.title), | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |                 buttonGeoJson, | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |                 buttonCSV, | 
					
						
							| 
									
										
										
										
											2022-03-18 01:21:00 +01:00
										 |  |  |                 buttonSvg, | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |                 includeMetaToggle, | 
					
						
							| 
									
										
										
										
											2022-03-17 23:19:20 +01:00
										 |  |  |                 t.licenseInfo.SetClass("link-underline")]) | 
					
						
							| 
									
										
										
										
											2021-07-26 21:03:27 +02:00
										 |  |  |             .SetClass("w-full flex flex-col border-4 border-gray-300 rounded-3xl p-4") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-26 20:59:55 +02:00
										 |  |  |         super( | 
					
						
							|  |  |  |             downloadButtons, | 
					
						
							| 
									
										
										
										
											2022-03-17 23:19:20 +01:00
										 |  |  |             t.noDataLoaded, | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |             state.featurePipeline.somethingLoaded) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-18 01:21:00 +01:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Converts a geojson to an SVG | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * const feature = { | 
					
						
							|  |  |  |      *       "type": "Feature", | 
					
						
							|  |  |  |      *       "properties": {}, | 
					
						
							|  |  |  |      *       "geometry": { | 
					
						
							|  |  |  |      *         "type": "LineString", | 
					
						
							|  |  |  |      *         "coordinates": [ | 
					
						
							|  |  |  |      *           [-180, 80], | 
					
						
							|  |  |  |      *           [180, -80] | 
					
						
							|  |  |  |      *         ] | 
					
						
							|  |  |  |      *       } | 
					
						
							|  |  |  |      * } | 
					
						
							|  |  |  |      * const perLayer = new Map<string, any[]>([["testlayer", [feature]]]) | 
					
						
							|  |  |  |      * DownloadPanel.asSvg(perLayer).replace(/\n/g, "") // => `<svg width="1000px" height="1000px" viewBox="0 0 1000 1000">    <g id="testlayer" inkscape:groupmode="layer" inkscape:label="testlayer">        <path d="M0,27.77777777777778 1000,472.22222222222223" style="fill:none;stroke-width:1" stroke="#ff0000"/>    </g></svg>`
 | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public static asSvg(perLayer: Map<string, any[]>, | 
					
						
							|  |  |  |                         options?: | 
					
						
							|  |  |  |                             { | 
					
						
							|  |  |  |                                 layers?: LayerConfig[], | 
					
						
							|  |  |  |                                 width?: 1000 | number, | 
					
						
							|  |  |  |                                 height?: 1000 | number, | 
					
						
							|  |  |  |                                 mapExtent?: BBox | 
					
						
							|  |  |  |                                 unit?: "px" | "mm" | string | 
					
						
							|  |  |  |                             }) { | 
					
						
							|  |  |  |         options = options ?? {} | 
					
						
							|  |  |  |         const w = options.width ?? 1000 | 
					
						
							|  |  |  |         const h = options.height ?? 1000 | 
					
						
							|  |  |  |         const unit = options.unit ?? "px" | 
					
						
							|  |  |  |         const mapExtent = {left: -180, bottom: -90, right: 180, top: 90} | 
					
						
							|  |  |  |         if (options.mapExtent !== undefined) { | 
					
						
							|  |  |  |             const bbox = options.mapExtent | 
					
						
							|  |  |  |             mapExtent.left = bbox.minLon | 
					
						
							|  |  |  |             mapExtent.right = bbox.maxLon | 
					
						
							|  |  |  |             mapExtent.bottom = bbox.minLat | 
					
						
							|  |  |  |             mapExtent.top = bbox.maxLat | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const elements: string [] = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (const layer of Array.from(perLayer.keys())) { | 
					
						
							|  |  |  |             const features = perLayer.get(layer) | 
					
						
							|  |  |  |             if(features.length === 0){ | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             const layerDef = options?.layers?.find(l => l.id === layer) | 
					
						
							|  |  |  |             const rendering = layerDef?.lineRendering[0] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 const converter = geojson2svg({ | 
					
						
							|  |  |  |                 viewportSize: {width: w, height: h}, | 
					
						
							|  |  |  |                 mapExtent, | 
					
						
							|  |  |  |                 output: 'svg', | 
					
						
							|  |  |  |                 attributes:[ | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         property: "style", | 
					
						
							|  |  |  |                         type:'static', | 
					
						
							|  |  |  |                         value: "fill:none;stroke-width:1" | 
					
						
							|  |  |  |                     }, | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         property: 'properties.stroke', | 
					
						
							|  |  |  |                         type:'dynamic', | 
					
						
							|  |  |  |                         key: 'stroke' | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for (const feature of features) { | 
					
						
							|  |  |  |                 const stroke = rendering?.color?.GetRenderValue(feature.properties)?.txt ?? "#ff0000" | 
					
						
							|  |  |  |                 const color = Utils.colorAsHex( Utils.color(stroke)) | 
					
						
							|  |  |  |                 feature.properties.stroke = color | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |             const groupPaths: string[] = converter.convert({type: "FeatureCollection", features}) | 
					
						
							|  |  |  |             const group = `    <g id="${layer}" inkscape:groupmode="layer" inkscape:label="${layer}">\n` + | 
					
						
							|  |  |  |                 groupPaths.map(p => "        " + p).join("\n") | 
					
						
							|  |  |  |                 + "\n    </g>" | 
					
						
							|  |  |  |             elements.push(group) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const header = `<svg width="${w}${unit}" height="${h}${unit}" viewBox="0 0 ${w} ${h}">` | 
					
						
							|  |  |  |         return header + "\n" + elements.join("\n") + "\n</svg>" | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Gets all geojson as geojson feature | 
					
						
							|  |  |  |      * @param state | 
					
						
							|  |  |  |      * @param includeMetaData | 
					
						
							|  |  |  |      * @private | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |     private static getCleanGeoJson(state: { | 
					
						
							|  |  |  |         featurePipeline: FeaturePipeline, | 
					
						
							| 
									
										
										
										
											2022-02-16 01:34:28 +01:00
										 |  |  |         currentBounds: UIEventSource<BBox>, | 
					
						
							|  |  |  |         filteredLayers: UIEventSource<FilteredLayer[]> | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |     }, includeMetaData: boolean) { | 
					
						
							| 
									
										
										
										
											2022-03-18 01:21:00 +01:00
										 |  |  |         const perLayer = DownloadPanel.getCleanGeoJsonPerLayer(state, includeMetaData) | 
					
						
							|  |  |  |         const features = [].concat(...Array.from(perLayer.values())) | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |             type: "FeatureCollection", | 
					
						
							|  |  |  |             features | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private static getCleanGeoJsonPerLayer(state: { | 
					
						
							|  |  |  |         featurePipeline: FeaturePipeline, | 
					
						
							|  |  |  |         currentBounds: UIEventSource<BBox>, | 
					
						
							|  |  |  |         filteredLayers: UIEventSource<FilteredLayer[]> | 
					
						
							|  |  |  |     }, includeMetaData: boolean): Map<string, any[]> /*{layerId --> geojsonFeatures[]}*/ { | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-18 01:21:00 +01:00
										 |  |  |         const perLayer = new Map<string, any[]>(); | 
					
						
							| 
									
										
										
										
											2022-02-16 01:34:28 +01:00
										 |  |  |         const neededLayers = state.filteredLayers.data.map(l => l.layerDef.id) | 
					
						
							|  |  |  |         const bbox = state.currentBounds.data | 
					
						
							|  |  |  |         const featureList = state.featurePipeline.GetAllFeaturesAndMetaWithin(bbox, new Set(neededLayers)); | 
					
						
							| 
									
										
										
										
											2022-03-18 01:21:00 +01:00
										 |  |  |         outer : for (const tile of featureList) { | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |             if(Constants.priviliged_layers.indexOf(tile.layer) >= 0){ | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |              | 
					
						
							| 
									
										
										
										
											2022-02-16 01:34:28 +01:00
										 |  |  |             const layer = state.filteredLayers.data.find(fl => fl.layerDef.id === tile.layer) | 
					
						
							| 
									
										
										
										
											2022-03-18 01:21:00 +01:00
										 |  |  |             if (!perLayer.has(tile.layer)) { | 
					
						
							|  |  |  |                 perLayer.set(tile.layer, []) | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             const featureList = perLayer.get(tile.layer) | 
					
						
							| 
									
										
										
										
											2022-02-16 01:34:28 +01:00
										 |  |  |             const filters = layer.appliedFilters.data | 
					
						
							|  |  |  |             for (const feature of tile.features) { | 
					
						
							| 
									
										
										
										
											2022-03-18 01:21:00 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 if (!bbox.overlapsWith(BBox.get(feature))) { | 
					
						
							| 
									
										
										
										
											2022-02-16 01:34:28 +01:00
										 |  |  |                     continue | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2022-03-18 01:21:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-16 01:34:28 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 if (filters !== undefined) { | 
					
						
							|  |  |  |                     for (let key of Array.from(filters.keys())) { | 
					
						
							|  |  |  |                         const filter: FilterState = filters.get(key) | 
					
						
							| 
									
										
										
										
											2022-03-18 01:21:00 +01:00
										 |  |  |                         if (filter?.currentFilter === undefined) { | 
					
						
							| 
									
										
										
										
											2022-02-16 01:34:28 +01:00
										 |  |  |                             continue | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                         if (!filter.currentFilter.matchesProperties(feature.properties)) { | 
					
						
							| 
									
										
										
										
											2022-03-18 01:21:00 +01:00
										 |  |  |                             continue outer; | 
					
						
							| 
									
										
										
										
											2022-02-16 01:34:28 +01:00
										 |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |                 const cleaned = { | 
					
						
							|  |  |  |                     type: feature.type, | 
					
						
							| 
									
										
										
										
											2022-03-17 23:19:20 +01:00
										 |  |  |                     geometry: {...feature.geometry}, | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |                     properties: {...feature.properties} | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (!includeMetaData) { | 
					
						
							|  |  |  |                     for (const key in cleaned.properties) { | 
					
						
							|  |  |  |                         if (key === "_lon" || key === "_lat") { | 
					
						
							|  |  |  |                             continue; | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                         if (key.startsWith("_")) { | 
					
						
							|  |  |  |                             delete feature.properties[key] | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 const datedKeys = [].concat(SimpleMetaTagger.metatags.filter(tagging => tagging.includesDates).map(tagging => tagging.keys)) | 
					
						
							|  |  |  |                 for (const key of datedKeys) { | 
					
						
							|  |  |  |                     delete feature.properties[key] | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-18 01:21:00 +01:00
										 |  |  |                 featureList.push(feature) | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-18 01:21:00 +01:00
										 |  |  |         return perLayer | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-16 01:42:09 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | } |