Google Maps and deck.gl HeatmapLayer

Visualize data with a deck.gl heatmap layer

This example uses uses the deck.gl HeatmapLayer to show bicycle parking capacity. Use the HeatmapLayer to visualize spatial distribution of data.

TypeScript

 // Initialize and add the map 
 let 
  
 map 
 : 
  
 google.maps.Map 
 ; 
 // Use global types for Deck.gl components 
 let 
  
 heatmapLayer 
 : 
  
 deck.HeatmapLayer 
 ; 
 let 
  
 googleMapsOverlay 
 : 
  
 deck.GoogleMapsOverlay 
 ; 
 let 
  
 marker 
 : 
  
 google.maps.marker.AdvancedMarkerElement 
  
 | 
  
 undefined 
 ; 
 let 
  
 infoWindow 
 : 
  
 google.maps.InfoWindow 
 ; 
 // Declare global namespace for Deck.gl to satisfy TypeScript compiler 
 declare 
  
 namespace 
  
 deck 
  
 { 
  
 class 
  
 HeatmapLayer 
  
 { 
  
 constructor 
 ( 
 props 
 : 
  
 any 
 ); 
  
 props 
 : 
  
 any 
 ; 
  
 clone 
 ( 
 props 
 : 
  
 any 
 ) 
 : 
  
 HeatmapLayer 
 ; 
  
 pickable 
 : 
  
 boolean 
 ; 
  
 // Added pickable property 
  
 } 
  
 class 
  
 GoogleMapsOverlay 
  
 { 
  
 constructor 
 ( 
 props 
 : 
  
 any 
 ); 
  
 setMap 
 ( 
 map 
 : 
  
 google.maps.Map 
  
 | 
  
 null 
 ) 
 : 
  
 void 
 ; 
  
 setProps 
 ( 
 props 
 : 
  
 any 
 ) 
 : 
  
 void 
 ; 
  
 } 
  
 // Add other Deck.gl types used globally if needed 
 } 
 async 
  
 function 
  
 initMap 
 () 
 : 
  
 Promise<void> 
  
 { 
  
 // Progress bar logic moved from index.html 
  
 var 
  
 progress 
 , 
  
 progressDiv 
  
 = 
  
 document 
 . 
 querySelector 
 ( 
 ".mdc-linear-progress" 
 ); 
  
 if 
  
 ( 
 progressDiv 
 ) 
  
 { 
  
 // Assuming 'mdc' is globally available, potentially loaded via a script tag 
  
 // If not, you might need to import it or add type definitions. 
  
 // @ts-ignore 
  
 progress 
  
 = 
  
 new 
  
 mdc 
 . 
 linearProgress 
 . 
 MDCLinearProgress 
 ( 
 progressDiv 
  
 as 
  
 HTMLElement 
 ); 
  
 progress 
 . 
 open 
 (); 
  
 progress 
 . 
 determinate 
  
 = 
  
 false 
 ; 
  
 progress 
 . 
 done 
  
 = 
  
 function 
  
 () 
  
 { 
  
 progress 
 . 
 close 
 (); 
  
 progressDiv 
 ? 
 . 
 remove 
 (); 
  
 // Use optional chaining in case progressDiv is null 
  
 }; 
  
 } 
  
 // The location for the map center. 
  
 const 
  
 position 
  
 = 
  
 { 
 lat 
 : 
 37.77325660358167 
 , 
  
 lng 
 :- 
 122.41712341793448 
 }; 
  
 // Using the center from original deckgl-polygon.js 
  
 //  Request needed libraries. 
  
 const 
  
 { 
 Map 
 , 
  
 InfoWindow 
 } 
  
 = 
  
 await 
  
 google 
 . 
 maps 
 . 
 importLibrary 
 ( 
 'maps' 
 ) 
  
 as 
  
 google 
 . 
 maps 
 . 
 MapsLibrary 
 ; 
  
 const 
  
 { 
 AdvancedMarkerElement 
 } 
  
 = 
  
 await 
  
 google 
 . 
 maps 
 . 
 importLibrary 
 ( 
 'marker' 
 ) 
  
 as 
  
 google 
 . 
 maps 
 . 
 MarkerLibrary 
 ; 
  
 const 
  
 mapDiv 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'map' 
 ); 
  
 if 
  
 ( 
 ! 
 mapDiv 
 ) 
  
 { 
  
 console 
 . 
 error 
 ( 
 'Map element not found!' 
 ); 
  
 return 
 ; 
  
 } 
  
 // The map, centered at the specified position 
  
 map 
  
 = 
  
 new 
  
 Map 
 ( 
 mapDiv 
 , 
  
 { 
  
 zoom 
 : 
  
 13 
 , 
  
 // Using the zoom from original deckgl-polygon.js 
  
 center 
 : 
  
 position 
 , 
  
 tilt 
 : 
  
 90 
 , 
  
 // Add tilt 
  
 heading 
 : 
  
 - 
 25 
 , 
  
 // Add heading 
  
 mapId 
 : 
  
 '6b73a9fe7e831a00' 
 , 
  
 fullscreenControl 
 : 
  
 false 
 , 
  
 // Disable fullscreen control 
  
 clickableIcons 
 : 
  
 false 
 , 
  
 // Disable clicks on base map POIs 
  
 }); 
  
 // Deck.gl Layer and Overlay 
  
 // Use global deck object 
  
 heatmapLayer 
  
 = 
  
 new 
  
 deck 
 . 
 HeatmapLayer 
 ({ 
  
 // Assign to the outer heatmapLayer 
  
 id 
 : 
  
 'HeatmapLayer' 
 , 
  
 // Change layer ID 
  
 data 
 : 
  
 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json' 
 , 
  
 // Use the loaded data 
  
 getPosition 
 : 
  
 ( 
 d 
 : 
  
 any 
 ) 
  
 = 
>  
 d 
 . 
 COORDINATES 
 , 
  
 // Use 'any' for simplicity, or define a proper type 
  
 getWeight 
 : 
  
 ( 
 d 
 : 
  
 any 
 ) 
  
 = 
>  
 d 
 . 
 SPACES 
 , 
  
 // Use 'any' for simplicity, or define a proper type 
  
 radiusPixels 
 : 
  
 25 
 , 
  
 // Adjust radius as in user's example 
  
 visible 
 : 
  
 true 
 , 
  
 pickable 
 : 
  
 true 
 , 
  
 onHover 
 : 
  
 ( 
 info 
 : 
  
 any 
 ) 
  
 = 
>  
 { 
  
 // Use 'any' for info for simplicity, or define a proper type 
  
 const 
  
 tooltip 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'tooltip' 
 ); 
  
 if 
  
 ( 
 tooltip 
 ) 
  
 { 
  
 console 
 . 
 log 
 ( 
 'Hovered object:' 
 , 
  
 info 
 . 
 object 
 ); 
  
 if 
  
 ( 
 info 
 . 
 object 
 ) 
  
 { 
  
 // Format data for tooltip (display ADDRESS, RACKS, SPACES) 
  
 let 
  
 tooltipContent 
  
 = 
  
 '<h4>Bike Parking Info:</h4>' 
 ; 
  
 // Updated title 
  
 if 
  
 ( 
 info 
 . 
 object 
 . 
 ADDRESS 
  
 !== 
  
 undefined 
 ) 
  
 { 
  
 tooltipContent 
  
 += 
  
 `<strong>Address:</strong> 
 ${ 
 info 
 . 
 object 
 . 
 ADDRESS 
 } 
< br>` 
 ; 
  
 } 
  
 if 
  
 ( 
 info 
 . 
 object 
 . 
 RACKS 
  
 !== 
  
 undefined 
 ) 
  
 { 
  
 tooltipContent 
  
 += 
  
 `<strong>Racks:</strong> 
 ${ 
 info 
 . 
 object 
 . 
 RACKS 
 } 
< br>` 
 ; 
  
 } 
  
 if 
  
 ( 
 info 
 . 
 object 
 . 
 SPACES 
  
 !== 
  
 undefined 
 ) 
  
 { 
  
 tooltipContent 
  
 += 
  
 `<strong>Spaces:</strong> 
 ${ 
 info 
 . 
 object 
 . 
 SPACES 
 } 
< br>` 
 ; 
  
 } 
  
 tooltip 
 . 
 innerHTML 
  
 = 
  
 tooltipContent 
 ; 
  
 tooltip 
 . 
 style 
 . 
 left 
  
 = 
  
 info 
 . 
 x 
  
 + 
  
 'px' 
 ; 
  
 tooltip 
 . 
 style 
 . 
 top 
  
 = 
  
 info 
 . 
 y 
  
 + 
  
 'px' 
 ; 
  
 tooltip 
 . 
 style 
 . 
 display 
  
 = 
  
 'block' 
 ; 
  
 } 
  
 else 
  
 { 
  
 tooltip 
 . 
 style 
 . 
 display 
  
 = 
  
 'none' 
 ; 
  
 } 
  
 console 
 . 
 log 
 ( 
 'Tooltip content set to:' 
 , 
  
 tooltip 
 . 
 innerHTML 
 ); 
  
 } 
  
 } 
  
 }); 
  
 heatmapLayer 
 . 
 pickable 
  
 = 
  
 true 
 ; 
  
 // Ensure pickable is true after creation 
  
 // Use global deck object 
  
 googleMapsOverlay 
  
 = 
  
 new 
  
 deck 
 . 
 GoogleMapsOverlay 
 ({ 
  
 // Assign to the outer googleMapsOverlay 
  
 layers 
 : 
  
 [ 
 heatmapLayer 
 ], 
  
 controller 
 : 
  
 true 
  
 // Enable Deck.gl to control map view 
  
 }); 
  
 googleMapsOverlay 
 . 
 setMap 
 ( 
 map 
 ); 
  
 // Hide progress bar after data is loaded and layer is added 
  
 if 
  
 ( 
 progress 
 ) 
  
 { 
  
 // Check if progress is defined 
  
 // Add a small delay to ensure the progress bar is removed 
  
 setTimeout 
 (() 
  
 = 
>  
 { 
  
 // @ts-ignore 
  
 progress 
 . 
 done 
 (); 
  
 // hides progress bar 
  
 }, 
  
 100 
 ); 
  
 // 100ms delay 
  
 } 
  
 // Create a single InfoWindow instance 
  
 infoWindow 
  
 = 
  
 new 
  
 InfoWindow 
 (); 
  
 // Add click listener to the map 
  
 map 
 . 
 addListener 
 ( 
 'click' 
 , 
  
 async 
  
 ( 
 event 
 : 
  
 google.maps.MapMouseEvent 
 ) 
  
 = 
>  
 { 
  
 const 
  
 latLng 
  
 = 
  
 event 
 . 
 latLng 
 ; 
  
 if 
  
 ( 
 ! 
 latLng 
 ) 
  
 return 
 ; 
  
 // Ensure latLng is not null 
  
 if 
  
 ( 
 ! 
 marker 
 ) 
  
 { 
  
 // Create the marker on the first click 
  
 marker 
  
 = 
  
 new 
  
 AdvancedMarkerElement 
 ({ 
  
 map 
 : 
  
 map 
 , 
  
 position 
 : 
  
 latLng 
 , 
  
 gmpClickable 
 : 
  
 true 
 , 
  
 }); 
  
 // Add click listener to the marker 
  
 marker 
 . 
 addListener 
 ( 
 "click" 
 , 
  
 () 
  
 = 
>  
 { 
  
 infoWindow 
 . 
 close 
 (); 
  
 const 
  
 content 
  
 = 
  
 ` 
 <div>Location: 
 ${ 
 latLng 
 . 
 lat 
 (). 
 toFixed 
 ( 
 3 
 ) 
 } 
 , 
 ${ 
 latLng 
 . 
 lng 
 (). 
 toFixed 
 ( 
 3 
 ) 
 } 
< /div 
> <div><a href="https://www.google.com/maps/search/?api=1&query= 
 ${ 
 latLng 
 . 
 lat 
 () 
 } 
 , 
 ${ 
 latLng 
 . 
 lng 
 () 
 } 
 " target="_blank">Open in Google Maps</a></div> 
 ` 
 ; 
  
 infoWindow 
 . 
 setContent 
 ( 
 content 
 ); 
  
 infoWindow 
 . 
 open 
 ( 
 map 
 , 
  
 marker 
 ); 
  
 }); 
  
 // Open InfoWindow immediately on first click 
  
 const 
  
 content 
  
 = 
  
 ` 
 <div>Location: 
 ${ 
 latLng 
 . 
 lat 
 (). 
 toFixed 
 ( 
 3 
 ) 
 } 
 , 
 ${ 
 latLng 
 . 
 lng 
 (). 
 toFixed 
 ( 
 3 
 ) 
 } 
< /div 
> <div><a href="https://www.google.com/maps/search/?api=1&query= 
 ${ 
 latLng 
 . 
 lat 
 () 
 } 
 , 
 ${ 
 latLng 
 . 
 lng 
 () 
 } 
 " target="_blank">Open in Google Maps</a></div> 
 ` 
 ; 
  
 infoWindow 
 . 
 setContent 
 ( 
 content 
 ); 
  
 infoWindow 
 . 
 open 
 ( 
 map 
 , 
  
 marker 
 ); 
  
 } 
  
 else 
  
 { 
  
 // Move the existing marker on subsequent clicks 
  
 marker 
 . 
 position 
  
 = 
  
 latLng 
 ; 
  
 // InfoWindow remains open 
  
 const 
  
 content 
  
 = 
  
 ` 
 <div>Location: 
 ${ 
 latLng 
 . 
 lat 
 (). 
 toFixed 
 ( 
 3 
 ) 
 } 
 , 
 ${ 
 latLng 
 . 
 lng 
 (). 
 toFixed 
 ( 
 3 
 ) 
 } 
< /div 
> <div><a href="https://www.google.com/maps/search/?api=1&query= 
 ${ 
 latLng 
 . 
 lat 
 () 
 } 
 , 
 ${ 
 latLng 
 . 
 lng 
 () 
 } 
 " target="_blank">Open in Google Maps</a></div> 
 ` 
 ; 
  
 infoWindow 
 . 
 setContent 
 ( 
 content 
 ); 
  
 infoWindow 
 . 
 open 
 ( 
 map 
 , 
  
 marker 
 ); 
  
 } 
  
 }); 
  
 // Button functionality 
  
 const 
  
 toggleButton 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'toggleButton' 
 ); 
  
 if 
  
 ( 
 toggleButton 
 ) 
  
 { 
  
 // Check if toggleButton is found 
  
 toggleButton 
 . 
 addEventListener 
 ( 
 'click' 
 , 
  
 () 
  
 = 
>  
 { 
  
 const 
  
 currentVisible 
  
 = 
  
 heatmapLayer 
 . 
 props 
 . 
 visible 
 ; 
  
 // Create a new layer instance with toggled visibility and update the overlay 
  
 const 
  
 newLayer 
  
 = 
  
 heatmapLayer 
 . 
 clone 
 ({ 
  
 visible 
 : 
  
 ! 
 currentVisible 
  
 }); 
  
 googleMapsOverlay 
 . 
 setProps 
 ({ 
  
 layers 
 : 
  
 [ 
 newLayer 
 ] 
  
 }); 
  
 heatmapLayer 
  
 = 
  
 newLayer 
 ; 
  
 // Update the heatmapLayer variable 
  
 toggleButton 
 . 
 textContent 
  
 = 
  
 ! 
 currentVisible 
  
 ? 
  
 'Hide Heatmap Layer' 
  
 : 
  
 'Show Heatmap Layer' 
 ; 
  
 }); 
  
 } 
 } 
 initMap 
 (); 
  

