@opengeoweb/webmap 2.1.4 → 2.3.0

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/index.esm.js CHANGED
@@ -492,6 +492,14 @@ var addStylesForLayer = function addStylesForLayer(layerObjectFromWMS) {
492
492
  });
493
493
  };
494
494
 
495
+ /**
496
+ * Returns the current time in ms
497
+ * @returns the current time in ms
498
+ */
499
+
500
+ var getCurrentImageTime = function getCurrentImageTime() {
501
+ return new Date().getTime();
502
+ };
495
503
  /**
496
504
  * WMImage provides an API to the HTML image element. It is used for caching and easier access to images.
497
505
  */
@@ -513,6 +521,8 @@ var WMImage = /*#__PURE__*/function () {
513
521
  this._isLoaded = undefined;
514
522
  this._isLoading = undefined;
515
523
  this._hasError = undefined;
524
+ this._imageTimeStampAtError = getCurrentImageTime();
525
+ this._numFailedAttempts = 0;
516
526
  this.srcToLoad = src;
517
527
  this.loadEventCallback = callback;
518
528
  this.el = new Image();
@@ -531,6 +541,9 @@ var WMImage = /*#__PURE__*/function () {
531
541
  this.hasError = this.hasError.bind(this);
532
542
  this._load = this._load.bind(this);
533
543
  this.load = this.load.bind(this);
544
+ this.forceReload = this.forceReload.bind(this);
545
+ this.getLastErrorMSecondsAgo = this.getLastErrorMSecondsAgo.bind(this);
546
+ this.getNumFailedAttempts = this.getNumFailedAttempts.bind(this);
534
547
  this._loadEvent = this._loadEvent.bind(this);
535
548
  this.getWidth = this.getWidth.bind(this);
536
549
  this.getHeight = this.getHeight.bind(this);
@@ -541,6 +554,9 @@ var WMImage = /*#__PURE__*/function () {
541
554
  _this._loadEvent(false);
542
555
  });
543
556
  this.el.addEventListener('error', function () {
557
+ _this._imageTimeStampAtError = getCurrentImageTime();
558
+ _this._numFailedAttempts += 1;
559
+
544
560
  _this._loadEvent(true);
545
561
  });
546
562
 
@@ -560,6 +576,8 @@ var WMImage = /*#__PURE__*/function () {
560
576
  this._isLoaded = false;
561
577
  this._isLoading = false;
562
578
  this._hasError = false;
579
+ this._numFailedAttempts = 0;
580
+ this._imageTimeStampAtError = getCurrentImageTime();
563
581
  }
564
582
  /**
565
583
  * Returns true if the image has been loaded
@@ -580,6 +598,25 @@ var WMImage = /*#__PURE__*/function () {
580
598
  value: function isLoading() {
581
599
  return this._isLoading;
582
600
  }
601
+ /**
602
+ * Get the amount of milliseconds since the image experienced an image load.
603
+ * @returns milliseconds since the image had an error
604
+ */
605
+
606
+ }, {
607
+ key: "getLastErrorMSecondsAgo",
608
+ value: function getLastErrorMSecondsAgo() {
609
+ return getCurrentImageTime() - this._imageTimeStampAtError;
610
+ }
611
+ /**
612
+ * @returns The number of failed attempts for this image
613
+ */
614
+
615
+ }, {
616
+ key: "getNumFailedAttempts",
617
+ value: function getNumFailedAttempts() {
618
+ return this._numFailedAttempts;
619
+ }
583
620
  /**
584
621
  * Set source of image, but it will not load the image yet.
585
622
  * @param src URL of the image to load
@@ -605,6 +642,8 @@ var WMImage = /*#__PURE__*/function () {
605
642
  return;
606
643
  }
607
644
 
645
+ this._numFailedAttempts = 0;
646
+ this._imageTimeStampAtError = getCurrentImageTime();
608
647
  this._isLoaded = false;
609
648
  }
610
649
  /**
@@ -644,6 +683,14 @@ var WMImage = /*#__PURE__*/function () {
644
683
  this._load();
645
684
  }
646
685
  }, {
686
+ key: "forceReload",
687
+ value: function forceReload() {
688
+ this._isLoaded = false;
689
+ this._srcLoaded = undefined;
690
+
691
+ this._load();
692
+ }
693
+ }, {
647
694
  key: "_getImageWithHeaders",
648
695
  value: function _getImageWithHeaders(url, headers) {
649
696
  var _this2 = this;
@@ -714,8 +761,12 @@ var WMImage = /*#__PURE__*/function () {
714
761
  var hostName = "".concat(splittedHREF[0], "//").concat(splittedHREF[1], "/");
715
762
  this.srcToLoad = hostName + this.srcToLoad;
716
763
  }
764
+ /*
765
+ * If the image has already loaded this source succesfully (without error), simply trigger the loadevent.
766
+ */
717
767
 
718
- if (this.srcToLoad === this._srcLoaded) {
768
+
769
+ if (this.srcToLoad === this._srcLoaded && !this._hasError) {
719
770
  this._loadEvent(false);
720
771
 
721
772
  return;
@@ -1337,13 +1388,14 @@ var addAlternativeImage = function addAlternativeImage(newLayer) {
1337
1388
  /* It is not in the list, so add it */
1338
1389
 
1339
1390
  if (imageIsAlreadyIndex === -1) {
1340
- sharedImagesList[sigImageUrl].push(newLayer);
1341
- }
1342
- /* Ensure that the oldest images are removed, keep a maximum of 10 images per signature url */
1391
+ /* Ensure that the oldest images are removed, keep a maximum of 10 images per signature url */
1392
+ while (sharedImagesList[sigImageUrl].length >= 10) {
1393
+ sharedImagesList[sigImageUrl].pop();
1394
+ }
1395
+ /* Add it to the beginning of the array for efficieny in searching at a later stage */
1343
1396
 
1344
1397
 
1345
- while (sharedImagesList[sigImageUrl].length > 10) {
1346
- sharedImagesList[sigImageUrl].splice(0, 1);
1398
+ sharedImagesList[sigImageUrl].unshift(newLayer);
1347
1399
  }
1348
1400
  };
1349
1401
  /**
@@ -1368,38 +1420,17 @@ var removeAlternativeImage = function removeAlternativeImage(imageUrl) {
1368
1420
  }
1369
1421
  };
1370
1422
  /**
1371
- * Algorithm two determine how much two bboxes are similar
1372
- * @param rect1
1373
- * @param rect2
1374
- * @returns
1375
- */
1376
-
1377
- var geClosestBBox = function geClosestBBox(rect1, rect2) {
1378
- var diffTLX = rect1.left - rect2.left;
1379
- var diffTLY = rect1.top - rect2.top;
1380
- var diffBRX = rect1.right - rect2.right;
1381
- var diffBRY = rect1.bottom - rect2.bottom;
1382
- return Math.sqrt(diffTLX * diffTLX + diffTLY * diffTLY) + Math.sqrt(diffBRX * diffBRX + diffBRY * diffBRY);
1383
- };
1384
- /**
1385
- * Sort the imageslist by overlapping area, highest area first
1423
+ * Sort the imageslist by imagedate, newewst image first.
1386
1424
  * @param imagesList
1387
- * @param bbox
1388
1425
  * @returns
1389
1426
  */
1390
1427
 
1391
- var sortByOverlappingArea = function sortByOverlappingArea(imagesList, bbox) {
1392
- var toSort = imagesList.map(function (im) {
1393
- return {
1394
- dist: geClosestBBox(im.bbox, bbox),
1395
- image: im
1396
- };
1397
- });
1398
- var sorted = toSort.sort(function (a, b) {
1399
- return a.dist > b.dist ? 1 : -1;
1428
+ var sortByImageDate = function sortByImageDate(imagesList) {
1429
+ var sorted = imagesList.sort(function (a, b) {
1430
+ return a.imageAge < b.imageAge ? 1 : -1;
1400
1431
  });
1401
1432
  return sorted.map(function (i) {
1402
- return i.image;
1433
+ return i;
1403
1434
  });
1404
1435
  };
1405
1436
  /**
@@ -1410,17 +1441,18 @@ var sortByOverlappingArea = function sortByOverlappingArea(imagesList, bbox) {
1410
1441
  * @returns A canvasLayer, containing the new image source with its geo properties
1411
1442
  */
1412
1443
 
1413
- var getAlternativeImage = function getAlternativeImage(imageUrl, imageStore, bbox) {
1444
+ var getAlternativeImage = function getAlternativeImage(imageUrl, imageStore, // eslint-disable-next-line no-unused-vars
1445
+ _bbox) {
1414
1446
  var sigImageUrl = makeQueryStringWithoutGeoInfo(imageUrl);
1415
1447
  var imagesForUrl = sharedImagesList[sigImageUrl];
1416
1448
 
1417
1449
  if (imagesForUrl && imagesForUrl.length > 0) {
1418
- var sortedImagesForUrl = sortByOverlappingArea(imagesForUrl, bbox);
1419
- /* Find the first image in this list which is also loaded */
1450
+ var sortedImagesForUrl = sortByImageDate(imagesForUrl);
1451
+ /* Find the first image in this list which is also loaded and has no error */
1420
1452
 
1421
1453
  var index = sortedImagesForUrl.findIndex(function (altImage) {
1422
1454
  var cachedImage = imageStore.getImageForSrc(altImage.imageSource);
1423
- return cachedImage && cachedImage.isLoaded();
1455
+ return cachedImage && cachedImage.isLoaded() && !cachedImage.isLoading() && !cachedImage.hasError();
1424
1456
  });
1425
1457
  if (index !== -1) return sortedImagesForUrl[index];
1426
1458
  }
@@ -1588,16 +1620,29 @@ var WMCanvasBuffer = /*#__PURE__*/function () {
1588
1620
  for (var j = 0; j < this.layers.length; j += 1) {
1589
1621
  var imageToDisplay = this._imageStore.getImageForSrc(this.layers[j].imageSource);
1590
1622
 
1591
- if (!imageToDisplay || imageToDisplay.isLoaded() === false) {
1592
- /* Find closest image */
1623
+ if (imageToDisplay && imageToDisplay.isLoaded() && !imageToDisplay.isLoading() && !imageToDisplay.hasError()) {
1624
+ // Draw this image, it is loaded and OK
1625
+ this._drawImage(this.layers[j]);
1626
+ } else {
1627
+ /**
1628
+ * Check if this image has an error.
1629
+ * If the error has occured some time ago, and the amount of retries is low, just retry to load the image.
1630
+ */
1631
+ if (imageToDisplay.hasError()) {
1632
+ if (imageToDisplay.isLoading() === false && imageToDisplay.getLastErrorMSecondsAgo() > 5000) {
1633
+ if (imageToDisplay.getNumFailedAttempts() < 10) {
1634
+ imageToDisplay.forceReload();
1635
+ }
1636
+ }
1637
+ }
1638
+ /* Find closest alternative and display instead image */
1639
+
1640
+
1593
1641
  var im = getAlternativeImage(this.layers[j].imageSource, this._imageStore, this._currentnewbbox);
1594
1642
 
1595
1643
  if (im) {
1596
1644
  this._drawImage(im);
1597
1645
  }
1598
- } else {
1599
- // Draw
1600
- this._drawImage(this.layers[j]);
1601
1646
  }
1602
1647
  }
1603
1648
 
@@ -1687,6 +1732,7 @@ var WMCanvasBuffer = /*#__PURE__*/function () {
1687
1732
 
1688
1733
  var newLayer = {
1689
1734
  imageSource: imageSource,
1735
+ imageAge: getCurrentImageTime(),
1690
1736
  opacity: opacity,
1691
1737
  bbox: {
1692
1738
  left: bbox.left,
@@ -1907,8 +1953,9 @@ var WMJSAnimate = /*#__PURE__*/function () {
1907
1953
  for (var i = 0; i < this._map.animationList[nextStep].requests.length; i += 1) {
1908
1954
  var url = this._map.animationList[nextStep].requests[i].url;
1909
1955
  var image = getMapImageStore.getImageForSrc(url);
1956
+ /* Get a loaded image which has no error */
1910
1957
 
1911
- if (image && image.isLoaded()) {
1958
+ if (image && image.isLoaded() && !image.isLoading() && !image.hasError()) {
1912
1959
  numReady += 1;
1913
1960
  } else {
1914
1961
  /* Check if a similar image is available instead, then we can continue with the smoother animation */
@@ -1916,11 +1963,14 @@ var WMJSAnimate = /*#__PURE__*/function () {
1916
1963
 
1917
1964
  if (im) {
1918
1965
  numReady += 1;
1966
+ } else if (!(image && image.isLoading())) {
1967
+ /* No alternatives and current image is not loading, so lets continue the animation */
1968
+ numReady += 1;
1919
1969
  }
1920
1970
  }
1921
1971
  }
1922
1972
 
1923
- if (numReady === this._map.animationList[nextStep].requests.length) {
1973
+ if (numReady >= this._map.animationList[nextStep].requests.length) {
1924
1974
  continueAnimation = true;
1925
1975
  }
1926
1976
 
@@ -1974,9 +2024,11 @@ var WMJSAnimate = /*#__PURE__*/function () {
1974
2024
  this._map.setDimension(animationStep.name, animationStep.value, false);
1975
2025
 
1976
2026
  this._map.animationList[index].imagesInPrefetch = prefetch(this._map.animationList[index].requests);
1977
- getNumImagesLoading += this._map.animationList[index].imagesInPrefetch.length; // imageStore.getNumImagesLoading();
2027
+ getNumImagesLoading = this._imageStore.getNumImagesLoading();
1978
2028
 
1979
- if (getNumImagesLoading > maxSimultaneousLoads - 1) break;
2029
+ if (getNumImagesLoading > maxSimultaneousLoads - 1) {
2030
+ break;
2031
+ }
1980
2032
  }
1981
2033
  }
1982
2034
  }
@@ -2877,6 +2929,7 @@ var WMJSDimension = /*#__PURE__*/function () {
2877
2929
  this.initialize = this.initialize.bind(this);
2878
2930
  this.getValue = this.getValue.bind(this);
2879
2931
  this.setValue = this.setValue.bind(this);
2932
+ this.getValues = this.setValue.bind(this);
2880
2933
  this.setClosestValue = this.setClosestValue.bind(this);
2881
2934
  this.addTimeRangeDurationToValue = this.addTimeRangeDurationToValue.bind(this);
2882
2935
  this.setTimeRangeDuration = this.setTimeRangeDuration.bind(this);
@@ -2885,6 +2938,7 @@ var WMJSDimension = /*#__PURE__*/function () {
2885
2938
  this.get = this.get.bind(this);
2886
2939
  this.getFirstValue = this.getFirstValue.bind(this);
2887
2940
  this.getLastValue = this.getLastValue.bind(this);
2941
+ this.getDimInterval = this.getDimInterval.bind(this);
2888
2942
  this.getIndexForValue = this.getIndexForValue.bind(this);
2889
2943
  this.size = this.size.bind(this);
2890
2944
  this.clone = this.clone.bind(this);
@@ -3159,6 +3213,17 @@ var WMJSDimension = /*#__PURE__*/function () {
3159
3213
 
3160
3214
  this.currentValue = value;
3161
3215
  }
3216
+ /**
3217
+ * Returns values of the dimension, according to values defined in WMS specification, e.g. 2011-01-01T00:00:00Z/2012-01-01T00:00:00Z/P1M or list of values.
3218
+ * @returns
3219
+ */
3220
+
3221
+ }, {
3222
+ key: "getValues",
3223
+ value: function getValues() {
3224
+ this.initialize();
3225
+ return this.values;
3226
+ }
3162
3227
  }, {
3163
3228
  key: "setClosestValue",
3164
3229
  value: function setClosestValue() {
@@ -3301,6 +3366,17 @@ var WMJSDimension = /*#__PURE__*/function () {
3301
3366
  if (this._type === 'anyvalue') return this._allValues[index];
3302
3367
  return null;
3303
3368
  }
3369
+ /**
3370
+ * Hint about the timestep size / time resolution of this dimension.
3371
+ * @returns The dimTimeInterval
3372
+ */
3373
+
3374
+ }, {
3375
+ key: "getDimInterval",
3376
+ value: function getDimInterval() {
3377
+ this.initialize();
3378
+ return this.dimTimeInterval;
3379
+ }
3304
3380
  /**
3305
3381
  * Shorthand functionf or getValueForIndex
3306
3382
  */
@@ -3499,6 +3575,7 @@ var MapPin = /*#__PURE__*/function () {
3499
3575
  this.geoPosX = null;
3500
3576
  this.geoPosY = null;
3501
3577
  this.displayMapPin = true;
3578
+ this.disableMapPin = false;
3502
3579
 
3503
3580
  this.redrawMap = function () {
3504
3581
  _this._map.draw();
@@ -3519,6 +3596,8 @@ var MapPin = /*#__PURE__*/function () {
3519
3596
  var x = _x;
3520
3597
  var y = _y;
3521
3598
  if (!x || !y) return;
3599
+ if (_this.disableMapPin && !_bbox) return; // we still want to update the pin position when the bbox changes.
3600
+
3522
3601
  _this.x = parseInt(x, 10);
3523
3602
  _this.y = parseInt(y, 10);
3524
3603
  _this.exactX = parseFloat(x);
@@ -3543,6 +3622,12 @@ var MapPin = /*#__PURE__*/function () {
3543
3622
  _this.setMapPin(newpos.x, newpos.y);
3544
3623
  };
3545
3624
 
3625
+ this.toggleDisableMapPin = function () {
3626
+ _this.disableMapPin = !_this.disableMapPin;
3627
+
3628
+ _this.redrawMap();
3629
+ };
3630
+
3546
3631
  this.showMapPin = function () {
3547
3632
  _this.displayMapPin = true;
3548
3633
 
@@ -3562,7 +3647,7 @@ var MapPin = /*#__PURE__*/function () {
3562
3647
  key: "drawMarker",
3563
3648
  value: function drawMarker(ctx) {
3564
3649
  if (isDefined(this.x) && isDefined(this.y)) {
3565
- WMDrawMarker(ctx, this.x, this.y, '#051039');
3650
+ WMDrawMarker(ctx, this.x, this.y, this.disableMapPin ? '#808080' : '#051039');
3566
3651
  }
3567
3652
  }
3568
3653
  }]);
@@ -5127,7 +5212,6 @@ var WMJSMap = /*#__PURE__*/function () {
5127
5212
  this.mainElement.style.width = "".concat(this.width, "px");
5128
5213
  this.mainElement.style.height = "".concat(this.height, "px");
5129
5214
  this.setBBOX(this.resizeBBOX);
5130
- this.mapPin.repositionMapPin();
5131
5215
  this.showBoundingBox();
5132
5216
  this.divBuffer.resize(this.getWidth().toString(), this.getHeight().toString());
5133
5217
  this.repositionLegendGraphic();
@@ -6724,6 +6808,7 @@ var WMJSMap = /*#__PURE__*/function () {
6724
6808
 
6725
6809
  this.updateBBOX.setBBOX(this.bbox);
6726
6810
  this.drawnBBOX.setBBOX(this.bbox);
6811
+ this.mapPin.repositionMapPin(this.bbox);
6727
6812
  /* Undo part */
6728
6813
 
6729
6814
  if (this.DoRedo === 0 && this.DoUndo === 0) {
@@ -7513,7 +7598,7 @@ rootNode, path) {
7513
7598
 
7514
7599
  if (layers[j].Layer) {
7515
7600
  nodeObject.children = [];
7516
- recursivelyFindLayer(toArray(layers[j].Layer), nodeObject.children, path + layers[j].Title.value);
7601
+ recursivelyFindLayer(toArray(layers[j].Layer), nodeObject.children, [].concat(_toConsumableArray(path), [layers[j].Title.value]));
7517
7602
  }
7518
7603
  } // Sort nodes alphabetically.
7519
7604
 
@@ -7841,7 +7926,7 @@ var WMJSService = /*#__PURE__*/function () {
7841
7926
  nodeStructure.text = I18n.unnamed_service.text;
7842
7927
  }
7843
7928
 
7844
- recursivelyFindLayer(WMSLayers, nodeStructure.children, '');
7929
+ recursivelyFindLayer(WMSLayers, nodeStructure.children, []);
7845
7930
  succes(nodeStructure);
7846
7931
  };
7847
7932
 
package/index.umd.js CHANGED
@@ -455,6 +455,14 @@
455
455
  * Copyright 2020 - Koninklijk Nederlands Meteorologisch Instituut (KNMI)
456
456
  * Copyright 2020 - Finnish Meteorological Institute (FMI)
457
457
  * */
458
+ /**
459
+ * Returns the current time in ms
460
+ * @returns the current time in ms
461
+ */
462
+
463
+ var getCurrentImageTime = function getCurrentImageTime() {
464
+ return new Date().getTime();
465
+ };
458
466
  /**
459
467
  * WMImage provides an API to the HTML image element. It is used for caching and easier access to images.
460
468
  */
@@ -476,6 +484,8 @@
476
484
  this._isLoaded = undefined;
477
485
  this._isLoading = undefined;
478
486
  this._hasError = undefined;
487
+ this._imageTimeStampAtError = getCurrentImageTime();
488
+ this._numFailedAttempts = 0;
479
489
  this.srcToLoad = src;
480
490
  this.loadEventCallback = callback;
481
491
  this.el = new Image();
@@ -494,6 +504,9 @@
494
504
  this.hasError = this.hasError.bind(this);
495
505
  this._load = this._load.bind(this);
496
506
  this.load = this.load.bind(this);
507
+ this.forceReload = this.forceReload.bind(this);
508
+ this.getLastErrorMSecondsAgo = this.getLastErrorMSecondsAgo.bind(this);
509
+ this.getNumFailedAttempts = this.getNumFailedAttempts.bind(this);
497
510
  this._loadEvent = this._loadEvent.bind(this);
498
511
  this.getWidth = this.getWidth.bind(this);
499
512
  this.getHeight = this.getHeight.bind(this);
@@ -504,6 +517,9 @@
504
517
  _this._loadEvent(false);
505
518
  });
506
519
  this.el.addEventListener('error', function () {
520
+ _this._imageTimeStampAtError = getCurrentImageTime();
521
+ _this._numFailedAttempts += 1;
522
+
507
523
  _this._loadEvent(true);
508
524
  });
509
525
 
@@ -521,6 +537,8 @@
521
537
  this._isLoaded = false;
522
538
  this._isLoading = false;
523
539
  this._hasError = false;
540
+ this._numFailedAttempts = 0;
541
+ this._imageTimeStampAtError = getCurrentImageTime();
524
542
  };
525
543
  /**
526
544
  * Returns true if the image has been loaded
@@ -539,6 +557,23 @@
539
557
  WMImage.prototype.isLoading = function () {
540
558
  return this._isLoading;
541
559
  };
560
+ /**
561
+ * Get the amount of milliseconds since the image experienced an image load.
562
+ * @returns milliseconds since the image had an error
563
+ */
564
+
565
+
566
+ WMImage.prototype.getLastErrorMSecondsAgo = function () {
567
+ return getCurrentImageTime() - this._imageTimeStampAtError;
568
+ };
569
+ /**
570
+ * @returns The number of failed attempts for this image
571
+ */
572
+
573
+
574
+ WMImage.prototype.getNumFailedAttempts = function () {
575
+ return this._numFailedAttempts;
576
+ };
542
577
  /**
543
578
  * Set source of image, but it will not load the image yet.
544
579
  * @param src URL of the image to load
@@ -563,6 +598,8 @@
563
598
  return;
564
599
  }
565
600
 
601
+ this._numFailedAttempts = 0;
602
+ this._imageTimeStampAtError = getCurrentImageTime();
566
603
  this._isLoaded = false;
567
604
  };
568
605
  /**
@@ -598,6 +635,13 @@
598
635
  this._load();
599
636
  };
600
637
 
638
+ WMImage.prototype.forceReload = function () {
639
+ this._isLoaded = false;
640
+ this._srcLoaded = undefined;
641
+
642
+ this._load();
643
+ };
644
+
601
645
  WMImage.prototype._getImageWithHeaders = function (url, headers) {
602
646
  var _this = this;
603
647
 
@@ -666,8 +710,12 @@
666
710
  var hostName = splittedHREF[0] + "//" + splittedHREF[1] + "/";
667
711
  this.srcToLoad = hostName + this.srcToLoad;
668
712
  }
713
+ /*
714
+ * If the image has already loaded this source succesfully (without error), simply trigger the loadevent.
715
+ */
716
+
669
717
 
670
- if (this.srcToLoad === this._srcLoaded) {
718
+ if (this.srcToLoad === this._srcLoaded && !this._hasError) {
671
719
  this._loadEvent(false);
672
720
 
673
721
  return;
@@ -1329,13 +1377,14 @@
1329
1377
  /* It is not in the list, so add it */
1330
1378
 
1331
1379
  if (imageIsAlreadyIndex === -1) {
1332
- sharedImagesList[sigImageUrl].push(newLayer);
1333
- }
1334
- /* Ensure that the oldest images are removed, keep a maximum of 10 images per signature url */
1380
+ /* Ensure that the oldest images are removed, keep a maximum of 10 images per signature url */
1381
+ while (sharedImagesList[sigImageUrl].length >= 10) {
1382
+ sharedImagesList[sigImageUrl].pop();
1383
+ }
1384
+ /* Add it to the beginning of the array for efficieny in searching at a later stage */
1335
1385
 
1336
1386
 
1337
- while (sharedImagesList[sigImageUrl].length > 10) {
1338
- sharedImagesList[sigImageUrl].splice(0, 1);
1387
+ sharedImagesList[sigImageUrl].unshift(newLayer);
1339
1388
  }
1340
1389
  };
1341
1390
  /**
@@ -1360,38 +1409,17 @@
1360
1409
  }
1361
1410
  };
1362
1411
  /**
1363
- * Algorithm two determine how much two bboxes are similar
1364
- * @param rect1
1365
- * @param rect2
1366
- * @returns
1367
- */
1368
-
1369
- var geClosestBBox = function geClosestBBox(rect1, rect2) {
1370
- var diffTLX = rect1.left - rect2.left;
1371
- var diffTLY = rect1.top - rect2.top;
1372
- var diffBRX = rect1.right - rect2.right;
1373
- var diffBRY = rect1.bottom - rect2.bottom;
1374
- return Math.sqrt(diffTLX * diffTLX + diffTLY * diffTLY) + Math.sqrt(diffBRX * diffBRX + diffBRY * diffBRY);
1375
- };
1376
- /**
1377
- * Sort the imageslist by overlapping area, highest area first
1412
+ * Sort the imageslist by imagedate, newewst image first.
1378
1413
  * @param imagesList
1379
- * @param bbox
1380
1414
  * @returns
1381
1415
  */
1382
1416
 
1383
- var sortByOverlappingArea = function sortByOverlappingArea(imagesList, bbox) {
1384
- var toSort = imagesList.map(function (im) {
1385
- return {
1386
- dist: geClosestBBox(im.bbox, bbox),
1387
- image: im
1388
- };
1389
- });
1390
- var sorted = toSort.sort(function (a, b) {
1391
- return a.dist > b.dist ? 1 : -1;
1417
+ var sortByImageDate = function sortByImageDate(imagesList) {
1418
+ var sorted = imagesList.sort(function (a, b) {
1419
+ return a.imageAge < b.imageAge ? 1 : -1;
1392
1420
  });
1393
1421
  return sorted.map(function (i) {
1394
- return i.image;
1422
+ return i;
1395
1423
  });
1396
1424
  };
1397
1425
  /**
@@ -1402,17 +1430,18 @@
1402
1430
  * @returns A canvasLayer, containing the new image source with its geo properties
1403
1431
  */
1404
1432
 
1405
- var getAlternativeImage = function getAlternativeImage(imageUrl, imageStore, bbox) {
1433
+ var getAlternativeImage = function getAlternativeImage(imageUrl, imageStore, // eslint-disable-next-line no-unused-vars
1434
+ _bbox) {
1406
1435
  var sigImageUrl = makeQueryStringWithoutGeoInfo(imageUrl);
1407
1436
  var imagesForUrl = sharedImagesList[sigImageUrl];
1408
1437
 
1409
1438
  if (imagesForUrl && imagesForUrl.length > 0) {
1410
- var sortedImagesForUrl = sortByOverlappingArea(imagesForUrl, bbox);
1411
- /* Find the first image in this list which is also loaded */
1439
+ var sortedImagesForUrl = sortByImageDate(imagesForUrl);
1440
+ /* Find the first image in this list which is also loaded and has no error */
1412
1441
 
1413
1442
  var index = sortedImagesForUrl.findIndex(function (altImage) {
1414
1443
  var cachedImage = imageStore.getImageForSrc(altImage.imageSource);
1415
- return cachedImage && cachedImage.isLoaded();
1444
+ return cachedImage && cachedImage.isLoaded() && !cachedImage.isLoading() && !cachedImage.hasError();
1416
1445
  });
1417
1446
  if (index !== -1) return sortedImagesForUrl[index];
1418
1447
  }
@@ -1573,16 +1602,29 @@
1573
1602
  for (var j = 0; j < this.layers.length; j += 1) {
1574
1603
  var imageToDisplay = this._imageStore.getImageForSrc(this.layers[j].imageSource);
1575
1604
 
1576
- if (!imageToDisplay || imageToDisplay.isLoaded() === false) {
1577
- /* Find closest image */
1605
+ if (imageToDisplay && imageToDisplay.isLoaded() && !imageToDisplay.isLoading() && !imageToDisplay.hasError()) {
1606
+ // Draw this image, it is loaded and OK
1607
+ this._drawImage(this.layers[j]);
1608
+ } else {
1609
+ /**
1610
+ * Check if this image has an error.
1611
+ * If the error has occured some time ago, and the amount of retries is low, just retry to load the image.
1612
+ */
1613
+ if (imageToDisplay.hasError()) {
1614
+ if (imageToDisplay.isLoading() === false && imageToDisplay.getLastErrorMSecondsAgo() > 5000) {
1615
+ if (imageToDisplay.getNumFailedAttempts() < 10) {
1616
+ imageToDisplay.forceReload();
1617
+ }
1618
+ }
1619
+ }
1620
+ /* Find closest alternative and display instead image */
1621
+
1622
+
1578
1623
  var im = getAlternativeImage(this.layers[j].imageSource, this._imageStore, this._currentnewbbox);
1579
1624
 
1580
1625
  if (im) {
1581
1626
  this._drawImage(im);
1582
1627
  }
1583
- } else {
1584
- // Draw
1585
- this._drawImage(this.layers[j]);
1586
1628
  }
1587
1629
  }
1588
1630
 
@@ -1669,6 +1711,7 @@
1669
1711
 
1670
1712
  var newLayer = {
1671
1713
  imageSource: imageSource,
1714
+ imageAge: getCurrentImageTime(),
1672
1715
  opacity: opacity,
1673
1716
  bbox: {
1674
1717
  left: bbox.left,
@@ -1897,8 +1940,9 @@
1897
1940
  for (var i = 0; i < this._map.animationList[nextStep].requests.length; i += 1) {
1898
1941
  var url = this._map.animationList[nextStep].requests[i].url;
1899
1942
  var image = getMapImageStore.getImageForSrc(url);
1943
+ /* Get a loaded image which has no error */
1900
1944
 
1901
- if (image && image.isLoaded()) {
1945
+ if (image && image.isLoaded() && !image.isLoading() && !image.hasError()) {
1902
1946
  numReady += 1;
1903
1947
  } else {
1904
1948
  /* Check if a similar image is available instead, then we can continue with the smoother animation */
@@ -1906,11 +1950,14 @@
1906
1950
 
1907
1951
  if (im) {
1908
1952
  numReady += 1;
1953
+ } else if (!(image && image.isLoading())) {
1954
+ /* No alternatives and current image is not loading, so lets continue the animation */
1955
+ numReady += 1;
1909
1956
  }
1910
1957
  }
1911
1958
  }
1912
1959
 
1913
- if (numReady === this._map.animationList[nextStep].requests.length) {
1960
+ if (numReady >= this._map.animationList[nextStep].requests.length) {
1914
1961
  continueAnimation = true;
1915
1962
  }
1916
1963
 
@@ -1963,9 +2010,11 @@
1963
2010
  this._map.setDimension(animationStep.name, animationStep.value, false);
1964
2011
 
1965
2012
  this._map.animationList[index].imagesInPrefetch = prefetch(this._map.animationList[index].requests);
1966
- getNumImagesLoading += this._map.animationList[index].imagesInPrefetch.length; // imageStore.getNumImagesLoading();
2013
+ getNumImagesLoading = this._imageStore.getNumImagesLoading();
1967
2014
 
1968
- if (getNumImagesLoading > maxSimultaneousLoads - 1) break;
2015
+ if (getNumImagesLoading > maxSimultaneousLoads - 1) {
2016
+ break;
2017
+ }
1969
2018
  }
1970
2019
  }
1971
2020
  }
@@ -2903,6 +2952,7 @@
2903
2952
  this.initialize = this.initialize.bind(this);
2904
2953
  this.getValue = this.getValue.bind(this);
2905
2954
  this.setValue = this.setValue.bind(this);
2955
+ this.getValues = this.setValue.bind(this);
2906
2956
  this.setClosestValue = this.setClosestValue.bind(this);
2907
2957
  this.addTimeRangeDurationToValue = this.addTimeRangeDurationToValue.bind(this);
2908
2958
  this.setTimeRangeDuration = this.setTimeRangeDuration.bind(this);
@@ -2911,6 +2961,7 @@
2911
2961
  this.get = this.get.bind(this);
2912
2962
  this.getFirstValue = this.getFirstValue.bind(this);
2913
2963
  this.getLastValue = this.getLastValue.bind(this);
2964
+ this.getDimInterval = this.getDimInterval.bind(this);
2914
2965
  this.getIndexForValue = this.getIndexForValue.bind(this);
2915
2966
  this.size = this.size.bind(this);
2916
2967
  this.clone = this.clone.bind(this);
@@ -3189,6 +3240,16 @@
3189
3240
 
3190
3241
  this.currentValue = value;
3191
3242
  };
3243
+ /**
3244
+ * Returns values of the dimension, according to values defined in WMS specification, e.g. 2011-01-01T00:00:00Z/2012-01-01T00:00:00Z/P1M or list of values.
3245
+ * @returns
3246
+ */
3247
+
3248
+
3249
+ WMJSDimension.prototype.getValues = function () {
3250
+ this.initialize();
3251
+ return this.values;
3252
+ };
3192
3253
 
3193
3254
  WMJSDimension.prototype.setClosestValue = function (newValue, evenWhenOutsideRange) {
3194
3255
  if (newValue === void 0) {
@@ -3335,6 +3396,16 @@
3335
3396
  if (this._type === 'anyvalue') return this._allValues[index];
3336
3397
  return null;
3337
3398
  };
3399
+ /**
3400
+ * Hint about the timestep size / time resolution of this dimension.
3401
+ * @returns The dimTimeInterval
3402
+ */
3403
+
3404
+
3405
+ WMJSDimension.prototype.getDimInterval = function () {
3406
+ this.initialize();
3407
+ return this.dimTimeInterval;
3408
+ };
3338
3409
  /**
3339
3410
  * Shorthand functionf or getValueForIndex
3340
3411
  */
@@ -3549,6 +3620,7 @@
3549
3620
  this.geoPosX = null;
3550
3621
  this.geoPosY = null;
3551
3622
  this.displayMapPin = true;
3623
+ this.disableMapPin = false;
3552
3624
 
3553
3625
  this.redrawMap = function () {
3554
3626
  _this._map.draw();
@@ -3569,6 +3641,8 @@
3569
3641
  var x = _x;
3570
3642
  var y = _y;
3571
3643
  if (!x || !y) return;
3644
+ if (_this.disableMapPin && !_bbox) return; // we still want to update the pin position when the bbox changes.
3645
+
3572
3646
  _this.x = parseInt(x, 10);
3573
3647
  _this.y = parseInt(y, 10);
3574
3648
  _this.exactX = parseFloat(x);
@@ -3593,6 +3667,12 @@
3593
3667
  _this.setMapPin(newpos.x, newpos.y);
3594
3668
  };
3595
3669
 
3670
+ this.toggleDisableMapPin = function () {
3671
+ _this.disableMapPin = !_this.disableMapPin;
3672
+
3673
+ _this.redrawMap();
3674
+ };
3675
+
3596
3676
  this.showMapPin = function () {
3597
3677
  _this.displayMapPin = true;
3598
3678
 
@@ -3610,7 +3690,7 @@
3610
3690
 
3611
3691
  MapPin.prototype.drawMarker = function (ctx) {
3612
3692
  if (isDefined(this.x) && isDefined(this.y)) {
3613
- WMDrawMarker(ctx, this.x, this.y, '#051039');
3693
+ WMDrawMarker(ctx, this.x, this.y, this.disableMapPin ? '#808080' : '#051039');
3614
3694
  }
3615
3695
  };
3616
3696
 
@@ -5140,7 +5220,6 @@
5140
5220
  this.mainElement.style.width = this.width + "px";
5141
5221
  this.mainElement.style.height = this.height + "px";
5142
5222
  this.setBBOX(this.resizeBBOX);
5143
- this.mapPin.repositionMapPin();
5144
5223
  this.showBoundingBox();
5145
5224
  this.divBuffer.resize(this.getWidth().toString(), this.getHeight().toString());
5146
5225
  this.repositionLegendGraphic();
@@ -6678,6 +6757,7 @@
6678
6757
 
6679
6758
  this.updateBBOX.setBBOX(this.bbox);
6680
6759
  this.drawnBBOX.setBBOX(this.bbox);
6760
+ this.mapPin.repositionMapPin(this.bbox);
6681
6761
  /* Undo part */
6682
6762
 
6683
6763
  if (this.DoRedo === 0 && this.DoUndo === 0) {
@@ -7485,7 +7565,7 @@
7485
7565
 
7486
7566
  if (layers[j].Layer) {
7487
7567
  nodeObject.children = [];
7488
- recursivelyFindLayer(toArray(layers[j].Layer), nodeObject.children, path + layers[j].Title.value);
7568
+ recursivelyFindLayer(toArray(layers[j].Layer), nodeObject.children, __spreadArray(__spreadArray([], __read(path)), [layers[j].Title.value]));
7489
7569
  }
7490
7570
  } // Sort nodes alphabetically.
7491
7571
 
@@ -7805,7 +7885,7 @@
7805
7885
  nodeStructure.text = I18n.unnamed_service.text;
7806
7886
  }
7807
7887
 
7808
- recursivelyFindLayer(WMSLayers, nodeStructure.children, '');
7888
+ recursivelyFindLayer(WMSLayers, nodeStructure.children, []);
7809
7889
  succes(nodeStructure);
7810
7890
  };
7811
7891
 
@@ -18,6 +18,7 @@ interface CanvasBBOX {
18
18
  export interface CanvasGeoLayer {
19
19
  bbox: CanvasBBOX;
20
20
  opacity: number;
21
+ imageAge: number;
21
22
  imageSource: string;
22
23
  }
23
24
  /**
@@ -49,12 +50,11 @@ export declare const getSharedImagesList: () => Record<string, CanvasGeoLayer[]>
49
50
  */
50
51
  export declare const geClosestBBox: (rect1: CanvasBBOX, rect2: CanvasBBOX) => number;
51
52
  /**
52
- * Sort the imageslist by overlapping area, highest area first
53
+ * Sort the imageslist by imagedate, newewst image first.
53
54
  * @param imagesList
54
- * @param bbox
55
55
  * @returns
56
56
  */
57
- export declare const sortByOverlappingArea: (imagesList: CanvasGeoLayer[], bbox: CanvasBBOX) => CanvasGeoLayer[];
57
+ export declare const sortByImageDate: (imagesList: CanvasGeoLayer[]) => CanvasGeoLayer[];
58
58
  /**
59
59
  * Function to find the an alternative available image for an image url that is not yet loaded
60
60
  * @param imageUrl The image source / url to find an alternative for
@@ -62,7 +62,7 @@ export declare const sortByOverlappingArea: (imagesList: CanvasGeoLayer[], bbox:
62
62
  * @param bbox The boundingbox to search this image for
63
63
  * @returns A canvasLayer, containing the new image source with its geo properties
64
64
  */
65
- export declare const getAlternativeImage: (imageUrl: string, imageStore: WMImageStore, bbox: CanvasBBOX) => CanvasGeoLayer;
65
+ export declare const getAlternativeImage: (imageUrl: string, imageStore: WMImageStore, _bbox: CanvasBBOX) => CanvasGeoLayer;
66
66
  export default class WMCanvasBuffer {
67
67
  canvas: HTMLCanvasElement;
68
68
  _ctx: CanvasRenderingContext2D;
@@ -2,6 +2,11 @@ export interface WMImageOptions {
2
2
  randomizer?: boolean;
3
3
  headers: Headers[];
4
4
  }
5
+ /**
6
+ * Returns the current time in ms
7
+ * @returns the current time in ms
8
+ */
9
+ export declare const getCurrentImageTime: () => number;
5
10
  /**
6
11
  * WMImage provides an API to the HTML image element. It is used for caching and easier access to images.
7
12
  */
@@ -16,6 +21,8 @@ export default class WMImage {
16
21
  loadEventCallback: (image: WMImage) => void;
17
22
  el: HTMLImageElement;
18
23
  headers: Headers[];
24
+ _imageTimeStampAtError: number;
25
+ _numFailedAttempts: number;
19
26
  /**
20
27
  *
21
28
  * @param src Optionally set the source URL to load
@@ -32,6 +39,15 @@ export default class WMImage {
32
39
  * Returns true if the image is still loading
33
40
  */
34
41
  isLoading(): boolean;
42
+ /**
43
+ * Get the amount of milliseconds since the image experienced an image load.
44
+ * @returns milliseconds since the image had an error
45
+ */
46
+ getLastErrorMSecondsAgo(): number;
47
+ /**
48
+ * @returns The number of failed attempts for this image
49
+ */
50
+ getNumFailedAttempts(): number;
35
51
  /**
36
52
  * Set source of image, but it will not load the image yet.
37
53
  * @param src URL of the image to load
@@ -54,6 +70,7 @@ export default class WMImage {
54
70
  * Start loading the image
55
71
  */
56
72
  load(): void;
73
+ forceReload(): void;
57
74
  private _getImageWithHeaders;
58
75
  private _load;
59
76
  private _loadEvent;
@@ -1,3 +1,4 @@
1
+ import { DateInterval } from './WMTime';
1
2
  interface WMJSDimensionConfig {
2
3
  name?: string;
3
4
  units?: string;
@@ -24,15 +25,15 @@ export default class WMJSDimension {
24
25
  synced: boolean;
25
26
  used?: boolean;
26
27
  values?: string;
27
- timeRangeDuration?: any;
28
+ private timeRangeDuration?;
28
29
  private _initialized;
29
30
  private _timeRangeDurationDate;
30
31
  private _allDates;
31
32
  private _type;
32
33
  private _allValues;
33
- dimMinValue: string;
34
- dimMaxValue: string;
35
- dimTimeInterval: any;
34
+ private dimMinValue;
35
+ private dimMaxValue;
36
+ private dimTimeInterval;
36
37
  constructor(config?: WMJSDimensionConfig);
37
38
  generateAllValues(): number[];
38
39
  setTimeValuesForReferenceTime(referenceTimeValueToSet: string, referenceTimeDim: WMJSDimension): void;
@@ -49,6 +50,11 @@ export default class WMJSDimension {
49
50
  * @param forceValue When set to false,check if the given value is outside range, and do nothing if this is the case
50
51
  */
51
52
  setValue(value: string, forceValue?: boolean): void;
53
+ /**
54
+ * Returns values of the dimension, according to values defined in WMS specification, e.g. 2011-01-01T00:00:00Z/2012-01-01T00:00:00Z/P1M or list of values.
55
+ * @returns
56
+ */
57
+ getValues(): string;
52
58
  setClosestValue(newValue?: any, evenWhenOutsideRange?: boolean): void;
53
59
  addTimeRangeDurationToValue(value: string): string;
54
60
  setTimeRangeDuration(duration: string): void;
@@ -57,6 +63,11 @@ export default class WMJSDimension {
57
63
  * Get dimension value for specified index
58
64
  */
59
65
  getValueForIndex(index: number): string;
66
+ /**
67
+ * Hint about the timestep size / time resolution of this dimension.
68
+ * @returns The dimTimeInterval
69
+ */
70
+ getDimInterval(): DateInterval;
60
71
  /**
61
72
  * Shorthand functionf or getValueForIndex
62
73
  */
@@ -21,14 +21,14 @@ interface NodeObject {
21
21
  text: string;
22
22
  leaf: boolean;
23
23
  name?: string;
24
- path?: string;
24
+ path?: string[];
25
25
  children?: [];
26
26
  keywords?: string[];
27
27
  abstract?: string;
28
28
  styles?: Style[];
29
29
  }
30
30
  export declare const sortByKey: (array: NodeObject[], key: string) => NodeObject[];
31
- export declare const recursivelyFindLayer: (layers: any[], rootNode: any[], path: string) => void;
31
+ export declare const recursivelyFindLayer: (layers: any[], rootNode: any[], path: string[]) => void;
32
32
  /**
33
33
  * WMJSService Class
34
34
  *
@@ -9,15 +9,17 @@ declare class MapPin {
9
9
  private geoPosX;
10
10
  private geoPosY;
11
11
  displayMapPin: boolean;
12
+ disableMapPin: boolean;
12
13
  constructor(_map: WMJSMap);
13
14
  redrawMap: () => void;
14
15
  drawMarker(ctx: CanvasRenderingContext2D): void;
15
- repositionMapPin: (_bbox?: WMBBOX) => void;
16
+ repositionMapPin: (_bbox: WMBBOX) => void;
16
17
  setMapPin: (_x: number, _y: number, _bbox?: WMBBOX) => void;
17
18
  positionMapPinByLatLon: (coord: {
18
19
  x: number;
19
20
  y: number;
20
21
  }) => void;
22
+ toggleDisableMapPin: () => void;
21
23
  showMapPin: () => void;
22
24
  hideMapPin: () => void;
23
25
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeoweb/webmap",
3
- "version": "2.1.4",
3
+ "version": "2.3.0",
4
4
  "description": "GeoWeb webmap library for the opengeoweb project",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {