diff --git a/README.md b/README.md index 845fb7e6375..63a0f70db33 100644 --- a/README.md +++ b/README.md @@ -131,25 +131,23 @@ We'll go through the simplest way to use the Canadian Geospatial Platform Viewer ### Using the viewer on your own project -For the moment, the released bundle of the viewer is hosted under: +For the moment, the developement bundle of the viewer is hosted under: ``` https://canadian-geospatial-platform.github.io/geoview/public/cgpv-main.js ``` -_As the viewer is still in development, this bundle will always contain the latest commits._ +_As the viewer is still in development, this bundle will always contain the latest commits. We really recommand to use one of our release on your web server_ -To use the viewer on your own project, you need to include the above script in a script tag in the header of your **HTML** file +To use the viewer on your own project, you need to include the above script in a script tag in your **HTML** file ```html - - - ... + ``` @@ -165,7 +163,7 @@ For the viewer to recognize that you are trying to render a map on the page, you It's **recommended** to pass in an **id attribute**, if an id is not passed then the viewer will auto generate an id for you. If you want to use APIs that control this map then you will need to view all created maps on the page and figure out the id of the created map. -_Tip: to view all maps on the page you can console out the maps using this function `console.log(cgpv.api.maps)_ +_Tip: to view all maps on the page you can console out the maps using this function: console.log(cgpv.api.maps)_ Below is an example of a simple map, with an id **mapOne**. This map will be using LCC projection (EPSG:3978) and will have a zoom of 4, a center of 60 latitude and -100 longtitude. The interaction of the map will be dynamic (meaning that you can move around and zoom in/out). It will use the transport, shaded with labels as the basemap. It will display an esri dynamic layer with multiple sub layers. The language of the map will be English. @@ -223,9 +221,6 @@ Full example: ```html - - -
+ +``` + ### By providing parameters in the URL You can provide a URL with search parameters for the config properties. This is useful for sharing links of certain configurations. An example link will look like @@ -77,40 +84,44 @@ An example of this: class="geoview-map" data-lang="en" data-config="{ - 'map': { - 'interaction': 'dynamic', - 'viewSettings': { - 'initialView': { - 'zoomAndCenter': [12, [45,75]] - }, - 'projection': 3978 - }, - 'basemapOptions': { - 'basemapId': 'transport', - 'shaded': true, - 'labeled': true - }, - 'listOfGeoviewLayerConfig': [ - { - 'geoviewLayerId': 'wmsLYR1', - 'geoviewLayerName': { - 'en': 'Première Nation / First Nation', - 'fr': 'Première Nation / First Nation' - }, - 'metadataAccessPath': { - 'en': 'https://services.aadnc-aandc.gc.ca/geomatics/services/Donnees_Ouvertes-Open_Data/Premiere_Nation_First_Nation/MapServer/WMSServer', - 'fr': 'https://services.aadnc-aandc.gc.ca/geomatics/services/Donnees_Ouvertes-Open_Data/Premiere_Nation_First_Nation/MapServer/WMSServer' - }, - 'geoviewLayerType': 'ogcWms', - 'listOfLayerEntryConfig': [{ 'layerId': '0' }] - } - ], - }, - 'theme': 'geo.ca', - 'components': ['north-arrow', 'overview-map'], - 'corePackages': [], - 'externalPackages': [] - }' + 'map': { + 'interaction': 'dynamic', + 'viewSettings': { + 'projection': 3978 + }, + 'basemapOptions': { + 'basemapId': 'transport', + 'shaded': false, + 'labeled': true + }, + 'listOfGeoviewLayerConfig': [{ + 'geoviewLayerId': 'wmsLYR1', + 'geoviewLayerName': { + 'en': 'earthquakes', + 'fr': 'earthquakes' + }, + 'metadataAccessPath': { + 'en': 'https://maps-cartes.services.geo.ca/server_serveur/rest/services/NRCan/earthquakes_en/MapServer/', + 'fr': 'https://maps-cartes.services.geo.ca/server_serveur/rest/services/NRCan/earthquakes_en/MapServer/' + }, + 'geoviewLayerType': 'esriDynamic', + 'listOfLayerEntryConfig': [ + { + 'layerId': '0' + } + ] + }] + }, + 'components': ['overview-map'], + 'footerBar': { + 'tabs': { + 'core': ['legend', 'layers', 'details', 'data-table'] + } + }, + 'corePackages': [], + 'theme': 'geo.ca' + } + " > ``` @@ -118,7 +129,9 @@ _Note: if you provides both **data-config** and **data-config-url**, in the same ### By providing the config object in a function call in the init function -You can also provide the same config object in a function call after the **init** function has been called +You can also provide the same config object in a function call after the **init** function has been called. The div need to exist on the map and the id pass to the function. + +_Note: the div **MUST NOT** have a **geoview-map** class or a warning will be shown_ An example of this: @@ -126,13 +139,15 @@ An example of this:
- ``` diff --git a/packages/geoview-core/package.json b/packages/geoview-core/package.json index 93c0c99331c..049bb790057 100644 --- a/packages/geoview-core/package.json +++ b/packages/geoview-core/package.json @@ -71,7 +71,7 @@ "linkifyjs": "^4.1.0", "lodash": "^4.17.21", "material-react-table": "^2.13.2", - "ol": "^9.2.4", + "ol": "^10.1.0", "ol-mapbox-style": "^12.2.1", "proj4": "^2.7.5", "prop-types": "^15.8.1", diff --git a/packages/geoview-core/public/datasets/geojson/processes-metadata.json b/packages/geoview-core/public/datasets/geojson/processes-metadata.json index f15dc2d6943..e393da01b5e 100644 --- a/packages/geoview-core/public/datasets/geojson/processes-metadata.json +++ b/packages/geoview-core/public/datasets/geojson/processes-metadata.json @@ -40,9 +40,6 @@ } } }, - "initialSettings": { - "extent": [-177.29275972275843, 34.301384132573055, -9.977229778239453, 84.400481529465] - }, "style": { "Point": { "styleType": "simple", diff --git a/packages/geoview-core/public/templates/default-config.html b/packages/geoview-core/public/templates/default-config.html index 9c076e873af..5c4c059f907 100644 --- a/packages/geoview-core/public/templates/default-config.html +++ b/packages/geoview-core/public/templates/default-config.html @@ -280,7 +280,7 @@

7. Load config from function call

'components': ['overview-map'], 'corePackages': [], 'theme': 'geo.ca' - }` + }` ); // initialize cgpv and api events, a callback is optional, used if calling api's after the rendering is ready diff --git a/packages/geoview-core/public/templates/demos-navigator.html b/packages/geoview-core/public/templates/demos-navigator.html index 9ceffc0f539..54ccd68b03f 100644 --- a/packages/geoview-core/public/templates/demos-navigator.html +++ b/packages/geoview-core/public/templates/demos-navigator.html @@ -215,14 +215,13 @@

Configurations Navigator

const id = document.createAttribute('id'); const style = document.createAttribute('style'); id.value = 'sandboxMap'; - style.value = 'height: 150px !important; width: 100px;'; newDiv.setAttributeNode(id); document.getElementById('mapSection').appendChild(newDiv); // wait until map is removed before recreating it setTimeout(() => { // create map - cgpv.api.createMapFromConfig('sandboxMap', e.target.value); + cgpv.api.createMapFromConfig('sandboxMap', e.target.value, 800); }, 1500); // fetch JSON config file to show in the text are section diff --git a/packages/geoview-core/public/templates/demos/demo-function-event.html b/packages/geoview-core/public/templates/demos/demo-function-event.html index 85d22d6f45b..90bc9a0ee7a 100644 --- a/packages/geoview-core/public/templates/demos/demo-function-event.html +++ b/packages/geoview-core/public/templates/demos/demo-function-event.html @@ -241,7 +241,7 @@

Events that will generate notifications:

}); // listen to layer visibility changed event (individual geoview layer) - cgpv.api.maps.Map1.layer.getGeoviewLayer('rcs.f4c51eaa-a6ca-48b9-a1fc-b0651da20509.en').onVisibleChanged((sender, payload) => { + cgpv.api.maps.Map1.layer.getGeoviewLayer('f4c51eaa-a6ca-48b9-a1fc-b0651da20509').onVisibleChanged((sender, payload) => { cgpv.api.maps.Map1.notifications.addNotificationSuccess(`layer ${payload.layerPath} visibility set to ${payload.visible} - individual`); }); diff --git a/packages/geoview-core/public/templates/demos/demo-geojson-inject.html b/packages/geoview-core/public/templates/demos/demo-geojson-inject.html index 6799f3f471c..29c647714d2 100644 --- a/packages/geoview-core/public/templates/demos/demo-geojson-inject.html +++ b/packages/geoview-core/public/templates/demos/demo-geojson-inject.html @@ -66,6 +66,17 @@

Empty GeoJSON Layer

'layerId': 'blank.json' } ] + }, + { + 'geoviewLayerId': 'uniqueValueId', + 'geoviewLayerName': { 'en': 'dummy layer' }, + 'metadataAccessPath': { 'en': 'https://maps-cartes.services.geo.ca/server_serveur/rest/services/NRCan/Temporal_Test_Bed_en/MapServer/' }, + 'geoviewLayerType': 'esriFeature', + 'listOfLayerEntryConfig': [ + { + 'layerId': '0' + } + ] } ] }, @@ -169,7 +180,7 @@

API Functions:

var addPoints2Button = document.getElementById('Add-points2'); const points2 = - '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"data":"92;43.54;12.3;66;75","label":"Red;Green;Blue;Yellow;Orange","creationDate":"14/01/2020T12:00:00-05:00"},"geometry":{"type":"Point","coordinates":[-141,52]}},{"type":"Feature","properties":{"data":"92;43.54;12.3;66;75","label":"Red;Green;Blue;Yellow;Orange","creationDate":"16/02/2020T12:00:00-05:00"},"geometry":{"type":"Point","coordinates":[-141,83]}},{"type":"Feature","properties":{"data":"553;54;32;5.43;55","label":"Red;Green;Blue;Yellow;Orange;","creationDate":"19/08/2022T14:00:00-05:00"},"geometry":{"type":"Point","coordinates":[-53,83]}},{"type":"Feature","properties":{"data":"255;255;255;5.43;55","label":"Red;Green;Blue;Yellow;Orange;","creationDate":"30/10/2022T12:00:00-05:00"},"geometry":{"type":"Point","coordinates":[-53,49]}},{"type":"Feature","properties":{"Red":92,"Green":"1 Start, this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text. END","Blue":"2 Start, this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text. END","Yellow":"3 Start, this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text. END","Orange":75,"creationDate":"15/02/2022T05:00:00-05:00"},"geometry":{"type":"Point","coordinates":[-100,57]}},{"type":"Feature","properties":{"Red":553,"Green":54,"Blue":32.3,"Yellow":5.43,"Orange":55,"creationDate":"07/01/2020T12:00:00-05:00"},"geometry":{"type":"Point","coordinates":[-113,55]}},{"type":"Feature","properties":{"Red":255,"Green":255,"Blue":32.3,"Yellow":5.43,"Orange":55,"creationDate":"15/02/2019T17:00:00-05:00"},"geometry":{"type":"Point","coordinates":[-103,54]}}]}'; + '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"data":"92;43.54;12.3;66;75","label":"Red;Green;Blue;Yellow;Orange","creationDate":"14/01/2020T12:00:00-05:00"},"geometry":{"type":"Point","coordinates":[-141,52]}},{"type":"Feature","properties":{"data":"92;43.54;12.3;66;75","label":"Red;Green;Blue;Yellow;Orange","creationDate":"16/02/2020T12:00:00-05:00"},"geometry":{"type":"Point","coordinates":[-141,83]}},{"type":"Feature","properties":{"data":"553;54;32;5.43;55","label":"Red;Green;Blue;Yellow;Orange;","creationDate":"19/08/2022T14:00:00-05:00"},"geometry":{"type":"Point","coordinates":[-53,83]}},{"type":"Feature","properties":{"data":"255;255;255;5.43;55","label":"Red;Green;Blue;Yellow;Orange;","creationDate":"30/10/2022T12:00:00-05:00"},"geometry":{"type":"Point","coordinates":[-53,49]}},{"type":"Feature","properties":{"Red":92,"Green":"1 Start, this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text. END","Blue":"2 Start, this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text. END","Yellow":"3 Start, this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text.this is very long text. END","Orange":75,"creationDate":"15/02/2022T05:00:00-05:00"},"geometry":{"type":"Point","coordinates":[-100,57]}},{"type":"Feature","properties":{"Red":553,"Green":54,"Blue":32.3,"Yellow":5.43,"Orange":55,"creationDate":"07/01/2020T12:00:00-05:00"},"geometry":{"type":"Point","coordinates":[-113,55]}},{"type":"Feature","properties":{"Red":255,"Green":255,"Blue":32.3,"Yellow":5.43,"Orange":55,"creationDate":"15/02/2019T17:00:00-05:00"},"geometry":{"type":"Point","coordinates":[-72.33, 46.33]}}]}'; // add an event listener when a button is clicked addPoints2Button.addEventListener('click', () => { diff --git a/packages/geoview-core/public/templates/sandbox.html b/packages/geoview-core/public/templates/sandbox.html index 73ad317ed38..c1a7c5657f1 100644 --- a/packages/geoview-core/public/templates/sandbox.html +++ b/packages/geoview-core/public/templates/sandbox.html @@ -363,7 +363,7 @@

Sanbox Map

}, 'corePackages': [], 'theme': 'geo.ca' - }` + }` ); // initialize cgpv and api events, a callback is optional, used if calling api's after the rendering is ready diff --git a/packages/geoview-core/src/api/api.ts b/packages/geoview-core/src/api/api.ts index 304a65cab38..c4eaee47331 100644 --- a/packages/geoview-core/src/api/api.ts +++ b/packages/geoview-core/src/api/api.ts @@ -103,12 +103,14 @@ export class API { * * @param {string} divId - id of the div to create map in * @param {string} mapConfig - config passed in from the function call (string or url of a config path) + * @param {number} divHeight - height of the div to inject the map in (mandatory if the map reloads) */ // This function is called by the template, and since the template use the instance of the object from cgpv.api, this function has to be on the instance, not static. Refactor this? // eslint-disable-next-line @typescript-eslint/class-methods-use-this - async createMapFromConfig(divId: string, mapConfig: string): Promise { + async createMapFromConfig(divId: string, mapConfig: string, divHeight?: number): Promise { // Get the map div const mapDiv = document.getElementById(divId); + if (divHeight) mapDiv!.style.height = `${divHeight}px`; // If found the map div if (mapDiv) { diff --git a/packages/geoview-core/src/api/event-processors/event-processor-children/feature-info-event-processor.ts b/packages/geoview-core/src/api/event-processors/event-processor-children/feature-info-event-processor.ts index 63be5f9b2ee..422fa53bbdf 100644 --- a/packages/geoview-core/src/api/event-processors/event-processor-children/feature-info-event-processor.ts +++ b/packages/geoview-core/src/api/event-processors/event-processor-children/feature-info-event-processor.ts @@ -6,6 +6,7 @@ import { UIEventProcessor } from './ui-event-processor'; import { TypeResultSetEntry } from '@/geo/map/map-schema-types'; import { IFeatureInfoState, TypeFeatureInfoResultSetEntry } from '@/core/stores/store-interface-and-intial-values/feature-info-state'; import { GeoviewStoreType } from '@/core/stores/geoview-store'; +import { MapEventProcessor } from './map-event-processor'; // GV Important: See notes in header of MapEventProcessor file for information on the paradigm to apply when working with UIEventProcessor vs UIState @@ -67,7 +68,38 @@ export class FeatureInfoEventProcessor extends AbstractEventProcessor { } /** - * Deletes the specified layer path from the layer sets in the store.The update of the array will also trigger an update in a batched manner. + * Get the selectedLayerPath value + * @param {string} mapId - The map identifier + * @returns {string}} the selected layer path + */ + static getSelectedLayerPath(mapId: string): string { + return this.getFeatureInfoState(mapId).selectedLayerPath; + } + + /** + * Deletes the feature from a resultSet for a specific layerPath. At the same time it check for + * removing the higlight and the click marker if selected layer path is the reset path + * @param {string} mapId - The map identifier + * @param {string} layerPath - The layer path to delete features from resultSet + */ + static resetResultSet(mapId: string, layerPath: string): void { + const { resultSet } = MapEventProcessor.getMapViewerLayerAPI(mapId).featureInfoLayerSet; + if (resultSet[layerPath]) { + resultSet[layerPath].features = []; + this.propagateFeatureInfoToStore(mapId, 'click', resultSet[layerPath]).catch((err) => + logger.logError('Not able to reset resultSet', err, layerPath) + ); + } + + // Remove highlighted features and marker if it is the selected layer path + if (FeatureInfoEventProcessor.getSelectedLayerPath(mapId) === layerPath) { + MapEventProcessor.removeHighlightedFeature(mapId, 'all'); + MapEventProcessor.clickMarkerIconHide(mapId); + } + } + + /** + * Deletes the specified layer path from the layer sets in the store. The update of the array will also trigger an update in a batched manner. * @param {string} mapId - The map identifier * @param {string} layerPath - The layer path to delete * @returns {Promise} diff --git a/packages/geoview-core/src/app.tsx b/packages/geoview-core/src/app.tsx index 30d9bbe349a..6f333361c2b 100644 --- a/packages/geoview-core/src/app.tsx +++ b/packages/geoview-core/src/app.tsx @@ -190,7 +190,7 @@ export async function initMapDivFromFunctionCall(mapDiv: HTMLElement, mapConfig: mapDiv.classList.add('geoview-map'); // Add a compatibility flag on the div so that when a map is loaded via function call, it's subsequently ignored in eventual init() calls. - // This is useful in case that a html first calls for example `cgpv.api.createMapFromConfig('LNG1', config);` and then + // This is useful in case that a html first calls for example `cgpv.api.createMapFromConfig('LNG1', config, divHeight);` and then // calls `cgpv.init()` (let's say for other maps on the page), the map LNG1 isn't being initialized twice. // Remember that init() grabs all maps with geoview-map class and we just added that class manually above, so we need that flag. mapDiv.classList.add('geoview-map-func-call'); diff --git a/packages/geoview-core/src/core/components/data-table/data-panel.tsx b/packages/geoview-core/src/core/components/data-table/data-panel.tsx index 5a0da5d20d5..782744a2294 100644 --- a/packages/geoview-core/src/core/components/data-table/data-panel.tsx +++ b/packages/geoview-core/src/core/components/data-table/data-panel.tsx @@ -263,7 +263,7 @@ export function Datapanel({ fullWidth = false, containerType = CONTAINER_TYPE.FO layerFeatures: getFeaturesOfLayer(layer.layerPath), tooltip: getLayerTooltip(layer.layerName ?? '', layer.layerPath), mapFilteredIcon: isMapFilteredSelectedForLayer(layer.layerPath) && ( - + ), })); // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/packages/geoview-core/src/core/components/data-table/filter-map.tsx b/packages/geoview-core/src/core/components/data-table/filter-map.tsx index 1fac1be1b2c..bfaff0e1aa3 100644 --- a/packages/geoview-core/src/core/components/data-table/filter-map.tsx +++ b/packages/geoview-core/src/core/components/data-table/filter-map.tsx @@ -32,7 +32,7 @@ function FilterMap({ layerPath, isGlobalFilterOn }: FilterMapProps): JSX.Element const { t } = useTranslation(); return ( - + setMapFilteredEntry(!datatableSettings[layerPath].mapFilteredRecord ?? true, layerPath)} diff --git a/packages/geoview-core/src/core/components/details/feature-detail-modal.tsx b/packages/geoview-core/src/core/components/details/feature-detail-modal.tsx index 388e86d166b..17851e1e00f 100644 --- a/packages/geoview-core/src/core/components/details/feature-detail-modal.tsx +++ b/packages/geoview-core/src/core/components/details/feature-detail-modal.tsx @@ -1,4 +1,4 @@ -import { useMemo } from 'react'; +import { useMemo, useState } from 'react'; import { useTheme } from '@mui/material/styles'; import { useTranslation } from 'react-i18next'; import { useDataTableSelectedFeature } from '@/core/stores/store-interface-and-intial-values/data-table-state'; @@ -27,6 +27,7 @@ export default function FeatureDetailModal(): JSX.Element { const { disableFocusTrap } = useUIStoreActions(); const activeModalId = useUIActiveFocusItem().activeElementId; const feature = useDataTableSelectedFeature()!; + const [nameFieldValue, setNameFieldValue] = useState(''); /** * Build features list to displayed in table @@ -35,6 +36,10 @@ export default function FeatureDetailModal(): JSX.Element { // Log logger.logTraceUseMemo('DETAILS PANEL - Feature Detail Modal - featureInfoList'); + // Extract value of field info nameField for item symbol description + const nameFieldValueTmp = feature.fieldInfo[feature.nameField !== null ? feature.nameField : 0]; + setNameFieldValue(nameFieldValueTmp !== undefined ? (nameFieldValueTmp.value as string) : ''); + return Object.keys(feature?.fieldInfo ?? {}).map((fieldName) => { return { fieldKey: feature.fieldInfo[fieldName]!.fieldKey, @@ -59,7 +64,7 @@ export default function FeatureDetailModal(): JSX.Element { - {feature.nameField} + {nameFieldValue} diff --git a/packages/geoview-core/src/core/components/lightbox/lightbox.tsx b/packages/geoview-core/src/core/components/lightbox/lightbox.tsx index 4bfed1cbd6b..bb54aff0bc9 100644 --- a/packages/geoview-core/src/core/components/lightbox/lightbox.tsx +++ b/packages/geoview-core/src/core/components/lightbox/lightbox.tsx @@ -68,10 +68,10 @@ export function LightboxImg(props: LightboxProps): JSX.Element { controller={{ closeOnPullDown, closeOnBackdropClick }} animation={{ fade, swipe }} labels={{ - Next: t('lightbox.next') as string, - Previous: t('lightbox.previous') as string, - Close: t('lightbox.close') as string, - Download: t('lightbox.download') as string, + Next: t('lightbox.next') || undefined, + Previous: t('lightbox.previous') || undefined, + Close: t('lightbox.close') || undefined, + Download: t('lightbox.download') || undefined, }} on={{ entered: () => { diff --git a/packages/geoview-core/src/core/containers/shell.tsx b/packages/geoview-core/src/core/containers/shell.tsx index 0b76d00027a..65289912c13 100644 --- a/packages/geoview-core/src/core/containers/shell.tsx +++ b/packages/geoview-core/src/core/containers/shell.tsx @@ -198,12 +198,15 @@ export function Shell(props: ShellProps): JSX.Element { * Set the map height based on mapDiv */ useEffect(() => { + // Log + logger.logTraceUseEffect('SHELL - setOrigHeight geoviewElement', geoviewElement); + if (mapContainerRef.current && mapShellContainerRef.current) { // NOTE: grab height from data attribute of parent div, if not present then grab client height const height = geoviewElement!.dataset?.height ?? `${geoviewElement!.clientHeight}px`; setOrigHeight(height); } - }, [mapLoaded, mapId, geoviewElement]); + }, [geoviewElement]); /** * Update map height when switch on/off the fullscreen diff --git a/packages/geoview-core/src/core/utils/config/reader/uuid-config-reader.ts b/packages/geoview-core/src/core/utils/config/reader/uuid-config-reader.ts index d9c5911d2ba..d568a516f51 100644 --- a/packages/geoview-core/src/core/utils/config/reader/uuid-config-reader.ts +++ b/packages/geoview-core/src/core/utils/config/reader/uuid-config-reader.ts @@ -89,7 +89,7 @@ export class UUIDmapConfigReader { if (layerType === CONST_LAYER_TYPES.ESRI_DYNAMIC && !isFeature) { const geoviewLayerConfig: TypeEsriDynamicLayerConfig = { - geoviewLayerId: `${id}`, + geoviewLayerId: `${(id as string).split('.')[1]}`, // Remove rcs. and .[lang] from geocore response geoviewLayerName: createLocalizedString(name), metadataAccessPath: createLocalizedString(url), geoviewLayerType: CONST_LAYER_TYPES.ESRI_DYNAMIC, diff --git a/packages/geoview-core/src/core/utils/date-mgt.ts b/packages/geoview-core/src/core/utils/date-mgt.ts index b5d7e9c3d55..e1f4426342c 100644 --- a/packages/geoview-core/src/core/utils/date-mgt.ts +++ b/packages/geoview-core/src/core/utils/date-mgt.ts @@ -408,17 +408,36 @@ export abstract class DateMgt { const min: string = endsWithZ ? `${dayjs(date1).utc(false).format(format).slice(0, -6)}Z` : dayjs(date1).utc(false).format(format); const max: string = endsWithZ ? `${dayjs(date2).utc(false).format(format).slice(0, -6)}Z` : dayjs(date2).utc(false).format(format); - // create intervalle items + // create interval items const msDuration: number = dayjs.duration(durationCheck).asMilliseconds(); const calcDuration = dayjs.duration(msDuration); const items: string[] = []; let i = 0; items.push(min); + + let nextDate: string; do { - let nextDate: string = dayjs(items[i]).add(calcDuration).utc(false).format(format); + // When we deal with MONTH duration, dayjs doesnt know if the month is 30 or 31 days. This creates bad intervals... + // NOTE: We do this ONLY when duration is for month and nothing else.... + if (durationCheck.endsWith('M')) { + // Add the month duration manually and increase years if needed + const dateValue = items[i].split('-'); + const monthValueUpdated = Number(dateValue[1]) + calcDuration.months(); + const yearValue = monthValueUpdated <= 12 ? dateValue[0] : String(Number(dateValue[0]) + 1); + const monthValue = monthValueUpdated <= 12 ? monthValueUpdated : monthValueUpdated - 12; + + nextDate = dayjs(`${yearValue}-${String(monthValue).padStart(2, '0')}-${dateValue[2]}`) + .utc(false) + .format(format); + } else { + nextDate = dayjs(items[i]).add(calcDuration).utc(false).format(format); + } + + // Check if we need to remove time information then push if (endsWithZ) nextDate = `${nextDate.slice(0, -6)}Z`; items.push(nextDate); + // Apply a correction if it is a leap year if (msDuration === 31536000000 && items[i].slice(4, 10) !== items[i + 1].slice(4, 10)) { nextDate = dayjs(items[i]) @@ -428,11 +447,12 @@ export abstract class DateMgt { if (endsWithZ) nextDate = `${nextDate.slice(0, -6)}Z`; items[i + 1] = nextDate; } + i++; } while (dayjs(items[items.length - 1]).isBefore(max)); - // add last item - items.push(max); + // add last item if needed + if (items[items.length - 1] !== max) items.push(max); return items; } diff --git a/packages/geoview-core/src/geo/layer/geometry/geometry.ts b/packages/geoview-core/src/geo/layer/geometry/geometry.ts index 0390e5c48ab..876d61941bb 100644 --- a/packages/geoview-core/src/geo/layer/geometry/geometry.ts +++ b/packages/geoview-core/src/geo/layer/geometry/geometry.ts @@ -1,5 +1,5 @@ import VectorLayer from 'ol/layer/Vector'; -import Feature, { FeatureLike } from 'ol/Feature'; +import Feature from 'ol/Feature'; import VectorSource, { Options as VectorSourceOptions } from 'ol/source/Vector'; import { Geometry as OLGeometry, Circle, LineString, Point, Polygon } from 'ol/geom'; import { Coordinate } from 'ol/coordinate'; @@ -22,7 +22,7 @@ import { TypeFeatureCircleStyle, TypeFeatureStyle, TypeIconStyle } from './geome */ interface FeatureCollection { geometryGroupId: string; - vectorLayer: VectorLayer; + vectorLayer: VectorLayer; vectorSource: VectorSource; } @@ -428,7 +428,7 @@ export class GeometryApi { createGeometryGroup( geometryGroupId: string, options?: { - vectorLayerOptions?: VectorLayerOptions>; + vectorLayerOptions?: VectorLayerOptions; vectorSourceOptions?: VectorSourceOptions; } ): FeatureCollection { @@ -438,7 +438,7 @@ export class GeometryApi { if (!geometryGroup) { const vectorSource = new VectorSource(geometryGroupOptions.vectorSourceOptions); - const vectorLayer = new VectorLayer({ + const vectorLayer = new VectorLayer({ ...geometryGroupOptions.vectorLayerOptions, source: vectorSource, }); @@ -514,7 +514,7 @@ export class GeometryApi { for (let i = 0; i < this.geometryGroups.length; i++) { const geometries = this.geometryGroups[i].vectorLayer.getSource()?.getFeatures() || []; for (let j = 0; j < geometries.length; j++) { - const geometry = geometries[j]; + const geometry = geometries[j] as Feature; if (geometry.get('featureId') === featureId) returnValue.push(this.geometryGroups[i]); } @@ -567,7 +567,7 @@ export class GeometryApi { } try { - geometryGroup.vectorLayer.getSource()?.addFeature(geometry); + geometryGroup.vectorLayer.getSource()?.addFeature(geometry as never); geometryGroup.vectorLayer.changed(); } catch (error) { logger.logError(`Error adding geometry to group ${geometryGroupId}`, error); @@ -587,7 +587,7 @@ export class GeometryApi { ?.getFeatures() .forEach((layerGeometry) => { if (geometry === layerGeometry) { - this.geometryGroups[i].vectorLayer.getSource()?.removeFeature(geometry); + this.geometryGroups[i].vectorLayer.getSource()?.removeFeature(geometry as never); } }); this.geometryGroups[i].vectorLayer.changed(); @@ -609,7 +609,7 @@ export class GeometryApi { ?.getFeatures() .forEach((layerGeometry) => { if (geometry === layerGeometry) { - geometryGroup.vectorLayer.getSource()?.removeFeature(geometry); + geometryGroup.vectorLayer.getSource()?.removeFeature(geometry as never); } }); geometryGroup.vectorLayer.changed(); diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/raster/esri-dynamic.ts b/packages/geoview-core/src/geo/layer/geoview-layers/raster/esri-dynamic.ts index 497907bd850..3415aecc9ae 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/raster/esri-dynamic.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/raster/esri-dynamic.ts @@ -944,14 +944,12 @@ export class EsriDynamic extends AbstractGeoViewRaster { if (combineLegendFilter) filterValueToUse = this.getViewFilter(layerPath); // Convert date constants using the externalFragmentsOrder derived from the externalDateFormat - // GV this regex is different then the other layers because if not we have this error: The source image cannot be decoded. // TODO: Standardize the regex across all layer types const searchDateEntry = [ - ...filterValueToUse.matchAll( - /(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/gi + ...`${filterValueToUse?.replaceAll(/\s{2,}/g, ' ').trim()} `.matchAll( + /(?<=^date\b\s')[\d/\-T\s:+Z]{4,25}(?=')|(?<=[(\s]date\b\s')[\d/\-T\s:+Z]{4,25}(?=')/gi ), ]; - searchDateEntry.reverse(); searchDateEntry.forEach((dateFound) => { // If the date has a time zone, keep it as is, otherwise reverse its time zone by changing its sign diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/raster/vector-tiles.ts b/packages/geoview-core/src/geo/layer/geoview-layers/raster/vector-tiles.ts index dc4e5a47d78..d27d0d78e69 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/raster/vector-tiles.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/raster/vector-tiles.ts @@ -1,5 +1,3 @@ -import View from 'ol/View'; -import Map from 'ol/Map'; import BaseLayer from 'ol/layer/Base'; import TileLayer from 'ol/layer/Tile'; import VectorTileLayer from 'ol/layer/VectorTile'; @@ -8,7 +6,7 @@ import VectorTileSource, { Options as SourceOptions } from 'ol/source/VectorTile import TileGrid, { Options as TileGridOptions } from 'ol/tilegrid/TileGrid'; import { Extent } from 'ol/extent'; -import olms, { applyStyle } from 'ol-mapbox-style'; +import { applyStyle } from 'ol-mapbox-style'; import Feature from 'ol/Feature'; import { MVT } from 'ol/format'; @@ -218,10 +216,10 @@ export class VectorTiles extends AbstractGeoViewRaster { const requestResult = this.emitLayerRequesting({ config: layerConfig, source }); // If any response - let olLayer: VectorTileLayer | undefined; + let olLayer: VectorTileLayer | undefined; if (requestResult.length > 0) { // Get the OpenLayer that was created - olLayer = requestResult[0] as VectorTileLayer; + olLayer = requestResult[0] as VectorTileLayer; } // If no olLayer was obtained @@ -325,26 +323,6 @@ export class VectorTiles extends AbstractGeoViewRaster { return sourceExtent; } - // TODO: This section needs documentation (a header at least). Also, is it normal to have things hardcoded like that? - // GV Layers Refactoring - Obsolete (just should be removed?) - static async addVectorTileLayer(): Promise { - // GV from code sandbox https://codesandbox.io/s/vector-tile-info-forked-g28jud?file=/main.js it works good - // GV from inside GeoView, even when not use, something is wrong. - const map = await olms( - 'LYR3', - 'https://tiles.arcgis.com/tiles/HsjBaDykC1mjhXz9/arcgis/rest/services/CBMT3978_v11/VectorTileServer/resources/styles/root.json?f=json' - ); - - // Configure the map with a view with EPSG:3978 projection - (map as Map).setView( - new View({ - projection: 'EPSG:3857', - center: [(-2750565.340500001 + -936703.1849000007) / 2, (3583872.5053000003 + 4659267.001500003) / 2], - zoom: 5, - }) - ); - } - /** * Set Vector Tile style * @@ -355,6 +333,6 @@ export class VectorTiles extends AbstractGeoViewRaster { // GV Layers Refactoring - Obsolete (just should be removed?) setVectorTileStyle(layerPath: string, styleUrl: string): Promise { // FIXME: Check if this should be removed or done somewhere else? - return applyStyle(this.getMapViewer().layer.getOLLayer(layerPath) as VectorTileLayer, styleUrl); + return applyStyle(this.getMapViewer().layer.getOLLayer(layerPath) as VectorTileLayer, styleUrl); } } diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/vector/abstract-geoview-vector.ts b/packages/geoview-core/src/geo/layer/geoview-layers/vector/abstract-geoview-vector.ts index bf162929477..f79989b409f 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/vector/abstract-geoview-vector.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/vector/abstract-geoview-vector.ts @@ -363,7 +363,7 @@ export abstract class AbstractGeoViewVector extends AbstractGeoViewLayer { if (!olLayer) { // We're working in old LAYERS_HYBRID_MODE (in the new mode the code below is handled in the new classes) // Create the vector layer options. - const layerOptions: VectorLayerOptions = { + const layerOptions: VectorLayerOptions = { properties: { layerConfig }, source: vectorSource, style: (feature) => { diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/vector/geojson.ts b/packages/geoview-core/src/geo/layer/geoview-layers/vector/geojson.ts index 4043b7b5c1d..c85e929a067 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/vector/geojson.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/vector/geojson.ts @@ -32,7 +32,7 @@ import { AppEventProcessor } from '@/api/event-processors/event-processor-childr import { Projection } from '@/geo/utils/projection'; import { LegendEventProcessor } from '@/api/event-processors/event-processor-children/legend-event-processor'; import { DataTableEventProcessor } from '@/api/event-processors/event-processor-children/data-table-event-processor'; -import { MapEventProcessor } from '@/api/event-processors/event-processor-children/map-event-processor'; +import { FeatureInfoEventProcessor } from '@/api/event-processors/event-processor-children/feature-info-event-processor'; export interface TypeSourceGeoJSONInitialConfig extends Omit { format: 'GeoJSON'; @@ -257,7 +257,7 @@ export class GeoJSON extends AbstractGeoViewVector { featureProjection: this.getMapViewer().getProjection(), }); - const olLayer = this.getOLLayer(layerPath) as VectorLayer; + const olLayer = this.getOLLayer(layerPath) as VectorLayer>; if (olLayer && features.length) { // Remove current features and add new ones @@ -265,14 +265,16 @@ export class GeoJSON extends AbstractGeoViewVector { olLayer!.getSource()?.addFeatures(features); olLayer.changed(); + // TODO: This is coupled with the processor. Maybe we should have a processor event to trigger this and + // TODO.CONT: keep this functio not tie with UI. // Update the bounds in the store const bounds = this.getBounds(layerPath); if (bounds) { LegendEventProcessor.setLayerBounds(this.mapId, layerPath, bounds); } - // Remove highlighted features - MapEventProcessor.removeHighlightedFeature(this.mapId, 'all'); + // Reset the feature info result set + FeatureInfoEventProcessor.resetResultSet(this.mapId, layerPath); // Update feature info DataTableEventProcessor.triggerGetAllFeatureInfo(this.mapId, layerPath).catch((error) => { diff --git a/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-esri-dynamic.ts b/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-esri-dynamic.ts index 6fae0261236..b0550275de4 100644 --- a/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-esri-dynamic.ts +++ b/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-esri-dynamic.ts @@ -692,11 +692,10 @@ export class GVEsriDynamic extends AbstractGVRaster { if (combineLegendFilter) filterValueToUse = this.getViewFilter(); // Convert date constants using the externalFragmentsOrder derived from the externalDateFormat - // GV this regex is different then the other layers because if not we have this error: The source image cannot be decoded. // TODO: Standardize the regex across all layer types const searchDateEntry = [ - ...filterValueToUse.matchAll( - /(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/gi + ...`${filterValueToUse?.replaceAll(/\s{2,}/g, ' ').trim()} `.matchAll( + /(?<=^date\b\s')[\d/\-T\s:+Z]{4,25}(?=')|(?<=[(\s]date\b\s')[\d/\-T\s:+Z]{4,25}(?=')/gi ), ]; searchDateEntry.reverse(); diff --git a/packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector-tile.ts b/packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector-tile.ts index 8b544422586..5b929db5c17 100644 --- a/packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector-tile.ts +++ b/packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector-tile.ts @@ -1,6 +1,5 @@ import VectorTile from 'ol/source/VectorTile'; import VectorTileLayer from 'ol/layer/VectorTile'; -import Feature from 'ol/Feature'; import { Extent } from 'ol/extent'; import { AbstractGVLayer } from '../abstract-gv-layer'; @@ -16,11 +15,11 @@ export abstract class AbstractGVVectorTile extends AbstractGVLayer { */ // Disabling 'any', because too many renderer types in OpenLayers // eslint-disable-next-line @typescript-eslint/no-explicit-any - override getOLLayer(): VectorTileLayer { + override getOLLayer(): VectorTileLayer { // Call parent and cast // Disabling 'any', because too many renderer types in OpenLayers // eslint-disable-next-line @typescript-eslint/no-explicit-any - return super.getOLLayer() as VectorTileLayer; + return super.getOLLayer() as VectorTileLayer; } /** diff --git a/packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector.ts b/packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector.ts index 5bf5d75ef4e..e5c94cee2c2 100644 --- a/packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector.ts +++ b/packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector.ts @@ -43,7 +43,7 @@ export abstract class AbstractGVVector extends AbstractGVLayer { const label = getLocalizedValue(layerConfig.layerName, language) || layerConfig.layerId; // Create the vector layer options. - const layerOptions: VectorLayerOptions = { + const layerOptions: VectorLayerOptions = { properties: { layerConfig }, source: olSource, style: (feature) => { @@ -71,11 +71,11 @@ export abstract class AbstractGVVector extends AbstractGVLayer { */ // Disabling 'any', because too many renderer types in OpenLayers // eslint-disable-next-line @typescript-eslint/no-explicit-any - override getOLLayer(): VectorLayer { + override getOLLayer(): VectorLayer { // Call parent and cast // Disabling 'any', because too many renderer types in OpenLayers // eslint-disable-next-line @typescript-eslint/no-explicit-any - return super.getOLLayer() as VectorLayer; + return super.getOLLayer() as VectorLayer; } /** @@ -272,8 +272,8 @@ export abstract class AbstractGVVector extends AbstractGVLayer { // Determine max extent from features let calculatedExtent: Extent | undefined; requestedFeatures.forEach((feature) => { - if (feature?.getGeometry()) { - const extent = feature.getGeometry()?.getExtent(); + if ((feature as unknown as Feature)?.getGeometry()) { + const extent = (feature as unknown as Feature).getGeometry()?.getExtent(); if (extent) { // If calculatedExtent has not been defined, set it to extent if (!calculatedExtent) calculatedExtent = extent; @@ -296,7 +296,7 @@ export abstract class AbstractGVVector extends AbstractGVLayer { const mapProjection: ProjectionLike = this.getMapViewer().getProjection().getCode(); const format = new FormatGeoJSON(); - const geoJsonStr = format.writeFeatures((this.getOLLayer() as VectorLayer).getSource()!.getFeatures(), { + const geoJsonStr = format.writeFeatures((this.getOLLayer() as VectorLayer).getSource()!.getFeatures(), { dataProjection: 'EPSG:4326', // Output projection, featureProjection: mapProjection, }); diff --git a/packages/geoview-core/src/geo/map/feature-highlight.ts b/packages/geoview-core/src/geo/map/feature-highlight.ts index 9a581753dbf..0f97878fbb9 100644 --- a/packages/geoview-core/src/geo/map/feature-highlight.ts +++ b/packages/geoview-core/src/geo/map/feature-highlight.ts @@ -27,7 +27,7 @@ export class FeatureHighlight { /** The hidden layer to display animations. */ // GV It's public, to save an eslint warning, because even if it's not read in this class, it's actually important to instanciate per OpenLayer design. - overlayLayer: VectorLayer; + overlayLayer: VectorLayer; // Used to access point markers pointMarkers: PointMarkers; diff --git a/packages/geoview-core/src/geo/map/map-viewer.ts b/packages/geoview-core/src/geo/map/map-viewer.ts index cab4950ccf6..2ebdb3550f6 100644 --- a/packages/geoview-core/src/geo/map/map-viewer.ts +++ b/packages/geoview-core/src/geo/map/map-viewer.ts @@ -608,6 +608,7 @@ export class MapViewer { // Zoom to extent provided in config, it present if (this.mapFeaturesConfig.map.viewSettings.initialView?.extent) + // TODO: Timeout allows for map height to be set before zoom happens, so padding is applied properly setTimeout( // eslint-disable-next-line @typescript-eslint/no-misused-promises () => @@ -629,23 +630,25 @@ export class MapViewer { ? this.mapFeaturesConfig.map.viewSettings.initialView.layerIds : this.layer.getGeoviewLayerIds(); - let layerExtents = this.layer.getExtentOfMultipleLayers(layerIdsToZoomTo); - - // If extents have infinity, use default instead - if (layerExtents.includes(Infinity)) - layerExtents = this.convertExtentLngLatToMapProj(CV_MAP_EXTENTS[this.mapFeaturesConfig.map.viewSettings.projection]); - - // Zoom to calculated extent - if (layerExtents.length) - // TODO: Timeout allows for map height to be set before zoom happens, so padding is applied properly - setTimeout( - // eslint-disable-next-line @typescript-eslint/no-misused-promises - () => - this.zoomToExtent(layerExtents).catch((error) => - logger.logPromiseFailed('promiseMapLayers in #checkMapLayersProcessed in map-viewer', error) - ), - 200 - ); + this.onMapLayersLoaded(() => { + let layerExtents = this.layer.getExtentOfMultipleLayers(layerIdsToZoomTo); + + // If extents have infinity, use default instead + if (layerExtents.includes(Infinity)) + layerExtents = this.convertExtentLngLatToMapProj(CV_MAP_EXTENTS[this.mapFeaturesConfig.map.viewSettings.projection]); + + // Zoom to calculated extent + if (layerExtents.length) + // TODO: Timeout allows for map height to be set before zoom happens, so padding is applied properly + setTimeout( + // eslint-disable-next-line @typescript-eslint/no-misused-promises + () => + this.zoomToExtent(layerExtents).catch((error) => + logger.logPromiseFailed('promiseMapLayers in #checkMapLayersProcessed in map-viewer', error) + ), + 200 + ); + }); } } @@ -1203,11 +1206,17 @@ export class MapViewer { // If no config is provided, get the original from the store const config = mapConfig || MapEventProcessor.getGeoViewMapConfig(this.mapId); + // Get map height + // GV: This is important because on reload, the mapHeight is set to 0px then reset to a bad value. + // GV.CONT: This fix maintain the height on reload for the createMapFromConfig function. On first past the optional + // GV.CONT: does not have to be provided because the div exist and map will take its height. + const height = this.map.getSize() !== undefined ? this.map.getSize()![1] : 800; + // Remove the map const mapDiv = await this.remove(false); // TODO: There is still as problem with bad config schema value and layers loading... should be refactor when config is done - api.createMapFromConfig(mapDiv.id, JSON.stringify(config)).catch((error) => { + api.createMapFromConfig(mapDiv.id, JSON.stringify(config), height).catch((error) => { // Log logger.logError(`Couldn't reload the map in map-viewer`, error); }); diff --git a/packages/geoview-swiper/package.json b/packages/geoview-swiper/package.json index f45ef0e6372..283291bf3e3 100644 --- a/packages/geoview-swiper/package.json +++ b/packages/geoview-swiper/package.json @@ -12,7 +12,7 @@ "dependencies": { "geoview-core": "workspace:~1.0.0", "lodash": "^4.17.21", - "ol": "^9.2.4", + "ol": "^10.1.0", "react-draggable": "^4.4.5" }, "devDependencies": { @@ -39,4 +39,4 @@ "resolutions": { "@types/react": "^18.2.0" } -} +} \ No newline at end of file