JavaScript

 // Initialize and add the map 
 let 
  
 map 
 ; 
 // Use global types for Deck.gl components 
 let 
  
 heatmapLayer 
 ; 
 let 
  
 googleMapsOverlay 
 ; 
 let 
  
 marker 
 ; 
 let 
  
 infoWindow 
 ; 
 async 
  
 function 
  
 initMap 
 () 
  
 { 
  
 // Progress bar logic moved from index.html 
  
 var 
  
 progress 
 , 
  
 progressDiv 
  
 = 
  
 document 
 . 
 querySelector 
 ( 
 ".mdc-linear-progress" 
 ); 
  
 if 
  
 ( 
 progressDiv 
 ) 
  
 { 
  
 // Assuming 'mdc' is globally available, potentially loaded via a script tag 
  
 // If not, you might need to import it or add type definitions. 
  
 // @ts-ignore 
  
 progress 
  
 = 
  
 new 
  
 mdc 
 . 
 linearProgress 
 . 
 MDCLinearProgress 
 ( 
 progressDiv 
 ); 
  
 progress 
 . 
 open 
 (); 
  
 progress 
 . 
 determinate 
  
 = 
  
 false 
 ; 
  
 progress 
 . 
 done 
  
 = 
  
 function 
  
 () 
  
 { 
  
 progress 
 . 
 close 
 (); 
  
 progressDiv 
 ? 
 . 
 remove 
 (); 
  
 // Use optional chaining in case progressDiv is null 
  
 }; 
  
 } 
  
 // The location for the map center. 
  
 const 
  
 position 
  
 = 
  
 { 
  
 lat 
 : 
  
 37.77325660358167 
 , 
  
 lng 
 : 
  
 - 
 122.41712341793448 
  
 }; 
  
 // Using the center from original deckgl-polygon.js 
  
 //  Request needed libraries. 
  
 const 
  
 { 
  
 Map 
 , 
  
 InfoWindow 
  
 } 
  
 = 
  
 await 
  
 google 
 . 
 maps 
 . 
 importLibrary 
 ( 
 'maps' 
 ); 
  
 const 
  
 { 
  
 AdvancedMarkerElement 
  
 } 
  
 = 
  
 await 
  
 google 
 . 
 maps 
 . 
 importLibrary 
 ( 
 'marker' 
 ); 
  
 const 
  
 mapDiv 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'map' 
 ); 
  
 if 
  
 ( 
 ! 
 mapDiv 
 ) 
  
 { 
  
 console 
 . 
 error 
 ( 
 'Map element not found!' 
 ); 
  
 return 
 ; 
  
 } 
  
 // The map, centered at the specified position 
  
 map 
  
 = 
  
 new 
  
 Map 
 ( 
 mapDiv 
 , 
  
 { 
  
 zoom 
 : 
  
 13 
 , 
  
 // Using the zoom from original deckgl-polygon.js 
  
 center 
 : 
  
 position 
 , 
  
 tilt 
 : 
  
 90 
 , 
  
 // Add tilt 
  
 heading 
 : 
  
 - 
 25 
 , 
  
 // Add heading 
  
 mapId 
 : 
  
 '6b73a9fe7e831a00' 
 , 
  
 fullscreenControl 
 : 
  
 false 
 , 
  
 // Disable fullscreen control 
  
 clickableIcons 
 : 
  
 false 
 , 
  
 // Disable clicks on base map POIs 
  
 }); 
  
 // Deck.gl Layer and Overlay 
  
 // Use global deck object 
  
 heatmapLayer 
  
 = 
  
 new 
  
 deck 
 . 
 HeatmapLayer 
 ({ 
  
 id 
 : 
  
 'HeatmapLayer' 
 , 
  
 // Change layer ID 
  
 data 
 : 
  
 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json' 
 , 
  
 // Use the loaded data 
  
 getPosition 
 : 
  
 ( 
 d 
 ) 
  
 = 
>  
 d 
 . 
 COORDINATES 
 , 
  
 // Use 'any' for simplicity, or define a proper type 
  
 getWeight 
 : 
  
 ( 
 d 
 ) 
  
 = 
>  
 d 
 . 
 SPACES 
 , 
  
 // Use 'any' for simplicity, or define a proper type 
  
 radiusPixels 
 : 
  
 25 
 , 
  
 // Adjust radius as in user's example 
  
 visible 
 : 
  
 true 
 , 
  
 pickable 
 : 
  
 true 
 , 
  
 onHover 
 : 
  
 ( 
 info 
 ) 
  
 = 
>  
 { 
  
 const 
  
 tooltip 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'tooltip' 
 ); 
  
 if 
  
 ( 
 tooltip 
 ) 
  
 { 
  
 console 
 . 
 log 
 ( 
 'Hovered object:' 
 , 
  
 info 
 . 
 object 
 ); 
  
 if 
  
 ( 
 info 
 . 
 object 
 ) 
  
 { 
  
 // Format data for tooltip (display ADDRESS, RACKS, SPACES) 
  
 let 
  
 tooltipContent 
  
 = 
  
 '<h4>Bike Parking Info:</h4>' 
 ; 
  
 // Updated title 
  
 if 
  
 ( 
 info 
 . 
 object 
 . 
 ADDRESS 
  
 !== 
  
 undefined 
 ) 
  
 { 
  
 tooltipContent 
  
 += 
  
 `<strong>Address:</strong> 
 ${ 
 info 
 . 
 object 
 . 
 ADDRESS 
 } 
< br>` 
 ; 
  
 } 
  
 if 
  
 ( 
 info 
 . 
 object 
 . 
 RACKS 
  
 !== 
  
 undefined 
 ) 
  
 { 
  
 tooltipContent 
  
 += 
  
 `<strong>Racks:</strong> 
 ${ 
 info 
 . 
 object 
 . 
 RACKS 
 } 
< br>` 
 ; 
  
 } 
  
 if 
  
 ( 
 info 
 . 
 object 
 . 
 SPACES 
  
 !== 
  
 undefined 
 ) 
  
 { 
  
 tooltipContent 
  
 += 
  
 `<strong>Spaces:</strong> 
 ${ 
 info 
 . 
 object 
 . 
 SPACES 
 } 
< br>` 
 ; 
  
 } 
  
 tooltip 
 . 
 innerHTML 
  
 = 
  
 tooltipContent 
 ; 
  
 tooltip 
 . 
 style 
 . 
 left 
  
 = 
  
 info 
 . 
 x 
  
 + 
  
 'px' 
 ; 
  
 tooltip 
 . 
 style 
 . 
 top 
  
 = 
  
 info 
 . 
 y 
  
 + 
  
 'px' 
 ; 
  
 tooltip 
 . 
 style 
 . 
 display 
  
 = 
  
 'block' 
 ; 
  
 } 
  
 else 
  
 { 
  
 tooltip 
 . 
 style 
 . 
 display 
  
 = 
  
 'none' 
 ; 
  
 } 
  
 console 
 . 
 log 
 ( 
 'Tooltip content set to:' 
 , 
  
 tooltip 
 . 
 innerHTML 
 ); 
  
 } 
  
 } 
  
 }); 
  
 heatmapLayer 
 . 
 pickable 
  
 = 
  
 true 
 ; 
  
 // Ensure pickable is true after creation 
  
 // Use global deck object 
  
 googleMapsOverlay 
  
 = 
  
 new 
  
 deck 
 . 
 GoogleMapsOverlay 
 ({ 
  
 layers 
 : 
  
 [ 
 heatmapLayer 
 ], 
  
 controller 
 : 
  
 true 
  
 // Enable Deck.gl to control map view 
  
 }); 
  
 googleMapsOverlay 
 . 
 setMap 
 ( 
 map 
 ); 
  
 // Hide progress bar after data is loaded and layer is added 
  
 if 
  
 ( 
 progress 
 ) 
  
 { 
  
 // Check if progress is defined 
  
 // Add a small delay to ensure the progress bar is removed 
  
 setTimeout 
 (() 
  
 = 
>  
 { 
  
 // @ts-ignore 
  
 progress 
 . 
 done 
 (); 
  
 // hides progress bar 
  
 }, 
  
 100 
 ); 
  
 // 100ms delay 
  
 } 
  
 // Create a single InfoWindow instance 
  
 infoWindow 
  
 = 
  
 new 
  
 InfoWindow 
 (); 
  
 // Add click listener to the map 
  
 map 
 . 
 addListener 
 ( 
 'click' 
 , 
  
 async 
  
 ( 
 event 
 ) 
  
 = 
>  
 { 
  
 const 
  
 latLng 
  
 = 
  
 event 
 . 
 latLng 
 ; 
  
 if 
  
 ( 
 ! 
 latLng 
 ) 
  
 return 
 ; 
  
 // Ensure latLng is not null 
  
 if 
  
 ( 
 ! 
 marker 
 ) 
  
 { 
  
 // Create the marker on the first click 
  
 marker 
  
 = 
  
 new 
  
 AdvancedMarkerElement 
 ({ 
  
 map 
 : 
  
 map 
 , 
  
 position 
 : 
  
 latLng 
 , 
  
 gmpClickable 
 : 
  
 true 
 , 
  
 }); 
  
 // Add click listener to the marker 
  
 marker 
 . 
 addListener 
 ( 
 "click" 
 , 
  
 () 
  
 = 
>  
 { 
  
 infoWindow 
 . 
 close 
 (); 
  
 const 
  
 content 
  
 = 
  
 ` 
 <div>Location: 
 ${ 
 latLng 
 . 
 lat 
 (). 
 toFixed 
 ( 
 3 
 ) 
 } 
 , 
 ${ 
 latLng 
 . 
 lng 
 (). 
 toFixed 
 ( 
 3 
 ) 
 } 
< /div 
> <div><a href="https://www.google.com/maps/search/?api=1&query= 
 ${ 
 latLng 
 . 
 lat 
 () 
 } 
 , 
 ${ 
 latLng 
 . 
 lng 
 () 
 } 
 " target="_blank">Open in Google Maps</a></div> 
 ` 
 ; 
  
 infoWindow 
 . 
 setContent 
 ( 
 content 
 ); 
  
 infoWindow 
 . 
 open 
 ( 
 map 
 , 
  
 marker 
 ); 
  
 }); 
  
 // Open InfoWindow immediately on first click 
  
 const 
  
 content 
  
 = 
  
 ` 
 <div>Location: 
 ${ 
 latLng 
 . 
 lat 
 (). 
 toFixed 
 ( 
 3 
 ) 
 } 
 , 
 ${ 
 latLng 
 . 
 lng 
 (). 
 toFixed 
 ( 
 3 
 ) 
 } 
< /div 
> <div><a href="https://www.google.com/maps/search/?api=1&query= 
 ${ 
 latLng 
 . 
 lat 
 () 
 } 
 , 
 ${ 
 latLng 
 . 
 lng 
 () 
 } 
 " target="_blank">Open in Google Maps</a></div> 
 ` 
 ; 
  
 infoWindow 
 . 
 setContent 
 ( 
 content 
 ); 
  
 infoWindow 
 . 
 open 
 ( 
 map 
 , 
  
 marker 
 ); 
  
 } 
  
 else 
  
 { 
  
 // Move the existing marker on subsequent clicks 
  
 marker 
 . 
 position 
  
 = 
  
 latLng 
 ; 
  
 // InfoWindow remains open 
  
 const 
  
 content 
  
 = 
  
 ` 
 <div>Location: 
 ${ 
 latLng 
 . 
 lat 
 (). 
 toFixed 
 ( 
 3 
 ) 
 } 
 , 
 ${ 
 latLng 
 . 
 lng 
 (). 
 toFixed 
 ( 
 3 
 ) 
 } 
< /div 
> <div><a href="https://www.google.com/maps/search/?api=1&query= 
 ${ 
 latLng 
 . 
 lat 
 () 
 } 
 , 
 ${ 
 latLng 
 . 
 lng 
 () 
 } 
 " target="_blank">Open in Google Maps</a></div> 
 ` 
 ; 
  
 infoWindow 
 . 
 setContent 
 ( 
 content 
 ); 
  
 infoWindow 
 . 
 open 
 ( 
 map 
 , 
  
 marker 
 ); 
  
 } 
  
 }); 
  
 // Button functionality 
  
 const 
  
 toggleButton 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'toggleButton' 
 ); 
  
 if 
  
 ( 
 toggleButton 
 ) 
  
 { 
  
 // Check if toggleButton is found 
  
 toggleButton 
 . 
 addEventListener 
 ( 
 'click' 
 , 
  
 () 
  
 = 
>  
 { 
  
 const 
  
 currentVisible 
  
 = 
  
 heatmapLayer 
 . 
 props 
 . 
 visible 
 ; 
  
 // Create a new layer instance with toggled visibility and update the overlay 
  
 const 
  
 newLayer 
  
 = 
  
 heatmapLayer 
 . 
 clone 
 ({ 
  
 visible 
 : 
  
 ! 
 currentVisible 
  
 }); 
  
 googleMapsOverlay 
 . 
 setProps 
 ({ 
  
 layers 
 : 
  
 [ 
 newLayer 
 ] 
  
 }); 
  
 heatmapLayer 
  
 = 
  
 newLayer 
 ; 
  
 // Update the heatmapLayer variable 
  
 toggleButton 
 . 
 textContent 
  
 = 
  
 ! 
 currentVisible 
  
 ? 
  
 'Hide Heatmap Layer' 
  
 : 
  
 'Show Heatmap Layer' 
 ; 
  
 }); 
  
 } 
 } 
 initMap 
 (); 
  

