From 654878eb6546dea789fe0b4e2036eac0b00f2d49 Mon Sep 17 00:00:00 2001 From: Alex <94073946+Alex-NRCan@users.noreply.github.com> Date: Fri, 13 Dec 2024 13:50:17 -0500 Subject: [PATCH] Fixing extent densification (#2659) Fixing the reading of WMS extents from a GetCapabilities call Added support for projection EPSG:4269 and 102100 Tweaking Drop-Down-Button styling --- .../src/geo/layer/gv-layers/raster/gv-wms.ts | 31 +++--- .../geoview-core/src/geo/map/map-viewer.ts | 13 ++- .../geoview-core/src/geo/utils/projection.ts | 96 ++++++++++++++----- .../button-drop-down-style.ts | 3 +- 4 files changed, 97 insertions(+), 46 deletions(-) diff --git a/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-wms.ts b/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-wms.ts index 12fd85c0bb8..d482cf3f506 100644 --- a/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-wms.ts +++ b/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-wms.ts @@ -565,9 +565,6 @@ export class GVWMS extends AbstractGVRaster { // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done const layerConfig = this.getLayerConfig(); - // Get the source projection - const sourceProjection = this.getOLSource().getProjection() || undefined; - // Get the layer config bounds let layerConfigBounds = layerConfig?.initialSettings?.bounds; @@ -577,8 +574,8 @@ export class GVWMS extends AbstractGVRaster { layerConfigBounds = this.getMapViewer().convertExtentFromProjToMapProj(layerConfigBounds, 'EPSG:4326'); } - // Get the layer bounds from metadata - const metadataExtent = this.#getBoundsExtentFromMetadata(sourceProjection?.getCode() || ''); + // Get the layer bounds from metadata, favoring a bounds in the same project as the map + const metadataExtent = this.#getBoundsExtentFromMetadata(this.getMapViewer().getProjection().getCode()); // If any let layerBounds; @@ -614,22 +611,22 @@ export class GVWMS extends AbstractGVRaster { if (boundingBoxes) { // Find the one with the right projection for (let i = 0; i < (boundingBoxes.length as number); i++) { - if (boundingBoxes[i].crs === projection) - return [ - boundingBoxes[i].crs as string, - // TODO: Check - Is it always in that order, 1, 0, 3, 2 or does that depend on the projection? - [boundingBoxes[i].extent[1], boundingBoxes[i].extent[0], boundingBoxes[i].extent[3], boundingBoxes[i].extent[2]] as Extent, - ]; + // Read the extent info from the GetCap + const { crs, extent } = boundingBoxes[i] as unknown as { crs: string; extent: Extent }; + + // If it's the crs we want + if (crs === projection) { + const extentSafe: Extent = Projection.readExtentCarefully(crs, extent); + return [crs, extentSafe]; + } } - // Not found. If any + // At this point, none could be found. If there's any to go with, we try our best... if (boundingBoxes.length > 0) { // Take the first one and return the bounds and projection - return [ - boundingBoxes[0].crs as string, - // TODO: Check - Is it always in that order, 1, 0, 3, 2 or does that depend on the projection? - [boundingBoxes[0].extent[1], boundingBoxes[0].extent[0], boundingBoxes[0].extent[3], boundingBoxes[0].extent[2]] as Extent, - ]; + const { crs, extent } = boundingBoxes[0] as unknown as { crs: string; extent: Extent }; + const extentSafe: Extent = Projection.readExtentCarefully(crs, extent); + return [crs, extentSafe]; } } diff --git a/packages/geoview-core/src/geo/map/map-viewer.ts b/packages/geoview-core/src/geo/map/map-viewer.ts index 12365f032ac..419bfb3cea7 100644 --- a/packages/geoview-core/src/geo/map/map-viewer.ts +++ b/packages/geoview-core/src/geo/map/map-viewer.ts @@ -76,6 +76,9 @@ export class MapViewer { // Minimum delay (in milliseconds) for map to be in loading state static readonly #MIN_DELAY_LOADING = 2000; + // The default densification number when forming layer extents, to make ture to compensate for earth curvature + static DEFAULT_STOPS: number = 25; + // map config properties mapFeaturesConfig: TypeMapFeaturesConfig; @@ -1441,11 +1444,12 @@ export class MapViewer { /** * Transforms extent from LngLat to the current projection of the map. * @param {Extent} extent - The LngLat extent + * @param {number} stops - The number of stops to perform densification on the extent * @returns {Extent} The extent in the map projection */ - convertExtentLngLatToMapProj(extent: Extent): Extent { + convertExtentLngLatToMapProj(extent: Extent, stops: number = MapViewer.DEFAULT_STOPS): Extent { // Redirect - return this.convertExtentFromProjToMapProj(extent, Projection.PROJECTION_NAMES.LNGLAT); + return this.convertExtentFromProjToMapProj(extent, Projection.PROJECTION_NAMES.LNGLAT, stops); } /** @@ -1494,12 +1498,13 @@ export class MapViewer { * Transforms extent from given projection to the current projection of the map. * @param {Extent} extent - The given extent * @param {ProjectionLike} fromProj - The projection of the given extent + * @param {number} stops - The number of stops to perform densification on the extent * @returns {Extent} The extent in the map projection */ - convertExtentFromProjToMapProj(extent: Extent, fromProj: ProjectionLike): Extent { + convertExtentFromProjToMapProj(extent: Extent, fromProj: ProjectionLike, stops: number = MapViewer.DEFAULT_STOPS): Extent { // If different projections if (fromProj !== this.getProjection().getCode()) { - return Projection.transformExtentFromProj(extent, fromProj, this.getProjection()); + return Projection.transformExtentFromProj(extent, fromProj, this.getProjection(), stops); } // Same projection diff --git a/packages/geoview-core/src/geo/utils/projection.ts b/packages/geoview-core/src/geo/utils/projection.ts index 6515e237cbf..cacad26bfc6 100644 --- a/packages/geoview-core/src/geo/utils/projection.ts +++ b/packages/geoview-core/src/geo/utils/projection.ts @@ -16,7 +16,7 @@ import { logger } from '@/core/utils/logger'; import { TypeJsonObject } from '@/core/types/global-types'; /** - * Class used to handle functions for trasforming projections + * Class used to handle functions for transforming projections * * @exports * @class Projection @@ -34,9 +34,11 @@ export abstract class Projection { 3578: 'EPSG:3578', LCC: 'EPSG:3978', 3979: 'EPSG:3979', - 102184: 'EPSG:102184', // TODO: Minor - This is technically supposed to be ESRI:102184, but more things would need to change in order to support this, works now - 102190: 'EPSG:102190', // TODO: Minor - This is technically supposed to be ESRI:102190, but some things would need to change in order to support this, works now + 102100: 'EPSG:102100', // TODO: Minor - The official name of this projection is ESRI:102100 (not EPSG:102100). However, for the purpose of simplification in GeoView code base, we name it with EPSG prefix. + 102184: 'EPSG:102184', // TODO: Minor - The official name of this projection is ESRI:102184 (not EPSG:102184). However, for the purpose of simplification in GeoView code base, we name it with EPSG prefix. + 102190: 'EPSG:102190', // TODO: Minor - The official name of this projection is ESRI:102190 (not EPSG:102190). However, for the purpose of simplification in GeoView code base, we name it with EPSG prefix. WM: 'EPSG:3857', + 4269: 'EPSG:4269', LNGLAT: 'EPSG:4326', CSRS: 'EPSG:4617', CSRS98: 'EPSG:4140', @@ -225,9 +227,14 @@ export abstract class Projection { */ static getProjectionFromObj(projection: TypeJsonObject | undefined): olProjection | undefined { // If wkid - if (projection && projection.wkid) { - // Redirect - return Projection.getProjectionFromProj(`EPSG:${projection.wkid}`); + if (projection) { + if (projection.latestWkid) { + return Projection.getProjectionFromProj(`EPSG:${projection.latestWkid}`); + } + if (projection.wkid) { + // Redirect + return Projection.getProjectionFromProj(`EPSG:${projection.wkid}`); + } } // If wkt @@ -289,11 +296,31 @@ export abstract class Projection { static getResolution(projection: string, center: Coordinate): number { return getPointResolution(projection, 1, center, 'm'); } + + /** + * Reads an extent and verifies if it might be reversed (ymin,xmin,ymax,ymin) and when + * so puts it back in order (xmin,ymin,xmax,ymax). + * @param {string} projection The projection the extent is in + * @param {Extent} extent The extent to check + * @returns {Extent} The extent in order (xmin,ymin,xmax,ymax). + */ + static readExtentCarefully(projection: string, extent: Extent): Extent { + // Sometimes (e.g. with 4326, 4269, and others?) the extent coordinates might be in wrong order. + if (projection === 'EPSG:4326' || projection === 'EPSG:4269') { + // If any number in 1 and 3 position, as absolute, is greater than 90, it's reversed for sure + if (Math.abs(extent[1]) > 90 || Math.abs(extent[3]) > 90) { + // Careful! + return [extent[1], extent[0], extent[3], extent[2]]; + } + } + + // All good + return extent; + } } /** - * Initialize CRS84 Projection - * @private + * Initializes the CRS84 Projection */ function initCRS84Projection(): void { const newDefinition = proj4.defs(Projection.PROJECTION_NAMES.LNGLAT); @@ -306,8 +333,7 @@ function initCRS84Projection(): void { } /** - * Initialize WM Projection - * @private + * Initializes the WM Projection */ function initWMProjection(): void { const projection = olGetProjection(Projection.PROJECTION_NAMES.WM); @@ -315,8 +341,7 @@ function initWMProjection(): void { } /** - * initialize LCC projection - * @private + * Initializes the LCC projection */ function initLCCProjection(): void { // define 3978 projection @@ -331,8 +356,7 @@ function initLCCProjection(): void { } /** - * initialize CSRS projection - * @private + * Initializes the CSRS projection */ function initCSRSProjection(): void { // define 4617 projection @@ -344,8 +368,7 @@ function initCSRSProjection(): void { } /** - * initialize CSRS98 projection - * @private + * Initializes the CSRS98 projection */ function initCSRS98Projection(): void { // define 4140 projection @@ -358,8 +381,7 @@ function initCSRS98Projection(): void { } /** - * initialize EPSG:3578 projection - * @private + * Initializes the EPSG:3578 projection */ function init3578Projection(): void { proj4.defs( @@ -374,8 +396,19 @@ function init3578Projection(): void { } /** - * initialize EPSG:3979 projection - * @private + * Initializes the EPSG:4269 projection + */ +function init4269Projection(): void { + proj4.defs(Projection.PROJECTION_NAMES[4269], '+proj=longlat +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +no_defs +type=crs'); + register(proj4); + + const projection = olGetProjection(Projection.PROJECTION_NAMES[4269]); + + if (projection) Projection.PROJECTIONS['4269'] = projection; +} + +/** + * Initializes the EPSG:3979 projection */ function init3979Projection(): void { proj4.defs( @@ -390,8 +423,22 @@ function init3979Projection(): void { } /** - * initialize EPSG:102184 (ESRI:102184) projection - * @private + * Initializes the EPSG:102100 (ESRI:102100) projection + */ +function init102100Projection(): void { + proj4.defs( + Projection.PROJECTION_NAMES[102100], + '+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs +type=crs' + ); + register(proj4); + + const projection = olGetProjection(Projection.PROJECTION_NAMES[102100]); + + if (projection) Projection.PROJECTIONS['102100'] = projection; +} + +/** + * Initializes the EPSG:102184 (ESRI:102184) projection */ function init102184Projection(): void { proj4.defs( @@ -406,8 +453,7 @@ function init102184Projection(): void { } /** - * initialize EPSG:102190 (ESRI:102190) projection - * @private + * Initializes the EPSG:102190 (ESRI:102190) projection */ function init102190Projection(): void { proj4.defs( @@ -429,6 +475,8 @@ initCSRSProjection(); initCSRS98Projection(); init3578Projection(); init3979Projection(); +init4269Projection(); +init102100Projection(); init102184Projection(); init102190Projection(); logger.logInfo('Projections initialized'); diff --git a/packages/geoview-core/src/ui/button-drop-down/button-drop-down-style.ts b/packages/geoview-core/src/ui/button-drop-down/button-drop-down-style.ts index a79f8861af1..65cbfad6297 100644 --- a/packages/geoview-core/src/ui/button-drop-down/button-drop-down-style.ts +++ b/packages/geoview-core/src/ui/button-drop-down/button-drop-down-style.ts @@ -11,7 +11,8 @@ export const getSxClasses = (theme: Theme): SxStyles => ({ buttonDropDown: { display: 'flex', fontSize: theme?.typography?.fontSize, - height: 50, + color: theme.palette.geoViewColor?.primary.dark, + backgroundColor: theme.palette.geoViewColor?.bgColor.dark[50], }, buttonText: {}, buttonArrow: {