mapicgc-gl-js 1.0.7 → 1.0.8

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.
Files changed (52) hide show
  1. package/README.md +2 -4
  2. package/dist/array-utils-flat-DeB-J5jn.mjs +1523 -0
  3. package/dist/assets/maplibre-gl-worker-HMCkgiD4.js +10 -0
  4. package/dist/buffer-transform-CH0poqqn.mjs +941 -0
  5. package/dist/expression-CtaQKvTZ.mjs +3444 -0
  6. package/dist/get-attribute-from-layouts-DqvACIaG.mjs +936 -0
  7. package/dist/{html2canvas-DhrjCa5K-6_3CwS_m.mjs → html2canvas-Cgp0Hyhk-HIw4n-A9.mjs} +105 -109
  8. package/dist/{index.es-D_FsJY9H-CO3QIBGM.mjs → index.es-lgdlL0gw-Bl7I-U5B.mjs} +176 -176
  9. package/dist/mapicgc-gl-js.css +1 -1
  10. package/dist/mapicgc-gl.css +199 -0
  11. package/dist/mapicgc-gl.js +5079 -856
  12. package/dist/mapicgc-gl.mjs +85573 -61841
  13. package/dist/purify.es-DNpVkA56-DxzGf9c8.mjs +769 -0
  14. package/dist/{chunk-efA98nb6-Dfm7IivL.mjs → rolldown-runtime-3b4jIN3o-CmciV3xi.mjs} +2 -2
  15. package/dist/{typeof-CTd55yxz-BxqQpTq1.mjs → typeof-C3azmBUr-9LkZMuan.mjs} +1 -1
  16. package/dist/webgl-Cep-x0-T.mjs +784 -0
  17. package/package.json +20 -15
  18. package/public/mapicgc-gl.css +199 -0
  19. package/src/controls/ApiTerritorialControl.js +222 -0
  20. package/src/index.js +18 -1
  21. package/src/map/Map.js +245 -144
  22. package/src/mapicgc-config.json +1303 -8
  23. package/src/plugins/CompareMaps.js +2 -1
  24. package/src/territorial/ApiTerritorialControl.js +1 -0
  25. package/src/territorial/mostraPanellInfo.js +250 -0
  26. package/src/territorial/searchApiTerritorialICGC.js +250 -0
  27. package/test/exemples/addApiTerritorialICGC.html +113 -0
  28. package/test/exemples/addBasemap.html +4 -4
  29. package/test/exemples/addCompare.html +7 -6
  30. package/test/exemples/addControl.html +12 -6
  31. package/test/exemples/addFeatureQuery.html +5 -4
  32. package/test/exemples/addGeocoderICGC.html +5 -4
  33. package/test/exemples/addICGCTerrain.html +5 -4
  34. package/test/exemples/addICGCTerrainCustomStyle.html +5 -4
  35. package/test/exemples/addImageLayerICGC.html +5 -4
  36. package/test/exemples/addLayerGeojson.html +5 -4
  37. package/test/exemples/addLayerTree.html +5 -4
  38. package/test/exemples/addLogo.html +5 -3
  39. package/test/exemples/addMapStyle.html +5 -4
  40. package/test/exemples/addMarker.html +5 -4
  41. package/test/exemples/addMouseCoordinateControl.html +5 -4
  42. package/test/exemples/addReliefStyle.html +6 -5
  43. package/test/exemples/addVectorLayerICGC.html +5 -4
  44. package/test/exemples/advancedExemple.html +5 -4
  45. package/test/exemples/fetchData.html +6 -5
  46. package/test/exemples/fetchDataAndMenu.html +5 -4
  47. package/test/exemples/styleOrto3D.html +8 -4
  48. package/test/vitest/ApiTerritorialControl.test.js +76 -0
  49. package/vite.config.mjs +16 -5
  50. package/dist/purify.es-DVGU85zV-CJOZ7ABI.mjs +0 -549
  51. package/dist/webgl-device-B1jTrfKA.mjs +0 -6880
  52. package/dist/webgl-device-D8jkwRzb.mjs +0 -2
