@geogirafe/lib-geoportal 1.1.0-dev.2536443365 → 1.1.0-dev.2545448597

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.
@@ -26,6 +26,7 @@ export default class GeoGirafeApi extends GirafeHTMLElement {
26
26
  private manageCrosshairAttribute;
27
27
  private manageTooltipAttribute;
28
28
  private manageMarkersAttribute;
29
+ private markerStringToMapMarker;
29
30
  private initialize;
30
31
  private injectConfigMetaTags;
31
32
  }
@@ -10,6 +10,7 @@ import MenuButtonComponent from '../components/menubutton/component.js';
10
10
  import MapCustomContextMenuComponent from '../components/context-menu/custom-context-menu/component.js';
11
11
  import SearchComponent from '../components/search/component.js';
12
12
  import SelectionWindowComponent from '../components/selectionwindow/component.js';
13
+ import { splitTrimAndConvertToNumber } from '../tools/utils/utils.js';
13
14
  export default class GeoGirafeApi extends GirafeHTMLElement {
14
15
  templateUrl = null;
15
16
  styleUrls = null;
@@ -245,17 +246,10 @@ export default class GeoGirafeApi extends GirafeHTMLElement {
245
246
  const markers = this.getAttributeFromConfig('markers');
246
247
  if (markers) {
247
248
  const markerValues = markers.split(';');
248
- for (const makerValue of markerValues) {
249
- const content = makerValue.split('|');
250
- const coords = content[0].split(',');
251
- const x = Number(coords[0].trim());
252
- const y = Number(coords[1].trim());
253
- if (!Number.isNaN(x) && !Number.isNaN(y)) {
254
- const imageUrl = content[1].trim();
255
- this.context.stateManager.state.position.markers.push({
256
- position: [x, y],
257
- imageUrl: imageUrl
258
- });
249
+ for (const markerValue of markerValues) {
250
+ const marker = this.markerStringToMapMarker(markerValue);
251
+ if (marker) {
252
+ this.context.stateManager.state.position.markers.push(marker);
259
253
  }
260
254
  else {
261
255
  console.warn('Invalid marker coordinates');
@@ -263,6 +257,43 @@ export default class GeoGirafeApi extends GirafeHTMLElement {
263
257
  }
264
258
  }
265
259
  }
260
+ markerStringToMapMarker(markerValue) {
261
+ console.log('markerStringToMapMarker', markerValue);
262
+ const contentParts = markerValue.split('|');
263
+ const needToGuessThirdParameter = contentParts.length == 3;
264
+ if (needToGuessThirdParameter) {
265
+ console.warn(`Marker has only 3 Parameters! Guessing if third Parameter '${contentParts[2]}' is Size or Offset`);
266
+ }
267
+ const coords = contentParts[0].split(',');
268
+ const x = Number(coords[0].trim());
269
+ const y = Number(coords[1].trim());
270
+ if (!Number.isNaN(x) && !Number.isNaN(y)) {
271
+ const imageUrl = contentParts[1].trim();
272
+ let size;
273
+ let offset;
274
+ if (needToGuessThirdParameter) {
275
+ if (contentParts[2].match(/[+-]+/gm)) {
276
+ offset = splitTrimAndConvertToNumber(contentParts[2]);
277
+ }
278
+ else {
279
+ size = splitTrimAndConvertToNumber(contentParts[2]);
280
+ }
281
+ }
282
+ else if (contentParts.length > 2) {
283
+ size = splitTrimAndConvertToNumber(contentParts[2]);
284
+ offset = splitTrimAndConvertToNumber(contentParts[3]);
285
+ }
286
+ return {
287
+ position: [x, y],
288
+ imageUrl: imageUrl,
289
+ size: size,
290
+ offset: offset
291
+ };
292
+ }
293
+ else {
294
+ return undefined;
295
+ }
296
+ }
266
297
  async initialize() {
267
298
  await this.context.initialize();
268
299
  // Register Coordinate Reference Systems (CRS) definitions in PROJ4
@@ -135,7 +135,7 @@ export default class MapComponent extends GirafeHTMLElement {
135
135
  this.subscribe('position.markers', () => {
136
136
  this.clearAllMarkers();
137
137
  for (const marker of this.state.position.markers) {
138
- this.addMarker(marker.position, marker.imageUrl);
138
+ this.addMarker(marker);
139
139
  }
140
140
  });
141
141
  this.subscribe('globe.display', async () => {
@@ -177,7 +177,7 @@ export default class MapComponent extends GirafeHTMLElement {
177
177
  }
178
178
  this.showCrosshair(this.state.position);
179
179
  for (const marker of this.state.position.markers) {
180
- this.addMarker(marker.position, marker.imageUrl);
180
+ this.addMarker(marker);
181
181
  }
182
182
  }
183
183
  });
@@ -1017,23 +1017,24 @@ export default class MapComponent extends GirafeHTMLElement {
1017
1017
  this.state.position = position;
1018
1018
  if (position.markers.length > 0) {
1019
1019
  // Add marker to the map
1020
- this.addMarker(position.markers[0].position, position.markers[0].imageUrl);
1020
+ this.addMarker(position.markers[0]);
1021
1021
  }
1022
1022
  }
1023
1023
  }
1024
1024
  clearAllMarkers() {
1025
1025
  this.markerSource.clear();
1026
1026
  }
1027
- addMarker(position, imageUrl) {
1027
+ addMarker(mapMarker) {
1028
1028
  const iconStyle = new Style({
1029
1029
  image: new Icon({
1030
- //anchor: [0.5, 1], // Point d'ancrage (centre en bas)
1031
- src: imageUrl
1032
- //scale: 0.5, // Ajustez la taille si nécessaire
1030
+ displacement: mapMarker.offset,
1031
+ src: mapMarker.imageUrl,
1032
+ width: mapMarker.size ? mapMarker.size[0] : undefined,
1033
+ height: mapMarker.size ? mapMarker.size[1] : undefined
1033
1034
  })
1034
1035
  });
1035
1036
  const marker = new Feature({
1036
- geometry: new Point(position)
1037
+ geometry: new Point(mapMarker.position)
1037
1038
  });
1038
1039
  marker.setStyle(iconStyle);
1039
1040
  this.markerSource.addFeature(marker);
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "name": "GeoGirafe PSC",
6
6
  "url": "https://doc.geomapfish.dev"
7
7
  },
8
- "version": "1.1.0-dev.2536443365",
8
+ "version": "1.1.0-dev.2545448597",
9
9
  "type": "module",
10
10
  "engines": {
11
11
  "node": ">=20.19.0"
@@ -290,6 +290,21 @@
290
290
  </div>
291
291
  </section>
292
292
 
293
+ <!-- Multiple markers with size and/or offset -->
294
+ <h2>Add multiple markers with size and/or offset on the map</h2>
295
+ <p class="descr">Add multiple markers at the defined coordinates. You can specify size and offset. If you only
296
+ specify three parameters, the third parameter can be the size if you <strong>don't</strong> include a sign
297
+ (e.g. <code>24,24</code>) or the offset if you <strong>do</strong> include a sign (e.g. <code>+2,-6</code>).
298
+ </p>
299
+ <section>
300
+ <div class="row">
301
+ <div class="left">
302
+ <geogirafe-map markers="api.demo.markersWithSizeAndOrOffset" />
303
+ </div>
304
+ <div class="right"></div>
305
+ </div>
306
+ </section>
307
+
293
308
  <!-- Layers -->
294
309
  <h2>Add a layer to the map</h2>
295
310
  <p class="descr">Add a layer to the map. The layer name must be defined in the themes.json file.</p>
@@ -302,7 +317,7 @@
302
317
  </div>
303
318
  </section>
304
319
 
305
- <!-- Mulaiple layers -->
320
+ <!-- Multiple layers -->
306
321
  <h2>Add multiple layers to the map</h2>
307
322
  <p class="descr">Add multiple layers to the map. The layer names must be defined in the themes.json file.</p>
308
323
  <section>
@@ -1 +1 @@
1
- {"version":"1.1.0-dev.2536443365", "build":"2536443365", "date":"19/05/2026"}
1
+ {"version":"1.1.0-dev.2545448597", "build":"2545448597", "date":"22/05/2026"}
package/tools/main.d.ts CHANGED
@@ -120,7 +120,7 @@ export { unByKeyAll, getOlayerByName, removeUnwantedOlParams, polygonFromCircle,
120
120
  export { getPropertyByPath, setPropertyByPath, createObjectFromPath, deletePropertyByPath, mergeObjects } from './utils/pathUtils.js';
121
121
  export { generateQrCode } from './utils/qrcode.js';
122
122
  export { default as ServiceWorkerHelper } from './utils/swhelper.js';
123
- export { systemIsInDarkMode, isSafari, isFirefox, getValidIndex, minMax, hexToRgbaArray, rgbStrToRgbaArray, colorToRgbaArray, isValidEmail, applyOpacityToLayers, applyFeaturesToSelection, linkify, applyDefaultPrefixToUrl } from './utils/utils.js';
123
+ export { systemIsInDarkMode, isSafari, isFirefox, getValidIndex, minMax, hexToRgbaArray, rgbStrToRgbaArray, colorToRgbaArray, isValidEmail, applyOpacityToLayers, applyFeaturesToSelection, linkify, applyDefaultPrefixToUrl, splitTrimAndConvertToNumber } from './utils/utils.js';
124
124
  export { default as VendorSpecificOgcServerManager } from './vendorspecificogcservermanager.js';
125
125
  export type { WfsClientOptions, WfsClientOptionalOptions, QueryableLayerWms, GetFeatureOptionsPartial } from './wfs/wfsclient.js';
126
126
  export { default as WfsClient, WfsClientMapServer, WfsClientQgis, WfsClientGeorama, WfsClientDefault, WfsClientGeoServer } from './wfs/wfsclient.js';
package/tools/main.js CHANGED
@@ -94,7 +94,7 @@ export { unByKeyAll, getOlayerByName, removeUnwantedOlParams, polygonFromCircle,
94
94
  export { getPropertyByPath, setPropertyByPath, createObjectFromPath, deletePropertyByPath, mergeObjects } from './utils/pathUtils.js';
95
95
  export { generateQrCode } from './utils/qrcode.js';
96
96
  export { default as ServiceWorkerHelper } from './utils/swhelper.js';
97
- export { systemIsInDarkMode, isSafari, isFirefox, getValidIndex, minMax, hexToRgbaArray, rgbStrToRgbaArray, colorToRgbaArray, isValidEmail, applyOpacityToLayers, applyFeaturesToSelection, linkify, applyDefaultPrefixToUrl } from './utils/utils.js';
97
+ export { systemIsInDarkMode, isSafari, isFirefox, getValidIndex, minMax, hexToRgbaArray, rgbStrToRgbaArray, colorToRgbaArray, isValidEmail, applyOpacityToLayers, applyFeaturesToSelection, linkify, applyDefaultPrefixToUrl, splitTrimAndConvertToNumber } from './utils/utils.js';
98
98
  export { default as VendorSpecificOgcServerManager } from './vendorspecificogcservermanager.js';
99
99
  export { default as WfsClient, WfsClientMapServer, WfsClientQgis, WfsClientGeorama, WfsClientDefault, WfsClientGeoServer } from './wfs/wfsclient.js';
100
100
  export { default as WfsFilter } from './wfs/wfsfilter.js';
@@ -2,6 +2,8 @@ import { Coordinate } from 'ol/coordinate.js';
2
2
  export type MapMarker = {
3
3
  imageUrl: string;
4
4
  position: Coordinate;
5
+ size?: number[];
6
+ offset?: number[];
5
7
  };
6
8
  declare class MapPosition {
7
9
  center: Coordinate;
@@ -31,7 +31,9 @@ class MapPosition {
31
31
  for (const marker of this.markers) {
32
32
  position.markers.push({
33
33
  imageUrl: marker.imageUrl,
34
- position: marker.position
34
+ position: marker.position,
35
+ size: marker.size,
36
+ offset: marker.offset
35
37
  });
36
38
  }
37
39
  return position;
@@ -21,7 +21,12 @@ export default class PermalinkManager extends GirafeSingleton {
21
21
  hasMapPosition(): boolean | "" | null;
22
22
  private hasToolTip;
23
23
  private hasMarker;
24
+ private hasMarkerSize;
25
+ private hasMarkerOffset;
24
26
  getMapPosition(targetProjection: Projection): MapPosition | undefined;
27
+ private addCenter;
28
+ private addTooltip;
29
+ private addMarker;
25
30
  hasSearch(): boolean;
26
31
  getSearchTerm(): string;
27
32
  hasThemes(): boolean;
@@ -5,6 +5,7 @@ import MapPosition from '../state/mapposition.js';
5
5
  import { get as getProjection, transform } from 'ol/proj.js';
6
6
  import { isCoordinateInDegrees } from '../utils/olutils.js';
7
7
  import { BASEMAP_VISIBLE_PARAMETER, SEARCH_VISIBLE_PARAMETER } from './permalinkmanager-constants.js';
8
+ import { splitTrimAndConvertToNumber } from '../utils/utils.js';
8
9
  export default class PermalinkManager extends GirafeSingleton {
9
10
  urlParamKeys = [
10
11
  'map_x',
@@ -13,6 +14,8 @@ export default class PermalinkManager extends GirafeSingleton {
13
14
  'map_crosshair',
14
15
  'map_tooltip',
15
16
  'map_marker',
17
+ 'map_marker_size',
18
+ 'map_marker_offset',
16
19
  'search',
17
20
  'basemap',
18
21
  'themes',
@@ -83,46 +86,71 @@ export default class PermalinkManager extends GirafeSingleton {
83
86
  hasMarker() {
84
87
  return this.params['map_marker'] !== null;
85
88
  }
89
+ hasMarkerSize() {
90
+ return this.params['map_marker_size'] !== null;
91
+ }
92
+ hasMarkerOffset() {
93
+ return this.params['map_marker_offset'] !== null;
94
+ }
86
95
  getMapPosition(targetProjection) {
87
96
  if (this.hasMapPosition()) {
88
97
  const position = new MapPosition();
89
- let center = [Number.parseFloat(this.params['map_x']), Number.parseFloat(this.params['map_y'])];
90
- // Transform position to the target projection by making an educated guess about the current CRS
91
- // of the permalink map position
92
- const defaultProjection = this.context.configManager.getDefaultConfigValue('map.srid');
93
- const projectionInUrl = getProjection(isCoordinateInDegrees(position.center) ? 'EPSG:4326' : defaultProjection);
94
- if (projectionInUrl.getCode() !== this.state.projection) {
95
- center = transform(center, projectionInUrl, targetProjection);
96
- }
97
- position.center = center;
98
+ this.addCenter(position, targetProjection);
98
99
  const defaultZoom = this.context.configManager.getDefaultConfigValue('map.startZoom');
99
100
  position.zoom = Number.parseInt(this.params['map_zoom'] ?? defaultZoom);
100
101
  if (this.params['map_crosshair'] === 'true') {
101
- position.crosshair = center;
102
- }
103
- if (this.hasToolTip()) {
104
- const content = DOMPurify.sanitize(this.params['map_tooltip'], {
105
- ALLOWED_TAGS: ['br', 'b', 'div', 'em', 'i', 'p', 'strong'],
106
- ALLOWED_ATTR: []
107
- });
108
- position.tooltip = {
109
- content: content,
110
- position: center
111
- };
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
- });
102
+ position.crosshair = position.center;
119
103
  }
104
+ this.addTooltip(position);
105
+ this.addMarker(position);
120
106
  if (position.isValid) {
121
107
  return position;
122
108
  }
123
109
  }
124
110
  return undefined;
125
111
  }
112
+ addCenter(position, targetProjection) {
113
+ let center = [Number.parseFloat(this.params['map_x']), Number.parseFloat(this.params['map_y'])];
114
+ // Transform position to the target projection by making an educated guess about the current CRS
115
+ // of the permalink map position
116
+ const defaultProjection = this.context.configManager.getDefaultConfigValue('map.srid');
117
+ const projectionInUrl = getProjection(isCoordinateInDegrees(position.center) ? 'EPSG:4326' : defaultProjection);
118
+ if (projectionInUrl.getCode() !== this.state.projection) {
119
+ center = transform(center, projectionInUrl, targetProjection);
120
+ }
121
+ position.center = center;
122
+ }
123
+ addTooltip(position) {
124
+ if (this.hasToolTip()) {
125
+ const content = DOMPurify.sanitize(this.params['map_tooltip'], {
126
+ ALLOWED_TAGS: ['br', 'b', 'div', 'em', 'i', 'p', 'strong'],
127
+ ALLOWED_ATTR: []
128
+ });
129
+ position.tooltip = {
130
+ content: content,
131
+ position: position.center
132
+ };
133
+ }
134
+ }
135
+ addMarker(position) {
136
+ if (this.hasMarker()) {
137
+ const imageUrl = DOMPurify.sanitize(this.params['map_marker']);
138
+ let size;
139
+ let offset;
140
+ if (this.hasMarkerSize()) {
141
+ size = splitTrimAndConvertToNumber(DOMPurify.sanitize(this.params['map_marker_size']));
142
+ }
143
+ if (this.hasMarkerOffset()) {
144
+ offset = splitTrimAndConvertToNumber(DOMPurify.sanitize(this.params['map_marker_offset']));
145
+ }
146
+ position.markers.push({
147
+ imageUrl: imageUrl,
148
+ position: position.center,
149
+ size: size,
150
+ offset: offset
151
+ });
152
+ }
153
+ }
126
154
  hasSearch() {
127
155
  return this.params['search'] !== null;
128
156
  }
@@ -77,3 +77,8 @@ export declare const linkify: (str: string) => string;
77
77
  * @returns
78
78
  */
79
79
  export declare function applyDefaultPrefixToUrl(context: IGirafeContext, metadataUrl?: string): string | undefined;
80
+ /**
81
+ * Splits a String containing Numbers into an Array of Numbers. The Separator is a comma.
82
+ * @param numbersAsString The String containing Numbers.
83
+ */
84
+ export declare const splitTrimAndConvertToNumber: (numbersAsString: string) => number[];
@@ -199,3 +199,13 @@ export function applyDefaultPrefixToUrl(context, metadataUrl) {
199
199
  }
200
200
  return metadataUrl;
201
201
  }
202
+ /**
203
+ * Splits a String containing Numbers into an Array of Numbers. The Separator is a comma.
204
+ * @param numbersAsString The String containing Numbers.
205
+ */
206
+ export const splitTrimAndConvertToNumber = (numbersAsString) => {
207
+ return numbersAsString
208
+ .split(',')
209
+ .map((e) => e.trim())
210
+ .map(Number);
211
+ };