@@ -404,10 +404,25 @@ function extractWMTSInfo(xmlDoc, baseUrl) {
404404 const styleName = queryOWS ( styleEl , 'Identifier' ) ?. textContent ;
405405 const styleTitle = queryOWS ( styleEl , 'Title' ) ?. textContent || styleName ;
406406 const isDefault = styleEl . getAttribute ( 'isDefault' ) === 'true' ;
407- styles . push ( { name : styleName , title : styleTitle , isDefault } ) ;
407+
408+ // Extract LegendURL information (WMTS format uses attributes on LegendURL element)
409+ const legendURLs = [ ] ;
410+ const legendElements = styleEl . querySelectorAll ( 'LegendURL' ) ;
411+ legendElements . forEach ( legendEl => {
412+ const href = legendEl . getAttribute ( 'xlink:href' ) || legendEl . getAttributeNS ( 'http://www.w3.org/1999/xlink' , 'href' ) || '' ;
413+ const width = legendEl . getAttribute ( 'width' ) ;
414+ const height = legendEl . getAttribute ( 'height' ) ;
415+ const format = legendEl . getAttribute ( 'format' ) ;
416+
417+ if ( href ) {
418+ legendURLs . push ( { width, height, format, href } ) ;
419+ }
420+ } ) ;
421+
422+ styles . push ( { name : styleName , title : styleTitle , isDefault, legendURLs } ) ;
408423 } ) ;
409424 if ( styles . length === 0 ) {
410- styles . push ( { name : 'default' , title : 'Default' , isDefault : true } ) ;
425+ styles . push ( { name : 'default' , title : 'Default' , isDefault : true , legendURLs : [ ] } ) ;
411426 }
412427
413428 const formats = [ ] ;
@@ -418,6 +433,47 @@ function extractWMTSInfo(xmlDoc, baseUrl) {
418433 const infoFormatElements = queryAllOWS ( layerEl , 'InfoFormat' ) ;
419434 infoFormatElements . forEach ( fmt => infoFormats . push ( fmt . textContent ) ) ;
420435
436+ // Extract dimensions
437+ const dimensions = [ ] ;
438+ const dimensionElements = queryAllOWS ( layerEl , 'Dimension' ) ;
439+ dimensionElements . forEach ( dimEl => {
440+ const dimName = queryOWS ( dimEl , 'Identifier' ) ?. textContent ;
441+ const dimDefault = queryOWS ( dimEl , 'Default' ) ?. textContent ;
442+ const dimUnits = queryOWS ( dimEl , 'UOM' ) ?. textContent ;
443+
444+ if ( dimName ) {
445+ // Parse all Value elements
446+ const valueElements = queryAllOWS ( dimEl , 'Value' ) ;
447+ let allValues = [ ] ;
448+
449+ valueElements . forEach ( valEl => {
450+ const valContent = valEl . textContent . trim ( ) ;
451+ if ( valContent ) {
452+ const parsedValues = parseISO8601Interval ( valContent ) ;
453+ allValues = allValues . concat ( parsedValues ) ;
454+ }
455+ } ) ;
456+
457+ const valueCount = allValues . length ;
458+ const usesTemplate = valueCount > 200 ;
459+
460+ // If using template (>200 values), only store default value
461+ const values = usesTemplate ? [ dimDefault || allValues [ 0 ] ] : allValues ;
462+
463+ if ( values . length > 0 ) {
464+ dimensions . push ( {
465+ name : dimName ,
466+ units : dimUnits || '' ,
467+ default : dimDefault || values [ 0 ] ,
468+ values : values ,
469+ valueCount : valueCount ,
470+ usesTemplate : usesTemplate
471+ } ) ;
472+ console . log ( 'Parsed WMTS dimension:' , dimName , 'with' , valueCount , 'total values' , usesTemplate ? '(using template with default only)' : '(full value list)' ) ;
473+ }
474+ }
475+ } ) ;
476+
421477 const tmsLinks = [ ] ;
422478 const tmsLinkElements = queryAllOWS ( layerEl , 'TileMatrixSetLink' ) ;
423479 tmsLinkElements . forEach ( link => {
@@ -463,7 +519,8 @@ function extractWMTSInfo(xmlDoc, baseUrl) {
463519 resourceURLs,
464520 queryable,
465521 licenseUrl : '' ,
466- licenseTitle : ''
522+ licenseTitle : '' ,
523+ dimensions
467524 } ) ;
468525 } ) ;
469526
@@ -733,7 +790,15 @@ function displayWMTSInfo(info, source, url) {
733790 const tileResources = layer . resourceURLs [ 'tile' ] || [ ] ;
734791 const pngResource = tileResources . find ( r => r . format && r . format . includes ( 'png' ) ) || tileResources [ 0 ] ;
735792 const tileTemplate = pngResource ? pngResource . template : '' ;
736- const previewUrl = buildWMTSTileUrl ( tileTemplate , layer , firstTMS ? firstTMS . identifier : '' , defaultStyle . name , 'image/png' , '2' , '1' , '1' ) ;
793+ let previewUrl = buildWMTSTileUrl ( tileTemplate , layer , firstTMS ? firstTMS . identifier : '' , defaultStyle . name , 'image/png' , '2' , '1' , '1' ) ;
794+
795+ // Replace dimension parameters with default values for preview
796+ if ( layer . dimensions && layer . dimensions . length > 0 ) {
797+ layer . dimensions . forEach ( function ( dimension ) {
798+ const dimPattern = new RegExp ( '\\{' + dimension . name + '\\}' , 'g' ) ;
799+ previewUrl = previewUrl . replace ( dimPattern , dimension . default ) ;
800+ } ) ;
801+ }
737802 const queryResources = layer . resourceURLs [ 'FeatureInfo' ] || [ ] ;
738803 const hasQuery = layer . queryable && queryResources . length > 0 ;
739804
@@ -762,9 +827,19 @@ function displayWMTSInfo(info, source, url) {
762827
763828 const formatHtml = layer . formats . length > 0 ? '<div class="format-selector"><label for="img-format-' + index + '">Image Format:</label><select id="img-format-' + index + '" class="format-select">' + formatOptions + '</select></div>' : '' ;
764829
830+ // Add dimension selectors HTML
831+ const dimensionHtml = ( layer . dimensions && layer . dimensions . length > 0 ) ? layer . dimensions . map ( ( dim , dimIdx ) => {
832+ if ( dim . usesTemplate ) {
833+ return '<div class="dimension-info"><strong>' + dim . name + ':</strong> ' + dim . default + ' <em>(fixed value - ' + dim . valueCount + ' total values)</em></div>' ;
834+ } else {
835+ const dimOptions = dim . values . map ( val => '<option value="' + val + '"' + ( val === dim . default ? ' selected' : '' ) + '>' + val + '</option>' ) . join ( '' ) ;
836+ return '<div class="dimension-selector"><input type="checkbox" id="dim-enabled-' + index + '-' + dimIdx + '" class="dimension-checkbox" data-dimension-name="' + dim . name + '" checked /><label for="dim-' + index + '-' + dimIdx + '">' + dim . name + ':</label><select id="dim-' + index + '-' + dimIdx + '" class="dimension-select" data-dimension-name="' + dim . name + '">' + dimOptions + '</select></div>' ;
837+ }
838+ } ) . join ( '' ) : '' ;
839+
765840 const previewHtml = previewUrl ? '<img src="' + previewUrl + '" alt="Preview of ' + layer . title + '" class="layer-preview" id="preview-' + index + '" />' : '<p>No preview available</p>' ;
766841
767- return '<div class="layer-item" data-layer-index="' + index + '" data-service-type="WMTS"><div class="layer-controls"><div class="layer-header"><input type="checkbox" id="layer-' + index + '" class="layer-checkbox" /><label for="layer-' + index + '"><strong>' + layer . title + '</strong></label></div><p class="layer-name">Identifier: ' + layer . name + '</p>' + projectionHtml + abstractHtml + '<div class="bounds-selector"><input type="checkbox" id="bounds-' + index + '" class="bounds-checkbox" title="Include layer bounds" checked /><label for="bounds-' + index + '" class="bounds-label">Include Bounds</label></div>' + queryHtml + styleHtml + formatHtml + '</div><div class="layer-viewer-container" id="viewer-container-' + index + '">' + previewHtml + '</div></div>' ;
842+ return '<div class="layer-item" data-layer-index="' + index + '" data-service-type="WMTS"><div class="layer-controls"><div class="layer-header"><input type="checkbox" id="layer-' + index + '" class="layer-checkbox" /><label for="layer-' + index + '"><strong>' + layer . title + '</strong></label></div><p class="layer-name">Identifier: ' + layer . name + '</p>' + projectionHtml + abstractHtml + '<div class="bounds-selector"><input type="checkbox" id="bounds-' + index + '" class="bounds-checkbox" title="Include layer bounds" checked /><label for="bounds-' + index + '" class="bounds-label">Include Bounds</label></div>' + queryHtml + styleHtml + formatHtml + dimensionHtml + '</div><div class="layer-viewer-container" id="viewer-container-' + index + '">' + previewHtml + '</div></div>' ;
768843 } ) . join ( '' ) ;
769844
770845 const supportedCount = Object . values ( info . tileMatrixSets ) . filter ( tms => tms . supported ) . length ;
@@ -909,6 +984,62 @@ function displayWMTSInfo(info, source, url) {
909984 } ) ;
910985 }
911986 }
987+
988+ // Add event listeners to dimension selectors and checkboxes
989+ if ( layer . dimensions && layer . dimensions . length > 0 ) {
990+ layer . dimensions . forEach ( ( dim , dimIdx ) => {
991+ if ( ! dim . usesTemplate ) {
992+ const dimensionSelect = document . getElementById ( 'dim-' + index + '-' + dimIdx ) ;
993+ const dimensionCheckbox = document . getElementById ( 'dim-enabled-' + index + '-' + dimIdx ) ;
994+
995+ if ( dimensionSelect ) {
996+ dimensionSelect . addEventListener ( 'change' , function ( e ) {
997+ const layerCheckbox = document . getElementById ( 'layer-' + index ) ;
998+ if ( layerCheckbox . checked ) {
999+ const queryCheckbox = document . getElementById ( 'query-' + index ) ;
1000+ const queryEnabled = queryCheckbox ? queryCheckbox . checked : false ;
1001+ const formatSelect = document . getElementById ( 'format-' + index ) ;
1002+ const selectedFormat = formatSelect ? formatSelect . value : ( layer . infoFormats [ 0 ] || 'text/html' ) ;
1003+ const styleSelect = document . getElementById ( 'style-' + index ) ;
1004+ const selectedStyle = styleSelect ? styleSelect . value : ( layer . styles [ 0 ] ? layer . styles [ 0 ] . name : 'default' ) ;
1005+ const imgFormatSelect = document . getElementById ( 'img-format-' + index ) ;
1006+ const selectedImgFormat = imgFormatSelect ? imgFormatSelect . value : ( layer . formats [ 0 ] || 'image/png' ) ;
1007+ const projectionSelect = document . getElementById ( 'projection-' + index ) ;
1008+ const selectedProjection = projectionSelect ? projectionSelect . value : 'OSMTILE' ;
1009+ const boundsCheckbox = document . getElementById ( 'bounds-' + index ) ;
1010+ const boundsEnabled = boundsCheckbox ? boundsCheckbox . checked : true ;
1011+ // Recreate viewer with new dimension value
1012+ removeViewerForLayer ( index ) ;
1013+ createViewerForWMTSLayer ( index , layer , info , selectedFormat , queryEnabled , selectedStyle , selectedImgFormat , selectedProjection , boundsEnabled ) ;
1014+ }
1015+ } ) ;
1016+ }
1017+
1018+ if ( dimensionCheckbox ) {
1019+ dimensionCheckbox . addEventListener ( 'change' , function ( e ) {
1020+ const layerCheckbox = document . getElementById ( 'layer-' + index ) ;
1021+ if ( layerCheckbox . checked ) {
1022+ const queryCheckbox = document . getElementById ( 'query-' + index ) ;
1023+ const queryEnabled = queryCheckbox ? queryCheckbox . checked : false ;
1024+ const formatSelect = document . getElementById ( 'format-' + index ) ;
1025+ const selectedFormat = formatSelect ? formatSelect . value : ( layer . infoFormats [ 0 ] || 'text/html' ) ;
1026+ const styleSelect = document . getElementById ( 'style-' + index ) ;
1027+ const selectedStyle = styleSelect ? styleSelect . value : ( layer . styles [ 0 ] ? layer . styles [ 0 ] . name : 'default' ) ;
1028+ const imgFormatSelect = document . getElementById ( 'img-format-' + index ) ;
1029+ const selectedImgFormat = imgFormatSelect ? imgFormatSelect . value : ( layer . formats [ 0 ] || 'image/png' ) ;
1030+ const projectionSelect = document . getElementById ( 'projection-' + index ) ;
1031+ const selectedProjection = projectionSelect ? projectionSelect . value : 'OSMTILE' ;
1032+ const boundsCheckbox = document . getElementById ( 'bounds-' + index ) ;
1033+ const boundsEnabled = boundsCheckbox ? boundsCheckbox . checked : true ;
1034+ // Recreate viewer when dimension is enabled/disabled
1035+ removeViewerForLayer ( index ) ;
1036+ createViewerForWMTSLayer ( index , layer , info , selectedFormat , queryEnabled , selectedStyle , selectedImgFormat , selectedProjection , boundsEnabled ) ;
1037+ }
1038+ } ) ;
1039+ }
1040+ }
1041+ } ) ;
1042+ }
9121043 } ) ;
9131044}
9141045
@@ -1753,14 +1884,14 @@ function createViewerForWMTSLayer(index, layer, serviceInfo, selectedFormat, que
17531884 viewer . appendChild ( baseLayer ) ;
17541885 }
17551886
1756- addWMTSLayerToViewer ( viewer , layer , tileMatrixSet , selectedFormat , queryEnabled , styleName , imgFormat , includeBounds ) ;
1887+ addWMTSLayerToViewer ( viewer , layer , tileMatrixSet , selectedFormat , queryEnabled , styleName , imgFormat , includeBounds , index ) ;
17571888
17581889 container . appendChild ( viewer ) ;
17591890
17601891 console . log ( 'Created WMTS viewer for layer:' , layer . name ) ;
17611892}
17621893
1763- function addWMTSLayerToViewer ( viewer , layer , tileMatrixSet , selectedFormat , queryEnabled , selectedStyle , imageFormat , boundsEnabled ) {
1894+ function addWMTSLayerToViewer ( viewer , layer , tileMatrixSet , selectedFormat , queryEnabled , selectedStyle , imageFormat , boundsEnabled , layerIndex ) {
17641895 const viewerProjection = viewer . getAttribute ( 'projection' ) || 'OSMTILE' ;
17651896 const bbox = layer . bbox ;
17661897
@@ -1791,6 +1922,29 @@ function addWMTSLayerToViewer(viewer, layer, tileMatrixSet, selectedFormat, quer
17911922 }
17921923 mapLayer . appendChild ( licenseLink ) ;
17931924 }
1925+
1926+ // Add legend link for the selected style only (before map-extent)
1927+ if ( layer . styles && layer . styles . length > 0 && styleName ) {
1928+ const selectedStyle = layer . styles . find ( function ( style ) { return style . name === styleName ; } ) ;
1929+ if ( selectedStyle && selectedStyle . legendURLs && selectedStyle . legendURLs . length > 0 ) {
1930+ // Only add the first legend URL for the selected style
1931+ const legend = selectedStyle . legendURLs [ 0 ] ;
1932+ const legendLink = document . createElement ( 'map-link' ) ;
1933+ legendLink . setAttribute ( 'rel' , 'legend' ) ;
1934+ legendLink . setAttribute ( 'href' , legend . href ) ;
1935+ if ( selectedStyle . title ) {
1936+ legendLink . setAttribute ( 'title' , selectedStyle . title ) ;
1937+ }
1938+ if ( legend . width ) {
1939+ legendLink . setAttribute ( 'width' , legend . width ) ;
1940+ }
1941+ if ( legend . height ) {
1942+ legendLink . setAttribute ( 'height' , legend . height ) ;
1943+ }
1944+ mapLayer . appendChild ( legendLink ) ;
1945+ console . log ( 'Added WMTS legend link for selected style:' , selectedStyle . title ) ;
1946+ }
1947+ }
17941948
17951949 const mapExtent = document . createElement ( 'map-extent' ) ;
17961950 mapExtent . setAttribute ( 'units' , viewerProjection ) ;
@@ -1874,6 +2028,46 @@ function addWMTSLayerToViewer(viewer, layer, tileMatrixSet, selectedFormat, quer
18742028
18752029 mapExtent . appendChild ( mapSelect ) ;
18762030 }
2031+
2032+ // Add dimension selectors if dimensions are available and not using template
2033+ if ( layer . dimensions && layer . dimensions . length > 0 ) {
2034+ layer . dimensions . forEach ( function ( dimension , dimIdx ) {
2035+ if ( ! dimension . usesTemplate ) {
2036+ // Check if this dimension is enabled in the UI
2037+ const dimensionCheckbox = layerIndex !== undefined ? document . getElementById ( 'dim-enabled-' + layerIndex + '-' + dimIdx ) : null ;
2038+ const isDimensionEnabled = ! dimensionCheckbox || dimensionCheckbox . checked ;
2039+
2040+ if ( ! isDimensionEnabled ) {
2041+ console . log ( 'Skipping disabled WMTS dimension:' , dimension . name ) ;
2042+ return ;
2043+ }
2044+
2045+ // Get the selected value from the UI dropdown
2046+ const dimensionSelect = layerIndex !== undefined ? document . getElementById ( 'dim-' + layerIndex + '-' + dimIdx ) : null ;
2047+ const selectedValue = dimensionSelect ? dimensionSelect . value : dimension . default ;
2048+
2049+ const mapSelect = document . createElement ( 'map-select' ) ;
2050+ mapSelect . setAttribute ( 'id' , dimension . name + '-selector' ) ;
2051+ mapSelect . setAttribute ( 'name' , dimension . name ) ;
2052+
2053+ dimension . values . forEach ( function ( value ) {
2054+ const mapOption = document . createElement ( 'map-option' ) ;
2055+ mapOption . setAttribute ( 'value' , value ) ;
2056+ mapOption . textContent = value ;
2057+
2058+ // Mark the selected value from UI
2059+ if ( value === selectedValue ) {
2060+ mapOption . setAttribute ( 'selected' , '' ) ;
2061+ }
2062+
2063+ mapSelect . appendChild ( mapOption ) ;
2064+ } ) ;
2065+
2066+ mapExtent . appendChild ( mapSelect ) ;
2067+ console . log ( 'Added WMTS dimension selector for:' , dimension . name , 'with' , dimension . values . length , 'options' ) ;
2068+ }
2069+ } ) ;
2070+ }
18772071
18782072 const tileResources = layer . resourceURLs [ 'tile' ] || [ ] ;
18792073 let tileResource = tileResources . find ( function ( r ) { return r . format === imgFormat ; } ) || tileResources [ 0 ] ;
@@ -1908,6 +2102,21 @@ function addWMTSLayerToViewer(viewer, layer, tileMatrixSet, selectedFormat, quer
19082102 tref = tref . replace ( / { S t y l e } / g, styleName ) ;
19092103 tref = tref . replace ( / { s t y l e } / g, styleName ) ;
19102104
2105+ // Handle dimension parameters in template URL
2106+ if ( layer . dimensions && layer . dimensions . length > 0 ) {
2107+ layer . dimensions . forEach ( function ( dimension ) {
2108+ const dimPattern = new RegExp ( '\\{' + dimension . name + '\\}' , 'g' ) ;
2109+ if ( dimension . usesTemplate ) {
2110+ // Replace with literal default value for >200 value dimensions
2111+ tref = tref . replace ( dimPattern , dimension . default ) ;
2112+ console . log ( 'Replaced WMTS dimension' , dimension . name , 'with default value:' , dimension . default ) ;
2113+ } else {
2114+ // Keep as template variable for MapML substitution
2115+ tref = tref . replace ( dimPattern , '{' + dimension . name + '}' ) ;
2116+ }
2117+ } ) ;
2118+ }
2119+
19112120 mapLink . setAttribute ( 'tref' , tref ) ;
19122121 mapExtent . appendChild ( mapLink ) ;
19132122 }
@@ -1948,6 +2157,20 @@ function addWMTSLayerToViewer(viewer, layer, tileMatrixSet, selectedFormat, quer
19482157 qtref = qtref . replace ( / { I n f o F o r m a t } / g, selectedFormat ) ;
19492158 qtref = qtref . replace ( / { i n f o f o r m a t } / g, selectedFormat ) ;
19502159
2160+ // Handle dimension parameters in query template URL
2161+ if ( layer . dimensions && layer . dimensions . length > 0 ) {
2162+ layer . dimensions . forEach ( function ( dimension ) {
2163+ const dimPattern = new RegExp ( '\\{' + dimension . name + '\\}' , 'g' ) ;
2164+ if ( dimension . usesTemplate ) {
2165+ // Replace with literal default value for >200 value dimensions
2166+ qtref = qtref . replace ( dimPattern , dimension . default ) ;
2167+ } else {
2168+ // Keep as template variable for MapML substitution
2169+ qtref = qtref . replace ( dimPattern , '{' + dimension . name + '}' ) ;
2170+ }
2171+ } ) ;
2172+ }
2173+
19512174 queryLink . setAttribute ( 'tref' , qtref ) ;
19522175 mapExtent . appendChild ( queryLink ) ;
19532176 }
0 commit comments