@geogirafe/lib-geoportal 1.1.0-dev.2407028282 → 1.1.0-dev.2407606313

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.
@@ -8,6 +8,7 @@
8
8
  content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1, shrink-to-fit=no" />
9
9
 
10
10
  <meta http-equiv="X-UA-Compatible" content="ie=edge" />
11
+
11
12
  <title>GeoGirafe API</title>
12
13
  <style>
13
14
  body {
@@ -66,6 +67,7 @@
66
67
  .left {
67
68
  width: 50%;
68
69
  height: 100%;
70
+ position: relative;
69
71
  }
70
72
 
71
73
  .right {
@@ -73,7 +75,8 @@
73
75
  height: 100%;
74
76
  }
75
77
 
76
- girafe-map {
78
+ geogirafe-map {
79
+ position: absolute;
77
80
  display: block;
78
81
  width: 100%;
79
82
  height: 100%;
@@ -102,29 +105,54 @@
102
105
  h2 {
103
106
  border-bottom: solid 1px #aaa;
104
107
  padding-bottom: 0.5rem;
108
+ margin-bottom: 0;
109
+ width: 100%;
110
+ }
111
+ .descr {
105
112
  width: 100%;
113
+ text-align: left;
114
+ margin-bottom: 1rem;
106
115
  }
107
116
  </style>
117
+
108
118
  <script type="module" src="./src/api/api.ts"></script>
119
+ <link rel="stylesheet" href="./src/styles/api.css" />
120
+
109
121
  <script>
110
122
  document.addEventListener('DOMContentLoaded', () => {
111
123
  const apiOrigin = `${window.location.origin}${window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/'))}`;
112
124
  const elem = document.getElementById('include-code');
113
125
 
114
126
  elem.textContent = elem.textContent.replace('src="/geogirafe-api.js"', `src="${apiOrigin}/geogirafe-api.js"`);
127
+ elem.textContent = elem.textContent.replace(
128
+ 'href="/geogirafe-api.css"',
129
+ `src="${apiOrigin}/geogirafe-api.css"`
130
+ );
115
131
 
116
132
  const sections = document.querySelectorAll('section');
117
- sections.forEach((section) => {
133
+ for (const section of sections) {
118
134
  const leftDiv = section.querySelector('.left');
119
135
  const rightDiv = section.querySelector('.right');
120
136
  if (leftDiv && rightDiv) {
121
- let content = leftDiv.innerHTML;
122
- content = content.replace(/\s+/g, ' ').replace(/>\s+/g, '>').replace(/\s+</g, '<').trim();
123
- const pre = document.createElement('pre');
124
- pre.textContent = content;
125
- rightDiv.appendChild(pre);
137
+ const ggMap = leftDiv.querySelector('geogirafe-map');
138
+ if (ggMap) {
139
+ ggMap.addEventListener('geogirafe-api-ready', () => {
140
+ let content = leftDiv.innerHTML;
141
+ content = content
142
+ .replaceAll(/\s+/g, ' ')
143
+ .replaceAll(/>\s+/g, '>')
144
+ .replaceAll(/\s+</g, '<')
145
+ .replaceAll('=""', '')
146
+ .replaceAll(' style="display: block;"', '')
147
+ .replaceAll('><', '>\n<')
148
+ .trim();
149
+ const pre = document.createElement('pre');
150
+ pre.textContent = content;
151
+ rightDiv.appendChild(pre);
152
+ });
153
+ }
126
154
  }
127
- });
155
+ }
128
156
  });
129
157
  </script>
130
158
  </head>
@@ -150,12 +178,13 @@
150
178
  <pre id="include-code">
151
179
  &lt;head&gt;
152
180
  &lt;script type="module" crossorigin src="/geogirafe-api.js"&gt;&lt;/script&gt;
181
+ &lt;link rel="stylesheet" href="/geogirafe-api.css" /&gt;
153
182
  &lt;/head&gt;</pre
154
183
  >
155
184
  </section>
156
185
 
157
- <!-- Simple map -->
158
186
  <h2>Add a simple map view</h2>
187
+ <p class="descr">Add a simple map to the page, with its default configuration.</p>
159
188
  <section>
160
189
  <div class="row">
161
190
  <div class="left">
@@ -167,10 +196,11 @@
167
196
 
168
197
  <!-- With center -->
169
198
  <h2>Add a map with center coordinates</h2>
199
+ <p class="descr">Set the default center point.</p>
170
200
  <section>
171
201
  <div class="row">
172
202
  <div class="left">
173
- <geogirafe-map center="2614484, 1267592" />
203
+ <geogirafe-map center="api.demo.center" />
174
204
  </div>
175
205
  <div class="right"></div>
176
206
  </div>
@@ -178,10 +208,11 @@
178
208
 
179
209
  <!-- Custom zoom level -->
180
210
  <h2>Add a map with a custom zoomlevel</h2>
211
+ <p class="descr">Set the default zoom level.</p>
181
212
  <section>
182
213
  <div class="row">
183
214
  <div class="left">
184
- <geogirafe-map zoom="11" />
215
+ <geogirafe-map zoom="api.demo.zoom" />
185
216
  </div>
186
217
  <div class="right"></div>
187
218
  </div>
@@ -189,17 +220,19 @@
189
220
 
190
221
  <!-- Custom basemap -->
191
222
  <h2>Add a map with a custom basemap</h2>
223
+ <p class="descr">Set the default basemap.</p>
192
224
  <section>
193
225
  <div class="row">
194
226
  <div class="left">
195
- <geogirafe-map basemap="OF_Orthofoto2023Mai" />
227
+ <geogirafe-map basemap="api.demo.basemap" />
196
228
  </div>
197
229
  <div class="right"></div>
198
230
  </div>
199
231
  </section>
200
232
 
201
- <!-- Basemap selector -->
202
- <!-- <h2>Add a map with a basemap selector</h2>
233
+ <!-- Basemap selector -->
234
+ <h2>Add a map with a basemap selector</h2>
235
+ <p class="descr">Activate the basemap selector, allowing the user to select a different basemap.</p>
203
236
  <section>
204
237
  <div class="row">
205
238
  <div class="left">
@@ -207,7 +240,164 @@
207
240
  </div>
208
241
  <div class="right"></div>
209
242
  </div>
210
- </section> -->
243
+ </section>
244
+
245
+ <!-- Crosshair -->
246
+ <h2>Add a crosshair somewhere on the map</h2>
247
+ <p class="descr">Add a cross at the defined coordinates.</p>
248
+ <section>
249
+ <div class="row">
250
+ <div class="left">
251
+ <geogirafe-map crosshair="api.demo.crosshair" />
252
+ </div>
253
+ <div class="right"></div>
254
+ </div>
255
+ </section>
256
+
257
+ <!-- Tooltip -->
258
+ <h2>Add a toolip somewhere on the map</h2>
259
+ <p class="descr">Add a tooltip with cutom text at the defined coordinates.</p>
260
+ <section>
261
+ <div class="row">
262
+ <div class="left">
263
+ <geogirafe-map tooltip="api.demo.tooltip" />
264
+ </div>
265
+ <div class="right"></div>
266
+ </div>
267
+ </section>
268
+
269
+ <!-- Markers -->
270
+ <h2>Add a marker somewhere on the map</h2>
271
+ <p class="descr">Add a marker at the defined coordinates.</p>
272
+ <section>
273
+ <div class="row">
274
+ <div class="left">
275
+ <geogirafe-map markers="api.demo.marker" />
276
+ </div>
277
+ <div class="right"></div>
278
+ </div>
279
+ </section>
280
+
281
+ <!-- Multiple markers -->
282
+ <h2>Add multiple markers on the map</h2>
283
+ <p class="descr">Add multiple markers at the defined coordinates.</p>
284
+ <section>
285
+ <div class="row">
286
+ <div class="left">
287
+ <geogirafe-map markers="api.demo.markers" />
288
+ </div>
289
+ <div class="right"></div>
290
+ </div>
291
+ </section>
292
+
293
+ <!-- Layers -->
294
+ <h2>Add a layer to the map</h2>
295
+ <p class="descr">Add a layer to the map. The layer name must be defined in the themes.json file.</p>
296
+ <section>
297
+ <div class="row">
298
+ <div class="left">
299
+ <geogirafe-map layers="api.demo.layers" />
300
+ </div>
301
+ <div class="right"></div>
302
+ </div>
303
+ </section>
304
+
305
+ <!-- Mulaiple layers -->
306
+ <h2>Add multiple layers to the map</h2>
307
+ <p class="descr">Add multiple layers to the map. The layer names must be defined in the themes.json file.</p>
308
+ <section>
309
+ <div class="row">
310
+ <div class="left">
311
+ <geogirafe-map layers="api.demo.multiLayers" />
312
+ </div>
313
+ <div class="right"></div>
314
+ </div>
315
+ </section>
316
+
317
+ <!-- Layers with config -->
318
+ <h2>Add a layer to the map, including default opacity and filter</h2>
319
+ <p class="descr">
320
+ Add a layer to the map. The layer name must be defined in the themes.json file. The layer options follow the
321
+ format defined in the
322
+ <a target="_blank" href="https://doc.geogirafe.org/docs/core-concepts/permalink#layers-configuration"
323
+ >documentation</a
324
+ >.
325
+ </p>
326
+ <section>
327
+ <div class="row">
328
+ <div class="left">
329
+ <geogirafe-map center="api.demo.layersWithConfigCenter" layers="api.demo.layersWithConfig" />
330
+ </div>
331
+ <div class="right"></div>
332
+ </div>
333
+ </section>
334
+
335
+ <!-- Search bar -->
336
+ <h2>Add a map with a search bar</h2>
337
+ <p class="descr">Activate the searchbar, allowing the user to search for objects and layers.</p>
338
+ <section>
339
+ <div class="row">
340
+ <div class="left">
341
+ <geogirafe-map searchbar />
342
+ </div>
343
+ <div class="right"></div>
344
+ </div>
345
+ </section>
346
+
347
+ <!-- Selection box -->
348
+ <h2>Add a map and allow selection</h2>
349
+ <p class="descr">
350
+ Activate the selection window, allowing the user to select objects and display their properties.
351
+ </p>
352
+ <section>
353
+ <div class="row">
354
+ <div class="left">
355
+ <geogirafe-map layers="api.demo.layers" selectionbox />
356
+ </div>
357
+ <div class="right"></div>
358
+ </div>
359
+ </section>
360
+
361
+ <!-- Set values with javascript -->
362
+ <h2>Set map values with javascript</h2>
363
+ <p class="descr">This example shows how to use javascript to set attributes after the map creation.</p>
364
+ <section>
365
+ <div class="row">
366
+ <div class="left">
367
+ <button onclick="document.getElementById('my-map').setAttribute('center', 'api.demo.center');">
368
+ Recenter
369
+ </button>
370
+ <button onclick="document.getElementById('my-map').setAttribute('zoom', 'api.demo.zoom');">Zoom</button>
371
+ <button onclick="document.getElementById('my-map').setAttribute('basemap', 'api.demo.basemap');">
372
+ Set Basemap
373
+ </button>
374
+ <button onclick="document.getElementById('my-map').setAttribute('basemapselector', '');">
375
+ Enable basemap selector
376
+ </button>
377
+ <button onclick="document.getElementById('my-map').setAttribute('markers', 'api.demo.marker');">
378
+ Add Marker
379
+ </button>
380
+ <button onclick="document.getElementById('my-map').setAttribute('layers', 'api.demo.layers');">
381
+ Add Layer
382
+ </button>
383
+ <geogirafe-map id="my-map" />
384
+ </div>
385
+ <div class="right"></div>
386
+ </div>
387
+ </section>
211
388
  </div>
389
+
390
+ <!--
391
+ Still missing:
392
+ - searchfor: centerto search result
393
+ - center to bounding box
394
+ - projection: configure map projection
395
+ - legend:
396
+ - embeded: deactivate mouse scroll
397
+ - marker: offset, size, ...
398
+ - load markers from file
399
+ - load data from file
400
+
401
+ -->
212
402
  </body>
213
403
  </html>
@@ -1 +1 @@
1
- {"version":"1.1.0-dev.2407028282", "build":"2407028282", "date":"25/03/2026"}
1
+ {"version":"1.1.0-dev.2407606313", "build":"2407606313", "date":"25/03/2026"}
@@ -96,6 +96,10 @@ export default defineConfig(({ command, mode }) => {
96
96
  return 'geogirafe-api.js';
97
97
  }
98
98
  return 'assets/[name].[hash].js';
99
+ },
100
+ assetFileNames: (assetInfo) => {
101
+ if (assetInfo.name == 'api.css') return 'geogirafe-api.css';
102
+ return 'assets/[name].[hash].[ext]';
99
103
  }
100
104
  }
101
105
  }
@@ -223,6 +223,19 @@ declare class GirafeConfig {
223
223
  description: string;
224
224
  }[];
225
225
  };
