@opengeoweb/webmap 15.2.0 → 16.0.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
@@ -938,36 +938,6 @@ var getFirstPartOfDimensionValueSet = function getFirstPartOfDimensionValueSet(i
938
938
  return !(inputValue != null && inputValue.indexOf) || !(inputValue != null && inputValue.includes('/')) ? inputValue : inputValue.split('/')[0];
939
939
  };
940
940
 
941
- /* *
942
- * Licensed under the Apache License, Version 2.0 (the "License");
943
- * you may not use this file except in compliance with the License.
944
- * You may obtain a copy of the License at
945
- *
946
- * http://www.apache.org/licenses/LICENSE-2.0
947
- *
948
- * Unless required by applicable law or agreed to in writing, software
949
- * distributed under the License is distributed on an "AS IS" BASIS,
950
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
951
- * See the License for the specific language governing permissions and
952
- * limitations under the License.
953
- *
954
- * Copyright 2024 - Koninklijk Nederlands Meteorologisch Instituut (KNMI)
955
- * Copyright 2024 - Finnish Meteorological Institute (FMI)
956
- * Copyright 2024 - The Norwegian Meteorological Institute (MET Norway)
957
- * */
958
- var fakeMaxAgeResponsesByLayer = {
959
- 'observation:wis2_air_temperature': 600,
960
- 'fmi:observation:lightning': 300,
961
- 'fmi:observation:temperature': 3600,
962
- 'fmi:observation:dewpoint': 3600,
963
- 'fmi:observation:temperature_foreign': 3600,
964
- 'fmi:observation:dewpoint_foreign': 3600,
965
- 'fmi:observation:cloudceiling': 3600,
966
- 'fmi:observation:cloudheight': 3600,
967
- 'fmi:observation:cloudheight_foreign': 3600,
968
- 'fmi:observation:cloudceiling_foreign': 3600
969
- };
970
-
971
941
  /**
972
942
  * Returns the current time in ms
973
943
  * @returns the current time in ms
@@ -1057,6 +1027,8 @@ var WMImage = /*#__PURE__*/function () {
1057
1027
  this._hasError = false;
1058
1028
  this._numFailedAttempts = 0;
1059
1029
  this._imageTimeStampAtError = getCurrentImageTime();
1030
+ this._maxAgeSeconds = undefined;
1031
+ this._loadedAtTime = undefined;
1060
1032
  }
1061
1033
  /**
1062
1034
  * Returns true if the image has been loaded
@@ -1065,7 +1037,10 @@ var WMImage = /*#__PURE__*/function () {
1065
1037
  if (this._isLoading) {
1066
1038
  return false;
1067
1039
  }
1068
- return this._isLoaded;
1040
+ if (!this._isLoaded) {
1041
+ return false;
1042
+ }
1043
+ return !this.isStale();
1069
1044
  };
