UNPKG

@itwin/core-frontend

Version:
173 lines • 9.2 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module Tiles */ import { expectDefined, IModelStatus } from "@itwin/core-bentley"; import { ServerError } from "@itwin/core-common"; import { Point2d } from "@itwin/core-geometry"; import { MapLayerImageryProvider, MapLayerImageryProviderStatus, WmsCapabilities, WmsUtilities, } from "../../../../tile/internal"; // eslint-disable-next-line prefer-const let doToolTips = true; const scratchPoint2d = Point2d.createZero(); export class WmsMapLayerImageryProvider extends MapLayerImageryProvider { _capabilities; _allLayersRange; _subLayerRanges = new Map(); _baseUrl; _crsSupport; constructor(settings) { super(settings, false); this._baseUrl = WmsUtilities.getBaseUrl(this._settings.url); } async initialize() { try { const credentials = (this._settings.userName && this._settings.password ? { user: this._settings.userName, password: this._settings.password } : undefined); this._capabilities = await WmsCapabilities.create(this._baseUrl, credentials); if (undefined !== this._capabilities) { this._allLayersRange = this._capabilities.cartoRange; if (this._capabilities.layer && Array.isArray(this._capabilities.layer.subLayers)) { const mapCartoRanges = ((subLayer) => { if (Array.isArray(subLayer.children)) subLayer.children.forEach((child) => mapCartoRanges(child)); else if (subLayer.cartoRange) this._subLayerRanges.set(subLayer.name, subLayer.cartoRange); }); this._capabilities.layer.subLayers.forEach((subLayer) => mapCartoRanges(subLayer)); this._settings.subLayers.forEach((subLayer) => { if (subLayer.isNamed && this._settings.isSubLayerVisible(subLayer)) { const subLayerRange = this._subLayerRanges.get(subLayer.name); if (subLayerRange) if (this.cartoRange) this.cartoRange.extendRange(subLayerRange); else this.cartoRange = subLayerRange.clone(); } }); } if (!this.cartoRange) this.cartoRange = this._allLayersRange; this._crsSupport = this.getCrsSupport(); } } catch (error) { // Don't throw error if unauthorized status: // We want the tile tree to be created, so that end-user can get feedback on which layer is missing credentials. // When credentials will be provided, a new provider will be created, and initialization should be fine. if (error?.status === 401) { this.setStatus(MapLayerImageryProviderStatus.RequireAuth); } else { throw new ServerError(IModelStatus.ValidationFailed, ""); } } } getVisibleLayerString() { const layerNames = this.getVisibleLayers().map((layer) => layer.name); return layerNames.join("%2C"); } getVisibleLayers() { return this._settings.subLayers.filter((subLayer) => this._settings.isSubLayerVisible(subLayer) && subLayer.isNamed); } getVisibleLayersSrs() { const visibleLayers = this.getVisibleLayers(); const visibleLayerNames = visibleLayers.map((layer) => layer.name); return this._capabilities?.getSubLayersCrs(visibleLayerNames); } getQueryableLayers() { const layerNames = new Array(); const getQueryableSubLayers = ((subLayer) => { if (!subLayer) return; if (subLayer.queryable) layerNames.push(subLayer.name); subLayer.children?.forEach((childSubLayer) => getQueryableSubLayers(childSubLayer)); }); this._capabilities?.layer?.subLayers?.forEach((subLayer) => getQueryableSubLayers(subLayer)); return layerNames; } getVisibleQueryableLayersString() { const layers = new Array(); const queryable = this.getQueryableLayers(); const visibleLayerNames = this.getVisibleLayers().map((layer) => layer.name); queryable.forEach((layer) => { if (visibleLayerNames.includes(layer)) layers.push(layer); }); return layers.join("%2C"); } getCrsSupport() { const layersCrs = this.getVisibleLayersSrs(); let support3857; let support4326; if (layersCrs) { for (const [_layerName, crs] of layersCrs) { if (crs.find((layerCrs) => layerCrs.includes("3857")) === undefined) { support3857 = false; } else if (support3857 === undefined) { support3857 = true; } if (crs.find((layerCrs) => layerCrs.includes("4326")) === undefined) { support4326 = false; } else if (support4326 === undefined) { support4326 = true; } } } return { support3857: support3857 ?? false, support4326: support4326 ?? false }; } // WMS standard requires 'TRUE' or 'FALSE' (case sensitive) values. get transparentBackgroundString() { return this._settings.transparentBackground ? "TRUE" : "FALSE"; } // construct the Url from the desired Tile async constructUrl(row, column, zoomLevel) { let bboxString = ""; let crsString = ""; // We support 2 SRS: EPSG:3857 and EPSG:4326, we prefer EPSG:3857. if (this._crsSupport?.support3857) { bboxString = this.getEPSG3857ExtentString(row, column, zoomLevel); crsString = "EPSG%3A3857"; } else if (this._crsSupport?.support4326) { // The WMS 1.3.0 specification mandates using the axis ordering as defined in the EPSG database. // For instance, for EPSG:4326 the axis ordering is latitude/longitude, or north/east. // WMS 1.1.0 always requires the axis ordering to be longitude/latitude. *sigh* if (this._capabilities !== undefined) { bboxString = this.getEPSG4326TileExtentString(row, column, zoomLevel, this._capabilities?.isVersion13); // lat/long ordering crsString = "EPSG%3A4326"; } } const layerString = this.getVisibleLayerString(); if (bboxString.length === 0 || crsString.length === 0 || layerString.length === 0) return ""; const crsParamName = this._capabilities?.isVersion13 ? "CRS" : "SRS"; const tmpUrl = `${this._baseUrl}?SERVICE=WMS&VERSION=${this._capabilities?.version}&REQUEST=GetMap&FORMAT=image%2Fpng&TRANSPARENT=${this.transparentBackgroundString}&LAYERS=${layerString}&WIDTH=${this.tileSize}&HEIGHT=${this.tileSize}&${crsParamName}=${crsString}&STYLES=&BBOX=${bboxString}`; return this.appendCustomParams(tmpUrl); } async getToolTip(strings, quadId, carto, tree) { await super.getToolTip(strings, quadId, carto, tree); const infoFormats = this._capabilities?.featureInfoFormats; if (!doToolTips || undefined === infoFormats) return; let formatString = infoFormats.find((format) => format === "text/html"); if (!formatString) formatString = infoFormats[0]; const bboxString = this.getEPSG3857ExtentString(quadId.row, quadId.column, quadId.level); const layerString = this.getVisibleQueryableLayersString(); if (layerString.length === 0) return; const rectangle = tree.getTileRectangle(quadId); const fraction = expectDefined(rectangle.worldToLocal(Point2d.create(carto.longitude, carto.latitude, scratchPoint2d))); const x = Math.floor(.5 + fraction.x * this.tileSize); const y = Math.floor(.5 + (1.0 - fraction.y) * this.tileSize); const coordinateString = this._capabilities?.isVersion13 ? `&i=${x}&j=${y}` : `&x=${x}&y=${y}`; const crsParamName = this._capabilities?.isVersion13 ? "CRS" : "SRS"; let getFeatureUrl = `${this._baseUrl}?SERVICE=WMS&VERSION=${this._capabilities?.version}&REQUEST=GetFeatureInfo&LAYERS=${layerString}&WIDTH=${this.tileSize}&HEIGHT=${this.tileSize}&${crsParamName}=EPSG%3A3857&BBOX=${bboxString}&QUERY_LAYERS=${layerString}${coordinateString}&info_format=${formatString}`; getFeatureUrl = this.appendCustomParams(getFeatureUrl); return this.toolTipFromUrl(strings, getFeatureUrl); } } //# sourceMappingURL=WmsMapLayerImageryProvider.js.map