226
+ api?: {
227
+ demo: {
228
+ center: string;
229
+ zoom: string;
230
+ basemap: string;
231
+ crosshair: string;
232
+ tooltip: string;
233
+ layers: string;
234
+ multiLayers: string;
235
+ layersWithConfig: string;
236
+ layersWithConfigCenter: string;
237
+ };
238
+ };
226
239
  extendedConfig?: Record<string, object>;
227
240
  static readonly DEFAULT_LOCALE = "en-US";
228
241
  /**
@@ -261,5 +274,6 @@ declare class GirafeConfig {
261
274
  private initExternalLayers;
262
275
  private initExtendedConfig;
263
276
  private initOnboarding;
277
+ private initApiConfig;
264
278
  }
265
279
  export default GirafeConfig;
@@ -28,6 +28,7 @@ class GirafeConfig {
28
28
  userdata;
29
29
  contact;
30
30
  onboarding;
31
+ api;
31
32
  // The extended configuration can be used by third-party components or extensions
32
33
  // to add custom attributes to the GirafeConfig.
33
34
  extendedConfig;
@@ -66,6 +67,7 @@ class GirafeConfig {
66
67
  this.contact = this.initConfigContact(config);
67
68
  this.extendedConfig = this.initExtendedConfig(config);
68
69
  this.onboarding = this.initOnboarding(config);
70
+ this.api = this.initApiConfig(config);
69
71
  try {
70
72
  this.search = this.initConfigSearch(config);
71
73
  }
@@ -403,5 +405,8 @@ class GirafeConfig {
403
405
  initOnboarding(config) {
404
406
  return config.onboarding ?? undefined;
405
407
  }
408
+ initApiConfig(config) {
409
+ return config.api ?? undefined;
410
+ }
406
411
  }
407
412
  export default GirafeConfig;
package/tools/main.d.ts CHANGED
@@ -76,6 +76,7 @@ export type { default as IGirafePanel } from './state/igirafepanel.js';
76
76
  export { isGirafePanel } from './state/igirafepanel.js';
77
77
  export { default as LayersConfig } from './state/layersConfig.js';
78
78
  export { default as MapManager } from './state/mapManager.js';
79
+ export type { MapMarker } from './state/mapposition.js';
79
80
  export { default as MapPosition } from './state/mapposition.js';
80
81
  export type { InitialSelectionQuery } from './state/objectselection.js';
81
82
  export { default as ObjectSelection } from './state/objectselection.js';
@@ -12,7 +12,8 @@ export default class MapPositionSerializer {
12
12
  center: mapPosition.center,
13
13
  resolution: mapPosition.resolution,
14
14
  crosshair: mapPosition.crosshair,
15
- tooltip: mapPosition.tooltip
15
+ tooltip: mapPosition.tooltip,
16
+ markers: mapPosition.markers
16
17
  };
17
18
  return JSON.stringify(pos);
18
19
  }
@@ -23,6 +24,7 @@ export default class MapPositionSerializer {
23
24
  mapPosition.resolution = pos.resolution;
24
25
  mapPosition.crosshair = pos.crosshair;
25
26
  mapPosition.tooltip = pos.tooltip;
27
+ mapPosition.markers = pos.markers;
26
28
  this.state.position = mapPosition;
27
29
  }
28
30
  }
@@ -26,6 +26,10 @@ describe('MapPositionSerializer.serialize', () => {
26
26
  content: 'Test tooltip',
27
27
  position: [10, 20]
28
28
  };
29
+ mapPosition.markers.push({
30
+ imageUrl: 'http://url.to.marker',
31
+ position: [11, 22]
32
+ });
29
33
  const serialized = serializer.brainSerialize(mapPosition);
30
34
  expect(serialized).toBe(JSON.stringify({
31
35
  center: [10, 20],
@@ -34,7 +38,13 @@ describe('MapPositionSerializer.serialize', () => {
34
38
  tooltip: {
35
39
  content: 'Test tooltip',
36
40
  position: [10, 20]
37
- }
41
+ },
42
+ markers: [
43
+ {
44
+ imageUrl: 'http://url.to.marker',
45
+ position: [11, 22]
46
+ }
47
+ ]
38
48
  }));
39
49
  });
40
50
  });
@@ -47,7 +57,13 @@ describe('MapPositionSerializer.deserialize', () => {
47
57
  tooltip: {
48
58
  content: 'Test tooltip',
49
59
  position: [10, 20]
50
- }
60
+ },
61
+ markers: [
62
+ {
63
+ imageUrl: 'http://url.to.marker',
64
+ position: [11, 22]
65
+ }
66
+ ]
51
67
  });
52
68
  serializer.brainDeserialize(json);
53
69
  const state = context.stateManager.state;
@@ -59,6 +75,12 @@ describe('MapPositionSerializer.deserialize', () => {
59
75
  content: 'Test tooltip',
60
76
  position: [10, 20]
61
77
  });
78
+ expect(state.position.markers).toEqual([
79
+ {
80
+ imageUrl: 'http://url.to.marker',
81
+ position: [11, 22]
82
+ }
83
+ ]);
62
84
  });
63
85
  it('should override previous map position in state', () => {
64
86
  const state = context.stateManager.state;
@@ -1,4 +1,8 @@
1
1
  import { Coordinate } from 'ol/coordinate.js';
2
+ export type MapMarker = {
3
+ imageUrl: string;
4
+ position: Coordinate;
5
+ };
2
6
  declare class MapPosition {
3
7
  center: Coordinate;
4
8
  zoom?: number;
@@ -9,6 +13,7 @@ declare class MapPosition {
9
13
  content: string;
10
14
  position?: Coordinate;
11
15
  };
16
+ markers: MapMarker[];
12
17
  get isValid(): boolean;
13
18
  clone(): MapPosition;
14
19
  }
@@ -5,6 +5,7 @@ class MapPosition {
5
5
  scale;
6
6
  crosshair;
7
7
  tooltip;
8
+ markers = [];
8
9
  get isValid() {
9
10
  if (Number.isNaN(this.resolution)) {
10
11
  return false;
@@ -27,6 +28,12 @@ class MapPosition {
27
28
  position: this.tooltip.position ? [...this.tooltip.position] : undefined
28
29
  }
29
30
  : undefined;
31
+ for (const marker of this.markers) {
32
+ position.markers.push({
33
+ imageUrl: marker.imageUrl,
34
+ position: marker.position
35
+ });
36
+ }
30
37
  return position;
31
38
  }
32
39
  }
@@ -13,6 +13,7 @@ describe('MapPosition', () => {
13
13
  expect(mapPosition.scale).toBeUndefined();
14
14
  expect(mapPosition.crosshair).toBeUndefined();
15
15
  expect(mapPosition.tooltip).toBeUndefined();
16
+ expect(mapPosition.markers.length).toBe(0);
16
17
  });
17
18
  describe('isValid', () => {
18
19
  it('should return false if resolution is NaN', () => {
@@ -25,7 +25,8 @@ export default class ThemesHelper extends GirafeSingleton {
25
25
  addThemesFromUrl(): boolean;
26
26
  addGroupsFromUrl(): boolean;
27
27
  addLayersFromUrl(): boolean;
28
- private addLayerBaseFromUrl;
28
+ addLayerFromName(layername: string): boolean;
29
+ private addLayerBaseFromName;
29
30
  private extractLayerOptions;
30
31
  private extractLayerOptionOpacity;
31
32
  private extractLayerOptionFilter;
@@ -206,7 +206,7 @@ export default class ThemesHelper extends GirafeSingleton {
206
206
  let added = false;
207
207
  if (this.context.permalinkManager.hasGroups()) {
208
208
  for (const groupname of this.context.permalinkManager.getGroups()) {
209
- added = this.addLayerBaseFromUrl(groupname, 'group') || added;
209
+ added = this.addLayerBaseFromName(groupname, 'group') || added;
210
210
  }
211
211
  }
212
212
  return added;
@@ -215,38 +215,40 @@ export default class ThemesHelper extends GirafeSingleton {
215
215
  let added = false;
216
216
  if (this.context.permalinkManager.hasLayers()) {
217
217
  for (const layername of this.context.permalinkManager.getLayers()) {
218
- added = this.addLayerBaseFromUrl(layername, 'layer') || added;
218
+ added = this.addLayerBaseFromName(layername, 'layer') || added;
219
219
  }
220
220
  }
221
221
  return added;
222
222
  }
223
- addLayerBaseFromUrl(layer, type) {
224
- let added = false;
223
+ addLayerFromName(layername) {
224
+ return this.addLayerBaseFromName(layername, 'layer');
225
+ }
226
+ addLayerBaseFromName(layer, type) {
225
227
  const layerOptions = this.extractLayerOptions(layer, type);
226
- if (layerOptions) {
227
- const clonedTheme = this.getMinimalClonedThemeForLayer(layerOptions.originalLayer);
228
- this.mergeLayerWithExistingLayerTree(clonedTheme, this.state.layers.layersList);
229
- added = true;
230
- if (layerOptions.active) {
231
- const clonedLayer = this.findLayerRecursive(clonedTheme.children, layerOptions.originalLayer.name);
232
- if (clonedLayer) {
233
- this.context.layerManager.toggle(clonedLayer, 'on');
234
- if (layerOptions.opacity) {
235
- clonedLayer.opacity = layerOptions.opacity;
236
- }
237
- if (layerOptions.filter) {
238
- clonedLayer.filter = layerOptions.filter;
239
- }
240
- if (layerOptions.timeRestriction) {
241
- clonedLayer.timeRestriction = layerOptions.timeRestriction;
242
- }
243
- }
244
- }
245
- }
246
- else {
228
+ if (!layerOptions) {
247
229
  console.warn(`Layer ${layer} cannot be found`);
230
+ return false;
248
231
  }
249
- return added;
232
+ const clonedTheme = this.getMinimalClonedThemeForLayer(layerOptions.originalLayer);
233
+ this.mergeLayerWithExistingLayerTree(clonedTheme, this.state.layers.layersList);
234
+ if (!layerOptions.active) {
235
+ // Layer added, but inactive. No further config needed
236
+ return true;
237
+ }
238
+ const clonedLayer = this.findLayerRecursive(this.state.layers.layersList, layerOptions.originalLayer.name);
239
+ if (clonedLayer) {
240
+ this.context.layerManager.toggle(clonedLayer, 'on');
241
+ if (layerOptions.opacity) {
242
+ clonedLayer.opacity = layerOptions.opacity;
243
+ }
244
+ if (layerOptions.filter) {
245
+ clonedLayer.filter = layerOptions.filter;
246
+ }
247
+ if (layerOptions.timeRestriction) {
248
+ clonedLayer.timeRestriction = layerOptions.timeRestriction;
249
+ }
250
+ }
251
+ return true;
250
252
  }
251
253
  extractLayerOptions(urlParam, type) {
252
254
  let active = true;
@@ -349,12 +351,14 @@ export default class ThemesHelper extends GirafeSingleton {
349
351
  this.addLayerToLayerTree(newLayer, existingList, activate, layerTreeChanges, parent);
350
352
  return;
351
353
  }
352
- else if (newLayer.isHighlighted) {
354
+ if (newLayer.isHighlighted) {
355
+ // The layer is already present
353
356
  this.highlightLayerInLayerTree(existingLayer, activate, layerTreeChanges);
357
+ return;
354
358
  }
355
- // Otherwise, we have to merge the themes
356
- if ((newLayer instanceof ThemeLayer || newLayer instanceof GroupLayer) &&
357
- (existingLayer instanceof ThemeLayer || existingLayer instanceof GroupLayer)) {
359
+ // Otherwise, we have to merge the themes/groups
360
+ if ((newLayer instanceof ThemeLayer && existingLayer instanceof ThemeLayer) ||
361
+ (newLayer instanceof GroupLayer && existingLayer instanceof GroupLayer)) {
358
362
  for (const child of newLayer.children) {
359
363
  this.mergeLayerWithExistingLayerTree(child, existingLayer.children, activate, layerTreeChanges, existingLayer);
360
364
  }
@@ -19,7 +19,8 @@ export default class PermalinkManager extends GirafeSingleton {
19
19
  }[];
20
20
  } | null;
21
21
  hasMapPosition(): boolean | "" | null;
22
- hasToolTip(): boolean;
22
+ private hasToolTip;
23
+ private hasMarker;
23
24
  getMapPosition(targetProjection: Projection): MapPosition | undefined;
24
25
  hasSearch(): boolean;
25
26
  getSearchTerm(): string;
@@ -12,6 +12,7 @@ export default class PermalinkManager extends GirafeSingleton {
12
12
  'map_zoom',
13
13
  'map_crosshair',
14
14
  'map_tooltip',
15
+ 'map_marker',
15
16
  'search',
16
17
  'basemap',
17
18
  'themes',
@@ -79,6 +80,9 @@ export default class PermalinkManager extends GirafeSingleton {
79
80
  hasToolTip() {
80
81
  return this.params['map_tooltip'] !== null;
81
82
  }
83
+ hasMarker() {
84
+ return this.params['map_marker'] !== null;
85
+ }
82
86
  getMapPosition(targetProjection) {
83
87
  if (this.hasMapPosition()) {
84
88
  const position = new MapPosition();
@@ -106,6 +110,13 @@ export default class PermalinkManager extends GirafeSingleton {
106
110
  position: center
107
111
  };
108
112
  }
113
+ if (this.hasMarker()) {
114
+ const imageUrl = DOMPurify.sanitize(this.params['map_marker']);
115
+ position.markers.push({
116
+ imageUrl: imageUrl,
117
+ position: center
118
+ });
119
+ }
109
120
  if (position.isValid) {
110
121
  return position;
111
122
  }
@@ -13,6 +13,7 @@ const emptyUrlParameters = {
13
13
  map_zoom: null,
14
14
  map_crosshair: null,
15
15
  map_tooltip: null,
16
+ map_marker: null,
16
17
  search: null,
17
18
  basemap: null,
18
19
  themes: null,