diff --git a/package.json b/package.json index 3b26cf81093..6f2e781cbac 100644 --- a/package.json +++ b/package.json @@ -139,6 +139,7 @@ "@types/logfmt": "^1.2.1", "@types/mousetrap": "1.6.9", "@types/node": "16.11.36", + "@types/ol-ext": "npm:@siedlerchr/types-ol-ext@2.0.0", "@types/papaparse": "5.3.2", "@types/pluralize": "^0.0.29", "@types/prismjs": "1.26.0", @@ -337,6 +338,7 @@ "mousetrap-global-bind": "1.1.0", "moveable": "0.30.0", "ol": "6.14.1", + "ol-ext": "^3.2.25", "papaparse": "5.3.2", "pluralize": "^8.0.0", "prismjs": "1.28.0", diff --git a/packages/grafana-data/src/geo/layer.ts b/packages/grafana-data/src/geo/layer.ts index 10252788728..f873f4fb246 100644 --- a/packages/grafana-data/src/geo/layer.ts +++ b/packages/grafana-data/src/geo/layer.ts @@ -2,6 +2,7 @@ import { PluggableMap } from 'ol'; import BaseLayer from 'ol/layer/Base'; import { ReactNode } from 'react'; +import { EventBus } from '../events'; import { GrafanaTheme2 } from '../themes'; import { MatcherConfig, PanelData } from '../types'; import { PanelOptionsEditorBuilder } from '../utils'; @@ -79,6 +80,11 @@ export interface MapLayerHandler { * The update function should only be implemented if the layer type makes use of query data */ update?: (data: PanelData) => void; + + /** Optional callback to cleaup before getting removed */ + dispose?: () => void; + + /** return react node for the legend */ legend?: ReactNode; /** @@ -112,5 +118,10 @@ export interface MapLayerRegistryItem extends Registr * Function that configures transformation and returns a transformer * @param options */ - create: (map: PluggableMap, options: MapLayerOptions, theme: GrafanaTheme2) => Promise; + create: ( + map: PluggableMap, + options: MapLayerOptions, + eventBus: EventBus, + theme: GrafanaTheme2 + ) => Promise; } diff --git a/public/app/plugins/panel/geomap/GeomapPanel.tsx b/public/app/plugins/panel/geomap/GeomapPanel.tsx index effdc6e63b3..92203974659 100644 --- a/public/app/plugins/panel/geomap/GeomapPanel.tsx +++ b/public/app/plugins/panel/geomap/GeomapPanel.tsx @@ -100,6 +100,13 @@ export class GeomapPanel extends Component { this.panelContext = this.context as PanelContext; } + componentWillUnmount() { + this.subs.unsubscribe(); + for (const lyr of this.layers) { + lyr.handler.dispose?.(); + } + } + shouldComponentUpdate(nextProps: Props) { if (!this.map) { return true; // not yet initialized @@ -404,7 +411,7 @@ export class GeomapPanel extends Component { { layerFilter: (l) => { const hoverLayerState = (l as any).__state as MapLayerState; - return hoverLayerState.options.tooltip !== false; + return hoverLayerState?.options?.tooltip !== false; }, } ); @@ -467,6 +474,7 @@ export class GeomapPanel extends Component { const layers = this.layers.slice(0); try { const info = await this.initLayer(this.map, newOptions, current.isBasemap); + layers[layerIndex]?.handler.dispose?.(); layers[layerIndex] = info; group.setAt(layerIndex, info.layer); @@ -504,7 +512,7 @@ export class GeomapPanel extends Component { return Promise.reject('unknown layer: ' + options.type); } - const handler = await item.create(map, options, config.theme2); + const handler = await item.create(map, options, this.props.eventBus, config.theme2); const layer = handler.init(); if (options.opacity != null) { layer.setOpacity(1 - options.opacity); diff --git a/public/app/plugins/panel/geomap/globalStyles.ts b/public/app/plugins/panel/geomap/globalStyles.ts index 2d0f6f86001..b1675a635cf 100644 --- a/public/app/plugins/panel/geomap/globalStyles.ts +++ b/public/app/plugins/panel/geomap/globalStyles.ts @@ -3,6 +3,7 @@ import { css } from '@emotion/react'; import { GrafanaTheme2 } from '@grafana/data'; import 'ol/ol.css'; +import 'ol-ext/dist/ol-ext.css'; /** * Will be loaded *after* the css above diff --git a/public/app/plugins/panel/geomap/layers/basemaps/carto.ts b/public/app/plugins/panel/geomap/layers/basemaps/carto.ts index 25398f9a04d..9a1b0fb1a5d 100644 --- a/public/app/plugins/panel/geomap/layers/basemaps/carto.ts +++ b/public/app/plugins/panel/geomap/layers/basemaps/carto.ts @@ -2,7 +2,7 @@ import Map from 'ol/Map'; import TileLayer from 'ol/layer/Tile'; import XYZ from 'ol/source/XYZ'; -import { MapLayerRegistryItem, MapLayerOptions, GrafanaTheme2 } from '@grafana/data'; +import { MapLayerRegistryItem, MapLayerOptions, GrafanaTheme2, EventBus } from '@grafana/data'; // https://carto.com/help/building-maps/basemap-list/ @@ -33,7 +33,7 @@ export const carto: MapLayerRegistryItem = { * Function that configures transformation and returns a transformer * @param options */ - create: async (map: Map, options: MapLayerOptions, theme: GrafanaTheme2) => ({ + create: async (map: Map, options: MapLayerOptions, eventBus: EventBus, theme: GrafanaTheme2) => ({ init: () => { const cfg = { ...defaultCartoConfig, ...options.config }; let style = cfg.theme as string; diff --git a/public/app/plugins/panel/geomap/layers/basemaps/esri.ts b/public/app/plugins/panel/geomap/layers/basemaps/esri.ts index 321230129cf..87c3bb96170 100644 --- a/public/app/plugins/panel/geomap/layers/basemaps/esri.ts +++ b/public/app/plugins/panel/geomap/layers/basemaps/esri.ts @@ -1,6 +1,6 @@ import Map from 'ol/Map'; -import { MapLayerRegistryItem, MapLayerOptions, GrafanaTheme2, RegistryItem, Registry } from '@grafana/data'; +import { MapLayerRegistryItem, MapLayerOptions, GrafanaTheme2, RegistryItem, Registry, EventBus } from '@grafana/data'; import { xyzTiles, defaultXYZConfig, XYZConfig } from './generic'; @@ -60,7 +60,7 @@ export const esriXYZTiles: MapLayerRegistryItem = { description: 'Add layer from an ESRI ArcGIS MapServer', isBaseMap: true, - create: async (map: Map, options: MapLayerOptions, theme: GrafanaTheme2) => { + create: async (map: Map, options: MapLayerOptions, eventBus: EventBus, theme: GrafanaTheme2) => { const cfg = { ...options.config }; const svc = publicServiceRegistry.getIfExists(cfg.server ?? DEFAULT_SERVICE)!; if (svc.id !== CUSTOM_SERVICE) { @@ -69,7 +69,7 @@ export const esriXYZTiles: MapLayerRegistryItem = { cfg.attribution = `Tiles © ArcGIS`; } const opts = { ...options, config: cfg as XYZConfig }; - return xyzTiles.create(map, opts, theme).then((xyz) => { + return xyzTiles.create(map, opts, eventBus, theme).then((xyz) => { xyz.registerOptionsUI = (builder) => { builder .addSelect({ diff --git a/public/app/plugins/panel/geomap/layers/basemaps/generic.ts b/public/app/plugins/panel/geomap/layers/basemaps/generic.ts index 10feba2f1d2..1642465584c 100644 --- a/public/app/plugins/panel/geomap/layers/basemaps/generic.ts +++ b/public/app/plugins/panel/geomap/layers/basemaps/generic.ts @@ -2,7 +2,7 @@ import Map from 'ol/Map'; import TileLayer from 'ol/layer/Tile'; import XYZ from 'ol/source/XYZ'; -import { MapLayerRegistryItem, MapLayerOptions, GrafanaTheme2 } from '@grafana/data'; +import { MapLayerRegistryItem, MapLayerOptions, GrafanaTheme2, EventBus } from '@grafana/data'; export interface XYZConfig { url: string; @@ -23,7 +23,7 @@ export const xyzTiles: MapLayerRegistryItem = { description: 'Add map from a generic tile layer', isBaseMap: true, - create: async (map: Map, options: MapLayerOptions, theme: GrafanaTheme2) => ({ + create: async (map: Map, options: MapLayerOptions, eventBus: EventBus, theme: GrafanaTheme2) => ({ init: () => { const cfg = { ...options.config }; if (!cfg.url) { diff --git a/public/app/plugins/panel/geomap/layers/basemaps/osm.ts b/public/app/plugins/panel/geomap/layers/basemaps/osm.ts index 9808235541e..b826b79a0c4 100644 --- a/public/app/plugins/panel/geomap/layers/basemaps/osm.ts +++ b/public/app/plugins/panel/geomap/layers/basemaps/osm.ts @@ -2,7 +2,7 @@ import Map from 'ol/Map'; import TileLayer from 'ol/layer/Tile'; import OSM from 'ol/source/OSM'; -import { MapLayerRegistryItem, MapLayerOptions } from '@grafana/data'; +import { MapLayerRegistryItem, MapLayerOptions, EventBus } from '@grafana/data'; export const standard: MapLayerRegistryItem = { id: 'osm-standard', @@ -14,7 +14,7 @@ export const standard: MapLayerRegistryItem = { * Function that configures transformation and returns a transformer * @param options */ - create: async (map: Map, options: MapLayerOptions) => ({ + create: async (map: Map, options: MapLayerOptions, eventBus: EventBus) => ({ init: () => { return new TileLayer({ source: new OSM(), diff --git a/public/app/plugins/panel/geomap/layers/data/dayNightLayer.tsx b/public/app/plugins/panel/geomap/layers/data/dayNightLayer.tsx new file mode 100644 index 00000000000..488b3556b74 --- /dev/null +++ b/public/app/plugins/panel/geomap/layers/data/dayNightLayer.tsx @@ -0,0 +1,261 @@ +import { + MapLayerRegistryItem, + MapLayerOptions, + PanelData, + GrafanaTheme2, + EventBus, + DataHoverEvent, + DataHoverClearEvent, + PluginState, +} from '@grafana/data'; +import Map from 'ol/Map'; +import VectorLayer from 'ol/layer/Vector'; +import VectorSource from 'ol/source/Vector'; +import { Fill, Stroke, Style, Circle } from 'ol/style'; +import {Group as LayerGroup} from 'ol/layer'; +import Feature from 'ol/Feature'; +import Point from 'ol/geom/Point'; + +import DayNight from 'ol-ext/source/DayNight'; +import { fromLonLat } from 'ol/proj'; +import { Subscription } from 'rxjs'; +import { MultiLineString } from 'ol/geom'; +import { Coordinate } from 'ol/coordinate'; + +export enum ShowTime { + From = 'from', + To = 'to', +} + +// Configuration options for Circle overlays +export interface DayNightConfig { + show: ShowTime; + sun: boolean; + nightColor: string; +} + +const defaultConfig: DayNightConfig = { + show: ShowTime.To, + sun: false, + nightColor: '#a7a6ba4D' +}; + +export const DAY_NIGHT_LAYER_ID = 'dayNight'; + +// Used by default when nothing is configured +export const defaultDayNightConfig: MapLayerOptions = { + type: DAY_NIGHT_LAYER_ID, + name: '', // will get replaced + config: defaultConfig, + tooltip: true, +}; + +/** + * Map layer configuration for circle overlay + */ +export const dayNightLayer: MapLayerRegistryItem = { + id: DAY_NIGHT_LAYER_ID, + name: 'Night / Day', + description: 'Show day and night regions', + isBaseMap: false, + state: PluginState.alpha, + + /** + * Function that configures transformation and returns a transformer + * @param map + * @param options + * @param theme + */ + create: async (map: Map, options: MapLayerOptions, eventBus: EventBus, theme: GrafanaTheme2) => { + // Assert default values + const config = { + ...defaultConfig, + ...options?.config, + }; + + // DayNight source + const source = new DayNight({ }); + const sourceMethods = Object.getPrototypeOf(source); + const sourceLine = new DayNight({ }); + const sourceLineMethods = Object.getPrototypeOf(sourceLine); + + // Night polygon + const vectorLayer = new VectorLayer({ + source, + style: new Style({ + fill: new Fill({ + color: theme.visualization.getColorByName(config.nightColor) + }) + }) + }); + + // Night line (for crosshair sharing) + const nightLineLayer = new VectorLayer({ + source: new VectorSource({ + features: [], + }), + style: new Style ({ + stroke: new Stroke({ + color: '#607D8B', + width: 1.5, + lineDash: [2, 3], + }) + }), + }); + + // Sun circle + const sunFeature = new Feature({ + geometry: new Point([]), + }); + + const sunLayer = new VectorLayer({ + source: new VectorSource({ + features: [sunFeature], + }), + style: new Style({ + image: new Circle({ + radius: 13, + fill: new Fill({color: 'rgb(253,184,19)'}), + }) + }), + }); + + // Sun line (for crosshair sharing) + const sunLineFeature = new Feature({ + geometry: new Point([]), + }); + + const sunLineStyle = new Style({ + image: new Circle({ + radius: 13, + stroke: new Stroke({ + color: 'rgb(253,184,19)', + width: 1.5 + }) + }) + }); + + const sunLineStyleDash = new Style({ + image: new Circle({ + radius: 15, + stroke: new Stroke({ + color: '#607D8B', + width: 1.5, + lineDash: [2,3] + }) + }) + }); + + const sunLineLayer = new VectorLayer({ + source: new VectorSource({ + features: [sunLineFeature], + }), + style: [sunLineStyleDash, sunLineStyle], + }); + + // Build group of layers + // TODO: add blended night region to "connect" current night region to lines + const layer = new LayerGroup({ + layers: config.sun? [vectorLayer, sunLayer, sunLineLayer, nightLineLayer] : [vectorLayer, nightLineLayer] + }); + + // Crosshair sharing subscriptions + const subscriptions = new Subscription(); + + if (false) { + subscriptions.add( + eventBus.subscribe(DataHoverEvent, (event) => { + const time = event.payload?.point?.time as number; + if (time) { + const lineTime = new Date(time); + const nightLinePoints = sourceLine.getCoordinates(lineTime.toString(), 'line'); + nightLineLayer.getSource()?.clear(); + const lineStringArray:Coordinate[][] = []; + for (let l = 0; l < nightLinePoints.length - 1; l++){ + const x1:number = Object.values(nightLinePoints[l])[0]; + const y1:number = Object.values(nightLinePoints[l])[1]; + const x2:number = Object.values(nightLinePoints[l+1])[0]; + const y2:number = Object.values(nightLinePoints[l+1])[1]; + const lineString = [fromLonLat([x1, y1]),fromLonLat([x2, y2])]; + lineStringArray.push(lineString); + } + nightLineLayer.getSource()?.addFeature(new Feature({ + geometry: new MultiLineString(lineStringArray), + })) + + let sunLinePos: number[] = []; + sunLinePos = sourceLineMethods.getSunPosition(lineTime); + sunLineFeature.getGeometry()?.setCoordinates(fromLonLat(sunLinePos)); + sunLineFeature.setStyle([sunLineStyle, sunLineStyleDash]); + } + }) + ); + + subscriptions.add( + eventBus.subscribe(DataHoverClearEvent, (event) => { + nightLineLayer.getSource()?.clear(); + sunLineFeature.setStyle(new Style({})); + }) + ); + } + + return { + init: () => layer, + dispose: () => subscriptions.unsubscribe(), + update: (data: PanelData) => { + const from = new Date(data.timeRange.from.valueOf()); + const to = new Date(data.timeRange.to.valueOf()); + let selectedTime: Date = new Date(); + let sunPos: number[] = []; + // TODO: add option for "Both" + if (config.show === ShowTime.From){ + selectedTime = from; + } else { + selectedTime = to; + } + + source.setTime(selectedTime); + if (config.sun){ + sunPos = sourceMethods.getSunPosition(selectedTime); + sunFeature.getGeometry()?.setCoordinates(fromLonLat(sunPos)); + } + }, + + // Marker overlay options + registerOptionsUI: (builder) => { + if(!options.config?.nightColor) { + options.config = { ...defaultConfig, ...options.config} + } + + builder + .addRadio({ + path: 'config.show', + name: 'Show', + settings: { + options: [ + { label: 'From', value: ShowTime.From }, + { label: 'To', value: ShowTime.To }, + ], + }, + defaultValue: defaultConfig.show, + }); + builder.addColorPicker({ + path: 'config.nightColor', + name: 'Night region color', + description: 'Pick color of night region', + defaultValue: defaultConfig.nightColor, + settings: [{enableNamedColors: false}], + }); + builder.addBooleanSwitch({ + path: 'config.sun', + name: 'Display sun', + description: 'Show the sun', + defaultValue: defaultConfig.sun, + }); + }, + }; + }, + + // fill in the default values + defaultOptions: defaultConfig, +}; diff --git a/public/app/plugins/panel/geomap/layers/data/geojsonLayer.ts b/public/app/plugins/panel/geomap/layers/data/geojsonLayer.ts index 4ee52c38b67..5dcc7b741fd 100644 --- a/public/app/plugins/panel/geomap/layers/data/geojsonLayer.ts +++ b/public/app/plugins/panel/geomap/layers/data/geojsonLayer.ts @@ -5,6 +5,7 @@ import { GrafanaTheme2, PluginState, SelectableValue, + EventBus, } from '@grafana/data'; import Map from 'ol/Map'; import VectorLayer from 'ol/layer/Vector'; @@ -72,7 +73,7 @@ export const geojsonLayer: MapLayerRegistryItem = { * Function that configures transformation and returns a transformer * @param options */ - create: async (map: Map, options: MapLayerOptions, theme: GrafanaTheme2) => { + create: async (map: Map, options: MapLayerOptions, eventBus: EventBus, theme: GrafanaTheme2) => { const config = { ...defaultOptions, ...options.config }; const source = new VectorSource({ diff --git a/public/app/plugins/panel/geomap/layers/data/heatMap.tsx b/public/app/plugins/panel/geomap/layers/data/heatMap.tsx index 5a02e45e26f..24855548585 100644 --- a/public/app/plugins/panel/geomap/layers/data/heatMap.tsx +++ b/public/app/plugins/panel/geomap/layers/data/heatMap.tsx @@ -1,4 +1,5 @@ import { + EventBus, FieldType, getFieldColorModeForField, GrafanaTheme2, @@ -45,7 +46,7 @@ export const heatmapLayer: MapLayerRegistryItem = { * Function that configures transformation and returns a transformer * @param options */ - create: async (map: Map, options: MapLayerOptions, theme: GrafanaTheme2) => { + create: async (map: Map, options: MapLayerOptions, eventBus: EventBus, theme: GrafanaTheme2) => { const config = { ...defaultOptions, ...options.config }; const location = await getLocationMatchers(options.location); diff --git a/public/app/plugins/panel/geomap/layers/data/index.ts b/public/app/plugins/panel/geomap/layers/data/index.ts index 7e52b013d2a..e02fe6ff538 100644 --- a/public/app/plugins/panel/geomap/layers/data/index.ts +++ b/public/app/plugins/panel/geomap/layers/data/index.ts @@ -2,8 +2,9 @@ import { markersLayer } from './markersLayer'; import { geojsonLayer } from './geojsonLayer'; import { heatmapLayer } from './heatMap'; import { lastPointTracker } from './lastPointTracker'; +import { dayNightLayer } from './dayNightLayer'; /** * Registry for layer handlers */ -export const dataLayers = [markersLayer, heatmapLayer, lastPointTracker, geojsonLayer]; +export const dataLayers = [markersLayer, heatmapLayer, lastPointTracker, geojsonLayer, dayNightLayer]; diff --git a/public/app/plugins/panel/geomap/layers/data/lastPointTracker.ts b/public/app/plugins/panel/geomap/layers/data/lastPointTracker.ts index 94d7104727c..e8dabc0ca85 100644 --- a/public/app/plugins/panel/geomap/layers/data/lastPointTracker.ts +++ b/public/app/plugins/panel/geomap/layers/data/lastPointTracker.ts @@ -1,4 +1,4 @@ -import { MapLayerRegistryItem, MapLayerOptions, PanelData, GrafanaTheme2, PluginState } from '@grafana/data'; +import { MapLayerRegistryItem, MapLayerOptions, PanelData, GrafanaTheme2, PluginState, EventBus } from '@grafana/data'; import Map from 'ol/Map'; import Feature from 'ol/Feature'; import * as style from 'ol/style'; @@ -26,7 +26,7 @@ export const lastPointTracker: MapLayerRegistryItem = { * Function that configures transformation and returns a transformer * @param options */ - create: async (map: Map, options: MapLayerOptions, theme: GrafanaTheme2) => { + create: async (map: Map, options: MapLayerOptions, eventBus: EventBus, theme: GrafanaTheme2) => { const point = new Feature({}); const config = { ...defaultOptions, ...options.config }; diff --git a/public/app/plugins/panel/geomap/layers/data/markersLayer.tsx b/public/app/plugins/panel/geomap/layers/data/markersLayer.tsx index 122b0798643..ea4fbd94557 100644 --- a/public/app/plugins/panel/geomap/layers/data/markersLayer.tsx +++ b/public/app/plugins/panel/geomap/layers/data/markersLayer.tsx @@ -5,6 +5,7 @@ import { PanelData, GrafanaTheme2, FrameGeometrySourceMode, + EventBus, } from '@grafana/data'; import Map from 'ol/Map'; import { FeatureLike } from 'ol/Feature'; @@ -61,7 +62,7 @@ export const markersLayer: MapLayerRegistryItem = { * @param options * @param theme */ - create: async (map: Map, options: MapLayerOptions, theme: GrafanaTheme2) => { + create: async (map: Map, options: MapLayerOptions, eventBus: EventBus, theme: GrafanaTheme2) => { // Assert default values const config = { ...defaultOptions, diff --git a/public/app/plugins/panel/geomap/layers/registry.ts b/public/app/plugins/panel/geomap/layers/registry.ts index 3673dd13eb8..43434262881 100644 --- a/public/app/plugins/panel/geomap/layers/registry.ts +++ b/public/app/plugins/panel/geomap/layers/registry.ts @@ -5,6 +5,7 @@ import { Registry, MapLayerOptions, GrafanaTheme2, + EventBus, SelectableValue, PluginState, } from '@grafana/data'; @@ -26,18 +27,18 @@ export const defaultBaseLayer: MapLayerRegistryItem = { name: 'Default base layer', isBaseMap: true, - create: (map: Map, options: MapLayerOptions, theme: GrafanaTheme2) => { + create: (map: Map, options: MapLayerOptions, eventBus: EventBus, theme: GrafanaTheme2) => { const serverLayerType = config?.geomapDefaultBaseLayerConfig?.type; if (serverLayerType) { const layer = geomapLayerRegistry.getIfExists(serverLayerType); if (!layer) { throw new Error('Invalid basemap configuration on server'); } - return layer.create(map, config.geomapDefaultBaseLayerConfig!, theme); + return layer.create(map, config.geomapDefaultBaseLayerConfig!, eventBus, theme); } // For now use carto as our default basemap - return carto.create(map, options, theme); + return carto.create(map, options, eventBus, theme); }, }; @@ -57,16 +58,26 @@ interface RegistrySelectInfo { function getLayersSelection(items: Array>, current?: string): RegistrySelectInfo { const res: RegistrySelectInfo = { options: [], current: [] }; + const alpha: Array> = []; for (const layer of items) { - if (layer.state === PluginState.alpha && !hasAlphaPanels) { - continue; + const opt: SelectableValue = { label: layer.name, value: layer.id, description: layer.description }; + if (layer.state === PluginState.alpha) { + if (!hasAlphaPanels) { + continue; + } + opt.label = `${layer.name} (Alpha)`; + opt.icon = 'bolt'; + alpha.push(opt); + } else { + res.options.push(opt); } - const opt = { label: layer.name, value: layer.id, description: layer.description }; - res.options.push(opt); if (layer.id === current) { res.current.push(opt); } } + for (const p of alpha) { + res.options.push(p); + } return res; } diff --git a/yarn.lock b/yarn.lock index ffb067c5c30..71ec7452fc9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3221,7 +3221,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.18.3": +"@babel/runtime@npm:^7.14.0, @babel/runtime@npm:^7.18.3": version: 7.18.3 resolution: "@babel/runtime@npm:7.18.3" dependencies: @@ -10734,6 +10734,15 @@ __metadata: languageName: node linkType: hard +"@types/ol-ext@npm:@siedlerchr/types-ol-ext@2.0.0": + version: 2.0.0 + resolution: "@siedlerchr/types-ol-ext@npm:2.0.0" + dependencies: + jspdf: ^2.5.1 + checksum: f97642cbd3fbc6501d17bc8118931eb00c12cb8b92a89d055508fd24e62e0c37263da18144cada421d8dc342d74e8198179905184b32b8c260bf30e4f5d7e4cb + languageName: node + linkType: hard + "@types/overlayscrollbars@npm:^1.12.0": version: 1.12.1 resolution: "@types/overlayscrollbars@npm:1.12.1" @@ -10827,6 +10836,13 @@ __metadata: languageName: node linkType: hard +"@types/raf@npm:^3.4.0": + version: 3.4.0 + resolution: "@types/raf@npm:3.4.0" + checksum: d93e9b5244a081c64708b8918ef7e56936d6ef0144925b189e67d34127c0cb3a73fcf6866ab312db156554a66c26609dd056da5f7302f6658c049d6a37ed5f56 + languageName: node + linkType: hard + "@types/range-parser@npm:*": version: 1.2.4 resolution: "@types/range-parser@npm:1.2.4" @@ -13833,6 +13849,13 @@ __metadata: languageName: node linkType: hard +"base64-arraybuffer@npm:^1.0.2": + version: 1.0.2 + resolution: "base64-arraybuffer@npm:1.0.2" + checksum: 15e6400d2d028bf18be4ed97702b11418f8f8779fb8c743251c863b726638d52f69571d4cc1843224da7838abef0949c670bde46936663c45ad078e89fee5c62 + languageName: node + linkType: hard + "base64-js@npm:^1.0.2, base64-js@npm:^1.3.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" @@ -14299,6 +14322,15 @@ __metadata: languageName: node linkType: hard +"btoa@npm:^1.2.1": + version: 1.2.1 + resolution: "btoa@npm:1.2.1" + bin: + btoa: bin/btoa.js + checksum: afbf004fb1b1d530e053ffa66ef5bd3878b101c59d808ac947fcff96810b4452abba2b54be687adadea2ba9efc7af48b04228742789bf824ef93f103767e690c + languageName: node + linkType: hard + "buffer-crc32@npm:~0.2.3": version: 0.2.13 resolution: "buffer-crc32@npm:0.2.13" @@ -14634,6 +14666,22 @@ __metadata: languageName: node linkType: hard +"canvg@npm:^3.0.6": + version: 3.0.10 + resolution: "canvg@npm:3.0.10" + dependencies: + "@babel/runtime": ^7.12.5 + "@types/raf": ^3.4.0 + core-js: ^3.8.3 + raf: ^3.4.1 + regenerator-runtime: ^0.13.7 + rgbcolor: ^1.0.1 + stackblur-canvas: ^2.0.0 + svg-pathdata: ^6.0.3 + checksum: 2cfd86bcb9b56b43a97745cc672e696169b4c09e8850fb4f27bec5ebf173179d16feb594224d643a32f1ce01e47b55d44e0058419114d48d34f12c2452c65927 + languageName: node + linkType: hard + "capture-exit@npm:^2.0.0": version: 2.0.0 resolution: "capture-exit@npm:2.0.0" @@ -15832,6 +15880,13 @@ __metadata: languageName: node linkType: hard +"core-js@npm:^3.6.0, core-js@npm:^3.8.3": + version: 3.23.1 + resolution: "core-js@npm:3.23.1" + checksum: 460328e0b743900d48fc744aab475f6dabdae498cc73f6d038321b0ae1488cb5e73d1670b5914abfe7dd84d0843a828b5d4dd3eba0a5b6a4450f0d7094cda2a5 + languageName: node + linkType: hard + "core-util-is@npm:1.0.2": version: 1.0.2 resolution: "core-util-is@npm:1.0.2" @@ -16085,6 +16140,15 @@ __metadata: languageName: node linkType: hard +"css-line-break@npm:^2.1.0": + version: 2.1.0 + resolution: "css-line-break@npm:2.1.0" + dependencies: + utrie: ^1.0.2 + checksum: 37b1fe632b03be7a287cd394cef8b5285666343443125c510df9cfb6a4734a2c71e154ec8f7bbff72d7c339e1e5872989b1c52d86162aed27d6cc114725bb4d0 + languageName: node + linkType: hard + "css-loader@npm:6.7.1, css-loader@npm:^6.7.1": version: 6.7.1 resolution: "css-loader@npm:6.7.1" @@ -17644,6 +17708,13 @@ __metadata: languageName: node linkType: hard +"dompurify@npm:^2.2.0": + version: 2.3.8 + resolution: "dompurify@npm:2.3.8" + checksum: dc7b32ee57a03fe5166a850071200897cc13fa069287a709e3b2138052d73ec09a87026b9e28c8d2f254a74eaa52ef30644e98e54294c30acbca2a53f1bbc5f4 + languageName: node + linkType: hard + "domutils@npm:^2.5.2, domutils@npm:^2.6.0, domutils@npm:^2.7.0": version: 2.8.0 resolution: "domutils@npm:2.8.0" @@ -19384,6 +19455,13 @@ __metadata: languageName: node linkType: hard +"fflate@npm:^0.4.8": + version: 0.4.8 + resolution: "fflate@npm:0.4.8" + checksum: 29d8cbe44d5e7f53e7f5a160ac7f9cc025480c7b3bfd85c5f898cbe20dfa2dad4732daa534982664bf30b35896a90af44ea33ede5d94c5ffd1b8b0c0a0a56ca2 + languageName: node + linkType: hard + "figgy-pudding@npm:^3.5.1": version: 3.5.2 resolution: "figgy-pudding@npm:3.5.2" @@ -20720,6 +20798,7 @@ __metadata: "@types/logfmt": ^1.2.1 "@types/mousetrap": 1.6.9 "@types/node": 16.11.36 + "@types/ol-ext": "npm:@siedlerchr/types-ol-ext@2.0.0" "@types/papaparse": 5.3.2 "@types/pluralize": ^0.0.29 "@types/prismjs": 1.26.0 @@ -20850,6 +20929,7 @@ __metadata: node-notifier: 10.0.1 nodemon: 2.0.16 ol: 6.14.1 + ol-ext: ^3.2.25 papaparse: 5.3.2 pluralize: ^8.0.0 postcss: 8.4.14 @@ -21472,6 +21552,16 @@ __metadata: languageName: node linkType: hard +"html2canvas@npm:^1.0.0-rc.5": + version: 1.4.1 + resolution: "html2canvas@npm:1.4.1" + dependencies: + css-line-break: ^2.1.0 + text-segmentation: ^1.0.3 + checksum: c134324af57f3262eecf982e436a4843fded3c6cf61954440ffd682527e4dd350e0c2fafd217c0b6f9a455fe345d0c67b4505689796ab160d4ca7c91c3766739 + languageName: node + linkType: hard + "htmlparser2@npm:^6.1.0": version: 6.1.0 resolution: "htmlparser2@npm:6.1.0" @@ -24534,6 +24624,31 @@ __metadata: languageName: node linkType: hard +"jspdf@npm:^2.5.1": + version: 2.5.1 + resolution: "jspdf@npm:2.5.1" + dependencies: + "@babel/runtime": ^7.14.0 + atob: ^2.1.2 + btoa: ^1.2.1 + canvg: ^3.0.6 + core-js: ^3.6.0 + dompurify: ^2.2.0 + fflate: ^0.4.8 + html2canvas: ^1.0.0-rc.5 + dependenciesMeta: + canvg: + optional: true + core-js: + optional: true + dompurify: + optional: true + html2canvas: + optional: true + checksum: 9ecdccc50678cd780f0995157618630ca0da65576835983232d48001aab0b29e51af765e078808526d5e5e2e1ebf3cee460e03eaf590f875d160f2e0cb614a1e + languageName: node + linkType: hard + "jsprim@npm:^1.2.2": version: 1.4.1 resolution: "jsprim@npm:1.4.1" @@ -27247,6 +27362,15 @@ __metadata: languageName: node linkType: hard +"ol-ext@npm:^3.2.25": + version: 3.2.25 + resolution: "ol-ext@npm:3.2.25" + peerDependencies: + ol: ">= 5.3.0" + checksum: ebdf354089975c36258c456c1bf7b65a898fddc578d8112ad17ad8c571df1405992662c3fcfe13a23c400a8f2b7d9deb5efff4545a526d9a03386f4bcef565bf + languageName: node + linkType: hard + "ol-mapbox-style@npm:^7.1.1": version: 7.1.1 resolution: "ol-mapbox-style@npm:7.1.1" @@ -32192,6 +32316,13 @@ __metadata: languageName: node linkType: hard +"rgbcolor@npm:^1.0.1": + version: 1.0.1 + resolution: "rgbcolor@npm:1.0.1" + checksum: bd062ac007a3e979e2f83dc69feb3cc4f9bca7d8631899548394160e30c47e4f7e52b31aa3f66a69061ad56e899e812ec52f5c33686c085d72c9b3d22faed1c8 + languageName: node + linkType: hard + "rimraf@npm:3.0.2, rimraf@npm:^3.0.0, rimraf@npm:^3.0.2": version: 3.0.2 resolution: "rimraf@npm:3.0.2" @@ -33628,6 +33759,13 @@ __metadata: languageName: node linkType: hard +"stackblur-canvas@npm:^2.0.0": + version: 2.5.0 + resolution: "stackblur-canvas@npm:2.5.0" + checksum: 52b0dc595d3dbed94c8563d8fe34e0c8ff65b85eaf8fcfe7b0cd8893e93ae42c28b688f71d792fbaedf2a9b677dc474a2e721d8245165a3a0c0d794c0f6f8bba + languageName: node + linkType: hard + "stackframe@npm:^1.1.1": version: 1.2.0 resolution: "stackframe@npm:1.2.0" @@ -34267,6 +34405,13 @@ __metadata: languageName: node linkType: hard +"svg-pathdata@npm:^6.0.3": + version: 6.0.3 + resolution: "svg-pathdata@npm:6.0.3" + checksum: f0e55be50c654be5d259d70945ed7e5354bf78e51c6039b4045d9f7c49d703a0c33dda36751815aec2824d046c417c35226e7491246ffff3e9164735ea428446 + languageName: node + linkType: hard + "svg-tags@npm:^1.0.0": version: 1.0.0 resolution: "svg-tags@npm:1.0.0" @@ -34619,6 +34764,15 @@ __metadata: languageName: node linkType: hard +"text-segmentation@npm:^1.0.3": + version: 1.0.3 + resolution: "text-segmentation@npm:1.0.3" + dependencies: + utrie: ^1.0.2 + checksum: 2e24632d59567c55ab49ac324815e2f7a8043e63e26b109636322ac3e30692cee8679a448fd5d0f0598a345f407afd0e34ba612e22524cf576d382d84058c013 + languageName: node + linkType: hard + "text-table@npm:^0.2.0": version: 0.2.0 resolution: "text-table@npm:0.2.0" @@ -36031,6 +36185,15 @@ __metadata: languageName: node linkType: hard +"utrie@npm:^1.0.2": + version: 1.0.2 + resolution: "utrie@npm:1.0.2" + dependencies: + base64-arraybuffer: ^1.0.2 + checksum: c96fbb7d4d8855a154327da0b18e39b7511cc70a7e4bcc3658e24f424bb884312d72b5ba777500b8858e34d365dc6b1a921dc5ca2f0d341182519c6b78e280a5 + languageName: node + linkType: hard + "uuid-browser@npm:^3.1.0": version: 3.1.0 resolution: "uuid-browser@npm:3.1.0"