1070
1045
  _proto.isLoadedWithoutErrors = function isLoadedWithoutErrors() {
1071
1046
  return this.isLoaded() && !this.hasError();
@@ -1074,25 +1049,14 @@ var WMImage = /*#__PURE__*/function () {
1074
1049
  return !this._isLoaded && !this._isLoading;
1075
1050
  };
1076
1051
  _proto.isStale = function isStale() {
1077
- if (this._fakeMaxAge === undefined) {
1078
- try {
1079
- var layerName = new URL(this.getSrc()).searchParams.get('LAYERS');
1080
- if (layerName) {
1081
- var _fakeMaxAgeResponsesB;
1082
- this._fakeMaxAge = (_fakeMaxAgeResponsesB = fakeMaxAgeResponsesByLayer[layerName]) != null ? _fakeMaxAgeResponsesB : null;
1083
- } else {
1084
- this._fakeMaxAge = null;
1085
- }
1086
- } catch (_e) {
1087
- // failed to form URL
1088
- this._fakeMaxAge = null;
1089
- }
1052
+ if (!this._isLoaded || this._loadedAtTime === undefined) {
1053
+ return false;
1090
1054
  }
1091
- if (this._fakeMaxAge === null) {
1055
+ if (this._maxAgeSeconds === null || this._maxAgeSeconds === undefined) {
1092
1056
  return false;
1093
1057
  }
1094
- var imageAge = getCurrentImageTime() - this._loadStartTime;
1095
- var maxAge = this._fakeMaxAge * 1000;
1058
+ var imageAge = getCurrentImageTime() - this._loadedAtTime;
1059
+ var maxAge = this._maxAgeSeconds * 1000;
1096
1060
  return imageAge > maxAge;
1097
1061
  }
1098
1062
  /**
@@ -1202,8 +1166,39 @@ var WMImage = /*#__PURE__*/function () {
1202
1166
  }
1203
1167
  this._load();
1204
1168
  };
1205
- _proto._getImageWithHeaders = function _getImageWithHeaders(url, headers) {
1169
+ WMImage._parseMaxAgeFromCacheControl = function _parseMaxAgeFromCacheControl(cacheControlHeader) {
1170
+ if (!cacheControlHeader) {
1171
+ return null;
1172
+ }
1173
+ var maxAgeMatch = /(?:^|,)\s*max-age=(\d+)/i.exec(cacheControlHeader);
1174
+ if (!maxAgeMatch) {
1175
+ return null;
1176
+ }
1177
+ var parsed = Number(maxAgeMatch[1]);
1178
+ return Number.isFinite(parsed) ? parsed : null;
1179
+ };
1180
+ _proto._setMaxAgeFromResponseHeaders = function _setMaxAgeFromResponseHeaders(response) {
1181
+ this._maxAgeSeconds = WMImage._parseMaxAgeFromCacheControl(response.headers.get('cache-control'));
1182
+ };
1183
+ _proto._updateMaxAgeFromUrl = function _updateMaxAgeFromUrl(url) {
1206
1184
  var _this3 = this;
1185
+ fetch(new Request(url), {
1186
+ method: 'GET',
1187
+ mode: 'cors',
1188
+ cache: 'default',
1189
+ headers: new Headers({
1190
+ Range: 'bytes=0-0'
1191
+ })
1192
+ }).then(function (response) {
1193
+ var _response$body;
1194
+ _this3._setMaxAgeFromResponseHeaders(response);
1195
+ void ((_response$body = response.body) == null ? void 0 : _response$body.cancel());
1196
+ })["catch"](function () {
1197
+ // Keep existing behaviour if headers are not readable.
1198
+ });
1199
+ };
1200
+ _proto._getImageWithHeaders = function _getImageWithHeaders(url, headers) {
1201
+ var _this4 = this;
1207
1202
  var fetchHeaders = new Headers();
1208
1203
  if (headers && headers.length > 0) {
1209
1204
  for (var _iterator = _createForOfIteratorHelperLoose(headers), _step; !(_step = _iterator()).done;) {
@@ -1227,19 +1222,21 @@ var WMImage = /*#__PURE__*/function () {
1227
1222
  return window.btoa(binary);
1228
1223
  };
1229
1224
  fetch(request, options).then(function (response) {
1225
+ _this4._setMaxAgeFromResponseHeaders(response);
1230
1226
  response.arrayBuffer().then(function (buffer) {
1231
1227
  var base64Flag = 'data:image/png;base64,';
1232
1228
  var imageStr = arrayBufferToBase64(buffer);
1233
- _this3.getElement().src = base64Flag + imageStr;
1229
+ _this4.getElement().src = base64Flag + imageStr;
1234
1230
  })["catch"](function () {
1235
1231
  debugLogger(DebugType.Error);
1232
+ _this4._loadEvent(true);
1236
1233
  });
1237
1234
  })["catch"](function () {
1238
1235
  debugLogger(DebugType.Error, "Unable to fetch image " + url + " with headers [" + JSON.stringify(headers) + "]");
1239
1236
  if (url.startsWith('http://')) {
1240
1237
  debugLogger(DebugType.Error);
1241
1238
  }
1242
- _this3._loadEvent(true);
1239
+ _this4._loadEvent(true);
1243
1240
  });
1244
1241
  };
1245
1242
  _proto._load = function _load() {
@@ -1247,8 +1244,12 @@ var WMImage = /*#__PURE__*/function () {
1247
1244
  this._loadStartTime = getCurrentImageTime();
1248
1245
  this._hasError = false;
1249
1246
  if (this._isLoaded) {
1250
- this._loadEvent(false);
1251
- return;
1247
+ if (!this.isStale()) {
1248
+ this._loadEvent(false);
1249
+ return;
1250
+ }
1251
+ this._isLoaded = false;
1252
+ this._srcLoaded = undefined;
1252
1253
  }
1253
1254
  this._isLoading = true;
1254
1255
  if (!this.srcToLoad) {
@@ -1275,6 +1276,7 @@ var WMImage = /*#__PURE__*/function () {
1275
1276
  this._getImageWithHeaders(this.srcToLoad, this.headers);
1276
1277
  } else if (this.randomize) {
1277
1278
  /* Do a standard img.src url request */
1279
+ this._maxAgeSeconds = null;
1278
1280
  var newSrc = this.srcToLoad;
1279
1281
  if (!this.srcToLoad.includes('?')) {
1280
1282
  newSrc += '?';
@@ -1290,8 +1292,9 @@ var WMImage = /*#__PURE__*/function () {
1290
1292
  }
1291
1293
  this.getElement().src = newSrc;
1292
1294
  } else {
1293
- // setting src loads the image and the 'load' callback is called when its finished loading
1295
+ // Keep the browser image loading path for rendering reliability.
1294
1296
  this.getElement().src = this.srcToLoad;
1297
+ this._updateMaxAgeFromUrl(this.srcToLoad);
1295
1298
  }
1296
1299
  };
1297
1300
  _proto._loadEvent = function _loadEvent(hasError) {
@@ -1300,6 +1303,7 @@ var WMImage = /*#__PURE__*/function () {
1300
1303
  this._hasError = hasError;
1301
1304
  this._isLoading = false;
1302
1305
  this._isLoaded = true;
1306
+ this._loadedAtTime = hasError ? undefined : getCurrentImageTime();
1303
1307
  this._srcLoaded = this.srcToLoad;
1304
1308
  this._resolveAll();
1305
1309
  (_this$loadEventCallba = this.loadEventCallback) == null || _this$loadEventCallba.call(this, this);
@@ -1425,14 +1429,24 @@ var WMImageStore = /*#__PURE__*/function () {
1425
1429
  * @returns A WMImage which is ready to display. Can have a different extent and resolution then what was asked in srcToLoad
1426
1430
  */;
1427
1431
  _proto.getAltGeoReferencedImage = function getAltGeoReferencedImage(srcToLoad, _extent, _resolution) {
1432
+ var imagesbyGeoRefArraySortByImageLife = this.getAltGeoReferencedImages(srcToLoad, _extent, _resolution);
1433
+ // Now filter out the loaded images, which can be used as alternative
1434
+ var sortByImageLifeFilterdLoaded = imagesbyGeoRefArraySortByImageLife.filter(function (image) {
1435
+ return image.isLoadedWithoutErrors();
1436
+ });
1437
+ // Just return most recent available image: TODO: Possibly make use of extent and resolution to find a better one.
1438
+ // https://gitlab.com/opengeoweb/geoweb-assets/-/issues/4481
1439
+ return sortByImageLifeFilterdLoaded.length > 0 ? sortByImageLifeFilterdLoaded[0] : false;
1440
+ };
1441
+ _proto.getAltGeoReferencedImages = function getAltGeoReferencedImages(srcToLoad, _extent, _resolution) {
1428
1442
  var hashUrl = makeHashUrlForGeoReferencedImage(srcToLoad);
1429
1443
  if (!hashUrl) {
1430
- return false;
1444
+ return [];
1431
1445
  }
1432
1446
  // Sort the imagesbyGeoRef array
1433
1447
  var imagesbyGeoRefArray = this.imagesbyGeoRef.get(hashUrl);
1434
1448
  if (!imagesbyGeoRefArray || imagesbyGeoRefArray.length === 0) {
1435
- return false;
1449
+ return [];
1436
1450
  }
1437
1451
  // Clean the set, only keep the most recent images
1438
1452
  var imagesbyGeoRefArraySortByImageLife = imagesbyGeoRefArray.sort(function (imageA, imageB) {
@@ -1446,14 +1460,9 @@ var WMImageStore = /*#__PURE__*/function () {
1446
1460
  }).slice(0, WMIMAGESTORE_MAX_ALTIMAGES_TO_KEEP);
1447
1461
  // Set this array back in the array
1448
1462
  this.imagesbyGeoRef.set(hashUrl, imagesbyGeoRefArraySortByImageLife);
1449
- // Now filter out the loaded images, which can be used as alternative
1450
- var sortByImageLifeFilterdLoaded = imagesbyGeoRefArraySortByImageLife.filter(function (image) {
1451
- return image.isLoadedWithoutErrors();
1452
- });
1453
- // Just return most recent available image: TODO: Possibly make use of extent and resolution to find a better one.
1454
- return sortByImageLifeFilterdLoaded.length > 0 ? sortByImageLifeFilterdLoaded[0] : false;
1463
+ return imagesbyGeoRefArraySortByImageLife;
1455
1464
  };
1456
- _proto.getGeoReferencedImage = function getGeoReferencedImage(src, geoReference) {
1465
+ _proto.getImageAndSetGeoReference = function getImageAndSetGeoReference(src, geoReference) {
1457
1466
  var image = this.getImage(src);
1458
1467
  if (image) {
1459
1468
  // Make a hashUrl without BBOX
@@ -3137,7 +3146,10 @@ var WMJSDimension = /*#__PURE__*/function () {
3137
3146
  var timeToFind = new Date(timeStamp).toISOString().substring(0, 19) + "Z";
3138
3147
  return this.getClosestValue(timeToFind);
3139
3148
  };
3140
- _proto.getValueForSpecialString = function getValueForSpecialString(inputValue) {
3149
+ _proto.getValueForSpecialString = function getValueForSpecialString(inputValue, evenWhenOutsideRange) {
3150
+ if (evenWhenOutsideRange === void 0) {
3151
+ evenWhenOutsideRange = false;
3152
+ }
3141
3153
  // Check for special cases like: 'current', 'default, '', 'middle', 'earliest' and 'latest'
3142
3154
  switch (inputValue) {
3143
3155
  case 'current':
@@ -3147,12 +3159,16 @@ var WMJSDimension = /*#__PURE__*/function () {
3147
3159
  case 'middle':
3148
3160
  return this.getMiddleValue();
3149
3161
  case 'earliest':
3150
- case WMDateTooEarlyString:
3151
3162
  return this.getFirstValue();
3152
3163
  case 'latest':
3153
- case WMDateTooLateString:
3154
3164
  return this.getLastValue();
3155
3165
  }
3166
+ if (inputValue === WMDateTooEarlyString && evenWhenOutsideRange) {
3167
+ return this.getFirstValue();
3168
+ }
3169
+ if (inputValue === WMDateTooLateString && evenWhenOutsideRange) {
3170
+ return this.getLastValue();
3171
+ }
3156
3172
  return inputValue;
3157
3173
  };
3158
3174
  _proto.getExactMatchingValue = function getExactMatchingValue(inputValue) {
@@ -3180,11 +3196,11 @@ var WMJSDimension = /*#__PURE__*/function () {
3180
3196
  // Just make sure to return the first part if accidently start/stop/res was given.
3181
3197
  var firstPartValue = getFirstPartOfDimensionValueSet(inputValue);
3182
3198
  // Handle 'current', 'default, '', 'middle', 'earliest' and 'latest'
3183
- var newValue = this.getValueForSpecialString(firstPartValue);
3199
+ var newValue = this.getValueForSpecialString(firstPartValue, evenWhenOutsideRange);
3184
3200
  // Get the exact value as present in the dimension values
3185
3201
  var matchingValue = this.getExactMatchingValue(newValue);
3186
3202
  // Handle 'current', 'default, '', 'middle', 'earliest' and 'latest'
3187
- return evenWhenOutsideRange ? this.getValueForSpecialString(matchingValue) : matchingValue;
3203
+ return evenWhenOutsideRange ? this.getValueForSpecialString(matchingValue, evenWhenOutsideRange) : matchingValue;
3188
3204
  }
3189
3205
  /**
3190
3206
  * Get dimension value for specified index
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeoweb/webmap",
3
- "version": "15.2.0",
3
+ "version": "16.0.0",
4
4
  "description": "GeoWeb webmap library for the opengeoweb project",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -8,7 +8,7 @@
8
8
  "url": "git@gitlab.com:opengeoweb/opengeoweb.git"
9
9
  },
10
10
  "dependencies": {
11
- "@opengeoweb/shared": "15.2.0",
11
+ "@opengeoweb/shared": "16.0.0",
12
12
  "@tanstack/query-core": "^5.69.2",
13
13
  "lodash": "^4.17.21",
14
14
  "i18next": "^25.0.1",
@@ -34,7 +34,8 @@ export default class WMImage {
34
34
  _numFailedAttempts: number;
35
35
  _loadStartTime: number;
36
36
  _loadDuration?: number;
37
- _fakeMaxAge?: number | null;
37
+ _maxAgeSeconds?: number | null;
38
+ _loadedAtTime?: number;
38
39
  _resolveList: ((i: WMImage) => void)[];
39
40
  geoReference: WMGeoReference | null;
40
41
  hashUrl: string | undefined;
@@ -92,6 +93,9 @@ export default class WMImage {
92
93
  loadAwait(): Promise<WMImage>;
93
94
  load(): void;
94
95
  forceReload(randomize?: boolean): void;
96
+ private static _parseMaxAgeFromCacheControl;
97
+ private _setMaxAgeFromResponseHeaders;
98
+ private _updateMaxAgeFromUrl;
95
99
  private _getImageWithHeaders;
96
100
  _load(): void;
97
101
  _loadEvent(hasError: boolean): void;
@@ -49,7 +49,8 @@ export default class WMImageStore {
49
49
  * @returns A WMImage which is ready to display. Can have a different extent and resolution then what was asked in srcToLoad
50
50
  */
51
51
  getAltGeoReferencedImage(srcToLoad: string, _extent: number[], _resolution: number): WMImage | false;
52
- getGeoReferencedImage(src: string, geoReference: WMGeoReference): WMImage;
52
+ getAltGeoReferencedImages(srcToLoad: string, _extent: number[], _resolution: number): WMImage[];
53
+ getImageAndSetGeoReference(src: string, geoReference: WMGeoReference): WMImage;
53
54
  getNumCached(): number;
54
55
  /**
55
56
  * Get an WMImage object for given URL
@@ -74,7 +74,7 @@ export default class WMJSDimension implements Dimension {
74
74
  addTimeRangeDurationToValue(value: string): string;
75
75
  setTimeRangeDuration(duration: string): void;
76
76
  getClosestValueForTime(timeStamp: number): string;
77
- getValueForSpecialString(inputValue: string): string;
77
+ getValueForSpecialString(inputValue: string, evenWhenOutsideRange?: boolean): string;
78
78
  getExactMatchingValue(inputValue: string): string;
79
79
  getClosestValue(inputValue: string, evenWhenOutsideRange?: boolean): string;
80
80
  /**
@@ -34,6 +34,7 @@ export interface LayerFoundation extends TiledLayerFoundation {
34
34
  type?: string;
35
35
  geojson?: FeatureCollection;
36
36
  options?: EDRFeatureLayerOptions;
37
+ showLayerLegend?: boolean;
37
38
  }
38
39
  export interface LayerOptions extends LayerFoundation {
39
40
  id: string;