package/src/map/Map.js CHANGED
@@ -1,9 +1,11 @@
1
- import maplibregl from "maplibre-gl";
1
+
2
+ import * as maplibregl from "maplibre-gl";
2
3
  import "../../public/mapicgc-gl.css";
3
4
  import "@watergis/maplibre-gl-export/dist/maplibre-gl-export.css";
4
5
  import { deserialize } from "flatgeobuf/lib/mjs/geojson.js";
5
6
  import Pitch3DToggleControl from "../controls/Toggle3DControl.js";
6
- import { MapboxOverlay } from "@deck.gl/mapbox";
7
+
8
+ import { MapLibreOverlay } from "@deck.gl/maplibre";
7
9
  import { Tile3DLayer } from "@deck.gl/geo-layers";
8
10
  import { Tiles3DLoader } from "@loaders.gl/3d-tiles";
9
11
  import { AmbientLight, LightingEffect } from "@deck.gl/core";
@@ -18,6 +20,8 @@ import MaplibreGeocoder from "@maplibre/maplibre-gl-geocoder";
18
20
  import LogoControl from "../controls/LogoControl.js";
19
21
  import LegendControl from "../controls/LegendControl.js";
20
22
  import MouseCoordinatesControl from "../controls/MouseCoordinatesControl.js";
23
+ import ApiTerritorialControl from "../controls/ApiTerritorialControl.js";
24
+ import mostraPanellInfo from "../territorial/mostraPanellInfo.js";
21
25
  import Config from "../constants/ConfigICGC.js";
22
26
  import Legends from "../constants/Legends.js";
23
27
  import mapicgcConfig from "../mapicgc-config.json";
@@ -176,7 +180,18 @@ export default class Map {
176
180
  // Inicializar mapa con estilo JSON como de costumbre
177
181
  this.map = new maplibregl.Map(options);
178
182
  }
179
-
183
+ // this.map ja existeix: enganxem ara qualsevol listener que s'hagi
184
+ // registrat amb map.on(...) mentre encara esperàvem la configuració ICGC.
185
+ if (this._pendingOn) {
186
+ this._pendingOn.forEach(([type, func]) => {
187
+ try {
188
+ this.map.on(type, func);
189
+ } catch (error) {
190
+ console.error(`Error adding event ON listener: ${error.message}`);
191
+ }
192
+ });
193
+ this._pendingOn = [];
194
+ }
180
195
  this.map.on("load", () => {
181
196
  this.addAttributionControl()
182
197
  setTimeout(() => {
@@ -273,6 +288,52 @@ export default class Map {
273
288
  }
274
289
  //geocoder ends
275
290
 
291
+ /**
292
+ * Adds the territorial click lookup control from the Map API.
293
+ * @param {Object} [options={}] - Configuration for the territorial lookup.
294
+ * @param {boolean} [options.infoPanel=true] - Whether to show the docked info panel.
295
+ * @param {Function} [options.onResult] - Called with (resultat, lngLat) after each click. If it returns a value, that value is returned by the callback chain.
296
+ * @param {Array|string} [options.collections] - Collection ids to query. Defaults to the territorial API default collections when omitted.
297
+ * @param {Object|Array|string} [options.fields] - Optional field selector. Use an array for all collections, a comma-separated string, or an object keyed by collection id to filter the returned info.
298
+ * @param {Array|string} [options.fields.default] - Optional default field list when `options.fields` is an object.
299
+ * @param {boolean} [options.skipGeometry=true] - Omits geometries from the territorial API response during click lookups.
300
+ * @param {boolean} [options.IconButton=true] - Shows/hides the territorial icon button. If false, click query stays active without rendering a button.
301
+ * @param {string|Object} [options.icon] - Button icon as text, inline SVG string, image URL/data URI, or object `{ src, alt }`.
302
+ * @param {Object} [options.customPosition] - Optional free position inside map container.
303
+ * @param {number|string} [options.customPosition.top] - CSS top value.
304
+ * @param {number|string} [options.customPosition.right] - CSS right value.
305
+ * @param {number|string} [options.customPosition.bottom] - CSS bottom value.
306
+ * @param {number|string} [options.customPosition.left] - CSS left value.
307
+ * @param {number|string} [options.customPosition.zIndex] - CSS z-index value.
308
+ * @param {string} [position='top-right'] - Control position.
309
+ * @returns {ApiTerritorialControl|undefined} - The created control instance, or undefined if the control could not be added.
310
+ */
311
+ addApiTerritorialICGC(options = {}, position = "top-right") {
312
+ try {
313
+ const { infoPanel = true, onResult, ...controlOptions } = options || {};
314
+
315
+ const control = new ApiTerritorialControl({
316
+ ...controlOptions,
317
+ onResult: (resultat, lngLat) => {
318
+ if (infoPanel) {
319
+ mostraPanellInfo.call(this, resultat, lngLat);
320
+ }
321
+
322
+ if (typeof onResult === "function") {
323
+ return onResult.call(this, resultat, lngLat);
324
+ }
325
+
326
+ return resultat;
327
+ },
328
+ });
329
+
330
+ this.map.addControl(control, position);
331
+ return control;
332
+ } catch (error) {
333
+ console.error(`Error adding territorial control: ${error.message}`);
334
+ }
335
+ }
336
+
276
337
  async loadImage(urlImage) {
277
338
  try {
278
339
  return this.map.loadImage(urlImage);
@@ -395,139 +456,116 @@ export default class Map {
395
456
  */
396
457
  async fetchData(url, idLayer, options) {
397
458
  try {
398
- if (!options || options === undefined) {
399
- let opt = {
400
- type: "line",
401
- layout: {
402
- visibility: "visible",
403
- },
404
- paint: {
405
- "line-color": "grey",
406
- "line-width": 2,
407
- },
408
- layerPosition: "top", // select: 'top', 'lines' or 'labels'
409
- };
459
+ const mergedOptions = {
460
+ type: "auto",
461
+ layout: {
462
+ visibility: "visible",
463
+ },
464
+ paint: null,
465
+ layerPosition: "top", // select: 'top', 'lines' or 'labels'
466
+ polygonAsLine: true,
467
+ ...(options || {}),
468
+ };
410
469
 
411
- options = opt;
412
- }
413
- let layerPosition = options.layerPosition;
470
+ let layerPosition = mergedOptions.layerPosition;
414
471
  if (url.includes(".fgb")) {
415
- this.addFGBLayerICGC(url, idLayer, options);
472
+ this.addFGBLayerICGC(url, idLayer, mergedOptions);
416
473
  } else {
417
474
  const response = await fetch(url);
418
475
  const geojson = await response.json();
419
476
  let nameUser = idLayer;
420
477
  let keyLayer = this._dealOrderLayer(layerPosition);
421
- let type = geojson.features[0].geometry.type;
422
- if (type.includes("ine")) {
423
- if (options !== undefined) {
424
- this.map.addLayer(
425
- {
426
- id: nameUser,
427
- type: "line",
428
- source: {
429
- type: "geojson",
430
- data: geojson,
431
- },
432
- layout: options.layout || {},
433
- paint: options.paint || {},
434
- },
435
- keyLayer
436
- );
437
- } else {
438
- this.map.addLayer(
439
- {
440
- id: nameUser,
441
- type: "line",
442
- source: {
443
- type: "geojson",
444
- data: geojson,
445
- },
446
- layout: {
447
- visibility: "visible",
448
- },
449
- paint: {
450
- "line-color": "black",
451
- "line-width": 2,
452
- "line-opacity": 1,
453
- },
454
- },
455
- keyLayer
456
- );
478
+ const firstGeometryType =
479
+ geojson?.features?.find((feature) => feature?.geometry?.type)?.geometry
480
+ ?.type || "";
481
+
482
+ const requestedType = String(mergedOptions.type || "auto").toLowerCase();
483
+ const inputPaint =
484
+ mergedOptions.paint && typeof mergedOptions.paint === "object"
485
+ ? mergedOptions.paint
486
+ : {};
487
+
488
+ const hasLinePaint = Object.keys(inputPaint).some((key) =>
489
+ key.startsWith("line-")
490
+ );
491
+ const hasFillPaint = Object.keys(inputPaint).some((key) =>
492
+ key.startsWith("fill-")
493
+ );
494
+ const hasCirclePaint = Object.keys(inputPaint).some((key) =>
495
+ key.startsWith("circle-")
496
+ );
497
+
498
+ let resolvedType = firstGeometryType.includes("Point")
499
+ ? "circle"
500
+ : firstGeometryType.includes("Line")
501
+ ? "line"
502
+ : firstGeometryType.includes("Polygon")
503
+ ? mergedOptions.polygonAsLine
504
+ ? "line"
505
+ : "fill"
506
+ : "line";
507
+
508
+ if (requestedType !== "auto") {
509
+ if (requestedType.includes("point") || requestedType === "circle") {
510
+ resolvedType = "circle";
511
+ } else if (requestedType.includes("line")) {
512
+ resolvedType = "line";
513
+ } else if (
514
+ requestedType.includes("polygon") ||
515
+ requestedType.includes("fill")
516
+ ) {
517
+ resolvedType = "fill";
457
518
  }
458
- }
459
- if (type.includes("olygon")) {
460
- if (options !== undefined) {
461
- this.map.addLayer(
462
- {
463
- id: nameUser,
464
- type: "fill",
465
- source: {
466
- type: "geojson",
467
- data: geojson,
468
- },
469
- layout: options.layout,
470
- paint: options.paint,
471
- },
472
- keyLayer
473
- );
474
- } else {
475
- this.map.addLayer(
476
- {
477
- id: nameUser,
478
- type: "fill",
479
- source: {
480
- type: "geojson",
481
- data: geojson,
482
- },
483
- layout: {
484
- visibility: "visible",
485
- },
486
- paint: {
487
- "fill-color": "blue",
488
- "fill-opacity": 0.6,
489
- },
490
- },
491
- keyLayer
492
- );
519
+ } else if (firstGeometryType.includes("Polygon")) {
520
+ if (hasLinePaint && !hasFillPaint) {
521
+ resolvedType = "line";
522
+ } else if (hasFillPaint && !hasLinePaint) {
523
+ resolvedType = "fill";
493
524
  }
494
525
  }
495
- if (type.includes("oint")) {
496
- if (options !== undefined) {
497
- this.map.addLayer(
498
- {
499
- id: nameUser,
500
- type: "circle",
501
- source: {
502
- type: "geojson",
503
- data: geojson,
504
- },
505
- layout: options.layout,
506
- paint: options.paint,
507
- },
508
- keyLayer
509
- );
510
- } else {
511
- this.map.addLayer(
512
- {
513
- id: nameUser,
514
- type: "circle",
515
- source: {
516
- type: "geojson",
517
- data: geojson,
518
- },
519
- layout: {
520
- visibility: "visible",
521
- },
522
- paint: {
523
- "circle-color": "red",
524
- "circle-opacity": 0.85,
525
- },
526
- },
527
- keyLayer
528
- );
526
+
527
+ const fallbackLinePaint = {
528
+ "line-color": "black",
529
+ "line-width": 2,
530
+ "line-opacity": 1,
531
+ };
532
+ const fallbackFillPaint = {
533
+ "fill-color": "blue",
534
+ "fill-opacity": 0.6,
535
+ };
536
+ const fallbackCirclePaint = {
537
+ "circle-color": "red",
538
+ "circle-opacity": 0.85,
539
+ };
540
+
541
+ let resolvedPaint = inputPaint;
542
+ if (resolvedType === "line") {
543
+ if (!hasLinePaint) {
544
+ resolvedPaint = fallbackLinePaint;
545
+ }
546
+ } else if (resolvedType === "fill") {
547
+ if (!hasFillPaint) {
548
+ resolvedPaint = fallbackFillPaint;
549
+ }
550
+ } else if (resolvedType === "circle") {
551
+ if (!hasCirclePaint) {
552
+ resolvedPaint = fallbackCirclePaint;
529
553
  }
530
554
  }
555
+
556
+ this.map.addLayer(
557
+ {
558
+ id: nameUser,
559
+ type: resolvedType,
560
+ source: {
561
+ type: "geojson",
562
+ data: geojson,
563
+ },
564
+ layout: mergedOptions.layout || {},
565
+ paint: resolvedPaint,
566
+ },
567
+ keyLayer
568
+ );
531
569
  }
532
570
  } catch (error) {
533
571
  console.error(`Error fetching data: ${error.message}`);
@@ -977,13 +1015,20 @@ export default class Map {
977
1015
  * @param {Function} func - The callback function to be executed when the event occurs.
978
1016
  */
979
1017
  on(type, func) {
980
- setTimeout(() => {
1018
+ if (this.map) {
981
1019
  try {
982
1020
  return this.map.on(type, func);
983
1021
  } catch (error) {
984
1022
  console.error(`Error adding event ON listener: ${error.message}`);
985
1023
  }
986
- }, 100);
1024
+ // }, 100);
1025
+ } else {
1026
+ // this.map encara no existeix (fetch de Config.getConfigICGC() en curs).
1027
+ // Encuem el listener i el pengem de debò quan initTheMap() creï this.map,
1028
+ // en lloc d'endevinar un setTimeout fix que pot fallar amb xarxa lenta.
1029
+ if (!this._pendingOn) this._pendingOn = [];
1030
+ this._pendingOn.push([type, func]);
1031
+ }
987
1032
  }
988
1033
 
989
1034
  /**
@@ -3064,22 +3109,26 @@ export default class Map {
3064
3109
  * @function addFGBLayerICGC
3065
3110
  * @param {string} url - The url of the FGB layer.
3066
3111
  * @param {string} idLayer - Id for the layer.
3067
- * @param {object} options - Paint option for the layer
3112
+ * @param {object} options - Options for the layer.
3113
+ * @param {string} [options.type="auto"] - Layer type override (`auto`, `line`, `fill`, `circle`).
3114
+ * @param {object} [options.layout] - MapLibre layout options.
3115
+ * @param {object} [options.paint] - MapLibre paint options.
3116
+ * @param {string} [options.layerPosition="labels"] - Position of the layer (`top`, `lines`, `labels`).
3117
+ * @param {boolean} [options.polygonAsLine=true] - When `auto`, renders polygon datasets as outline lines.
3068
3118
  *
3069
3119
  */
3070
3120
  async addFGBLayerICGC(url, idLayer, options) {
3071
3121
  try {
3072
- if (!options) {
3073
- (options.layout = { visibility: true }),
3074
- (options.paint = {
3075
- "line-color": "#4832a8",
3076
- "line-opacity": 1,
3077
- "line-width": 1,
3078
- }),
3079
- (options.type = "lines"),
3080
- (options.layerPosition = "labels");
3081
- }
3082
- let keyLayer = this._dealOrderLayer(options.layerPosition);
3122
+ const mergedOptions = {
3123
+ type: "auto",
3124
+ layerPosition: "labels",
3125
+ layout: { visibility: "visible" },
3126
+ paint: null,
3127
+ polygonAsLine: true,
3128
+ ...(options || {}),
3129
+ };
3130
+
3131
+ let keyLayer = this._dealOrderLayer(mergedOptions.layerPosition);
3083
3132
 
3084
3133
  const response = await fetch(url);
3085
3134
  const fc = { type: "FeatureCollection", features: [] };
@@ -3109,7 +3158,7 @@ export default class Map {
3109
3158
  "text-field": ["get", "NOM_AC"],
3110
3159
  "text-transform": "none",
3111
3160
  "text-max-width": 25,
3112
- visibility: options.layout.visibility,
3161
+ visibility: mergedOptions.layout.visibility,
3113
3162
  "text-justify": "right",
3114
3163
  "text-anchor": "top",
3115
3164
  "text-allow-overlap": false,
@@ -3126,13 +3175,64 @@ export default class Map {
3126
3175
  keyLayer
3127
3176
  );
3128
3177
  } else {
3178
+ const firstGeometryType =
3179
+ fc.features.find((feature) => feature?.geometry?.type)?.geometry?.type || "";
3180
+ const requestedType = String(mergedOptions.type || "auto").toLowerCase();
3181
+
3182
+ const typeFromGeometry = firstGeometryType.includes("Point")
3183
+ ? "circle"
3184
+ : firstGeometryType.includes("Line")
3185
+ ? "line"
3186
+ : firstGeometryType.includes("Polygon")
3187
+ ? mergedOptions.polygonAsLine
3188
+ ? "line"
3189
+ : "fill"
3190
+ : "line";
3191
+
3192
+ let resolvedType = typeFromGeometry;
3193
+
3194
+ if (requestedType !== "auto") {
3195
+ if (requestedType.includes("point") || requestedType === "circle") {
3196
+ resolvedType = "circle";
3197
+ } else if (requestedType.includes("line")) {
3198
+ resolvedType = "line";
3199
+ } else if (
3200
+ requestedType.includes("polygon") ||
3201
+ requestedType.includes("fill")
3202
+ ) {
3203
+ resolvedType = "fill";
3204
+ }
3205
+ }
3206
+
3207
+ let resolvedPaint = mergedOptions.paint;
3208
+ if (!resolvedPaint) {
3209
+ if (resolvedType === "circle") {
3210
+ resolvedPaint = {
3211
+ "circle-color": "red",
3212
+ "circle-opacity": 0.85,
3213
+ "circle-radius": 4,
3214
+ };
3215
+ } else if (resolvedType === "fill") {
3216
+ resolvedPaint = {
3217
+ "fill-color": "#0000FF",
3218
+ "fill-opacity": 0,
3219
+ };
3220
+ } else {
3221
+ resolvedPaint = {
3222
+ "line-color": "#4832a8",
3223
+ "line-opacity": 1,
3224
+ "line-width": 1,
3225
+ };
3226
+ }
3227
+ }
3228
+
3129
3229
  this.map.addLayer(
3130
3230
  {
3131
3231
  id: idLayer,
3132
- type: options.type,
3232
+ type: resolvedType,
3133
3233
  source: src,
3134
- layout: options.layout,
3135
- paint: options.paint,
3234
+ layout: mergedOptions.layout,
3235
+ paint: resolvedPaint,
3136
3236
  },
3137
3237
  keyLayer
3138
3238
  );
@@ -3441,7 +3541,8 @@ export default class Map {
3441
3541
 
3442
3542
 
3443
3543
 
3444
- citiesMapboxLayer = new MapboxOverlay({
3544
+ // citiesMapboxLayer = new import { MapLibreOverlay } from "@deck.gl/maplibre";({
3545
+ citiesMapboxLayer = new MapLibreOverlay({
3445
3546
  interleaved: true,
3446
3547
  layers: [this._createCitiesMapboxLayer(false)], // Inicialment invisible
3447
3548
  effects: [lightingEffect],
@@ -3599,7 +3700,7 @@ export default class Map {
3599
3700
  pickable: false,
3600
3701
  });
3601
3702
  } catch (error) {
3602
- console.error(`Error adding MapboxOverlay: ${error.message}`);
3703
+ console.error(`Error adding MapLibreOverlay: ${error.message}`);
3603
3704
  return null;
3604
3705
  }
3605
3706
  }