CSS

 /* 
 * Always set the map height explicitly to define the size of the div element 
 * that contains the map. 
 */ 
  
 # 
 map 
  
 { 
  
 height 
 : 
  
 100 
 % 
 ; 
  
 flex-grow 
 : 
  
 1 
 ; 
  
 /* Make map take up remaining space */ 
 } 
 /* 
 * Optional: Makes the sample page fill the window. 
 */ 
 html 
 , 
 body 
  
 { 
  
 height 
 : 
  
 100 
 % 
 ; 
  
 margin 
 : 
  
 0 
 ; 
  
 padding 
 : 
  
 0 
 ; 
  
 display 
 : 
  
 flex 
 ; 
  
 /* Use flexbox for layout */ 
  
 flex-direction 
 : 
  
 column 
 ; 
  
 /* Stack children vertically */ 
  
 position 
 : 
  
 relative 
 ; 
  
 /* Set body as positioning context */ 
  
 font-family 
 : 
  
 'Roboto' 
 , 
  
 Arial 
 , 
  
 sans-serif 
 ; 
  
 /* Set font family */ 
 } 
 # 
 toggleButton 
  
 { 
 position 
 : 
  
 absolute 
 ; 
 top 
 : 
  
 70 
 px 
 ; 
  
 /* Position towards the top of the map area */ 
 left 
 : 
  
 50 
 % 
 ; 
 transform 
 : 
  
 translateX 
 ( 
 -50 
 % 
 ); 
 z-index 
 : 
  
 1000 
 ; 
  
 /* Ensure it's above the map */ 
 } 
 # 
 tooltip 
  
 { 
 position 
 : 
  
 absolute 
 ; 
 z-index 
 : 
  
 1001 
 ; 
  
 /* Ensure it's above the button and map */ 
 padding 
 : 
  
 10 
 px 
 ; 
 background 
 : 
  
 rgba 
 ( 
 0 
 , 
  
 0 
 , 
  
 0 
 , 
  
 0.8 
 ); 
 color 
 : 
  
 #fff 
 ; 
 border-radius 
 : 
  
 4 
 px 
 ; 
 pointer-events 
 : 
  
 none 
 ; 
  
 /* Allows interaction with elements behind the tooltip */ 
 display 
 : 
  
 none 
 ; 
  
 /* Hidden by default */ 
 font-size 
 : 
  
 126 
 x 
 ; 
 } 
 h1 
  
 { 
 text-align 
 : 
  
 center 
 ; 
 margin 
 : 
 10 
 px 
 ; 
 } 
 # 
 legend 
  
 { 
  
 position 
 : 
  
 absolute 
 ; 
  
 top 
 : 
  
 90 
 px 
 ; 
  
 /* Adjust position as needed */ 
  
 right 
 : 
  
 10 
 px 
 ; 
  
 /* Position on the left */ 
  
 z-index 
 : 
  
 1000 
 ; 
  
 /* Ensure it's above the map */ 
  
 background-color 
 : 
  
 rgba 
 ( 
 255 
 , 
  
 255 
 , 
  
 255 
 , 
  
 0.9 
 ); 
  
 padding 
 : 
  
 10 
 px 
 ; 
  
 border-radius 
 : 
  
 4 
 px 
 ; 
  
 font-size 
 : 
  
 14 
 px 
 ; 
 } 
 # 
 legend 
  
 h4 
  
 { 
  
 margin-top 
 : 
  
 0 
 ; 
  
 margin-bottom 
 : 
  
 5 
 px 
 ; 
 } 
 # 
 legend 
  
 div 
  
 { 
  
 display 
 : 
  
 flex 
 ; 
  
 align-items 
 : 
  
 center 
 ; 
  
 margin-bottom 
 : 
  
 3 
 px 
 ; 
 } 
 # 
 legend 
  
 span 
  
 { 
  
 display 
 : 
  
 inline-block 
 ; 
  
 width 
 : 
  
 20 
 px 
 ; 
  
 height 
 : 
  
 14 
 px 
 ; 
  
 margin-right 
 : 
  
 5 
 px 
 ; 
  
 border 
 : 
  
 1 
 px 
  
 solid 
  
 #000 
 ; 
  
 /* Add a border for visibility */ 
 } 
 /* Media query for mobile devices */ 
 @ 
 media 
  
 ( 
 max-width 
 : 
  
 600px 
 ) 
  
 { 
  
 # 
 legend 
  
 { 
  
 position 
 : 
  
 absolute 
 ; 
  
 bottom 
 : 
  
 50 
 px 
 ; 
  
 /* Position above the button */ 
  
 left 
 : 
  
 50 
 % 
 ; 
  
 /* Center horizontally */ 
  
 transform 
 : 
  
 translateX 
 ( 
 -50 
 % 
 ); 
  
 /* Adjust for centering */ 
  
 top 
 : 
  
 auto 
 ; 
  
 /* Remove top positioning */ 
  
 right 
 : 
  
 auto 
 ; 
  
 /* Remove right positioning */ 
  
 } 
  
 # 
 toggleButton 
  
 { 
  
 position 
 : 
  
 absolute 
 ; 
  
 bottom 
 : 
  
 10 
 px 
 ; 
  
 /* Position at the bottom */ 
  
 left 
 : 
  
 50 
 % 
 ; 
  
 /* Center horizontally */ 
  
 transform 
 : 
  
 translateX 
 ( 
 -50 
 % 
 ); 
  
 /* Adjust for centering */ 
  
 top 
 : 
  
 auto 
 ; 
  
 /* Remove top positioning */ 
  
 } 
 } 
  

