xv-webcomponents 1.22.2 → 1.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/maplibre-gl-CCZS3Rg5.js +900 -0
- package/dist/cjs/{xv-accordion-v2_48.cjs.entry.js → xv-accordion-v2_49.cjs.entry.js} +655 -66
- package/dist/cjs/xv-header-next.cjs.entry.js +1 -1
- package/dist/cjs/xv-mega-menu-next.cjs.entry.js +2 -2
- package/dist/cjs/xv-webcomponents.cjs.js +1 -1
- package/dist/collection/collection-manifest.json +1 -0
- package/dist/collection/components/xv-accordion/xv-accordion.js +24 -4
- package/dist/collection/components/xv-accordion-item/xv-accordion-item.js +11 -5
- package/dist/collection/components/xv-breadcrumbs/xv-breadcrumbs.js +1 -1
- package/dist/collection/components/xv-button/xv-button.js +2 -2
- package/dist/collection/components/xv-button-group/xv-button-group.js +1 -1
- package/dist/collection/components/xv-card/xv-card.js +1 -1
- package/dist/collection/components/xv-checkbox/xv-checkbox.js +2 -2
- package/dist/collection/components/xv-content-switcher/xv-content-switcher-item/xv-content-switcher-item.js +1 -1
- package/dist/collection/components/xv-content-switcher/xv-content-switcher.js +2 -2
- package/dist/collection/components/xv-data-table/xv-data-table-cell/xv-data-table-cell.js +1 -1
- package/dist/collection/components/xv-data-table/xv-data-table-collapse/xv-data-table-collapse.js +1 -1
- package/dist/collection/components/xv-data-table/xv-data-table-row/xv-data-table-row.js +2 -2
- package/dist/collection/components/xv-data-table/xv-data-table.js +1 -1
- package/dist/collection/components/xv-datepicker/xv-datepicker.js +63 -5
- package/dist/collection/components/xv-dropdown/xv-dropdown-item/xv-dropdown-item.js +1 -1
- package/dist/collection/components/xv-dropdown/xv-dropdown.js +1 -1
- package/dist/collection/components/xv-file-uploader/xv-file-uploader.js +1 -1
- package/dist/collection/components/xv-footer/xv-footer.js +1 -1
- package/dist/collection/components/xv-geo-map/xv-geo-map.css +907 -0
- package/dist/collection/components/xv-geo-map/xv-geo-map.js +984 -0
- package/dist/collection/components/xv-header/xv-header.js +1 -1
- package/dist/collection/components/xv-header-next/xv-header-next.js +1 -1
- package/dist/collection/components/xv-image-viewer/xv-image-viewer.js +1 -1
- package/dist/collection/components/xv-link/xv-link.js +1 -1
- package/dist/collection/components/xv-mega-menu/xv-mega-menu.js +2 -2
- package/dist/collection/components/xv-mega-menu-next/xv-mega-menu-next.js +2 -2
- package/dist/collection/components/xv-modal/xv-modal.js +1 -1
- package/dist/collection/components/xv-notification/xv-notification.js +1 -1
- package/dist/collection/components/xv-number-input/xv-number-input.js +2 -2
- package/dist/collection/components/xv-pagination/xv-pagination.js +1 -1
- package/dist/collection/components/xv-progress-indicator/xv-progress-indicator-item/xv-progress-indicator-item.js +1 -1
- package/dist/collection/components/xv-progress-indicator/xv-progress-indicator.js +1 -1
- package/dist/collection/components/xv-radio/xv-radio.js +2 -2
- package/dist/collection/components/xv-slider/xv-slider.js +1 -1
- package/dist/collection/components/xv-table/xv-table-cell/xv-table-cell.js +1 -1
- package/dist/collection/components/xv-table/xv-table-expand/xv-table-expand.js +1 -1
- package/dist/collection/components/xv-table/xv-table-row/xv-table-row.js +1 -1
- package/dist/collection/components/xv-table/xv-table.js +1 -1
- package/dist/collection/components/xv-tabs/xv-tab/xv-tab.js +1 -1
- package/dist/collection/components/xv-tag/xv-tag.js +1 -1
- package/dist/collection/components/xv-tag-combobox/xv-tag-combobox.js +2 -2
- package/dist/collection/components/xv-text-input/xv-text-input.js +2 -2
- package/dist/collection/components/xv-textarea/xv-textarea.js +2 -2
- package/dist/collection/components/xv-tile/xv-tile.js +2 -2
- package/dist/collection/components/xv-toggle/xv-toggle.js +1 -1
- package/dist/collection/components/xv-toggle-tip/xv-toggle-tip.js +1 -1
- package/dist/collection/components/xv-tooltip/xv-tooltip.js +2 -2
- package/dist/esm/loader.js +1 -1
- package/dist/esm/maplibre-gl-CS4Eagm9.js +813 -0
- package/dist/esm/maplibre-gl-shared.mjs +6 -0
- package/dist/esm/maplibre-gl-worker.mjs +6 -0
- package/dist/esm/{xv-accordion-v2_48.entry.js → xv-accordion-v2_49.entry.js} +655 -67
- package/dist/esm/xv-header-next.entry.js +1 -1
- package/dist/esm/xv-mega-menu-next.entry.js +2 -2
- package/dist/esm/xv-webcomponents.js +1 -1
- package/dist/types/components/xv-accordion/xv-accordion.d.ts +5 -4
- package/dist/types/components/xv-accordion-item/xv-accordion-item.d.ts +3 -0
- package/dist/types/components/xv-datepicker/xv-datepicker.d.ts +12 -0
- package/dist/types/components/xv-geo-map/xv-geo-map.d.ts +115 -0
- package/dist/types/components.d.ts +217 -0
- package/dist/xv-webcomponents/{p-511519a7.entry.js → p-77bd6e1f.entry.js} +2 -2
- package/dist/xv-webcomponents/{p-38e5f1e1.entry.js → p-93bd2dca.entry.js} +1 -1
- package/dist/xv-webcomponents/{p-7e05cb50.entry.js → p-9e3130ff.entry.js} +1 -1
- package/dist/xv-webcomponents/p-CS4Eagm9.js +10 -0
- package/dist/xv-webcomponents/xv-webcomponents.esm.js +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,984 @@
|
|
|
1
|
+
import { Host, h } from "@stencil/core";
|
|
2
|
+
// Xv.Api wraps responses in { data: ... } (ResponseEnvelopeFilter); the XvGeoTool API returns raw payloads.
|
|
3
|
+
const unwrap = (json) => (json != null && !Array.isArray(json) && 'data' in json ? json.data : json);
|
|
4
|
+
// Germany bounding box (with a small margin) — the map cannot be panned outside
|
|
5
|
+
const GERMANY_BOUNDS = [[4.5, 46.4], [16.5, 55.6]];
|
|
6
|
+
// amber: distinct from the green selection AND from CARTO's road/water tones
|
|
7
|
+
const PREVIEW_COLOR = '#f59e0b';
|
|
8
|
+
/**
|
|
9
|
+
* Minimal ATV planner map: MapLibre + PLZ boundaries from the XvGeoTool API.
|
|
10
|
+
* Click a PLZ to (de)select it. No further UI — selection state and reach
|
|
11
|
+
* data are emitted via `changeSelection` for the host page to display.
|
|
12
|
+
*
|
|
13
|
+
* MapLibre (~250 KB gzip) is loaded lazily when the component scrolls near
|
|
14
|
+
* the viewport, so the host page's initial load stays untouched.
|
|
15
|
+
*/
|
|
16
|
+
export class XvGeoMap {
|
|
17
|
+
constructor() {
|
|
18
|
+
/**
|
|
19
|
+
* Base URL of the geo API (Xv.Api v1/geo — contract-compatible with the XvGeoTool API).
|
|
20
|
+
* Default is the same-origin Front Door route, so no CORS is involved on crossvertise pages.
|
|
21
|
+
*/
|
|
22
|
+
this.apiBase = '/api-fd/v1/geo';
|
|
23
|
+
/** Indicator whose per-unit values feed the tooltips and the emitted population sum (e.g. ATV_DEVICES) */
|
|
24
|
+
this.indicator = 'MBI_P_T_2025';
|
|
25
|
+
/** Unit label shown behind the indicator value in the tooltip */
|
|
26
|
+
this.indicatorLabel = 'Einwohner';
|
|
27
|
+
/** Geo level to display and select: plz5, gemeinde, kreis, regbez, bundesland */
|
|
28
|
+
this.level = 'plz5';
|
|
29
|
+
/** Shape resolution: medium = simplified geometry (snappy), high = originals */
|
|
30
|
+
this.resolution = 'medium';
|
|
31
|
+
/**
|
|
32
|
+
* Raster tile URL template. Default: CARTO Positron — light, minimal, made for
|
|
33
|
+
* data overlays; fits the crossvertise design. For production traffic, license
|
|
34
|
+
* a tile plan (CARTO/MapTiler/Geofabrik) and set the keyed URL here.
|
|
35
|
+
*/
|
|
36
|
+
this.tiles = 'https://basemaps.cartocdn.com/rastertiles/light_all/{z}/{x}/{y}@2x.png';
|
|
37
|
+
this.attribution = '© CARTO, © OpenStreetMap contributors';
|
|
38
|
+
/** Initial map center */
|
|
39
|
+
this.centerLng = 10.45;
|
|
40
|
+
this.centerLat = 51.16;
|
|
41
|
+
this.zoom = 5.5;
|
|
42
|
+
/** Minimum zoom before boundaries are loaded and selectable (use ~5 for bundesland, 9 for plz5) */
|
|
43
|
+
this.selectZoom = 9;
|
|
44
|
+
/** Load the map bundle only when scrolled near the viewport (default: true) */
|
|
45
|
+
this.lazy = true;
|
|
46
|
+
this.ready = false;
|
|
47
|
+
this.loadError = false;
|
|
48
|
+
this.zoomGated = true;
|
|
49
|
+
this.searchResults = [];
|
|
50
|
+
this.searchQuery = '';
|
|
51
|
+
this.searchSeq = 0;
|
|
52
|
+
this.initStarted = false;
|
|
53
|
+
this.readyPromise = new Promise(res => (this.resolveReady = res));
|
|
54
|
+
this.units = new Map(); // code -> unit, accumulated across viewports
|
|
55
|
+
this.selected = new Map(); // code -> unit
|
|
56
|
+
this.preview = new Map(); // optimizer proposal (visual only)
|
|
57
|
+
this.populations = new Map(); // code -> indicator value (for tooltips + local sums)
|
|
58
|
+
this.fetchedBoxes = []; // areas already loaded
|
|
59
|
+
this.fetchSeq = 0;
|
|
60
|
+
this.popSeq = 0;
|
|
61
|
+
}
|
|
62
|
+
/** switching the level (e.g. PLZ mode -> Bundesländer mode) resets all loaded data and the selection */
|
|
63
|
+
levelChanged() {
|
|
64
|
+
if (!this.map)
|
|
65
|
+
return;
|
|
66
|
+
this.units.clear();
|
|
67
|
+
this.selected.clear();
|
|
68
|
+
this.populations.clear();
|
|
69
|
+
this.fetchedBoxes = [];
|
|
70
|
+
this.fetchSeq++;
|
|
71
|
+
this.renderUnits();
|
|
72
|
+
this.updateSelectedFilter();
|
|
73
|
+
this.emitSelection();
|
|
74
|
+
// follow the mode with the camera: out to the overview for coarse levels,
|
|
75
|
+
// in to selectable zoom for fine ones. Deferred so a select-zoom attribute
|
|
76
|
+
// changing in the same render is already applied.
|
|
77
|
+
setTimeout(() => {
|
|
78
|
+
var _a;
|
|
79
|
+
(_a = this.map) === null || _a === void 0 ? void 0 : _a.easeTo({ zoom: this.selectZoom + 0.5, duration: 800 });
|
|
80
|
+
this.refreshUnits();
|
|
81
|
+
}, 0);
|
|
82
|
+
}
|
|
83
|
+
selectZoomChanged() {
|
|
84
|
+
if (!this.map)
|
|
85
|
+
return;
|
|
86
|
+
['units-fill', 'units-hover', 'units-line'].forEach(l => this.map.setLayerZoomRange(l, this.selectZoom, 24));
|
|
87
|
+
this.refreshUnits();
|
|
88
|
+
}
|
|
89
|
+
componentDidLoad() {
|
|
90
|
+
if (!this.lazy || !('IntersectionObserver' in window)) {
|
|
91
|
+
this.initMap();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
this.observer = new IntersectionObserver(entries => {
|
|
95
|
+
var _a;
|
|
96
|
+
if (entries.some(e => e.isIntersecting)) {
|
|
97
|
+
(_a = this.observer) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
98
|
+
this.initMap();
|
|
99
|
+
}
|
|
100
|
+
}, { rootMargin: '300px' });
|
|
101
|
+
this.observer.observe(this.el);
|
|
102
|
+
}
|
|
103
|
+
disconnectedCallback() {
|
|
104
|
+
var _a, _b;
|
|
105
|
+
(_a = this.observer) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
106
|
+
(_b = this.map) === null || _b === void 0 ? void 0 : _b.remove();
|
|
107
|
+
}
|
|
108
|
+
async initMap() {
|
|
109
|
+
if (this.initStarted)
|
|
110
|
+
return;
|
|
111
|
+
this.initStarted = true;
|
|
112
|
+
try {
|
|
113
|
+
const maplibregl = await import('maplibre-gl');
|
|
114
|
+
if (this.workerUrl) {
|
|
115
|
+
maplibregl.setWorkerUrl(new URL(this.workerUrl, document.baseURI).href);
|
|
116
|
+
}
|
|
117
|
+
const map = new maplibregl.Map({
|
|
118
|
+
container: this.mapContainer,
|
|
119
|
+
style: {
|
|
120
|
+
version: 8,
|
|
121
|
+
sources: {
|
|
122
|
+
basemap: {
|
|
123
|
+
type: 'raster',
|
|
124
|
+
tiles: [this.tiles],
|
|
125
|
+
tileSize: 256,
|
|
126
|
+
attribution: this.attribution,
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
layers: [{ id: 'basemap', type: 'raster', source: 'basemap' }],
|
|
130
|
+
},
|
|
131
|
+
center: [this.centerLng, this.centerLat],
|
|
132
|
+
zoom: this.zoom,
|
|
133
|
+
minZoom: 5,
|
|
134
|
+
maxBounds: GERMANY_BOUNDS,
|
|
135
|
+
attributionControl: { compact: true },
|
|
136
|
+
});
|
|
137
|
+
this.map = map;
|
|
138
|
+
this.el._map = map; // e2e/test handle
|
|
139
|
+
map.addControl(new maplibregl.NavigationControl({ showCompass: false }));
|
|
140
|
+
map.on('load', () => {
|
|
141
|
+
// Design tokens (Masha, MAR-15783): selection = highlight-01 fill / interactive-02
|
|
142
|
+
// outline; optimizer preview = highlight-02 fill / interactive-01 outline. Fallbacks
|
|
143
|
+
// keep the previous XV green/amber where the host page lacks the tokens.
|
|
144
|
+
const style = getComputedStyle(this.el);
|
|
145
|
+
const token = (name, fallback) => style.getPropertyValue(name).trim() || fallback;
|
|
146
|
+
const accent = token('--link-primary', '#76b82a');
|
|
147
|
+
const selectionFill = token('--highlight-01', accent);
|
|
148
|
+
const selectionLine = token('--interactive-02', accent);
|
|
149
|
+
const previewFill = token('--highlight-02', PREVIEW_COLOR);
|
|
150
|
+
const previewLine = token('--interactive-01', PREVIEW_COLOR);
|
|
151
|
+
// the highlight tokens are pale full-strength tints — at the legacy 0.3 opacity they
|
|
152
|
+
// vanish over the basemap; the saturated fallbacks still need the low opacity
|
|
153
|
+
const hasTokens = !!style.getPropertyValue('--highlight-01').trim();
|
|
154
|
+
const selectedOpacity = hasTokens ? 0.85 : 0.35;
|
|
155
|
+
const previewOpacity = hasTokens ? 0.85 : 0.3;
|
|
156
|
+
map.addSource('units', { type: 'geojson', data: { type: 'FeatureCollection', features: [] } });
|
|
157
|
+
// boundary mesh only appears at selectable zoom (cleaner overview);
|
|
158
|
+
// the selected areas stay visible at every zoom level
|
|
159
|
+
map.addLayer({
|
|
160
|
+
id: 'units-fill',
|
|
161
|
+
type: 'fill',
|
|
162
|
+
source: 'units',
|
|
163
|
+
minzoom: this.selectZoom,
|
|
164
|
+
paint: { 'fill-color': selectionFill, 'fill-opacity': 0.04 },
|
|
165
|
+
});
|
|
166
|
+
map.addLayer({
|
|
167
|
+
id: 'units-selected',
|
|
168
|
+
type: 'fill',
|
|
169
|
+
source: 'units',
|
|
170
|
+
filter: ['in', ['get', 'code'], ['literal', []]],
|
|
171
|
+
paint: { 'fill-color': selectionFill, 'fill-opacity': selectedOpacity },
|
|
172
|
+
});
|
|
173
|
+
// optimizer preview: units a proposal would ADD, in their own tokens so they read
|
|
174
|
+
// as "suggested" next to the actual selection; visual only, never selected
|
|
175
|
+
map.addLayer({
|
|
176
|
+
id: 'units-preview',
|
|
177
|
+
type: 'fill',
|
|
178
|
+
source: 'units',
|
|
179
|
+
filter: ['in', ['get', 'code'], ['literal', []]],
|
|
180
|
+
paint: { 'fill-color': previewFill, 'fill-opacity': previewOpacity },
|
|
181
|
+
});
|
|
182
|
+
map.addLayer({
|
|
183
|
+
id: 'units-preview-line',
|
|
184
|
+
type: 'line',
|
|
185
|
+
source: 'units',
|
|
186
|
+
filter: ['in', ['get', 'code'], ['literal', []]],
|
|
187
|
+
paint: { 'line-color': previewLine, 'line-width': 2 },
|
|
188
|
+
});
|
|
189
|
+
map.addLayer({
|
|
190
|
+
id: 'units-hover',
|
|
191
|
+
type: 'fill',
|
|
192
|
+
source: 'units',
|
|
193
|
+
minzoom: this.selectZoom,
|
|
194
|
+
filter: ['==', ['get', 'code'], ''],
|
|
195
|
+
paint: { 'fill-color': selectionFill, 'fill-opacity': 0.15 },
|
|
196
|
+
});
|
|
197
|
+
map.addLayer({
|
|
198
|
+
id: 'units-line',
|
|
199
|
+
type: 'line',
|
|
200
|
+
source: 'units',
|
|
201
|
+
minzoom: this.selectZoom,
|
|
202
|
+
paint: { 'line-color': selectionLine, 'line-width': 2, 'line-dasharray': [2, 1.5] },
|
|
203
|
+
});
|
|
204
|
+
this.popup = new maplibregl.Popup({
|
|
205
|
+
closeButton: false,
|
|
206
|
+
closeOnClick: false,
|
|
207
|
+
offset: 12,
|
|
208
|
+
maxWidth: 'none',
|
|
209
|
+
className: 'xv-geo-map-tooltip',
|
|
210
|
+
});
|
|
211
|
+
// instant toggle; a double click fires click twice (select + deselect = net zero)
|
|
212
|
+
// and then zooms, so no debounce delay is needed
|
|
213
|
+
map.on('click', 'units-fill', e => { var _a, _b, _c; return this.toggleUnit((_c = (_b = (_a = e.features) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.properties) === null || _c === void 0 ? void 0 : _c.code); });
|
|
214
|
+
// below the selectable zoom nothing is clickable — make a click dive to the
|
|
215
|
+
// clicked area at select level instead of doing nothing
|
|
216
|
+
map.on('click', e => {
|
|
217
|
+
if (map.getZoom() < this.selectZoom) {
|
|
218
|
+
map.easeTo({ center: e.lngLat, zoom: this.selectZoom + 0.5, duration: 700 });
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
map.on('mousemove', 'units-fill', e => {
|
|
222
|
+
var _a, _b;
|
|
223
|
+
const props = (_b = (_a = e.features) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.properties;
|
|
224
|
+
if (!props)
|
|
225
|
+
return;
|
|
226
|
+
map.getCanvas().style.cursor = 'pointer';
|
|
227
|
+
map.setFilter('units-hover', ['==', ['get', 'code'], props.code]);
|
|
228
|
+
const pop = this.populations.get(props.code);
|
|
229
|
+
const selected = this.selected.has(props.code);
|
|
230
|
+
this.popup
|
|
231
|
+
.setLngLat(e.lngLat)
|
|
232
|
+
.setHTML(`<strong>${props.code}</strong> ${props.name}` +
|
|
233
|
+
(pop != null ? `<br>${pop.toLocaleString('de-DE')} ${this.indicatorLabel}` : '') +
|
|
234
|
+
(selected ? '<br><em>Klicken zum Abwählen</em>' : '<br><em>Klicken zum Auswählen</em>'))
|
|
235
|
+
.addTo(map);
|
|
236
|
+
});
|
|
237
|
+
map.on('mouseleave', 'units-fill', () => {
|
|
238
|
+
map.getCanvas().style.cursor = '';
|
|
239
|
+
map.setFilter('units-hover', ['==', ['get', 'code'], '']);
|
|
240
|
+
this.popup.remove();
|
|
241
|
+
});
|
|
242
|
+
map.on('moveend', () => this.refreshUnits());
|
|
243
|
+
this.ready = true;
|
|
244
|
+
this.mapReady.emit();
|
|
245
|
+
this.resolveReady();
|
|
246
|
+
this.refreshUnits();
|
|
247
|
+
});
|
|
248
|
+
map.on('error', () => {
|
|
249
|
+
if (!this.ready)
|
|
250
|
+
this.loadError = true;
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
catch (_a) {
|
|
254
|
+
this.loadError = true;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
async refreshUnits() {
|
|
258
|
+
if (!this.map)
|
|
259
|
+
return;
|
|
260
|
+
this.zoomGated = this.map.getZoom() < this.selectZoom;
|
|
261
|
+
if (this.zoomGated)
|
|
262
|
+
return;
|
|
263
|
+
const b = this.map.getBounds();
|
|
264
|
+
// skip the fetch entirely when the viewport is inside an already-loaded area
|
|
265
|
+
if (this.fetchedBoxes.some(f => b.getNorth() <= f.n && b.getSouth() >= f.s && b.getEast() <= f.e && b.getWest() >= f.w)) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
// fetch with a half-viewport margin so small pans stay within the loaded area
|
|
269
|
+
const mLat = (b.getNorth() - b.getSouth()) / 2;
|
|
270
|
+
const mLng = (b.getEast() - b.getWest()) / 2;
|
|
271
|
+
const box = { n: b.getNorth() + mLat, s: b.getSouth() - mLat, e: b.getEast() + mLng, w: b.getWest() - mLng };
|
|
272
|
+
const seq = ++this.fetchSeq;
|
|
273
|
+
try {
|
|
274
|
+
const res = await fetch(`${this.apiBase}/geo-units/${encodeURIComponent(this.level)}/by-bounds?resolution=${this.resolution}`, {
|
|
275
|
+
method: 'POST',
|
|
276
|
+
headers: { 'Content-Type': 'application/json' },
|
|
277
|
+
body: JSON.stringify({ north: box.n, south: box.s, east: box.e, west: box.w }),
|
|
278
|
+
});
|
|
279
|
+
if (!res.ok || seq !== this.fetchSeq)
|
|
280
|
+
return;
|
|
281
|
+
const list = unwrap(await res.json());
|
|
282
|
+
this.fetchedBoxes.push(box);
|
|
283
|
+
list.forEach(u => this.units.set(u.code, u));
|
|
284
|
+
this.renderUnits();
|
|
285
|
+
this.loadPopulations(list.filter(u => !this.populations.has(u.code)));
|
|
286
|
+
}
|
|
287
|
+
catch (_a) {
|
|
288
|
+
/* viewport fetch failed (offline/CORS) — map stays usable, next moveend retries */
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/** fetch the configured indicator per unit for hover tooltips (fire-and-forget) */
|
|
292
|
+
async loadPopulations(units) {
|
|
293
|
+
if (!units.length)
|
|
294
|
+
return;
|
|
295
|
+
try {
|
|
296
|
+
const res = await fetch(`${this.apiBase}/indicators/values`, {
|
|
297
|
+
method: 'POST',
|
|
298
|
+
headers: { 'Content-Type': 'application/json' },
|
|
299
|
+
body: JSON.stringify({ geoUnitIds: units.map(u => u.id), indicatorCodes: [this.indicator] }),
|
|
300
|
+
});
|
|
301
|
+
if (!res.ok)
|
|
302
|
+
return;
|
|
303
|
+
const idToCode = new Map(units.map(u => [u.id, u.code]));
|
|
304
|
+
for (const v of unwrap(await res.json())) {
|
|
305
|
+
const code = idToCode.get(v.geoUnitId);
|
|
306
|
+
if (code)
|
|
307
|
+
this.populations.set(code, v.value);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
catch (_a) {
|
|
311
|
+
/* tooltips just omit population */
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
renderUnits() {
|
|
315
|
+
// render everything loaded so far — panning back never flickers or re-parses
|
|
316
|
+
const source = this.map.getSource('units');
|
|
317
|
+
source === null || source === void 0 ? void 0 : source.setData({
|
|
318
|
+
type: 'FeatureCollection',
|
|
319
|
+
features: [...this.units.values()].map(u => ({
|
|
320
|
+
type: 'Feature',
|
|
321
|
+
properties: { code: u.code, name: u.name },
|
|
322
|
+
geometry: u.geography,
|
|
323
|
+
})),
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
toggleUnit(code) {
|
|
327
|
+
if (!code)
|
|
328
|
+
return;
|
|
329
|
+
if (this.selected.has(code))
|
|
330
|
+
this.selected.delete(code);
|
|
331
|
+
else
|
|
332
|
+
this.selected.set(code, this.units.get(code));
|
|
333
|
+
this.resetPreview(); // user interaction makes a shown proposal stale
|
|
334
|
+
this.updateSelectedFilter();
|
|
335
|
+
this.emitSelection();
|
|
336
|
+
}
|
|
337
|
+
updateSelectedFilter() {
|
|
338
|
+
this.map.setFilter('units-selected', ['in', ['get', 'code'], ['literal', [...this.selected.keys()]]]);
|
|
339
|
+
}
|
|
340
|
+
resetPreview() {
|
|
341
|
+
if (!this.preview.size)
|
|
342
|
+
return;
|
|
343
|
+
this.preview.clear();
|
|
344
|
+
this.updatePreviewFilter();
|
|
345
|
+
}
|
|
346
|
+
updatePreviewFilter() {
|
|
347
|
+
const codes = ['in', ['get', 'code'], ['literal', [...this.preview.keys()]]];
|
|
348
|
+
this.map.setFilter('units-preview', codes);
|
|
349
|
+
this.map.setFilter('units-preview-line', codes);
|
|
350
|
+
}
|
|
351
|
+
async emitSelection() {
|
|
352
|
+
var _a, _b, _c;
|
|
353
|
+
const postalCodes = [...this.selected.keys()];
|
|
354
|
+
const names = [...this.selected.values()].map(u => { var _a; return (_a = u === null || u === void 0 ? void 0 : u.name) !== null && _a !== void 0 ? _a : ''; });
|
|
355
|
+
const unitIds = [...this.selected.values()].map(u => u.id);
|
|
356
|
+
const seq = ++this.popSeq;
|
|
357
|
+
// populations are already cached per PLZ (tooltips) — sum locally, emit instantly
|
|
358
|
+
if (postalCodes.every(c => this.populations.has(c))) {
|
|
359
|
+
const population = postalCodes.reduce((sum, c) => sum + this.populations.get(c), 0);
|
|
360
|
+
const selection = { postalCodes, names, population: postalCodes.length ? population : null, unitIds };
|
|
361
|
+
this.changeSelection.emit(selection);
|
|
362
|
+
return selection;
|
|
363
|
+
}
|
|
364
|
+
// fallback: some populations not cached yet — aggregate server-side
|
|
365
|
+
let population = null;
|
|
366
|
+
try {
|
|
367
|
+
const res = await fetch(`${this.apiBase}/indicators/aggregate`, {
|
|
368
|
+
method: 'POST',
|
|
369
|
+
headers: { 'Content-Type': 'application/json' },
|
|
370
|
+
body: JSON.stringify({ geoUnitIds: unitIds, indicatorCodes: [this.indicator] }),
|
|
371
|
+
});
|
|
372
|
+
if (res.ok)
|
|
373
|
+
population = (_c = (_b = (_a = unwrap(await res.json())) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.sum) !== null && _c !== void 0 ? _c : null;
|
|
374
|
+
}
|
|
375
|
+
catch (_d) {
|
|
376
|
+
/* population stays null — host can fall back */
|
|
377
|
+
}
|
|
378
|
+
if (seq !== this.popSeq)
|
|
379
|
+
return null; // a newer selection superseded this one
|
|
380
|
+
const selection = { postalCodes, names, population, unitIds };
|
|
381
|
+
this.changeSelection.emit(selection);
|
|
382
|
+
return selection;
|
|
383
|
+
}
|
|
384
|
+
// ---- public API for host applications (Angular planner) ----
|
|
385
|
+
/** Select all PLZs within `km` around a start PLZ ("Umkreis"). Replaces the selection unless `add` is true. */
|
|
386
|
+
async selectByRadius(startPlz, km, add = false) {
|
|
387
|
+
await this.ensureReady();
|
|
388
|
+
const url = `${this.apiBase}/geo-units/${encodeURIComponent(this.level)}/by-radius` +
|
|
389
|
+
`?address=${encodeURIComponent(startPlz)}&km=${km}&country=DE&resolution=${this.resolution}`;
|
|
390
|
+
const res = await fetch(url);
|
|
391
|
+
if (!res.ok)
|
|
392
|
+
throw new Error(`by-radius failed: HTTP ${res.status}`);
|
|
393
|
+
return this.applySelection(unwrap(await res.json()), add);
|
|
394
|
+
}
|
|
395
|
+
/** Select an explicit list of PLZs ("PLZ-Liste" paste, query-param restore). Replaces the selection unless `add` is true. */
|
|
396
|
+
async selectPostalCodes(codes, add = false) {
|
|
397
|
+
await this.ensureReady();
|
|
398
|
+
// 1–5 digits: PLZ5 and Kreis codes are 5, Bundesland codes are 1–2 (a strict \d{5}
|
|
399
|
+
// silently dropped state selections restored via this method)
|
|
400
|
+
const clean = [...new Set(codes.map(c => String(c).trim()).filter(c => /^\d{1,5}$/.test(c)))];
|
|
401
|
+
if (!clean.length)
|
|
402
|
+
return this.applySelection([], add);
|
|
403
|
+
const res = await fetch(`${this.apiBase}/geo-units/${encodeURIComponent(this.level)}/lookup?resolution=${this.resolution}`, {
|
|
404
|
+
method: 'POST',
|
|
405
|
+
headers: { 'Content-Type': 'application/json' },
|
|
406
|
+
body: JSON.stringify({ codes: clean }),
|
|
407
|
+
});
|
|
408
|
+
if (!res.ok)
|
|
409
|
+
throw new Error(`lookup failed: HTTP ${res.status}`);
|
|
410
|
+
return this.applySelection(unwrap(await res.json()), add);
|
|
411
|
+
}
|
|
412
|
+
/** Clear the whole selection. */
|
|
413
|
+
async clearSelection() {
|
|
414
|
+
await this.ensureReady();
|
|
415
|
+
this.selected.clear();
|
|
416
|
+
this.resetPreview();
|
|
417
|
+
this.updateSelectedFilter();
|
|
418
|
+
this.emitSelection();
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Highlight what a radius extension would ADD (optimizer proposal): the units within
|
|
422
|
+
* `km` that are not part of the selection render in the preview color. Visual only —
|
|
423
|
+
* the selection and its change events stay untouched. Returns the number of added units.
|
|
424
|
+
*/
|
|
425
|
+
async previewByRadius(startPlz, km) {
|
|
426
|
+
await this.ensureReady();
|
|
427
|
+
const url = `${this.apiBase}/geo-units/${encodeURIComponent(this.level)}/by-radius` +
|
|
428
|
+
`?address=${encodeURIComponent(startPlz)}&km=${km}&country=DE&resolution=${this.resolution}`;
|
|
429
|
+
const res = await fetch(url);
|
|
430
|
+
if (!res.ok)
|
|
431
|
+
throw new Error(`by-radius failed: HTTP ${res.status}`);
|
|
432
|
+
const units = unwrap(await res.json());
|
|
433
|
+
this.preview.clear();
|
|
434
|
+
units.forEach(u => {
|
|
435
|
+
this.units.set(u.code, u);
|
|
436
|
+
if (!this.selected.has(u.code))
|
|
437
|
+
this.preview.set(u.code, u);
|
|
438
|
+
});
|
|
439
|
+
this.renderUnits();
|
|
440
|
+
this.updatePreviewFilter();
|
|
441
|
+
this.fitTo([...this.selected.values(), ...this.preview.values()]);
|
|
442
|
+
return this.preview.size;
|
|
443
|
+
}
|
|
444
|
+
/** Remove the optimizer proposal highlight. */
|
|
445
|
+
async clearPreview() {
|
|
446
|
+
await this.ensureReady();
|
|
447
|
+
this.resetPreview();
|
|
448
|
+
this.fitTo([...this.selected.values()]);
|
|
449
|
+
}
|
|
450
|
+
// ---- search box (MAR-15783): find areas by name or code, click to select ----
|
|
451
|
+
onSearchInput(value) {
|
|
452
|
+
this.searchQuery = value;
|
|
453
|
+
clearTimeout(this.searchDebounce);
|
|
454
|
+
const query = value.trim();
|
|
455
|
+
if (query.length < 2) {
|
|
456
|
+
this.searchResults = [];
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
this.searchDebounce = setTimeout(async () => {
|
|
460
|
+
var _a;
|
|
461
|
+
const seq = ++this.searchSeq;
|
|
462
|
+
try {
|
|
463
|
+
const res = await fetch(`${this.apiBase}/geo-units/${encodeURIComponent(this.level)}/search?q=${encodeURIComponent(query)}`);
|
|
464
|
+
if (!res.ok || seq !== this.searchSeq)
|
|
465
|
+
return;
|
|
466
|
+
this.searchResults = (_a = unwrap(await res.json())) !== null && _a !== void 0 ? _a : [];
|
|
467
|
+
}
|
|
468
|
+
catch (_b) {
|
|
469
|
+
this.searchResults = []; // search silently unavailable — the map stays usable
|
|
470
|
+
}
|
|
471
|
+
}, 300);
|
|
472
|
+
}
|
|
473
|
+
pickSearchResult(result) {
|
|
474
|
+
this.searchQuery = '';
|
|
475
|
+
this.searchResults = [];
|
|
476
|
+
// adds to the selection and flies there — exactly what "find this area" means here
|
|
477
|
+
void this.selectPostalCodes([result.code], true).catch(() => undefined);
|
|
478
|
+
}
|
|
479
|
+
/** A method call means the map is needed now — start loading even if it hasn't scrolled into view. */
|
|
480
|
+
ensureReady() {
|
|
481
|
+
var _a;
|
|
482
|
+
(_a = this.observer) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
483
|
+
this.initMap();
|
|
484
|
+
return this.readyPromise;
|
|
485
|
+
}
|
|
486
|
+
async applySelection(units, add) {
|
|
487
|
+
if (!add)
|
|
488
|
+
this.selected.clear();
|
|
489
|
+
units.forEach(u => {
|
|
490
|
+
this.units.set(u.code, u);
|
|
491
|
+
this.selected.set(u.code, u);
|
|
492
|
+
});
|
|
493
|
+
this.resetPreview();
|
|
494
|
+
this.renderUnits();
|
|
495
|
+
this.updateSelectedFilter();
|
|
496
|
+
this.fitTo([...this.selected.values()]);
|
|
497
|
+
await this.loadPopulations(units.filter(u => !this.populations.has(u.code)));
|
|
498
|
+
return this.emitSelection();
|
|
499
|
+
}
|
|
500
|
+
fitTo(units) {
|
|
501
|
+
if (!units.length)
|
|
502
|
+
return;
|
|
503
|
+
let minX = 180, minY = 90, maxX = -180, maxY = -90;
|
|
504
|
+
const scan = (c) => {
|
|
505
|
+
if (typeof c[0] === 'number') {
|
|
506
|
+
minX = Math.min(minX, c[0]);
|
|
507
|
+
maxX = Math.max(maxX, c[0]);
|
|
508
|
+
minY = Math.min(minY, c[1]);
|
|
509
|
+
maxY = Math.max(maxY, c[1]);
|
|
510
|
+
}
|
|
511
|
+
else
|
|
512
|
+
c.forEach(scan);
|
|
513
|
+
};
|
|
514
|
+
units.forEach(u => scan(u.geography.coordinates));
|
|
515
|
+
this.map.fitBounds([[minX, minY], [maxX, maxY]], { padding: 48, maxZoom: 12, duration: 800 });
|
|
516
|
+
}
|
|
517
|
+
render() {
|
|
518
|
+
return (h(Host, { key: 'b8f13f50cdb82e11502053eb7f14c31069580d5b' }, h("div", { key: '71e33adcb25ea4e35279ba8e060b9f9429126b6d', class: "container" }, h("div", { key: '14f9ad939968590fdde28d0be00d3fdf275234b5', class: "map", ref: el => (this.mapContainer = el) }), this.ready && (h("div", { key: '6711f30c53ac1c58090618af44b5dc5fde989223', class: "search" }, h("input", { key: '6e37e84e155721edd451c3465dc5ea426961b3dc', type: "search", placeholder: "Ort oder PLZ suchen", value: this.searchQuery, onInput: e => this.onSearchInput(e.target.value) }), this.searchResults.length > 0 && (h("ul", { key: 'a1df4aceef222e8d64f5d877b3ed73a821fe1f4b', class: "search-results" }, this.searchResults.map(r => (h("li", { key: r.code, onClick: () => this.pickSearchResult(r) }, h("strong", null, r.code), " ", r.name))))))), this.ready && this.zoomGated && (h("div", { key: '2079f7b7223fca39bd6d427b904c02b2a8643a9e', class: "hint" }, "Reinzoomen, um PLZ-Gebiete zu laden")), !this.ready && !this.loadError && (h("div", { key: 'd6ca85b7dd7407d6ed4af23abe74fe9b875f9cf6', class: "skeleton", "aria-busy": "true" }, h("xv-loader-v2", { key: 'b6548dd92dc724cf05c54274aa5d49a09a1f5362', variant: "vertical" }, "Karte wird geladen \u2026"))), this.loadError && h("div", { key: '7df300e7a5172e813d36638153285de64f17e4b9', class: "skeleton error" }, "Karte konnte nicht geladen werden."))));
|
|
519
|
+
}
|
|
520
|
+
static get is() { return "xv-geo-map-v2"; }
|
|
521
|
+
static get encapsulation() { return "shadow"; }
|
|
522
|
+
static get originalStyleUrls() {
|
|
523
|
+
return {
|
|
524
|
+
"$": ["xv-geo-map.scss"]
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
static get styleUrls() {
|
|
528
|
+
return {
|
|
529
|
+
"$": ["xv-geo-map.css"]
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
static get properties() {
|
|
533
|
+
return {
|
|
534
|
+
"apiBase": {
|
|
535
|
+
"type": "string",
|
|
536
|
+
"mutable": false,
|
|
537
|
+
"complexType": {
|
|
538
|
+
"original": "string",
|
|
539
|
+
"resolved": "string",
|
|
540
|
+
"references": {}
|
|
541
|
+
},
|
|
542
|
+
"required": false,
|
|
543
|
+
"optional": false,
|
|
544
|
+
"docs": {
|
|
545
|
+
"tags": [],
|
|
546
|
+
"text": "Base URL of the geo API (Xv.Api v1/geo \u2014 contract-compatible with the XvGeoTool API).\nDefault is the same-origin Front Door route, so no CORS is involved on crossvertise pages."
|
|
547
|
+
},
|
|
548
|
+
"getter": false,
|
|
549
|
+
"setter": false,
|
|
550
|
+
"reflect": false,
|
|
551
|
+
"attribute": "api-base",
|
|
552
|
+
"defaultValue": "'/api-fd/v1/geo'"
|
|
553
|
+
},
|
|
554
|
+
"indicator": {
|
|
555
|
+
"type": "string",
|
|
556
|
+
"mutable": false,
|
|
557
|
+
"complexType": {
|
|
558
|
+
"original": "string",
|
|
559
|
+
"resolved": "string",
|
|
560
|
+
"references": {}
|
|
561
|
+
},
|
|
562
|
+
"required": false,
|
|
563
|
+
"optional": false,
|
|
564
|
+
"docs": {
|
|
565
|
+
"tags": [],
|
|
566
|
+
"text": "Indicator whose per-unit values feed the tooltips and the emitted population sum (e.g. ATV_DEVICES)"
|
|
567
|
+
},
|
|
568
|
+
"getter": false,
|
|
569
|
+
"setter": false,
|
|
570
|
+
"reflect": false,
|
|
571
|
+
"attribute": "indicator",
|
|
572
|
+
"defaultValue": "'MBI_P_T_2025'"
|
|
573
|
+
},
|
|
574
|
+
"indicatorLabel": {
|
|
575
|
+
"type": "string",
|
|
576
|
+
"mutable": false,
|
|
577
|
+
"complexType": {
|
|
578
|
+
"original": "string",
|
|
579
|
+
"resolved": "string",
|
|
580
|
+
"references": {}
|
|
581
|
+
},
|
|
582
|
+
"required": false,
|
|
583
|
+
"optional": false,
|
|
584
|
+
"docs": {
|
|
585
|
+
"tags": [],
|
|
586
|
+
"text": "Unit label shown behind the indicator value in the tooltip"
|
|
587
|
+
},
|
|
588
|
+
"getter": false,
|
|
589
|
+
"setter": false,
|
|
590
|
+
"reflect": false,
|
|
591
|
+
"attribute": "indicator-label",
|
|
592
|
+
"defaultValue": "'Einwohner'"
|
|
593
|
+
},
|
|
594
|
+
"level": {
|
|
595
|
+
"type": "string",
|
|
596
|
+
"mutable": false,
|
|
597
|
+
"complexType": {
|
|
598
|
+
"original": "string",
|
|
599
|
+
"resolved": "string",
|
|
600
|
+
"references": {}
|
|
601
|
+
},
|
|
602
|
+
"required": false,
|
|
603
|
+
"optional": false,
|
|
604
|
+
"docs": {
|
|
605
|
+
"tags": [],
|
|
606
|
+
"text": "Geo level to display and select: plz5, gemeinde, kreis, regbez, bundesland"
|
|
607
|
+
},
|
|
608
|
+
"getter": false,
|
|
609
|
+
"setter": false,
|
|
610
|
+
"reflect": false,
|
|
611
|
+
"attribute": "level",
|
|
612
|
+
"defaultValue": "'plz5'"
|
|
613
|
+
},
|
|
614
|
+
"resolution": {
|
|
615
|
+
"type": "string",
|
|
616
|
+
"mutable": false,
|
|
617
|
+
"complexType": {
|
|
618
|
+
"original": "'medium' | 'high'",
|
|
619
|
+
"resolved": "\"high\" | \"medium\"",
|
|
620
|
+
"references": {}
|
|
621
|
+
},
|
|
622
|
+
"required": false,
|
|
623
|
+
"optional": false,
|
|
624
|
+
"docs": {
|
|
625
|
+
"tags": [],
|
|
626
|
+
"text": "Shape resolution: medium = simplified geometry (snappy), high = originals"
|
|
627
|
+
},
|
|
628
|
+
"getter": false,
|
|
629
|
+
"setter": false,
|
|
630
|
+
"reflect": false,
|
|
631
|
+
"attribute": "resolution",
|
|
632
|
+
"defaultValue": "'medium'"
|
|
633
|
+
},
|
|
634
|
+
"tiles": {
|
|
635
|
+
"type": "string",
|
|
636
|
+
"mutable": false,
|
|
637
|
+
"complexType": {
|
|
638
|
+
"original": "string",
|
|
639
|
+
"resolved": "string",
|
|
640
|
+
"references": {}
|
|
641
|
+
},
|
|
642
|
+
"required": false,
|
|
643
|
+
"optional": false,
|
|
644
|
+
"docs": {
|
|
645
|
+
"tags": [],
|
|
646
|
+
"text": "Raster tile URL template. Default: CARTO Positron \u2014 light, minimal, made for\ndata overlays; fits the crossvertise design. For production traffic, license\na tile plan (CARTO/MapTiler/Geofabrik) and set the keyed URL here."
|
|
647
|
+
},
|
|
648
|
+
"getter": false,
|
|
649
|
+
"setter": false,
|
|
650
|
+
"reflect": false,
|
|
651
|
+
"attribute": "tiles",
|
|
652
|
+
"defaultValue": "'https://basemaps.cartocdn.com/rastertiles/light_all/{z}/{x}/{y}@2x.png'"
|
|
653
|
+
},
|
|
654
|
+
"attribution": {
|
|
655
|
+
"type": "string",
|
|
656
|
+
"mutable": false,
|
|
657
|
+
"complexType": {
|
|
658
|
+
"original": "string",
|
|
659
|
+
"resolved": "string",
|
|
660
|
+
"references": {}
|
|
661
|
+
},
|
|
662
|
+
"required": false,
|
|
663
|
+
"optional": false,
|
|
664
|
+
"docs": {
|
|
665
|
+
"tags": [],
|
|
666
|
+
"text": ""
|
|
667
|
+
},
|
|
668
|
+
"getter": false,
|
|
669
|
+
"setter": false,
|
|
670
|
+
"reflect": false,
|
|
671
|
+
"attribute": "attribution",
|
|
672
|
+
"defaultValue": "'\u00A9 CARTO, \u00A9 OpenStreetMap contributors'"
|
|
673
|
+
},
|
|
674
|
+
"centerLng": {
|
|
675
|
+
"type": "number",
|
|
676
|
+
"mutable": false,
|
|
677
|
+
"complexType": {
|
|
678
|
+
"original": "number",
|
|
679
|
+
"resolved": "number",
|
|
680
|
+
"references": {}
|
|
681
|
+
},
|
|
682
|
+
"required": false,
|
|
683
|
+
"optional": false,
|
|
684
|
+
"docs": {
|
|
685
|
+
"tags": [],
|
|
686
|
+
"text": "Initial map center"
|
|
687
|
+
},
|
|
688
|
+
"getter": false,
|
|
689
|
+
"setter": false,
|
|
690
|
+
"reflect": false,
|
|
691
|
+
"attribute": "center-lng",
|
|
692
|
+
"defaultValue": "10.45"
|
|
693
|
+
},
|
|
694
|
+
"centerLat": {
|
|
695
|
+
"type": "number",
|
|
696
|
+
"mutable": false,
|
|
697
|
+
"complexType": {
|
|
698
|
+
"original": "number",
|
|
699
|
+
"resolved": "number",
|
|
700
|
+
"references": {}
|
|
701
|
+
},
|
|
702
|
+
"required": false,
|
|
703
|
+
"optional": false,
|
|
704
|
+
"docs": {
|
|
705
|
+
"tags": [],
|
|
706
|
+
"text": ""
|
|
707
|
+
},
|
|
708
|
+
"getter": false,
|
|
709
|
+
"setter": false,
|
|
710
|
+
"reflect": false,
|
|
711
|
+
"attribute": "center-lat",
|
|
712
|
+
"defaultValue": "51.16"
|
|
713
|
+
},
|
|
714
|
+
"zoom": {
|
|
715
|
+
"type": "number",
|
|
716
|
+
"mutable": false,
|
|
717
|
+
"complexType": {
|
|
718
|
+
"original": "number",
|
|
719
|
+
"resolved": "number",
|
|
720
|
+
"references": {}
|
|
721
|
+
},
|
|
722
|
+
"required": false,
|
|
723
|
+
"optional": false,
|
|
724
|
+
"docs": {
|
|
725
|
+
"tags": [],
|
|
726
|
+
"text": ""
|
|
727
|
+
},
|
|
728
|
+
"getter": false,
|
|
729
|
+
"setter": false,
|
|
730
|
+
"reflect": false,
|
|
731
|
+
"attribute": "zoom",
|
|
732
|
+
"defaultValue": "5.5"
|
|
733
|
+
},
|
|
734
|
+
"selectZoom": {
|
|
735
|
+
"type": "number",
|
|
736
|
+
"mutable": false,
|
|
737
|
+
"complexType": {
|
|
738
|
+
"original": "number",
|
|
739
|
+
"resolved": "number",
|
|
740
|
+
"references": {}
|
|
741
|
+
},
|
|
742
|
+
"required": false,
|
|
743
|
+
"optional": false,
|
|
744
|
+
"docs": {
|
|
745
|
+
"tags": [],
|
|
746
|
+
"text": "Minimum zoom before boundaries are loaded and selectable (use ~5 for bundesland, 9 for plz5)"
|
|
747
|
+
},
|
|
748
|
+
"getter": false,
|
|
749
|
+
"setter": false,
|
|
750
|
+
"reflect": false,
|
|
751
|
+
"attribute": "select-zoom",
|
|
752
|
+
"defaultValue": "9"
|
|
753
|
+
},
|
|
754
|
+
"workerUrl": {
|
|
755
|
+
"type": "string",
|
|
756
|
+
"mutable": false,
|
|
757
|
+
"complexType": {
|
|
758
|
+
"original": "string",
|
|
759
|
+
"resolved": "string",
|
|
760
|
+
"references": {}
|
|
761
|
+
},
|
|
762
|
+
"required": false,
|
|
763
|
+
"optional": true,
|
|
764
|
+
"docs": {
|
|
765
|
+
"tags": [],
|
|
766
|
+
"text": "Explicit URL of maplibre-gl-worker.mjs (maplibre-gl-shared.mjs must sit next to it).\nNeeded when the host bundler relocates the maplibre chunk so import.meta.url-relative\nworker resolution breaks (e.g. Angular/Vite dev server)."
|
|
767
|
+
},
|
|
768
|
+
"getter": false,
|
|
769
|
+
"setter": false,
|
|
770
|
+
"reflect": false,
|
|
771
|
+
"attribute": "worker-url"
|
|
772
|
+
},
|
|
773
|
+
"lazy": {
|
|
774
|
+
"type": "boolean",
|
|
775
|
+
"mutable": false,
|
|
776
|
+
"complexType": {
|
|
777
|
+
"original": "boolean",
|
|
778
|
+
"resolved": "boolean",
|
|
779
|
+
"references": {}
|
|
780
|
+
},
|
|
781
|
+
"required": false,
|
|
782
|
+
"optional": false,
|
|
783
|
+
"docs": {
|
|
784
|
+
"tags": [],
|
|
785
|
+
"text": "Load the map bundle only when scrolled near the viewport (default: true)"
|
|
786
|
+
},
|
|
787
|
+
"getter": false,
|
|
788
|
+
"setter": false,
|
|
789
|
+
"reflect": false,
|
|
790
|
+
"attribute": "lazy",
|
|
791
|
+
"defaultValue": "true"
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
static get states() {
|
|
796
|
+
return {
|
|
797
|
+
"ready": {},
|
|
798
|
+
"loadError": {},
|
|
799
|
+
"zoomGated": {},
|
|
800
|
+
"searchResults": {},
|
|
801
|
+
"searchQuery": {}
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
static get events() {
|
|
805
|
+
return [{
|
|
806
|
+
"method": "changeSelection",
|
|
807
|
+
"name": "changeSelection",
|
|
808
|
+
"bubbles": true,
|
|
809
|
+
"cancelable": true,
|
|
810
|
+
"composed": true,
|
|
811
|
+
"docs": {
|
|
812
|
+
"tags": [],
|
|
813
|
+
"text": "Selection changed: selected PLZs + population sum of the selection"
|
|
814
|
+
},
|
|
815
|
+
"complexType": {
|
|
816
|
+
"original": "GeoMapSelection",
|
|
817
|
+
"resolved": "GeoMapSelection",
|
|
818
|
+
"references": {
|
|
819
|
+
"GeoMapSelection": {
|
|
820
|
+
"location": "local",
|
|
821
|
+
"path": "/home/vsts/work/1/s/src/components/xv-geo-map/xv-geo-map.tsx",
|
|
822
|
+
"id": "src/components/xv-geo-map/xv-geo-map.tsx::GeoMapSelection"
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}, {
|
|
827
|
+
"method": "mapReady",
|
|
828
|
+
"name": "mapReady",
|
|
829
|
+
"bubbles": true,
|
|
830
|
+
"cancelable": true,
|
|
831
|
+
"composed": true,
|
|
832
|
+
"docs": {
|
|
833
|
+
"tags": [],
|
|
834
|
+
"text": "Map finished initializing"
|
|
835
|
+
},
|
|
836
|
+
"complexType": {
|
|
837
|
+
"original": "void",
|
|
838
|
+
"resolved": "void",
|
|
839
|
+
"references": {}
|
|
840
|
+
}
|
|
841
|
+
}];
|
|
842
|
+
}
|
|
843
|
+
static get methods() {
|
|
844
|
+
return {
|
|
845
|
+
"selectByRadius": {
|
|
846
|
+
"complexType": {
|
|
847
|
+
"signature": "(startPlz: string, km: number, add?: boolean) => Promise<GeoMapSelection | null>",
|
|
848
|
+
"parameters": [{
|
|
849
|
+
"name": "startPlz",
|
|
850
|
+
"type": "string",
|
|
851
|
+
"docs": ""
|
|
852
|
+
}, {
|
|
853
|
+
"name": "km",
|
|
854
|
+
"type": "number",
|
|
855
|
+
"docs": ""
|
|
856
|
+
}, {
|
|
857
|
+
"name": "add",
|
|
858
|
+
"type": "boolean",
|
|
859
|
+
"docs": ""
|
|
860
|
+
}],
|
|
861
|
+
"references": {
|
|
862
|
+
"Promise": {
|
|
863
|
+
"location": "global",
|
|
864
|
+
"id": "global::Promise"
|
|
865
|
+
},
|
|
866
|
+
"GeoMapSelection": {
|
|
867
|
+
"location": "local",
|
|
868
|
+
"path": "/home/vsts/work/1/s/src/components/xv-geo-map/xv-geo-map.tsx",
|
|
869
|
+
"id": "src/components/xv-geo-map/xv-geo-map.tsx::GeoMapSelection"
|
|
870
|
+
}
|
|
871
|
+
},
|
|
872
|
+
"return": "Promise<GeoMapSelection>"
|
|
873
|
+
},
|
|
874
|
+
"docs": {
|
|
875
|
+
"text": "Select all PLZs within `km` around a start PLZ (\"Umkreis\"). Replaces the selection unless `add` is true.",
|
|
876
|
+
"tags": []
|
|
877
|
+
}
|
|
878
|
+
},
|
|
879
|
+
"selectPostalCodes": {
|
|
880
|
+
"complexType": {
|
|
881
|
+
"signature": "(codes: string[], add?: boolean) => Promise<GeoMapSelection | null>",
|
|
882
|
+
"parameters": [{
|
|
883
|
+
"name": "codes",
|
|
884
|
+
"type": "string[]",
|
|
885
|
+
"docs": ""
|
|
886
|
+
}, {
|
|
887
|
+
"name": "add",
|
|
888
|
+
"type": "boolean",
|
|
889
|
+
"docs": ""
|
|
890
|
+
}],
|
|
891
|
+
"references": {
|
|
892
|
+
"Promise": {
|
|
893
|
+
"location": "global",
|
|
894
|
+
"id": "global::Promise"
|
|
895
|
+
},
|
|
896
|
+
"GeoMapSelection": {
|
|
897
|
+
"location": "local",
|
|
898
|
+
"path": "/home/vsts/work/1/s/src/components/xv-geo-map/xv-geo-map.tsx",
|
|
899
|
+
"id": "src/components/xv-geo-map/xv-geo-map.tsx::GeoMapSelection"
|
|
900
|
+
}
|
|
901
|
+
},
|
|
902
|
+
"return": "Promise<GeoMapSelection>"
|
|
903
|
+
},
|
|
904
|
+
"docs": {
|
|
905
|
+
"text": "Select an explicit list of PLZs (\"PLZ-Liste\" paste, query-param restore). Replaces the selection unless `add` is true.",
|
|
906
|
+
"tags": []
|
|
907
|
+
}
|
|
908
|
+
},
|
|
909
|
+
"clearSelection": {
|
|
910
|
+
"complexType": {
|
|
911
|
+
"signature": "() => Promise<void>",
|
|
912
|
+
"parameters": [],
|
|
913
|
+
"references": {
|
|
914
|
+
"Promise": {
|
|
915
|
+
"location": "global",
|
|
916
|
+
"id": "global::Promise"
|
|
917
|
+
}
|
|
918
|
+
},
|
|
919
|
+
"return": "Promise<void>"
|
|
920
|
+
},
|
|
921
|
+
"docs": {
|
|
922
|
+
"text": "Clear the whole selection.",
|
|
923
|
+
"tags": []
|
|
924
|
+
}
|
|
925
|
+
},
|
|
926
|
+
"previewByRadius": {
|
|
927
|
+
"complexType": {
|
|
928
|
+
"signature": "(startPlz: string, km: number) => Promise<number>",
|
|
929
|
+
"parameters": [{
|
|
930
|
+
"name": "startPlz",
|
|
931
|
+
"type": "string",
|
|
932
|
+
"docs": ""
|
|
933
|
+
}, {
|
|
934
|
+
"name": "km",
|
|
935
|
+
"type": "number",
|
|
936
|
+
"docs": ""
|
|
937
|
+
}],
|
|
938
|
+
"references": {
|
|
939
|
+
"Promise": {
|
|
940
|
+
"location": "global",
|
|
941
|
+
"id": "global::Promise"
|
|
942
|
+
},
|
|
943
|
+
"GeoUnit": {
|
|
944
|
+
"location": "global",
|
|
945
|
+
"id": "global::GeoUnit"
|
|
946
|
+
}
|
|
947
|
+
},
|
|
948
|
+
"return": "Promise<number>"
|
|
949
|
+
},
|
|
950
|
+
"docs": {
|
|
951
|
+
"text": "Highlight what a radius extension would ADD (optimizer proposal): the units within\n`km` that are not part of the selection render in the preview color. Visual only \u2014\nthe selection and its change events stay untouched. Returns the number of added units.",
|
|
952
|
+
"tags": []
|
|
953
|
+
}
|
|
954
|
+
},
|
|
955
|
+
"clearPreview": {
|
|
956
|
+
"complexType": {
|
|
957
|
+
"signature": "() => Promise<void>",
|
|
958
|
+
"parameters": [],
|
|
959
|
+
"references": {
|
|
960
|
+
"Promise": {
|
|
961
|
+
"location": "global",
|
|
962
|
+
"id": "global::Promise"
|
|
963
|
+
}
|
|
964
|
+
},
|
|
965
|
+
"return": "Promise<void>"
|
|
966
|
+
},
|
|
967
|
+
"docs": {
|
|
968
|
+
"text": "Remove the optimizer proposal highlight.",
|
|
969
|
+
"tags": []
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
static get elementRef() { return "el"; }
|
|
975
|
+
static get watchers() {
|
|
976
|
+
return [{
|
|
977
|
+
"propName": "level",
|
|
978
|
+
"methodName": "levelChanged"
|
|
979
|
+
}, {
|
|
980
|
+
"propName": "selectZoom",
|
|
981
|
+
"methodName": "selectZoomChanged"
|
|
982
|
+
}];
|
|
983
|
+
}
|
|
984
|
+
}
|