Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(WMS): adds proxy to WMS urls #2709

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,31 @@
{
"layerId": "msi-94-or-more",
"source": {
"wmsStyle": "msi-binary"
"wmsStyle": [
"msi-binary"
]
}
}
]
}
]
},
"theme": "geo.ca",
"components": ["north-arrow", "overview-map"],
"components": [
"north-arrow",
"overview-map"
],
"footerBar": {
"tabs": {
"core": ["legend", "layers", "details", "data-table", "time-slider"]
"core": [
"legend",
"layers",
"details",
"data-table",
"time-slider"
]
}
},
"corePackages": [],
"externalPackages": []
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -355,20 +355,23 @@ export function LayerDetails(props: LayerDetailsProps): JSX.Element {
</Box>
<Divider sx={{ marginTop: '20px', marginBottom: '10px' }} variant="middle" />
{layerDetails.layerAttribution &&
layerDetails.layerAttribution!.map((attribution) => {
return (
<Typography
sx={{
marginTop: '10px',
color: theme.palette.geoViewColor.textColor.light[200],
fontSize: theme.palette.geoViewFontSize.sm,
textAlign: 'center',
}}
key={generateId()}
>
{attribution.indexOf('©') === -1 ? `© ${attribution}` : attribution}
</Typography>
);
layerDetails.layerAttribution.map((attribution) => {
if (attribution) {
return (
<Typography
sx={{
marginTop: '10px',
color: theme.palette.geoViewColor.textColor.light[200],
fontSize: theme.palette.geoViewFontSize.sm,
textAlign: 'center',
}}
key={generateId()}
>
{attribution.indexOf('©') === -1 ? `© ${attribution}` : attribution}
</Typography>
);
}
return null;
})}
</>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { CONST_LAYER_TYPES } from '@/geo/layer/geoview-layers/abstract-geoview-l
import { CONST_LAYER_ENTRY_TYPES, TypeSourceImageWmsInitialConfig } from '@/geo/map/map-schema-types';
import { ConfigBaseClass } from '@/core/utils/config/validation-classes/config-base-class';
import { AbstractBaseLayerEntryConfig } from '@/core/utils/config/validation-classes/abstract-base-layer-entry-config';
import { WMS_PROXY_URL } from '@/core/utils/constant';

/** ******************************************************************************************************************************
* Type used to define a GeoView image layer to display on the map.
Expand All @@ -27,12 +28,31 @@ export class OgcWmsLayerEntryConfig extends AbstractBaseLayerEntryConfig {
super(layerConfig);
Object.assign(this, layerConfig);

// Append all WMS links with proxy url to avoid CORS issues
// TODO: Currently datacube layers are not working with the proxy, this should not be the case. Check and remove exception when possible
if (
this.geoviewLayerConfig.metadataAccessPath &&
!this.geoviewLayerConfig.metadataAccessPath?.startsWith(WMS_PROXY_URL) &&
!this.geoviewLayerConfig.metadataAccessPath.includes('datacube.services.geo.ca')
)
this.geoviewLayerConfig.metadataAccessPath = `${WMS_PROXY_URL}${this.geoviewLayerConfig.metadataAccessPath}`;

// if layerConfig.source.dataAccessPath is undefined, the metadataAccessPath defined on the root is used.
if (!this.source) this.source = {};

// Append all WMS links with proxy url to avoid CORS issues
// TODO: Currently datacube layers are not working with the proxy, this should not be the case. Check and remove exception when possible
if (
this.source.dataAccessPath &&
!this.source.dataAccessPath.startsWith(WMS_PROXY_URL) &&
!this.source.dataAccessPath.includes('datacube.services.geo.ca')
)
this.source.dataAccessPath = `${WMS_PROXY_URL}${this.source.dataAccessPath}`;

// When the dataAccessPath is undefined and the metadataAccessPath ends with ".xml", the dataAccessPath is temporarilly
// set to '' and will be filled in the fetchServiceMetadata method of the class WMS.
if (!this.source.dataAccessPath) this.source.dataAccessPath = '';

// When the dataAccessPath is undefined and the metadataAccessPath does not end with ".xml", the dataAccessPath is set
// to the same value of the corresponding metadataAccessPath.
if (this.geoviewLayerConfig.metadataAccessPath!.slice(-4).toLowerCase() !== '.xml')
Expand Down
2 changes: 2 additions & 0 deletions packages/geoview-core/src/core/utils/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,5 @@ export const CONTAINER_TYPE = {
APP_BAR: 'appBar',
FOOTER_BAR: 'footerBar',
} as const;

export const WMS_PROXY_URL = 'https://maps.canada.ca/wmsproxy/ws/wmsproxy/executeFromProxy?';
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ export abstract class AbstractGVLayer extends AbstractBaseLayer {

// Boolean indicating if the layer should be included in time awareness functions such as the Time Slider. True by default.
this.#isTimeAware = layerConfig.geoviewLayerConfig.isTimeAware === undefined ? true : layerConfig.geoviewLayerConfig.isTimeAware;

// If there is a layer style in the config, set it in the layer
if (layerConfig.layerStyle) this.setStyle(layerConfig.layerStyle);
}

/**
Expand All @@ -108,7 +111,9 @@ export abstract class AbstractGVLayer extends AbstractBaseLayer {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.#olSource as any).once(['featuresloadend', 'imageloadend', 'tileloadend'], this.onLoaded.bind(this));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.#olSource as any).once(['featuresloaderror', 'imageloaderror', 'tileloaderror'], this.onError.bind(this));
(this.#olSource as any).once(['featuresloaderror', 'tileloaderror'], this.onError.bind(this));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.#olSource as any).on(['imageloaderror'], this.onImageLoadError.bind(this));
}

/**
Expand Down Expand Up @@ -232,6 +237,17 @@ export abstract class AbstractGVLayer extends AbstractBaseLayer {
this.getLayerConfig().layerStatus = 'error';
}

/**
* Overridable method called when the layer image is in error and couldn't be loaded correctly.
* We do not put the layer status as error, as this could be specific to a zoom level and the layer is otherwise fine.
*/
protected onImageLoadError(): void {
// Add notification with the current zoom level
this.getMapViewer().notifications.showError(
`Error loading source image for layer ${this.getLayerPath()} at zoom level: ${this.getMapViewer().getView().getZoom()}`
);
}

/**
* Returns feature information for the layer specified.
* @param {QueryType} queryType - The type of query to perform.
Expand Down
31 changes: 30 additions & 1 deletion packages/geoview-core/src/geo/layer/gv-layers/raster/gv-wms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { loadImage } from '@/geo/utils/renderer/geoview-renderer';
import { AbstractGVRaster } from './abstract-gv-raster';
import { TypeLegend } from '@/core/stores/store-interface-and-intial-values/layer-state';
import { Projection } from '@/geo/utils/projection';
import { WMS_PROXY_URL } from '@/app';

/**
* Manages a WMS layer.
Expand Down Expand Up @@ -353,17 +354,45 @@ export class GVWMS extends AbstractGVRaster {

if (queryUrl) {
queryUrl = queryUrl.toLowerCase().startsWith('http:') ? `https${queryUrl.slice(4)}` : queryUrl;

axios
.get<TypeJsonObject>(queryUrl, { responseType: 'blob' })
.then((response) => {
// Text response means something went wrong
if (response.data.type === 'text/xml') {
resolve(null);
}

// Expected response, return it as image
resolve(readImage(Cast<Blob>(response.data)));
})
.catch(() => resolve(null));
.catch((error) => {
/** For some layers the layer loads fine through the proxy, but fetching the legend fails
* We try the fetch first without the proxy and if we get a network error, try again with the proxy.
*/
if (error.code === 'ERR_NETWORK') {
// Try appending link with proxy url to avoid CORS issues
queryUrl = `${WMS_PROXY_URL}${queryUrl}`;

axios
.get<TypeJsonObject>(queryUrl, { responseType: 'blob' })
.then((response) => {
// Text response means something went wrong
if (response.data.type === 'text/xml') {
resolve(null);
}

// Expected response, return it as image
resolve(readImage(Cast<Blob>(response.data)));
})
.catch(() => resolve(null));
// Not a CORS issue, return null
} else resolve(null);
});
// No URL to query
} else resolve(null);
});

return promisedImage;
}

Expand Down
Loading