Skip to content

Commit

Permalink
Merge pull request #828 from geoadmin/bug-PB-496-external-wmts-3d
Browse files Browse the repository at this point in the history
PB-496: External WMTS layer in 3d
  • Loading branch information
ltshb authored May 21, 2024
2 parents a2f8937 + de13e67 commit 1918899
Show file tree
Hide file tree
Showing 21 changed files with 599 additions and 79 deletions.
5 changes: 5 additions & 0 deletions src/api/layers/AbstractLayer.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ export default class AbstractLayer {
* is from another (external) source. Default is `false`
* @param {boolean} [layerData.isLoading=false] Set to true if some parts of the layer (e.g.
* metadata) are still loading. Default is `false`
* @param {LayerTimeConfig | null} [layerData.timeConfig=null] Time series config (if
* available). Default is `null`
* @throws InvalidLayerDataError if no `layerData` is given, or if `layerData.name` or
* `layerData.type` or `layer.baseUrl` aren't valid
*/
Expand All @@ -90,6 +92,7 @@ export default class AbstractLayer {
hasLegend = false,
isExternal = false,
isLoading = false,
timeConfig = null,
} = layerData
if (name === null) {
throw new InvalidLayerDataError('Missing layer name', layerData)
Expand Down Expand Up @@ -120,6 +123,8 @@ export default class AbstractLayer {
this.hasLegend = hasLegend
this.errorKey = null
this.hasError = false
this.timeConfig = timeConfig
this.hasMultipleTimestamps = this.timeConfig?.timeEntries?.length > 1
}

clone() {
Expand Down
12 changes: 12 additions & 0 deletions src/api/layers/ExternalLayer.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ export default class ExternalLayer extends AbstractLayer {
* @param {ExternalLayerGetFeatureInfoCapability | null} [externalLayerData.getFeatureInfoCapability=null]
* Configuration describing how to request this layer's server to get feature information.
* Default is `null`
* @param {LayerTimeConfig | null} [externalLayerData.timeConfig=null] Time series config (if
* available). Default is `null`
* @param {Number} [externalWmtsData.currentYear=null] Current year of the time series config to
* use. This parameter is needed as it is set in the URL while the timeConfig parameter is not
* yet available and parse later on from the GetCapabilities. Default is `null`
* @throws InvalidLayerDataError if no `externalLayerData` is given or if it is invalid
*/
constructor(externalLayerData) {
Expand All @@ -115,6 +120,8 @@ export default class ExternalLayer extends AbstractLayer {
availableProjections = [],
hasTooltip = false,
getFeatureInfoCapability = null,
timeConfig = null,
currentYear = null,
} = externalLayerData
// keeping this checks, even though it will be checked again by the super constructor, because we use the baseUrl
// to build our call to the super constructor (with a URL construction, which could raise an error if baseUrl is
Expand All @@ -135,6 +142,7 @@ export default class ExternalLayer extends AbstractLayer {
isLoading,
hasDescription: abstract?.length > 0 || legends?.length > 0,
hasLegend: legends?.length > 0,
timeConfig,
})
this.abstract = abstract
this.extent = extent
Expand All @@ -148,5 +156,9 @@ export default class ExternalLayer extends AbstractLayer {
this.availableProjections.push(WGS84)
}
this.getFeatureInfoCapability = getFeatureInfoCapability
this.currentYear = currentYear
if (currentYear && this.timeConfig) {
this.timeConfig.updateCurrentTimeEntry(this.timeConfig.getTimeEntryForYear(currentYear))
}
}
}
79 changes: 79 additions & 0 deletions src/api/layers/ExternalWMTSLayer.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,57 @@ import ExternalLayer from '@/api/layers/ExternalLayer.class'
import { InvalidLayerDataError } from '@/api/layers/InvalidLayerData.error'
import LayerTypes from '@/api/layers/LayerTypes.enum'

/**
* @readonly
* @enum {String}
*/
export const WMTSEncodingTypes = {
KVP: 'KVP',
REST: 'REST',
}

/**
* WMTS TileMatrixSet
*
* @class
*/
export class TileMatrixSet {
/**
* @param {String} id Identifier of the tile matrix set (see WMTS OGC spec)
* @param {CoordinateSystem} projection Coordinate system supported by the Tile Matrix Set
* @param {any} tileMatrix TileMatrix from GetCapabilities (see WMTS OGC spec)
*/
constructor(id, projection, tileMatrix) {
this.id = id
this.projection = projection
this.tileMatrix = tileMatrix
}
}

/**
* A WMTS Layer dimension
*
* See WMTS OGC Spec
*
* @class
*/
export class WMTSDimension {
/**
* @param {String} id Dimension identifier
* @param {String} dft Dimension default value
* @param {[String]} values All dimension values
* @param {Boolean} [optionals.current] Boolean flag if the dimension support current (see WMTS
* OGC spec)
*/
constructor(id, dft, values, optionals = {}) {
const { current = false } = optionals
this.id = id
this.default = dft
this.values = values
this.current = current
}
}

/**
* Metadata for an external WMTS layer, that will be defined through a GetCapabilities.xml endpoint
* (and a layer ID)
Expand Down Expand Up @@ -37,6 +88,20 @@ export default class ExternalWMTSLayer extends ExternalLayer {
* @param {CoordinateSystem[]} [externalWmtsData.availableProjections=[]] All projection that
* can be used to request this layer. Default is `[]`
* @param {ol/WMTS/Options} [externalWmtsData.options] WMTS Get Capabilities options
* @param {String} [externalWmtsData.getTileEncoding=REST] WMTS Get Tile encoding (KVP or REST).
* Default is `REST`
* @param {String | null} [externalWmtsData.urlTemplate=''] WMTS Get Tile url template for REST
* encoding. Default is `''`
* @param {String} [externalWmtsData.style='default'] WMTS layer style. Default is `'default'`
* @param {[TileMatrixSet]} [externalWmtsData.tileMatrixSets=[]] WMTS tile matrix sets
* identifiers. Default is `[]`
* @param {[WMTSDimension]} [externalWmtsData.dimensions=[]] WMTS tile dimensions. Default is
* `[]`
* @param {LayerTimeConfig | null} [externalLayerData.timeConfig=null] Time series config (if
* available). Default is `null`
* @param {Number} [externalWmtsData.currentYear=null] Current year of the time series config to
* use. This parameter is needed as it is set in the URL while the timeConfig parameter is not
* yet available and parse later on from the GetCapabilities. Default is `null`
* @throws InvalidLayerDataError if no `externalWmtsData` is given or if it is invalid
*/
constructor(externalWmtsData) {
Expand All @@ -56,6 +121,13 @@ export default class ExternalWMTSLayer extends ExternalLayer {
isLoading = true,
availableProjections = [],
options = null,
getTileEncoding = WMTSEncodingTypes.REST,
urlTemplate = '',
style = 'default',
tileMatrixSets = [],
dimensions = [],
timeConfig = null,
currentYear = null,
} = externalWmtsData
super({
name,
Expand All @@ -71,7 +143,14 @@ export default class ExternalWMTSLayer extends ExternalLayer {
legends,
isLoading,
availableProjections,
timeConfig,
currentYear,
})
this.options = options
this.getTileEncoding = getTileEncoding
this.urlTemplate = urlTemplate
this.style = style
this.tileMatrixSets = tileMatrixSets
this.dimensions = dimensions
}
}
3 changes: 1 addition & 2 deletions src/api/layers/GeoAdminLayer.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,13 @@ export default class GeoAdminLayer extends AbstractLayer {
isLoading,
hasDescription,
hasLegend,
timeConfig,
})
this.technicalName = technicalName
this.isBackground = isBackground
this.isHighlightable = isHighlightable
this.topics = topics
this.isSpecificFor3D = id.toLowerCase().endsWith('_3d')
this.timeConfig = timeConfig
this.hasMultipleTimestamps = this.timeConfig?.timeEntries?.length > 1
this.searchable = searchable
}

