@@ -472,15 +472,15 @@ async function initMap() {
472472 resetViewEl . id = 'reset-view-btn' ;
473473 resetViewEl . title = 'Reset to top-down view' ;
474474 resetViewEl . textContent = '⊙' ;
475- resetViewEl . style . cssText = 'background:rgba(0,0,0,.6);color:#fff;font-size:14px ;border:none;border-radius:4px;cursor:pointer;padding:2px 8px ;font-family:monospace;display:none;line- height:1 ' ;
475+ resetViewEl . style . cssText = 'background:rgba(0,0,0,.6);color:#fff;font-size:11px ;border:none;border-radius:4px;cursor:pointer;padding:3px 6px ;font-family:monospace;display:none;box-sizing:border-box; height:19px ' ;
476476 resetViewEl . addEventListener ( 'click' , ( ) => { map . easeTo ( { bearing : 0 , duration : 500 } ) ; } ) ;
477477 pitchWrap . appendChild ( pitchEl ) ;
478478 pitchWrap . appendChild ( resetViewEl ) ;
479479 document . getElementById ( 'map' ) . appendChild ( pitchWrap ) ;
480480 const exaggerationEl = document . createElement ( 'div' ) ;
481481 exaggerationEl . id = 'exaggeration-ctrl' ;
482482 exaggerationEl . style . cssText = 'position:absolute;bottom:24px;left:310px;background:rgba(0,0,0,.6);color:#fff;font-size:11px;padding:2px 8px;border-radius:4px;z-index:10;font-family:monospace;display:none;align-items:center;gap:6px' ;
483- exaggerationEl . innerHTML = '⛰ <input type="range" id="exaggeration-slider" min="1" max="3" step="0.1" value="1.5 " style="width:70px;accent-color:#fff;vertical-align:middle"> <span id="exaggeration-val">1.5 ×</span>' ;
483+ exaggerationEl . innerHTML = '⛰ <input type="range" id="exaggeration-slider" min="1" max="3" step="0.1" value="1.2 " style="width:70px;accent-color:#fff;vertical-align:middle"> <span id="exaggeration-val">1.2 ×</span>' ;
484484 document . getElementById ( 'map' ) . appendChild ( exaggerationEl ) ;
485485 document . getElementById ( 'exaggeration-slider' ) . addEventListener ( 'input' , ( e ) => {
486486 const val = parseFloat ( e . target . value ) ;
@@ -614,9 +614,13 @@ async function initMap() {
614614 // Elevation — only in 3D Terrain mode
615615 let elevationStr = '' ;
616616 if ( _mapStyle === 'terrain3d' ) {
617- const elev = map . queryTerrainElevation ( [ lng , lat ] ) ;
618- if ( elev !== null && elev !== undefined ) {
619- elevationStr = `${ Math . round ( elev ) . toLocaleString ( ) } m` ;
617+ try {
618+ const elev = map . queryTerrainElevation ( [ lng , lat ] ) ;
619+ if ( elev !== null && elev !== undefined ) {
620+ elevationStr = `${ Math . round ( elev ) . toLocaleString ( ) } m` ;
621+ }
622+ } catch ( e ) {
623+ // Elevation error when zoom exceeds source maxzoom — ignore
620624 }
621625 }
622626
@@ -760,6 +764,15 @@ function _applyTerrainAndProjection() {
760764
761765 // Set projection FIRST — before any camera moves — so easeTo never runs under the wrong projection
762766 if ( map . setProjection ) {
767+ // Add atmospheric fog for depth perception in 3D modes
768+ if ( map . setFog ) {
769+ map . setFog ( {
770+ color : 'white' ,
771+ 'high-color' : '#add8e6' ,
772+ 'horizon-blend' : 0.3 ,
773+ atmosphere : 0.5
774+ } ) ;
775+ }
763776 map . setProjection ( { type : isGlobe ? 'globe' : 'mercator' } ) ;
764777 }
765778
@@ -769,12 +782,17 @@ function _applyTerrainAndProjection() {
769782 map . addSource ( 'terrain-dem' , {
770783 type : 'raster-dem' ,
771784 tiles : [ 'https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png' ] ,
772- tileSize : 512 , maxzoom : 15 , encoding : 'terrarium'
785+ tileSize : 512 , maxzoom : 17 , encoding : 'terrarium'
773786 } ) ;
774787 }
775788 const sliderEl = document . getElementById ( 'exaggeration-slider' ) ;
776- const exaggeration = sliderEl ? parseFloat ( sliderEl . value ) : 1.5 ;
789+ const exaggeration = sliderEl ? parseFloat ( sliderEl . value ) : 1.2 ;
777790 map . setTerrain ( { source : 'terrain-dem' , exaggeration } ) ;
791+ map . setPaintProperty ( 'terrain-dem' , 'raster-blur' , 3 ) ;
792+ // Add sky layer for atmospheric background in 3D
793+ if ( map . getLayer ( 'sky' ) === undefined ) {
794+ map . addLayer ( { id : 'sky' , type : 'sky' , paint : { 'sky-type' : 'gradient' , 'sky-gradient' : [ 'interpolate' , [ 'linear' ] , [ 'sky-radial-progress' ] , 0 , 'rgba(255,255,255,0)' , 0.5 , '#87cefa' , 1 , '#4682b4' ] } } ) ;
795+ }
778796 if ( ! map . getLayer ( 'terrain-hillshade' ) ) {
779797 // Insert below the first road/label layer so hillshading shows through
780798 const firstSymbol = map . getStyle ( ) . layers . find ( l => l . type === 'line' || l . type === 'symbol' ) ;
@@ -783,14 +801,30 @@ function _applyTerrainAndProjection() {
783801 type : 'hillshade' ,
784802 source : 'terrain-dem' ,
785803 paint : {
786- 'hillshade-exaggeration' : 0.75 ,
787- 'hillshade-illumination-direction' : 315 ,
788- 'hillshade-illumination-anchor' : 'map ' ,
804+ 'hillshade-exaggeration' : 1.0 ,
805+ 'hillshade-illumination-direction' : 270 ,
806+ 'hillshade-illumination-anchor' : 'viewport ' ,
789807 'hillshade-shadow-color' : '#1a2a35' ,
790808 'hillshade-highlight-color' : '#f0f4f8' ,
791809 'hillshade-accent-color' : '#2d4a5a' ,
792810 }
793811 } , firstSymbol ?. id ) ;
812+ // Add 3D building extrusion if OpenMapTiles source is present
813+ if ( map . getSource ( 'openmaptiles' ) && ! map . getLayer ( '3d-buildings' ) ) {
814+ map . addLayer ( {
815+ id : '3d-buildings' ,
816+ source : 'openmaptiles' ,
817+ 'source-layer' : 'building' ,
818+ type : 'fill-extrusion' ,
819+ minzoom : 15 ,
820+ paint : {
821+ 'fill-extrusion-color' : '#ddd' ,
822+ 'fill-extrusion-height' : [ 'get' , 'height' ] ,
823+ 'fill-extrusion-base' : [ 'get' , 'min_height' ] ,
824+ 'fill-extrusion-opacity' : 0.6
825+ }
826+ } , 'terrain-hillshade' ) ;
827+ }
794828 }
795829 map . easeTo ( { pitch : 50 , bearing : 0 , duration : 800 } ) ;
796830 } else {
0 commit comments