1- import { Component , Element , Prop , State , h , Watch , Method , Event , EventEmitter } from '@stencil/core' ;
2- import maplibregl from 'maplibre-gl' ;
3- import { getPreviewSource , getPreviewLayers , getBoundsPreviewSource , getBoundsPreviewLayers , recordDeckGLCOGLayer } from '../../lib/sources' ;
4- import { MapboxOverlay as DeckOverlay } from '@deck.gl/mapbox' ;
1+ import { Component , Element , Prop , h , Watch , Method , Event , EventEmitter } from '@stencil/core' ;
52import { Protocol as PMTilesProtocol } from 'pmtiles' ;
3+ import maplibregl from 'maplibre-gl' ;
4+ import type { EaseToOptions } from 'maplibre-gl' ;
65
7- import { getElement } from '../../lib/elements' ;
86import type { OgmRecord } from '../../lib/record' ;
9- import type { AddLayerObject , EaseToOptions } from 'maplibre-gl' ;
10- import type { AddSourceObject } from '../../lib/sources' ;
7+ import { getElement } from '../../lib/elements' ;
8+ import { getSources } from '../../lib/sources' ;
9+ import { getMapPreviewers } from '../../lib/previewers' ;
10+ import Previewer from '../../lib/previewers/previewer' ;
1111
1212// Register PMTiles protocol
1313const protocol = new PMTilesProtocol ( ) ;
@@ -27,21 +27,15 @@ export class OgmMap {
2727 @Event ( ) mapIdle : EventEmitter < void > ;
2828 @Event ( ) mapLoading : EventEmitter < void > ;
2929
30- // Track sources and layers for bounds and preview
31- @State ( ) boundsSource : AddSourceObject | undefined ;
32- @State ( ) boundsLayers : AddLayerObject [ ] = [ ] ;
33- @State ( ) previewSource : AddSourceObject | undefined ;
34- @State ( ) previewLayers : AddLayerObject [ ] = [ ] ;
35-
3630 // MapLibre map instance
3731 private map : maplibregl . Map ;
3832
39- // Deck.gl overlay for rendering rasters
40- private deckOverlay : DeckOverlay ;
41-
4233 // Container element reference for fullscreen
4334 private containerEl : HTMLElement ;
4435
36+ // Previewers for the currently previewed sources
37+ private previewers : Previewer [ ] = [ ] ;
38+
4539 // Set up the mapLibre map and event bindings on load
4640 componentDidLoad ( ) {
4741 this . map = new maplibregl . Map ( {
@@ -50,13 +44,29 @@ export class OgmMap {
5044 cooperativeGestures : true ,
5145 style : this . baseMapStyle ,
5246 center : [ 0 , 0 ] ,
53- zoom : 1 ,
47+ zoom : 2 ,
48+ minZoom : 2 ,
5449 } ) ;
5550 this . getContainer ( ) ;
56- this . deckOverlay = new DeckOverlay ( { interleaved : true } ) ;
5751 this . addControls ( ) ;
5852 this . map . on ( 'idle' , ( ) => this . mapIdle . emit ( ) ) ;
5953 this . map . on ( 'load' , ( ) => this . previewRecord ( this . record ) ) ;
54+
55+ // View as a globe with atmosphere effects
56+ this . map . on ( 'style.load' , ( ) => {
57+ this . map . setProjection ( {
58+ type : 'globe' ,
59+ } ) ;
60+ this . map . setSky ( {
61+ 'sky-color' : '#199EF3' ,
62+ 'sky-horizon-blend' : 0.5 ,
63+ 'horizon-color' : '#ffffff' ,
64+ 'horizon-fog-blend' : 0.5 ,
65+ 'fog-color' : '#0000ff' ,
66+ 'fog-ground-blend' : 0.5 ,
67+ 'atmosphere-blend' : [ 'interpolate' , [ 'linear' ] , [ 'zoom' ] , 0 , 1 , 10 , 1 , 12 , 0 ] ,
68+ } ) ;
69+ } ) ;
6070 }
6171
6272 // Find the container element for the map (used for fullscreen control)
@@ -86,66 +96,41 @@ export class OgmMap {
8696 } ,
8797 } ) ,
8898 ) ;
89- this . map . addControl (
90- new maplibregl . AttributionControl ( {
91- compact : true ,
92- } ) ,
93- ) ;
94- this . map . addControl ( this . deckOverlay ) ;
9599 }
96100
97101 @Watch ( 'record' )
98102 async previewRecord ( record : OgmRecord ) {
99- // Clear the map of previous layers and sources; if nothing new, bail out
100- if ( this . record ) this . clearMap ( ) ;
101103 if ( ! record ) return ;
102-
103- // Indicate loading
104104 this . mapLoading . emit ( ) ;
105105
106- // Get the bounds
107- this . boundsSource = getBoundsPreviewSource ( this . record ) ;
108-
109- // If a COG, generate a Deck.gl COG layer
110- if ( this . record . references . cogUrl ) {
111- const deckLayer = recordDeckGLCOGLayer ( this . record ) ;
112- this . deckOverlay . setProps ( {
113- layers : [ deckLayer ] ,
114- } ) ;
115- }
106+ // Clear any existing preview layers and sources
107+ await Promise . all ( this . previewers . map ( previewer => previewer . clearPreview ( ) ) ) ;
108+ this . previewers = [ ] ;
116109
117- // Otherwise...
118- else if ( this . boundsSource ) {
119- // Add the sources for the record's geometry and the data preview
120- this . previewSource = await getPreviewSource ( this . record ) ;
110+ // Create sources and previewers using references
111+ const sources = getSources ( record ) ;
112+ const previewOptions = { fillColor : this . fillColor , lineColor : this . lineColor , opacity : this . previewOpacity / 100 } ;
113+ const previewers = await getMapPreviewers ( sources , this . map , previewOptions ) ;
121114
122- // If the record is not restricted and has a source, add preview layers
123- if ( this . previewSource && ! this . record . restricted ) {
124- this . previewLayers = await getPreviewLayers ( this . record , this . previewSource ) ;
125- }
126-
127- // Otherwise if we have bounds, just add the bounds preview layers
128- else {
129- this . boundsLayers = getBoundsPreviewLayers ( this . record ) ;
130- }
115+ // Preview each source
116+ for ( const previewer of previewers ) {
117+ this . previewers . push ( previewer ) ;
118+ await previewer . preview ( ) ;
131119 }
132120
133- // Fit the map to the record's bounding box, if we can
134- const bounds = this . record . getBounds ( ) ;
135- if ( bounds ) this . fitMapBounds ( bounds ) ;
136- }
121+ // Fit to bounds from the record
122+ const bounds = record . getBounds ( ) ;
123+ if ( bounds ) await this . fitMapBounds ( bounds ) ;
137124
138- // Remove all layers and sources from the map
139- clearMap ( ) {
140- this . boundsLayers = [ ] ;
141- this . previewLayers = [ ] ;
142- this . boundsSource = undefined ;
143- this . previewSource = undefined ;
125+ this . mapIdle . emit ( ) ;
144126 }
145127
146128 // Fit the map to the provided bounds
147- fitMapBounds ( bounds : maplibregl . LngLatBoundsLike ) {
148- this . map . fitBounds ( bounds , { padding : 40 } ) ;
129+ async fitMapBounds ( bounds : maplibregl . LngLatBoundsLike ) {
130+ return new Promise < void > ( resolve => {
131+ this . map . once ( 'moveend' , ( ) => resolve ( ) ) ;
132+ this . map . fitBounds ( bounds , { padding : this . padding } ) ;
133+ } ) ;
149134 }
150135
151136 // When padding is changed, move the map over to make room for the sidebar
@@ -160,94 +145,19 @@ export class OgmMap {
160145 return await this . map . easeTo ( options ) ;
161146 }
162147
163- // When new bounds/preview sources are set, swap them out for old ones
164- @Watch ( 'boundsSource' )
165- @Watch ( 'previewSource' )
166- updateSource ( source : AddSourceObject , oldSource : AddSourceObject ) {
167- if ( ! source && oldSource ) return this . removeSource ( oldSource . id ) ;
168- if ( source ) return this . addSource ( source ) ;
169- }
170-
171- // When new bounds/preview layers are set, swap them out for old ones
172- @Watch ( 'boundsLayers' )
173- @Watch ( 'previewLayers' )
174- updateLayers ( layers : AddLayerObject [ ] , oldLayers : AddLayerObject [ ] ) {
175- if ( ! layers || layers . length === 0 ) return oldLayers . forEach ( layer => this . removeLayer ( layer . id ) ) ;
176- layers . forEach ( layer => this . addLayer ( layer ) ) ;
177- }
178-
179- // Listen for opacity changes and adjust the preview layers
180- @Watch ( 'previewOpacity' )
181- updatePreviewOpacity ( opacity : number ) {
182- this . previewLayers . forEach ( layer => this . setLayerOpacity ( layer . id , opacity ) ) ;
183- }
184-
185- // Add a source to the map
186- private addSource ( source : AddSourceObject ) {
187- return this . map . addSource ( source . id , source . source ) ;
188- }
189-
190- // Remove a source from the map by ID
191- private removeSource ( id : string ) {
192- return this . map . removeSource ( id ) ;
193- }
194-
195- // Add a layer to the map and style it based on the theme
196- private addLayer ( layer : AddLayerObject ) {
197- this . map . addLayer ( layer ) ;
198- this . styleLayer ( layer . id ) ;
199- }
200-
201- // Remove a layer from the map by ID
202- private removeLayer ( id : string ) {
203- return this . map . removeLayer ( id ) ;
204- }
205-
206- // Style a layer based on the theme
207- private styleLayer ( id : string ) {
208- const layer = this . map . getLayer ( id ) ;
209- if ( ! layer ) return ;
210-
211- if ( layer . type === 'fill' ) {
212- this . map . setPaintProperty ( id , 'fill-color' , this . fillColor ) ;
213- this . map . setPaintProperty ( id , 'fill-outline-color' , this . lineColor ) ;
214- } else if ( layer . type === 'line' ) {
215- this . map . setPaintProperty ( id , 'line-color' , this . lineColor ) ;
216- } else if ( layer . type === 'circle' ) {
217- this . map . setPaintProperty ( id , 'circle-color' , this . fillColor ) ;
218- this . map . setPaintProperty ( id , 'circle-stroke-color' , this . lineColor ) ;
219- }
220- }
221-
222- // Set the opacity of a layer
223- private setLayerOpacity ( id : string , opacity : number ) {
224- const layer = this . map . getLayer ( id ) ;
225- if ( ! layer ) return ;
226-
227- if ( layer . type === 'raster' ) {
228- this . map . setPaintProperty ( layer . id , 'raster-opacity' , opacity / 100 ) ;
229- } else if ( layer . type === 'fill' ) {
230- this . map . setPaintProperty ( layer . id , 'fill-opacity' , opacity / 100 ) ;
231- } else if ( layer . type === 'line' ) {
232- this . map . setPaintProperty ( layer . id , 'line-opacity' , opacity / 100 ) ;
233- } else if ( layer . type === 'circle' ) {
234- this . map . setPaintProperty ( layer . id , 'circle-opacity' , opacity / 100 ) ;
235- }
236- }
237-
238148 // Base map style based on the theme
239149 private get baseMapStyle ( ) {
240150 return this . theme === 'dark' ? 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json' : 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json' ;
241151 }
242152
243153 // Fill colors for vector data based on the theme
244154 private get fillColor ( ) {
245- return window . getComputedStyle ( this . el ) . getPropertyValue ( '--sl-color-primary-200 ' ) ;
155+ return window . getComputedStyle ( this . el ) . getPropertyValue ( '--sl-color-primary-500 ' ) ;
246156 }
247157
248158 // Line/stroke color for vector data based on the theme
249159 private get lineColor ( ) {
250- return window . getComputedStyle ( this . el ) . getPropertyValue ( '--sl-color-primary-500 ' ) ;
160+ return window . getComputedStyle ( this . el ) . getPropertyValue ( '--sl-color-neutral-900 ' ) ;
251161 }
252162
253163 render ( ) {
0 commit comments