Expand Down
4 changes: 2 additions & 2 deletions src/api/layers/LayerTimeConfig.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,15 @@ export default class LayerTimeConfig {
* @returns {LayerTimeConfigEntry | null}
*/
getTimeEntryForYear(year) {
return this.timeEntries.find((entry) => entry.year === year)
return this.timeEntries.find((entry) => entry.year === year) ?? null
}

/**
* @param {String} timestamp
* @returns {LayerTimeConfigEntry | null}
*/
getTimeEntryForTimestamp(timestamp) {
return this.timeEntries.find((entry) => entry.timestamp === timestamp)
return this.timeEntries.find((entry) => entry.timestamp === timestamp) ?? null
}

clone() {
Expand Down
15 changes: 13 additions & 2 deletions src/api/layers/LayerTimeConfigEntry.class.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { isTimestampYYYYMMDD } from '@/utils/numberUtils'

/**
* Year we are using to describe the timestamp "all data" for WMS (and also for WMTS as there is no
* equivalent for that in the norm)
Expand Down Expand Up @@ -34,7 +36,7 @@ export const CURRENT_YEAR_WMTS_TIMESTAMP = 'current'
* to Vue reactivity engine.
*/
export default class LayerTimeConfigEntry {
/** @param {String} timestamp A full timestamp as YYYYYMMDD */
/** @param {String} timestamp A full timestamp as YYYYYMMDD or ISO 8601 format */
constructor(timestamp) {
this.timestamp = timestamp
if (
Expand All @@ -43,7 +45,16 @@ export default class LayerTimeConfigEntry {
) {
this.year = YEAR_TO_DESCRIBE_ALL_OR_CURRENT_DATA
} else {
this.year = parseInt(timestamp.substring(0, 4))
if (isTimestampYYYYMMDD(timestamp)) {
this.year = parseInt(timestamp.substring(0, 4))
} else {
const date = new Date(timestamp)
if (!isNaN(date)) {
this.year = date.getFullYear()
} else {
this.year = null
}
}
}
}
}
33 changes: 27 additions & 6 deletions src/api/layers/WMSCapabilitiesParser.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,23 @@ export default class WMSCapabilitiesParser {
*
* @param {string} layerId Layer ID of the layer to retrieve
* @param {CoordinateSystem} projection Projection currently used by the application
* @param {number} opacity
* @param {boolean} visible
* @param {boolean} ignoreError Don't throw exception in case of error, but return a default
* value or null
* @param {number} [opacity=1] Default is `1`
* @param {boolean} [visible=true] Default is `true`
* @param {Number | null} [currentYear=null] Current year to select for the time config. Only
* needed when a time config is present a year is pre-selected in the url parameter. Default
* is `null`
* @param {boolean} [ignoreError=true] Don't throw exception in case of error, but return a
* default value or null. Default is `true`
* @returns {ExternalWMSLayer | null} ExternalWMSLayer object or nul in case of error
*/
getExternalLayerObject(layerId, projection, opacity = 1, visible = true, ignoreError = true) {
getExternalLayerObject(
layerId,
projection,
opacity = 1,
visible = true,
currentYear = null,
ignoreError = true
) {
const { layer, parents } = this.findLayer(layerId)
if (!layer) {
const msg = `No WMS layer ${layerId} found in Capabilities ${this.originUrl.toString()}`
Expand All @@ -127,6 +137,7 @@ export default class WMSCapabilitiesParser {
projection,
opacity,
visible,
currentYear,
ignoreError
)
}
Expand Down Expand Up @@ -155,7 +166,15 @@ export default class WMSCapabilitiesParser {
).filter((layer) => !!layer)
}

_getExternalLayerObject(layer, parents, projection, opacity, visible, ignoreError) {
_getExternalLayerObject(
layer,
parents,
projection,
opacity,
visible,
currentYear,
ignoreError
) {
const {
layerId,
title,
Expand Down Expand Up @@ -200,6 +219,7 @@ export default class WMSCapabilitiesParser {
isLoading: false,
availableProjections,
getFeatureInfoCapability: this.getFeatureInfoCapability(ignoreError),
currentYear,
})
}
return new ExternalWMSLayer({
Expand All @@ -218,6 +238,7 @@ export default class WMSCapabilitiesParser {
availableProjections,
hasTooltip: queryable,
getFeatureInfoCapability: this.getFeatureInfoCapability(ignoreError),
currentYear,
})
}

Expand Down
Loading

0 comments on commit 1918899

Please sign in to comment.