@@ -85,17 +85,19 @@ export class DatasetCatalog {
8585 * Process a single STAC collection into a DatasetEntry.
8686 */
8787 processCollection ( collection , options = { } ) {
88- // Build asset allowlist and per-asset overrides from config
89- let allowedAssets = null ;
90- const assetOptions = new Map ( ) ;
88+ // Build ordered asset config list.
89+ // Using an array (not a Map) so the same STAC asset can appear multiple times
90+ // under different aliases — e.g. one PMTiles split into "fee" and "easement" layers
91+ // via { id: "pmtiles", alias: "fee", ... } and { id: "pmtiles", alias: "easement", ... }.
92+ let assetConfigList = null ;
9193 if ( Array . isArray ( options . assets ) ) {
92- allowedAssets = new Set ( ) ;
94+ assetConfigList = [ ] ;
9395 for ( const a of options . assets ) {
9496 if ( typeof a === 'string' ) {
95- allowedAssets . add ( a ) ;
97+ assetConfigList . push ( { key : a , assetId : a , config : { } } ) ;
9698 } else if ( a && a . id ) {
97- allowedAssets . add ( a . id ) ;
98- assetOptions . set ( a . id , a ) ;
99+ const key = a . alias || a . id ;
100+ assetConfigList . push ( { key , assetId : a . id , config : a } ) ;
99101 }
100102 }
101103 }
@@ -114,7 +116,7 @@ export class DatasetCatalog {
114116 columns : this . extractColumns ( collection ) ,
115117
116118 // Visual assets (for map display) — filtered by config
117- mapLayers : this . extractMapLayers ( collection , options , allowedAssets , assetOptions ) ,
119+ mapLayers : this . extractMapLayers ( collection , options , assetConfigList ) ,
118120
119121 // Parquet/H3 assets (for SQL via MCP) — always load all
120122 parquetAssets : this . extractParquetAssets ( collection ) ,
@@ -132,47 +134,85 @@ export class DatasetCatalog {
132134 /**
133135 * Extract map-displayable assets (PMTiles and COGs).
134136 * Each becomes a potential map layer.
137+ *
138+ * When assetConfigList is provided (filtered mode), iterates the config entries
139+ * in order — supporting multiple logical layers from one STAC asset via alias.
140+ * When null, all visual assets from the STAC collection are included.
141+ *
135142 * @param {Object } collection - STAC collection
136143 * @param {Object } options - Collection-level options
137- * @param {Set|null } allowedAssets - If set, only include these asset IDs
138- * @param {Map } assetOptions - Per-asset overrides keyed by asset ID
144+ * @param {Array|null } assetConfigList - Ordered list of {key, assetId, config}
139145 */
140- extractMapLayers ( collection , options = { } , allowedAssets = null , assetOptions = new Map ( ) ) {
146+ extractMapLayers ( collection , options = { } , assetConfigList = null ) {
141147 const layers = [ ] ;
142- const assets = collection . assets || { } ;
143-
144- for ( const [ assetId , asset ] of Object . entries ( assets ) ) {
145- // Skip if an asset allowlist is specified and this asset isn't in it
146- if ( allowedAssets && ! allowedAssets . has ( assetId ) ) continue ;
147-
148- const type = asset . type || '' ;
149- const perAsset = assetOptions . get ( assetId ) || { } ;
150-
151- if ( type . includes ( 'pmtiles' ) ) {
152- layers . push ( {
153- assetId,
154- layerType : 'vector' ,
155- title : perAsset . display_name || asset . title || assetId ,
156- url : asset . href ,
157- sourceLayer : asset [ 'vector:layers' ] ?. [ 0 ] || asset [ 'pmtiles:layer' ] || assetId ,
158- description : asset . description || '' ,
159- defaultStyle : perAsset . default_style || null ,
160- tooltipFields : perAsset . tooltip_fields || null ,
161- defaultVisible : perAsset . visible === true ,
162- } ) ;
163- } else if ( type . includes ( 'geotiff' ) || type . includes ( 'tiff' ) ) {
164- const colormap = perAsset . colormap || options . colormap || 'reds' ;
165- const rescale = perAsset . rescale || options . rescale || null ;
166-
167- layers . push ( {
168- assetId,
169- layerType : 'raster' ,
170- title : perAsset . display_name || asset . title || assetId ,
171- cogUrl : asset . href ,
172- colormap,
173- rescale,
174- description : asset . description || '' ,
175- } ) ;
148+ const stacAssets = collection . assets || { } ;
149+
150+ if ( assetConfigList ) {
151+ // Filtered mode: iterate config entries so aliases and ordering are respected
152+ for ( const { key, assetId, config } of assetConfigList ) {
153+ const asset = stacAssets [ assetId ] ;
154+ if ( ! asset ) continue ;
155+
156+ const type = asset . type || '' ;
157+
158+ if ( type . includes ( 'pmtiles' ) ) {
159+ layers . push ( {
160+ assetId : key ,
161+ layerType : 'vector' ,
162+ title : config . display_name || asset . title || assetId ,
163+ url : asset . href ,
164+ sourceLayer : asset [ 'vector:layers' ] ?. [ 0 ] || asset [ 'pmtiles:layer' ] || assetId ,
165+ description : asset . description || '' ,
166+ defaultStyle : config . default_style || null ,
167+ tooltipFields : config . tooltip_fields || null ,
168+ defaultVisible : config . visible === true ,
169+ defaultFilter : config . default_filter || null ,
170+ } ) ;
171+ } else if ( type . includes ( 'geotiff' ) || type . includes ( 'tiff' ) ) {
172+ layers . push ( {
173+ assetId : key ,
174+ layerType : 'raster' ,
175+ title : config . display_name || asset . title || assetId ,
176+ cogUrl : asset . href ,
177+ colormap : config . colormap || options . colormap || 'reds' ,
178+ rescale : config . rescale || options . rescale || null ,
179+ description : asset . description || '' ,
180+ defaultVisible : config . visible === true ,
181+ defaultFilter : config . default_filter || null ,
182+ } ) ;
183+ }
184+ }
185+ } else {
186+ // Unfiltered mode: include all visual assets from the STAC collection
187+ for ( const [ assetId , asset ] of Object . entries ( stacAssets ) ) {
188+ const type = asset . type || '' ;
189+
190+ if ( type . includes ( 'pmtiles' ) ) {
191+ layers . push ( {
192+ assetId,
193+ layerType : 'vector' ,
194+ title : asset . title || assetId ,
195+ url : asset . href ,
196+ sourceLayer : asset [ 'vector:layers' ] ?. [ 0 ] || asset [ 'pmtiles:layer' ] || assetId ,
197+ description : asset . description || '' ,
198+ defaultStyle : null ,
199+ tooltipFields : null ,
200+ defaultVisible : false ,
201+ defaultFilter : null ,
202+ } ) ;
203+ } else if ( type . includes ( 'geotiff' ) || type . includes ( 'tiff' ) ) {
204+ layers . push ( {
205+ assetId,
206+ layerType : 'raster' ,
207+ title : asset . title || assetId ,
208+ cogUrl : asset . href ,
209+ colormap : options . colormap || 'reds' ,
210+ rescale : options . rescale || null ,
211+ description : asset . description || '' ,
212+ defaultVisible : false ,
213+ defaultFilter : null ,
214+ } ) ;
215+ }
176216 }
177217 }
178218
@@ -364,6 +404,7 @@ export class DatasetCatalog {
364404 columns : ds . columns ,
365405 tooltipFields : ml . tooltipFields || null ,
366406 defaultVisible : ml . defaultVisible || false ,
407+ defaultFilter : ml . defaultFilter || null ,
367408 } ) ;
368409 } else if ( ml . layerType === 'raster' ) {
369410 let tilesUrl = `${ this . titilerUrl } /cog/tiles/WebMercatorQuad/{z}/{x}/{y}.png?url=${ encodeURIComponent ( ml . cogUrl ) } &colormap_name=${ ml . colormap } ` ;
0 commit comments