From 2fc1d0f723005b930b8e92fc10f22e0bbf6d60ee Mon Sep 17 00:00:00 2001 From: Suren Date: Thu, 9 Nov 2023 17:37:09 +0530 Subject: [PATCH 1/4] #9590: COG download metadata by default with abort fetch --- build/buildConfig.js | 3 +- build/testConfig.js | 3 +- web/client/actions/__tests__/catalog-test.js | 7 ++ web/client/actions/catalog.js | 5 +- web/client/api/catalog/COG.js | 42 +++++++++-- .../catalog/CatalogServiceEditor.jsx | 28 +++++-- .../__tests__/CatalogServiceEditor-test.jsx | 75 +++++++++++++++++++ .../CommonAdvancedSettings.jsx | 2 +- web/client/epics/catalog.js | 4 +- web/client/translations/data.de-DE.json | 2 +- web/client/translations/data.en-US.json | 2 +- web/client/translations/data.es-ES.json | 2 +- web/client/translations/data.fr-FR.json | 2 +- web/client/translations/data.it-IT.json | 2 +- 14 files changed, 156 insertions(+), 23 deletions(-) diff --git a/build/buildConfig.js b/build/buildConfig.js index ad64aff31d..72c46b70b8 100644 --- a/build/buildConfig.js +++ b/build/buildConfig.js @@ -194,7 +194,8 @@ module.exports = (...args) => mapArgumentsToObject(args, ({ jsonix: '@boundlessgeo/jsonix', // next libs are added because of this issue https://github.com/geosolutions-it/MapStore2/issues/4569 proj4: '@geosolutions/proj4', - "react-joyride": '@geosolutions/react-joyride' + "react-joyride": '@geosolutions/react-joyride', + "geotiff-remote": path.join(paths.base, "node_modules", "geotiff", "dist-module", "source", "remote") }, alias), ...(resolveModules && { modules: resolveModules }) }, diff --git a/build/testConfig.js b/build/testConfig.js index f4932f55d1..d1d2e0f2fc 100644 --- a/build/testConfig.js +++ b/build/testConfig.js @@ -140,7 +140,8 @@ module.exports = ({browsers = [ 'ChromeHeadless' ], files, path, testFile, singl jsonix: '@boundlessgeo/jsonix', // next libs are added because of this issue https://github.com/geosolutions-it/MapStore2/issues/4569 proj4: '@geosolutions/proj4', - "react-joyride": '@geosolutions/react-joyride' + "react-joyride": '@geosolutions/react-joyride', + "geotiff-remote": nodePath.join(__dirname, "..", "node_modules", "geotiff", "dist-module", "source", "remote") }, alias), extensions: ['.js', '.json', '.jsx'] }, diff --git a/web/client/actions/__tests__/catalog-test.js b/web/client/actions/__tests__/catalog-test.js index b50ae764b2..17fdf54de5 100644 --- a/web/client/actions/__tests__/catalog-test.js +++ b/web/client/actions/__tests__/catalog-test.js @@ -238,6 +238,13 @@ describe('Test correctness of the catalog actions', () => { expect(retval).toExist(); expect(retval.type).toBe(ADD_SERVICE); }); + it('addService with options', () => { + const options = {"test": "1"}; + var retval = addService(options); + expect(retval).toExist(); + expect(retval.type).toBe(ADD_SERVICE); + expect(retval.options).toEqual(options); + }); it('addCatalogService', () => { var retval = addCatalogService(service); diff --git a/web/client/actions/catalog.js b/web/client/actions/catalog.js index d4fd402bc2..fd41eeab71 100644 --- a/web/client/actions/catalog.js +++ b/web/client/actions/catalog.js @@ -170,9 +170,10 @@ export function changeUrl(url) { url }; } -export function addService() { +export function addService(options) { return { - type: ADD_SERVICE + type: ADD_SERVICE, + options }; } export function addCatalogService(service) { diff --git a/web/client/api/catalog/COG.js b/web/client/api/catalog/COG.js index a566aa4813..cda4772c8b 100644 --- a/web/client/api/catalog/COG.js +++ b/web/client/api/catalog/COG.js @@ -8,8 +8,10 @@ import get from 'lodash/get'; import isEmpty from 'lodash/isEmpty'; +import isNil from 'lodash/isNil'; import { Observable } from 'rxjs'; -import { fromUrl } from 'geotiff'; +import { GeoTIFF } from 'geotiff'; +import { makeRemoteSource } from 'geotiff-remote'; import { isValidURL } from '../../utils/URLUtils'; import ConfigUtils from '../../utils/ConfigUtils'; @@ -57,8 +59,22 @@ export const getProjectionFromGeoKeys = (image) => { return null; }; +const abortError = (reject) => reject(new DOMException("Aborted", "AbortError")); +/** + * getImage with abort fetching of data slices + */ +const getImage = (geotiff, signal) => { + if (signal?.aborted) { + return abortError(Promise.reject); + } + return new Promise((resolve, reject) => { + signal?.addEventListener("abort", () => abortError(reject)); + return geotiff.getImage() // Fetch and read first image to get medatadata of the tif + .then((image) => resolve(image)) + .catch(()=> abortError(reject)); + }); +}; let capabilitiesCache = {}; - export const getRecords = (_url, startPosition, maxRecords, text, info = {}) => { const service = get(info, 'options.service'); let layers = []; @@ -73,19 +89,35 @@ export const getRecords = (_url, startPosition, maxRecords, text, info = {}) => sources: [{url}], options: service.options || {} }; - if (service.fetchMetadata) { + const controller = get(info, 'options.controller'); + const isSave = get(info, 'options.save', false); + // Fetch metadata only on saving the service (skip on search) + if ((isNil(service.fetchMetadata) || service.fetchMetadata) && isSave) { const cached = capabilitiesCache[url]; if (cached && new Date().getTime() < cached.timestamp + (ConfigUtils.getConfigProp('cacheExpire') || 60) * 1000) { return {...cached.data}; } - return fromUrl(url) - .then(geotiff => geotiff.getImage()) + const signal = controller?.signal; + // geotiff's `fromUrl` & `getImage` function doesn't pass down signal parameter properly. Known issue (https://github.com/geotiffjs/geotiff.js/issues/330) + // Hence `fromSource` is called directly with source formulated + return GeoTIFF.fromSource(makeRemoteSource(url, {}), {}, signal) + .then(geotiff => getImage(geotiff, signal)) .then(image => { const crs = getProjectionFromGeoKeys(image); const extent = image.getBoundingBox(); const isProjectionDefined = isProjectionAvailable(crs); layer = { ...layer, + sourceMetadata: { + crs, + extent: extent, + width: image.getWidth(), + height: image.getHeight(), + tileWidth: image.getTileWidth(), + tileHeight: image.getTileHeight(), + origin: image.getOrigin(), + resolution: image.getResolution() + }, // skip adding bbox when geokeys or extent is empty ...(!isEmpty(extent) && !isEmpty(crs) && isProjectionDefined && { bbox: { diff --git a/web/client/components/catalog/CatalogServiceEditor.jsx b/web/client/components/catalog/CatalogServiceEditor.jsx index b4e747e75b..7ff1422130 100644 --- a/web/client/components/catalog/CatalogServiceEditor.jsx +++ b/web/client/components/catalog/CatalogServiceEditor.jsx @@ -17,7 +17,20 @@ import Message from "../I18N/Message"; import AdvancedSettings from './editor/AdvancedSettings'; import MainForm from './editor/MainForm'; -export default ({ +const withAbort = (Component) => { + return (props) => { + const [abortController, setAbortController] = useState(null); + const onSave = () => { + // Currently abort request on saving is applicable only for COG service + const controller = props.format === 'cog' ? new AbortController() : null; + setAbortController(controller); + return props.onAddService({save: true, controller}); + }; + const onCancel = () => abortController && props.saving ? abortController?.abort() : props.onChangeCatalogMode("view"); + return ; + }; +}; +const CatalogServiceEditor = ({ service = { title: "", type: "wms", @@ -39,9 +52,9 @@ export default ({ onChangeServiceProperty = () => {}, onToggleTemplate = () => {}, onToggleThumbnail = () => {}, - onAddService = () => {}, onDeleteService = () => {}, - onChangeCatalogMode = () => {}, + onCancel = () => {}, + onSaveService = () => {}, onFormatOptionsFetch = () => {}, selectedService, isLocalizedLayerStylesEnabled, @@ -50,7 +63,8 @@ export default ({ layerOptions = {}, infoFormatOptions, services, - autoSetVisibilityLimits = false + autoSetVisibilityLimits = false, + disabled }) => { const [valid, setValid] = useState(true); return ( - @@ -102,7 +116,7 @@ export default ({ : null } - @@ -110,3 +124,5 @@ export default ({ ); }; + +export default withAbort(CatalogServiceEditor); diff --git a/web/client/components/catalog/__tests__/CatalogServiceEditor-test.jsx b/web/client/components/catalog/__tests__/CatalogServiceEditor-test.jsx index 05e2d96463..2252cf661f 100644 --- a/web/client/components/catalog/__tests__/CatalogServiceEditor-test.jsx +++ b/web/client/components/catalog/__tests__/CatalogServiceEditor-test.jsx @@ -8,6 +8,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import expect from 'expect'; +import TestUtils from 'react-dom/test-utils'; import CatalogServiceEditor from '../CatalogServiceEditor'; import {defaultPlaceholder} from "../editor/MainFormUtils"; @@ -149,4 +150,78 @@ describe('Test CatalogServiceEditor', () => { let placeholder = defaultPlaceholder(service); expect(placeholder).toBe("e.g. https://mydomain.com/geoserver/wms"); }); + it('test saving service for COG type', () => { + const actions = { + onAddService: () => {} + }; + const spyOnAdd = expect.spyOn(actions, 'onAddService'); + ReactDOM.render(, document.getElementById("container")); + let buttons = document.querySelectorAll('.form-group button'); + let saveBtn; + buttons.forEach(btn => {if (btn.textContent === 'save') saveBtn = btn;}); + expect(saveBtn).toBeTruthy(); + TestUtils.Simulate.click(saveBtn); + expect(spyOnAdd).toHaveBeenCalled(); + let arg = spyOnAdd.calls[0].arguments[0]; + expect(arg.save).toBe(true); + expect(arg.controller).toBeTruthy(); + + ReactDOM.render(, document.getElementById("container")); + buttons = document.querySelectorAll('.form-group button'); + buttons.forEach(btn => {if (btn.textContent === 'save') saveBtn = btn;}); + expect(saveBtn).toBeTruthy(); + TestUtils.Simulate.click(saveBtn); + expect(spyOnAdd).toHaveBeenCalled(); + arg = spyOnAdd.calls[1].arguments[0]; + expect(arg.save).toBeTruthy(); + expect(arg.controller).toBeFalsy(); + }); + it('test cancel service', () => { + const actions = { + onChangeCatalogMode: () => {}, + onAddService: () => {} + }; + const spyOnCancel = expect.spyOn(actions, 'onChangeCatalogMode'); + ReactDOM.render(, document.getElementById("container")); + let buttons = document.querySelectorAll('.form-group button'); + let cancelBtn; + buttons.forEach(btn => {if (btn.textContent === 'cancel') cancelBtn = btn;}); + expect(cancelBtn).toBeTruthy(); + TestUtils.Simulate.click(cancelBtn); + expect(spyOnCancel).toHaveBeenCalled(); + let arg = spyOnCancel.calls[0].arguments[0]; + expect(arg).toBe('view'); + + const spyOnAdd = expect.spyOn(actions, 'onAddService'); + ReactDOM.render(, document.getElementById("container")); + buttons = document.querySelectorAll('.form-group button'); + let saveBtn; + buttons.forEach(btn => {if (btn.textContent === 'save') saveBtn = btn;}); + TestUtils.Simulate.click(saveBtn); + expect(spyOnAdd).toHaveBeenCalled(); + + ReactDOM.render(, document.getElementById("container")); + buttons = document.querySelectorAll('.form-group button'); + buttons.forEach(btn => {if (btn.textContent === 'cancel') cancelBtn = btn;}); + TestUtils.Simulate.click(cancelBtn); + expect(spyOnCancel.calls[1]).toBeFalsy(); + }); }); diff --git a/web/client/components/catalog/editor/AdvancedSettings/CommonAdvancedSettings.jsx b/web/client/components/catalog/editor/AdvancedSettings/CommonAdvancedSettings.jsx index 048fd385ae..45900f3d36 100644 --- a/web/client/components/catalog/editor/AdvancedSettings/CommonAdvancedSettings.jsx +++ b/web/client/components/catalog/editor/AdvancedSettings/CommonAdvancedSettings.jsx @@ -58,7 +58,7 @@ export default ({ onChangeServiceProperty("fetchMetadata", e.target.checked)} - checked={!isNil(service.fetchMetadata) ? service.fetchMetadata : false}> + checked={!isNil(service.fetchMetadata) ? service.fetchMetadata : true}>  } /> diff --git a/web/client/epics/catalog.js b/web/client/epics/catalog.js index 3545be647d..294b54b8a1 100644 --- a/web/client/epics/catalog.js +++ b/web/client/epics/catalog.js @@ -292,7 +292,7 @@ export default (API) => ({ */ newCatalogServiceAdded: (action$, store) => action$.ofType(ADD_SERVICE) - .switchMap(() => { + .switchMap(({options} = {}) => { const state = store.getState(); const newService = newServiceSelector(state); const maxRecords = pageSizeSelector(state); @@ -310,7 +310,7 @@ export default (API) => ({ startPosition: 1, maxRecords, text: "", - options: {service, isNewService: true} + options: {service, isNewService: true, ...options} }) ); }) diff --git a/web/client/translations/data.de-DE.json b/web/client/translations/data.de-DE.json index 826ec8c789..4cb2e38012 100644 --- a/web/client/translations/data.de-DE.json +++ b/web/client/translations/data.de-DE.json @@ -1553,7 +1553,7 @@ "tooltip": "Fügen Sie der Karte Ebenen hinzu", "autoload": "Suche in Dienstauswahl", "fetchMetadata": { - "label": "Laden Sie Dateimetadaten bei der Suche herunter", + "label": "Dateimetadaten beim Speichern herunterladen", "tooltip": "Diese Option ruft Metadaten ab, um das Zoomen auf Ebene zu unterstützen. Es kann den Suchvorgang verlangsamen, wenn die Bilder zu groß oder zu viele sind." }, "clearValueText": "Auswahl aufheben", diff --git a/web/client/translations/data.en-US.json b/web/client/translations/data.en-US.json index 0f422167aa..c96ccd2465 100644 --- a/web/client/translations/data.en-US.json +++ b/web/client/translations/data.en-US.json @@ -1514,7 +1514,7 @@ "tooltip": "Add layers to the map", "autoload": "Search on service selection", "fetchMetadata": { - "label": "Download file metadata on search", + "label": "Download file metadata on save", "tooltip": "This option will fetch metadata to support the zoom to layer. It may slow down the search operation if the images are too big or too many." }, "clearValueText": "Clear selection", diff --git a/web/client/translations/data.es-ES.json b/web/client/translations/data.es-ES.json index efbb9bce36..017b4d666b 100644 --- a/web/client/translations/data.es-ES.json +++ b/web/client/translations/data.es-ES.json @@ -1515,7 +1515,7 @@ "tooltip": "agregar capas al mapa", "autoload": "Buscar en la selección de servicios", "fetchMetadata": { - "label": "Descargar metadatos de archivos en la búsqueda", + "label": "Descargar metadatos del archivo al guardar", "tooltip": "Esta opción recuperará metadatos para admitir el zoom a la capa. Puede ralentizar la operación de búsqueda si las imágenes son demasiado grandes o demasiadas." }, "clearValueText": "Borrar selección", diff --git a/web/client/translations/data.fr-FR.json b/web/client/translations/data.fr-FR.json index 45e502647f..43390c83ee 100644 --- a/web/client/translations/data.fr-FR.json +++ b/web/client/translations/data.fr-FR.json @@ -1515,7 +1515,7 @@ "tooltip": "Ajouter des couches à la carte", "autoload": "Recherche sur la sélection du service", "fetchMetadata": { - "label": "Télécharger les métadonnées du fichier lors de la recherche", + "label": "Télécharger les métadonnées du fichier lors de l'enregistrement", "tooltip": "Cette option récupérera les métadonnées pour prendre en charge le zoom sur la couche. Cela peut ralentir l'opération de recherche si les images sont trop grandes ou trop nombreuses." }, "clearValueText": "Effacer la sélection", diff --git a/web/client/translations/data.it-IT.json b/web/client/translations/data.it-IT.json index 675f13b097..ce85ba5a4a 100644 --- a/web/client/translations/data.it-IT.json +++ b/web/client/translations/data.it-IT.json @@ -1513,7 +1513,7 @@ "title": "Catalogo", "autoload": "Ricerca alla selezione del servizio", "fetchMetadata": { - "label": "Scarica i metadati dei file durante la ricerca", + "label": "Scarica i metadati del file al salvataggio", "tooltip": "Questa opzione recupererà i metadati per supportare lo zoom a livello. Potrebbe rallentare l'operazione di ricerca se le immagini sono troppo grandi o troppe." }, "clearValueText": "Cancella selezione", From 4db5d362ae88c468bfc2219b1a4a08db1113463e Mon Sep 17 00:00:00 2001 From: Suren Date: Tue, 14 Nov 2023 18:02:32 +0530 Subject: [PATCH 2/4] Code refactor --- build/buildConfig.js | 3 +-- build/testConfig.js | 3 +-- web/client/api/catalog/COG.js | 16 ++++++---------- .../components/catalog/CatalogServiceEditor.jsx | 2 +- .../__tests__/CatalogServiceEditor-test.jsx | 15 +++++++++++++++ 5 files changed, 24 insertions(+), 15 deletions(-) diff --git a/build/buildConfig.js b/build/buildConfig.js index 72c46b70b8..ad64aff31d 100644 --- a/build/buildConfig.js +++ b/build/buildConfig.js @@ -194,8 +194,7 @@ module.exports = (...args) => mapArgumentsToObject(args, ({ jsonix: '@boundlessgeo/jsonix', // next libs are added because of this issue https://github.com/geosolutions-it/MapStore2/issues/4569 proj4: '@geosolutions/proj4', - "react-joyride": '@geosolutions/react-joyride', - "geotiff-remote": path.join(paths.base, "node_modules", "geotiff", "dist-module", "source", "remote") + "react-joyride": '@geosolutions/react-joyride' }, alias), ...(resolveModules && { modules: resolveModules }) }, diff --git a/build/testConfig.js b/build/testConfig.js index d1d2e0f2fc..f4932f55d1 100644 --- a/build/testConfig.js +++ b/build/testConfig.js @@ -140,8 +140,7 @@ module.exports = ({browsers = [ 'ChromeHeadless' ], files, path, testFile, singl jsonix: '@boundlessgeo/jsonix', // next libs are added because of this issue https://github.com/geosolutions-it/MapStore2/issues/4569 proj4: '@geosolutions/proj4', - "react-joyride": '@geosolutions/react-joyride', - "geotiff-remote": nodePath.join(__dirname, "..", "node_modules", "geotiff", "dist-module", "source", "remote") + "react-joyride": '@geosolutions/react-joyride' }, alias), extensions: ['.js', '.json', '.jsx'] }, diff --git a/web/client/api/catalog/COG.js b/web/client/api/catalog/COG.js index cda4772c8b..b48773f5ef 100644 --- a/web/client/api/catalog/COG.js +++ b/web/client/api/catalog/COG.js @@ -10,8 +10,7 @@ import get from 'lodash/get'; import isEmpty from 'lodash/isEmpty'; import isNil from 'lodash/isNil'; import { Observable } from 'rxjs'; -import { GeoTIFF } from 'geotiff'; -import { makeRemoteSource } from 'geotiff-remote'; +import { fromUrl as fromGeotiffUrl } from 'geotiff'; import { isValidURL } from '../../utils/URLUtils'; import ConfigUtils from '../../utils/ConfigUtils'; @@ -61,15 +60,16 @@ export const getProjectionFromGeoKeys = (image) => { }; const abortError = (reject) => reject(new DOMException("Aborted", "AbortError")); /** - * getImage with abort fetching of data slices + * fromUrl with abort fetching of data and data slices */ -const getImage = (geotiff, signal) => { +const fromUrl = (url, signal) => { if (signal?.aborted) { return abortError(Promise.reject); } return new Promise((resolve, reject) => { signal?.addEventListener("abort", () => abortError(reject)); - return geotiff.getImage() // Fetch and read first image to get medatadata of the tif + return fromGeotiffUrl(url) + .then((image)=> image.getImage()) // Fetch and read first image to get medatadata of the tif .then((image) => resolve(image)) .catch(()=> abortError(reject)); }); @@ -97,11 +97,7 @@ export const getRecords = (_url, startPosition, maxRecords, text, info = {}) => if (cached && new Date().getTime() < cached.timestamp + (ConfigUtils.getConfigProp('cacheExpire') || 60) * 1000) { return {...cached.data}; } - const signal = controller?.signal; - // geotiff's `fromUrl` & `getImage` function doesn't pass down signal parameter properly. Known issue (https://github.com/geotiffjs/geotiff.js/issues/330) - // Hence `fromSource` is called directly with source formulated - return GeoTIFF.fromSource(makeRemoteSource(url, {}), {}, signal) - .then(geotiff => getImage(geotiff, signal)) + return fromUrl(url, controller?.signal) .then(image => { const crs = getProjectionFromGeoKeys(image); const extent = image.getBoundingBox(); diff --git a/web/client/components/catalog/CatalogServiceEditor.jsx b/web/client/components/catalog/CatalogServiceEditor.jsx index 7ff1422130..5eb06219a5 100644 --- a/web/client/components/catalog/CatalogServiceEditor.jsx +++ b/web/client/components/catalog/CatalogServiceEditor.jsx @@ -111,7 +111,7 @@ const CatalogServiceEditor = ({ {service && !service.isNew - ? : null diff --git a/web/client/components/catalog/__tests__/CatalogServiceEditor-test.jsx b/web/client/components/catalog/__tests__/CatalogServiceEditor-test.jsx index 2252cf661f..f2f16f1bee 100644 --- a/web/client/components/catalog/__tests__/CatalogServiceEditor-test.jsx +++ b/web/client/components/catalog/__tests__/CatalogServiceEditor-test.jsx @@ -150,6 +150,21 @@ describe('Test CatalogServiceEditor', () => { let placeholder = defaultPlaceholder(service); expect(placeholder).toBe("e.g. https://mydomain.com/geoserver/wms"); }); + it('test save and delete button when saving', () => { + ReactDOM.render(, document.getElementById("container")); + let buttons = document.querySelectorAll('.form-group button'); + let saveBtn; let deleteBtn; + buttons.forEach(btn => {if (btn.textContent === 'save') saveBtn = btn;}); + buttons.forEach(btn => {if (btn.textContent === 'catalog.delete') deleteBtn = btn;}); + expect(saveBtn).toBeTruthy(); + expect(deleteBtn).toBeTruthy(); + expect(saveBtn.classList.contains("disabled")).toBeTruthy(); + expect(deleteBtn.classList.contains("disabled")).toBeTruthy(); + }); it('test saving service for COG type', () => { const actions = { onAddService: () => {} From f2645025b25573607b41cbfda5ff73024219b3b4 Mon Sep 17 00:00:00 2001 From: Suren Date: Thu, 16 Nov 2023 18:22:20 +0530 Subject: [PATCH 3/4] Added note --- web/client/api/catalog/COG.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/web/client/api/catalog/COG.js b/web/client/api/catalog/COG.js index b48773f5ef..ee603b523d 100644 --- a/web/client/api/catalog/COG.js +++ b/web/client/api/catalog/COG.js @@ -61,6 +61,8 @@ export const getProjectionFromGeoKeys = (image) => { const abortError = (reject) => reject(new DOMException("Aborted", "AbortError")); /** * fromUrl with abort fetching of data and data slices + * Note: The abort action will not cancel data fetch request but just the promise, + * because of the issue in geotiff.js https://github.com/geotiffjs/geotiff.js/issues/408 */ const fromUrl = (url, signal) => { if (signal?.aborted) { @@ -115,15 +117,17 @@ export const getRecords = (_url, startPosition, maxRecords, text, info = {}) => resolution: image.getResolution() }, // skip adding bbox when geokeys or extent is empty - ...(!isEmpty(extent) && !isEmpty(crs) && isProjectionDefined && { + ...(!isEmpty(extent) && !isEmpty(crs) && { bbox: { crs, - bounds: { - minx: extent[0], - miny: extent[1], - maxx: extent[2], - maxy: extent[3] - } + ...(isProjectionDefined && { + bounds: { + minx: extent[0], + miny: extent[1], + maxx: extent[2], + maxy: extent[3] + }} + ) } }) }; From 2ca4aa0aa028c007e5553d3c79c516885c665b03 Mon Sep 17 00:00:00 2001 From: Suren Date: Thu, 16 Nov 2023 18:23:09 +0530 Subject: [PATCH 4/4] updated note --- web/client/api/catalog/COG.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/client/api/catalog/COG.js b/web/client/api/catalog/COG.js index ee603b523d..a7de6e4814 100644 --- a/web/client/api/catalog/COG.js +++ b/web/client/api/catalog/COG.js @@ -62,7 +62,7 @@ const abortError = (reject) => reject(new DOMException("Aborted", "AbortError")) /** * fromUrl with abort fetching of data and data slices * Note: The abort action will not cancel data fetch request but just the promise, - * because of the issue in geotiff.js https://github.com/geotiffjs/geotiff.js/issues/408 + * because of the issue in https://github.com/geotiffjs/geotiff.js/issues/408 */ const fromUrl = (url, signal) => { if (signal?.aborted) {