HTML

<html>
  <head>
    <title>deck.gl HeatmapLayer and Google Maps Platform</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <!-- Use Material Design Progress indicator -->
    <link
      href="https://unpkg.com/material-components-web@6.0.0/dist/material-components-web.css"
      rel="stylesheet"
    />
    <script src="https://unpkg.com/material-components-web@6.0.0/dist/material-components-web.min.js"></script>
    <script src="https://unpkg.com/deck.gl@8.9.22/dist.min.js"></script>
    <script src="https://unpkg.com/@deck.gl/google-maps@8.9.22/dist.min.js"></script>

    <link rel="stylesheet" href="style.css">

    <script type="module" src="index.js" defer></script>
  </head>
  <body>
   <div
   role="progressbar"
   class="mdc-linear-progress"
   aria-label="Data Progress Bar"
 >
   <div class="mdc-linear-progress__buffer">
     <div class="mdc-linear-progress__buffer-bar"></div>
     <div class="mdc-linear-progress__buffer-dots"></div>
   </div>
   <div class="mdc-linear-progress__bar mdc-linear-progress__primary-bar">
     <span class="mdc-linear-progress__bar-inner"></span>
   </div>
   <div class="mdc-linear-progress__bar mdc-linear-progress__secondary-bar">
     <span class="mdc-linear-progress__bar-inner"></span>
   </div>
 </div>
 <script>
   var progress, progressDiv;
   progressDiv = document.querySelector(".mdc-linear-progress");
   progress = new mdc.linearProgress.MDCLinearProgress(progressDiv);
   progress.open();
   progress.determinate = false;
   progress.done = function () {
     progress.close();
     progressDiv.remove();
   };
 </script>

    <h1>Bike Parking Heatmap</h1>

    <div id="map"></div>

    <div id="legend">
      <h4>Bike Parking Spaces</h4>
      <div><span style="background-color: #ffffe5;"></span>0 - 33</div>
      <div><span style="background-color: #ffffb2;"></span>34 - 66</div>
      <div><span style="background-color: #fecc5c;"></span>67 - 100</div>
      <div><span style="background-color: #fd8d3c;"></span>101 - 133</div>
      <div><span style="background-color: #f03b20;"></span>134 - 166</div>
      <div><span style="background-color: #bd0026;"></span>167+</div>
    </div>

    <button id="toggleButton">Hide Heatmap Layer</button>

    <script>(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})
        ({key: "AIzaSyA6myHzS10YXdcazAFalmXvDkrYCp5cLc8", v: "weekly"});</script>

  </body>
</html>  

Try Sample

Clone Sample

Git and Node.js are required to run this sample locally. Follow these instructions to install Node.js and NPM. The following commands clone, install dependencies and start the sample application.

  
  git 
  
 clone 
  
 https 
 : 
 //github.com/googlemaps-samples/js-api-samples.git 
 
  
  cd 
  
 samples 
 / 
 deckgl 
 - 
 heatmap 
 
  
  npm 
  
 i 
 
  
  npm 
  
 start 
 
Create a Mobile Website
View Site in Mobile | Classic
Share by: