Draw on a map using Terra Draw

The following example shows how to add a drawing layer to a map using Terra Draw JavaScript libraries. The layer relies on OverlayView and enables users to draw, edit, and select features like polygons, lines, and points directly on the map interface. All geometries created by the user are returned as standard GeoJSON objects.

TypeScript

 import 
  
 { 
  
 Loader 
  
 } 
  
 from 
  
 '@googlemaps/js-api-loader' 
 ; 
 import 
  
 { 
  
 TerraDraw 
 , 
  
 TerraDrawSelectMode 
 , 
  
 TerraDrawPointMode 
 , 
  
 TerraDrawLineStringMode 
 , 
  
 TerraDrawPolygonMode 
 , 
  
 TerraDrawRectangleMode 
 , 
  
 TerraDrawCircleMode 
 , 
  
 TerraDrawFreehandMode 
 } 
  
 from 
  
 'terra-draw' 
 ; 
 import 
  
 { 
  
 TerraDrawGoogleMapsAdapter 
  
 } 
  
 from 
  
 'terra-draw-google-maps-adapter' 
 ; 
 const 
  
 colorPalette 
  
 = 
  
 [ 
  
 "#E74C3C" 
 , 
  
 "#FF0066" 
 , 
  
 "#9B59B6" 
 , 
  
 "#673AB7" 
 , 
  
 "#3F51B5" 
 , 
  
 "#3498DB" 
 , 
  
 "#03A9F4" 
 , 
  
 "#00BCD4" 
 , 
  
 "#009688" 
 , 
  
 "#27AE60" 
 , 
  
 "#8BC34A" 
 , 
  
 "#CDDC39" 
 , 
  
 "#F1C40F" 
 , 
  
 "#FFC107" 
 , 
  
 "#F39C12" 
 , 
  
 "#FF5722" 
 , 
  
 "#795548" 
 ]; 
 const 
  
 getRandomColor 
  
 = 
  
 () 
  
 = 
>  
 colorPalette 
 [ 
 Math 
 . 
 floor 
 ( 
 Math 
 . 
 random 
 () 
  
 * 
  
 colorPalette 
 . 
 length 
 )] 
  
 as 
  
 `# 
 ${ 
 string 
 } 
 ` 
 ; 
 function 
  
 processSnapshotForUndo 
 ( 
 snapshot 
 : 
  
 any 
 []) 
 : 
  
 any 
 [] 
  
 { 
  
 // console.log("Processing snapshot for undo:", snapshot); 
  
 return 
  
 snapshot 
 . 
 map 
 ( 
 feature 
  
 = 
>  
 { 
  
 const 
  
 newFeature 
  
 = 
  
 JSON 
 . 
 parse 
 ( 
 JSON 
 . 
 stringify 
 ( 
 feature 
 )); 
  
 if 
  
 ( 
 newFeature 
 . 
 properties 
 . 
 mode 
  
 === 
  
 'rectangle' 
 ) 
  
 { 
  
 // console.log("Processing rectangle for undo:", newFeature); 
  
 newFeature 
 . 
 geometry 
 . 
 type 
  
 = 
  
 'Polygon' 
 ; 
  
 newFeature 
 . 
 properties 
 . 
 mode 
  
 = 
  
 'polygon' 
 ; 
  
 } 
  
 else 
  
 if 
  
 ( 
 newFeature 
 . 
 properties 
 . 
 mode 
  
 === 
  
 'circle' 
 ) 
  
 { 
  
 // console.log("Processing circle for undo:", newFeature); 
  
 newFeature 
 . 
 geometry 
 . 
 type 
  
 = 
  
 'Polygon' 
 ; 
  
 // The radius is already in properties, so we just need to ensure the mode is correct for re-creation 
  
 newFeature 
 . 
 properties 
 . 
 mode 
  
 = 
  
 'circle' 
 ; 
  
 } 
  
 return 
  
 newFeature 
 ; 
  
 }); 
 } 
 function 
  
 setupModeButtons 
 () 
 : 
  
 void 
  
 { 
  
 const 
  
 modeUI 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'mode-ui' 
 ); 
  
 if 
  
 ( 
 ! 
 modeUI 
 ) 
  
 { 
  
 return 
 ; 
  
 } 
  
 const 
  
 modeButtons 
 : 
  
 { 
  
 [ 
 key 
 : 
  
 string 
 ] 
 : 
  
 string 
  
 } 
  
 = 
  
 { 
  
 'select-mode' 
 : 
  
 'select' 
 , 
  
 'point-mode' 
 : 
  
 'point' 
 , 
  
 'linestring-mode' 
 : 
  
 'linestring' 
 , 
  
 'polygon-mode' 
 : 
  
 'polygon' 
 , 
  
 'rectangle-mode' 
 : 
  
 'rectangle' 
 , 
  
 'circle-mode' 
 : 
  
 'circle' 
 , 
  
 'freehand-mode' 
 : 
  
 'freehand' 
 , 
  
 'clear-mode' 
 : 
  
 'static' 
  
 }; 
  
 for 
  
 ( 
 const 
  
 buttonId 
  
 in 
  
 modeButtons 
 ) 
  
 { 
  
 const 
  
 button 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 buttonId 
 ); 
  
 if 
  
 ( 
 button 
 ) 
  
 { 
  
 button 
 . 
 onclick 
  
 = 
  
 () 
  
 = 
>  
 { 
  
 setActiveButton 
 ( 
 buttonId 
 ); 
  
 const 
  
 modeName 
  
 = 
  
 modeButtons 
 [ 
 buttonId 
 ]; 
  
 if 
  
 ( 
 ! 
 draw 
 ) 
  
 { 
  
 return 
 ; 
  
 } 
  
 if 
  
 ( 
 modeName 
  
 === 
  
 'static' 
 ) 
  
 { 
  
 draw 
 . 
 clear 
 (); 
  
 draw 
 . 
 setMode 
 ( 
 'static' 
 ); 
  
 } 
  
 else 
  
 if 
  
 ( 
 modeName 
 ) 
  
 { 
  
 draw 
 . 
 setMode 
 ( 
 modeName 
 ); 
  
 } 
  
 }; 
  
 } 
  
 } 
 } 
 function 
  
 setActiveButton 
 ( 
 buttonId 
 : 
  
 string 
 ) 
 : 
  
 void 
  
 { 
  
 const 
  
 buttons 
  
 = 
  
 document 
 . 
 querySelectorAll 
 ( 
 '.mode-button' 
 ); 
  
 const 
  
 resizeButton 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'resize-button' 
 ); 
  
 const 
  
 isResizeActive 
  
 = 
  
 resizeButton 
 ? 
 . 
 classList 
 . 
 contains 
 ( 
 'active' 
 ); 
  
 buttons 
 . 
 forEach 
 ( 
 button 
  
 = 
>  
 { 
  
 if 
  
 ( 
 button 
 . 
 id 
  
 !== 
  
 'resize-button' 
 ) 
  
 { 
  
 button 
 . 
 classList 
 . 
 remove 
 ( 
 'active' 
 ); 
  
 } 
  
 }); 
  
 const 
  
 activeButton 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 buttonId 
 ); 
  
 if 
  
 ( 
 activeButton 
 ) 
  
 { 
  
 activeButton 
 . 
 classList 
 . 
 add 
 ( 
 'active' 
 ); 
  
 } 
  
 if 
  
 ( 
 isResizeActive 
 ) 
  
 { 
  
 resizeButton 
 ? 
 . 
 classList 
 . 
 add 
 ( 
 'active' 
 ); 
  
 } 
 } 
 function 
  
 initUI 
 () 
 : 
  
 void 
  
 { 
  
 setActiveButton 
 ( 
 'point-mode' 
 ); 
 } 
 let 
  
 map 
 : 
  
 google.maps.Map 
 ; 
 let 
  
 draw 
 : 
  
 TerraDraw 
 ; 
 let 
  
 currentMode 
 : 
  
 string 
  
 = 
  
 'static' 
 ; 
 let 
  
 history 
 : 
  
 any 
 [] 
  
 = 
  
 []; 
 let 
  
 redoHistory 
 : 
  
 any 
 [] 
  
 = 
  
 []; 
 let 
  
 selectedFeatureId 
 : 
  
 string 
  
 | 
  
 null 
  
 = 
  
 null 
 ; 
 let 
  
 isRestoring 
  
 = 
  
 false 
 ; 
 let 
  
 resizingEnabled 
  
 = 
  
 false 
 ; 
 let 
  
 debounceTimeout 
 : 
  
 number 
  
 | 
  
 undefined 
 ; 
 const 
  
 loader 
  
 = 
  
 new 
  
 Loader 
 ({ 
  
 apiKey 
 : 
  
 "AIzaSyA6myHzS10YXdcazAFalmXvDkrYCp5cLc8" 
 , 
  
 version 
 : 
  
 "weekly" 
 , 
  
 libraries 
 : 
  
 [ 
 "maps" 
 , 
  
 "drawing" 
 , 
  
 "marker" 
 ] 
 }); 
 loader 
 . 
 load 
 (). 
 then 
 ( 
 async 
  
 () 
  
 = 
>  
 { 
  
 try 
  
 { 
  
 const 
  
 { 
  
 Map 
  
 } 
  
 = 
  
 await 
  
 google 
 . 
 maps 
 . 
 importLibrary 
 ( 
 "maps" 
 ) 
  
 as 
  
 google 
 . 
 maps 
 . 
 MapsLibrary 
 ; 
  
 const 
  
 { 
  
 LatLngBounds 
  
 } 
  
 = 
  
 await 
  
 google 
 . 
 maps 
 . 
 importLibrary 
 ( 
 "core" 
 ) 
  
 as 
  
 google 
 . 
 maps 
 . 
 CoreLibrary 
 ; 
  
 const 
  
 { 
  
 Data 
  
 } 
  
 = 
  
 await 
  
 google 
 . 
 maps 
 . 
 importLibrary 
 ( 
 "maps" 
 ) 
  
 as 
  
 google 
 . 
 maps 
 . 
 MapsLibrary 
 ; 
  
 const 
  
 mapOptions 
 : 
  
 google.maps.MapOptions 
  
 = 
  
 { 
  
 center 
 : 
  
 { 
  
 lat 
 : 
  
 48.862 
 , 
  
 lng 
 : 
  
 2.342 
  
 }, 
  
 zoom 
 : 
  
 12 
 , 
  
 mapId 
 : 
 'c306b3c6dd3ed8d9' 
 , 
  
 // raster '6a17c323f461e521', 
  
 mapTypeId 
 : 
  
 'roadmap' 
 , 
  
 zoomControl 
 : 
 false 
 , 
  
 tilt 
 : 
  
 45 
 , 
  
 mapTypeControl 
 : 
  
 true 
 , 
  
 clickableIcons 
 : 
 false 
 , 
  
 streetViewControl 
 : 
 false 
 , 
  
 fullscreenControl 
 : 
 false 
 , 
  
 }; 
  
 const 
  
 mapDiv 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 "map" 
 ) 
  
 as 
  
 HTMLElement 
 ; 
  
 map 
  
 = 
  
 new 
  
 Map 
 ( 
 mapDiv 
 , 
  
 mapOptions 
 ); 
  
 map 
 . 
 addListener 
 ( 
 "click" 
 , 
  
 () 
  
 = 
>  
 { 
  
 if 
  
 ( 
 draw 
 ) 
  
 { 
  
 console 
 . 
 log 
 ( 
 "Current draw mode on map click:" 
 , 
  
 draw 
 . 
 getMode 
 ()); 
  
 } 
  
 }); 
  
 map 
 . 
 addListener 
 ( 
 "projection_changed" 
 , 
  
 () 
  
 = 
>  
 { 
  
 draw 
  
 = 
  
 new 
  
 TerraDraw 
 ({ 
  
 adapter 
 : 
  
 new 
  
 TerraDrawGoogleMapsAdapter 
 ({ 
  
 map 
 , 
  
 lib 
 : 
  
 google.maps 
 , 
  
 coordinatePrecision 
 : 
  
 9 
  
 }), 
  
 modes 
 : 
  
 [ 
  
 new 
  
 TerraDrawSelectMode 
 ({ 
  
 flags 
 : 
  
 { 
  
 polygon 
 : 
  
 { 
  
 feature 
 : 
  
 { 
  
 draggable 
 : 
  
 true 
 , 
  
 rotateable 
 : 
  
 true 
 , 
  
 coordinates 
 : 
  
 { 
  
 midpoints 
 : 
  
 true 
 , 
  
 draggable 
 : 
  
 true 
 , 
  
 deletable 
 : 
  
 true 
 , 
  
 }, 
  
 }, 
  
 }, 
  
 linestring 
 : 
  
 { 
  
 feature 
 : 
  
 { 
  
 draggable 
 : 
  
 true 
 , 
  
 rotateable 
 : 
  
 true 
 , 
  
 coordinates 
 : 
  
 { 
  
 midpoints 
 : 
  
 true 
 , 
  
 draggable 
 : 
  
 true 
 , 
  
 deletable 
 : 
  
 true 
 , 
  
 }, 
  
 }, 
  
 }, 
  
 point 
 : 
  
 { 
  
 feature 
 : 
  
 { 
  
 draggable 
 : 
  
 true 
 , 
  
 rotateable 
 : 
  
 true 
 , 
  
 }, 
  
 }, 
  
 rectangle 
 : 
  
 { 
  
 feature 
 : 
  
 { 
  
 draggable 
 : 
  
 true 
 , 
  
 rotateable 
 : 
  
 true 
 , 
  
 coordinates 
 : 
  
 { 
  
 midpoints 
 : 
  
 true 
 , 
  
 draggable 
 : 
  
 true 
 , 
  
 deletable 
 : 
  
 true 
 , 
  
 }, 
  
 }, 
  
 }, 
  
 circle 
 : 
  
 { 
  
 feature 
 : 
  
 { 
  
 draggable 
 : 
  
 true 
 , 
  
 rotateable 
 : 
  
 true 
 , 
  
 coordinates 
 : 
  
 { 
  
 midpoints 
 : 
  
 true 
 , 
  
 draggable 
 : 
  
 true 
 , 
  
 deletable 
 : 
  
 true 
 , 
  
 }, 
  
 }, 
  
 }, 
  
 freehand 
 : 
  
 { 
  
 feature 
 : 
  
 { 
  
 draggable 
 : 
  
 true 
 , 
  
 rotateable 
 : 
  
 true 
 , 
  
 coordinates 
 : 
  
 { 
  
 midpoints 
 : 
  
 true 
 , 
  
 draggable 
 : 
  
 true 
 , 
  
 deletable 
 : 
  
 true 
 , 
  
 }, 
  
 }, 
  
 }, 
  
 }, 
  
 }), 
  
 new 
  
 TerraDrawPointMode 
 ({ 
  
 editable 
 : 
  
 true 
 , 
  
 styles 
 : 
  
 { 
  
 pointColor 
 : 
  
 getRandomColor 
 () 
  
 }, 
  
 }), 
  
 new 
  
 TerraDrawLineStringMode 
 ({ 
  
 editable 
 : 
  
 true 
 , 
  
 styles 
 : 
  
 { 
  
 lineStringColor 
 : 
  
 getRandomColor 
 () 
  
 }, 
  
 }), 
  
 new 
  
 TerraDrawPolygonMode 
 ({ 
  
 editable 
 : 
  
 true 
 , 
  
 styles 
 : 
  
 (() 
  
 = 
>  
 { 
  
 const 
  
 color 
  
 = 
  
 getRandomColor 
 (); 
  
 return 
  
 { 
  
 fillColor 
 : 
  
 color 
 , 
  
 outlineColor 
 : 
  
 color 
 , 
  
 }; 
  
 })(), 
  
 }), 
  
 new 
  
 TerraDrawRectangleMode 
 ({ 
  
 styles 
 : 
  
 (() 
  
 = 
>  
 { 
  
 const 
  
 color 
  
 = 
  
 getRandomColor 
 (); 
  
 return 
  
 { 
  
 fillColor 
 : 
  
 color 
 , 
  
 outlineColor 
 : 
  
 color 
 , 
  
 }; 
  
 })(), 
  
 }), 
  
 new 
  
 TerraDrawCircleMode 
 ({ 
  
 styles 
 : 
  
 (() 
  
 = 
>  
 { 
  
 const 
  
 color 
  
 = 
  
 getRandomColor 
 (); 
  
 return 
  
 { 
  
 fillColor 
 : 
  
 color 
 , 
  
 outlineColor 
 : 
  
 color 
 , 
  
 }; 
  
 })(), 
  
 }), 
  
 new 
  
 TerraDrawFreehandMode 
 ({ 
  
 styles 
 : 
  
 (() 
  
 = 
>  
 { 
  
 const 
  
 color 
  
 = 
  
 getRandomColor 
 (); 
  
 return 
  
 { 
  
 fillColor 
 : 
  
 color 
 , 
  
 outlineColor 
 : 
  
 color 
 , 
  
 }; 
  
 })(), 
  
 }), 
  
 ], 
  
 }); 
  
 draw 
 . 
 start 
 (); 
  
 draw 
 . 
 on 
 ( 
 'ready' 
 , 
  
 () 
  
 = 
>  
 { 
  
 console 
 . 
 log 
 ( 
 "TerraDraw is ready!" 
 ); 
  
 initUI 
 (); 
  
 setupModeButtons 
 (); 
  
 draw 
 . 
 setMode 
 ( 
 'point' 
 ); 
  
 currentMode 
  
 = 
  
 'point' 
 ; 
  
 setActiveButton 
 ( 
 'point-mode' 
 ); 
  
 draw 
 . 
 on 
 ( 
 "select" 
 , 
  
 ( 
 id 
 ) 
  
 = 
>  
 { 
  
 // console.log(`Feature selected: ${id}`); 
  
 if 
  
 ( 
 selectedFeatureId 
 && 
 selectedFeatureId 
  
 !== 
  
 id 
 ) 
  
 { 
  
 draw 
 . 
 deselectFeature 
 ( 
 selectedFeatureId 
 ); 
  
 } 
  
 selectedFeatureId 
  
 = 
  
 id 
  
 as 
  
 string 
 ; 
  
 }); 
  
 draw 
 . 
 on 
 ( 
 "deselect" 
 , 
  
 () 
  
 = 
>  
 { 
  
 // console.log("Feature deselected"); 
  
 selectedFeatureId 
  
 = 
  
 null 
 ; 
  
 }); 
  
 history 
 . 
 push 
 ( 
 processSnapshotForUndo 
 ( 
 draw 
 . 
 getSnapshot 
 ())); 
  
 // Push initial empty state 
  
 draw 
 . 
 on 
 ( 
 "change" 
 , 
  
 ( 
 ids 
 , 
  
 type 
 ) 
  
 = 
>  
 { 
  
 if 
  
 ( 
 isRestoring 
 ) 
  
 { 
  
 return 
 ; 
  
 } 
  
 if 
  
 ( 
 debounceTimeout 
 ) 
  
 { 
  
 clearTimeout 
 ( 
 debounceTimeout 
 ); 
  
 } 
  
 debounceTimeout 
  
 = 
  
 window 
 . 
 setTimeout 
 (() 
  
 = 
>  
 { 
  
 const 
  
 snapshot 
  
 = 
  
 draw 
 . 
 getSnapshot 
 (); 
  
 const 
  
 processedSnapshot 
  
 = 
  
 processSnapshotForUndo 
 ( 
 snapshot 
 ); 
  
 const 
  
 filteredSnapshot 
  
 = 
  
 processedSnapshot 
 . 
 filter 
 ( 
  
 ( 
 f 
 ) 
  
 = 
>  
 ! 
 f 
 . 
 properties 
 . 
 midPoint 
 && 
 ! 
 f 
 . 
 properties 
 . 
 selectionPoint 
  
 ); 
  
 history 
 . 
 push 
 ( 
 filteredSnapshot 
 ); 
  
 redoHistory 
  
 = 
  
 []; 
  
 }, 
  
 200 
 ); 
  
 }); 
  
 const 
  
 exportButton 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'export-button' 
 ); 
  
 if 
  
 ( 
 exportButton 
 ) 
  
 { 
  
 exportButton 
 . 
 onclick 
  
 = 
  
 () 
  
 = 
>  
 { 
  
 const 
  
 features 
  
 = 
  
 draw 
 . 
 getSnapshot 
 (); 
  
 const 
  
 geojson 
  
 = 
  
 { 
  
 type 
 : 
  
 "FeatureCollection" 
 , 
  
 features 
 : 
  
 features 
 , 
  
 }; 
  
 const 
  
 data 
  
 = 
  
 JSON 
 . 
 stringify 
 ( 
 geojson 
 , 
  
 null 
 , 
  
 2 
 ); 
  
 const 
  
 blob 
  
 = 
  
 new 
  
 Blob 
 ([ 
 data 
 ], 
  
 { 
  
 type 
 : 
  
 "text/plain" 
  
 }); 
  
 const 
  
 url 
  
 = 
  
 URL 
 . 
 createObjectURL 
 ( 
 blob 
 ); 
  
 const 
  
 link 
  
 = 
  
 document 
 . 
 createElement 
 ( 
 "a" 
 ); 
  
 link 
 . 
 href 
  
 = 
  
 url 
 ; 
  
 link 
 . 
 download 
  
 = 
  
 "drawing.geojson" 
 ; 
  
 link 
 . 
 click 
 (); 
  
 URL 
 . 
 revokeObjectURL 
 ( 
 url 
 ); 
  
 }; 
  
 } 
  
 const 
  
 uploadButton 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'upload-button' 
 ); 
  
 const 
  
 uploadInput 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'upload-input' 
 ) 
  
 as 
  
 HTMLInputElement 
 ; 
  
 if 
  
 ( 
 uploadButton 
 && 
 uploadInput 
 ) 
  
 { 
  
 uploadButton 
 . 
 onclick 
  
 = 
  
 () 
  
 = 
>  
 { 
  
 uploadInput 
 . 
 click 
 (); 
  
 }; 
  
 uploadInput 
 . 
 onchange 
  
 = 
  
 ( 
 event 
 ) 
  
 = 
>  
 { 
  
 const 
  
 file 
  
 = 
  
 ( 
 event 
 . 
 target 
  
 as 
  
 HTMLInputElement 
 ). 
 files 
 ? 
 .[ 
 0 
 ]; 
  
 if 
  
 ( 
 file 
 ) 
  
 { 
  
 const 
  
 reader 
  
 = 
  
 new 
  
 FileReader 
 (); 
  
 reader 
 . 
 onload 
  
 = 
  
 ( 
 e 
 ) 
  
 = 
>  
 { 
  
 try 
  
 { 
  
 const 
  
 geojson 
  
 = 
  
 JSON 
 . 
 parse 
 ( 
 e 
 . 
 target 
 ? 
 . 
 result 
  
 as 
  
 string 
 ); 
  
 if 
  
 ( 
 geojson 
 . 
 type 
  
 === 
  
 "FeatureCollection" 
 ) 
  
 { 
  
 draw 
 . 
 addFeatures 
 ( 
 geojson 
 . 
 features 
 ); 
  
 } 
  
 else 
  
 { 
  
 alert 
 ( 
 "Invalid GeoJSON file: must be a FeatureCollection." 
 ); 
  
 } 
  
 } 
  
 catch 
  
 ( 
 error 
 ) 
  
 { 
  
 alert 
 ( 
 "Error parsing GeoJSON file." 
 ); 
  
 } 
  
 }; 
  
 reader 
 . 
 readAsText 
 ( 
 file 
 ); 
  
 } 
  
 }; 
  
 } 
  
 const 
  
 resizeButton 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'resize-button' 
 ); 
  
 if 
  
 ( 
 resizeButton 
 ) 
  
 { 
  
 resizeButton 
 . 
 onclick 
  
 = 
  
 () 
  
 = 
>  
 { 
  
 resizingEnabled 
  
 = 
  
 ! 
 resizingEnabled 
 ; 
  
 resizeButton 
 . 
 classList 
 . 
 toggle 
 ( 
 'active' 
 , 
  
 resizingEnabled 
 ); 
  
 const 
  
 flags 
  
 = 
  
 { 
  
 polygon 
 : 
  
 { 
  
 feature 
 : 
  
 { 
  
 draggable 
 : 
  
 true 
 , 
  
 coordinates 
 : 
  
 { 
  
 resizable 
 : 
  
 resizingEnabled 
  
 ? 
  
 'center' 
  
 : 
  
 undefined 
 , 
  
 draggable 
 : 
  
 ! 
 resizingEnabled 
  
 } 
  
 } 
  
 }, 
  
 linestring 
 : 
  
 { 
  
 feature 
 : 
  
 { 
  
 draggable 
 : 
  
 true 
 , 
  
 coordinates 
 : 
  
 { 
  
 resizable 
 : 
  
 resizingEnabled 
  
 ? 
  
 'center' 
  
 : 
  
 undefined 
 , 
  
 draggable 
 : 
  
 ! 
 resizingEnabled 
  
 } 
  
 } 
  
 }, 
  
 rectangle 
 : 
  
 { 
  
 feature 
 : 
  
 { 
  
 draggable 
 : 
  
 true 
 , 
  
 coordinates 
 : 
  
 { 
  
 resizable 
 : 
  
 resizingEnabled 
  
 ? 
  
 'center' 
  
 : 
  
 undefined 
 , 
  
 draggable 
 : 
  
 ! 
 resizingEnabled 
  
 } 
  
 } 
  
 }, 
  
 circle 
 : 
  
 { 
  
 feature 
 : 
  
 { 
  
 draggable 
 : 
  
 true 
 , 
  
 coordinates 
 : 
  
 { 
  
 resizable 
 : 
  
 resizingEnabled 
  
 ? 
  
 'center' 
  
 : 
  
 undefined 
 , 
  
 draggable 
 : 
  
 ! 
 resizingEnabled 
  
 } 
  
 } 
  
 }, 
  
 freehand 
 : 
  
 { 
  
 feature 
 : 
  
 { 
  
 draggable 
 : 
  
 true 
 , 
  
 coordinates 
 : 
  
 { 
  
 resizable 
 : 
  
 resizingEnabled 
  
 ? 
  
 'center' 
  
 : 
  
 undefined 
 , 
  
 draggable 
 : 
  
 ! 
 resizingEnabled 
  
 } 
  
 } 
  
 }, 
  
 }; 
  
 console 
 . 
 log 
 ( 
 "Updating flags:" 
 , 
  
 flags 
 ); 
  
 draw 
 . 
 updateModeOptions 
 ( 
 'select' 
 , 
  
 { 
  
 flags 
  
 }); 
  
 }; 
  
 } 
  
 const 
  
 deleteSelectedButton 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'delete-selected-button' 
 ); 
  
 if 
  
 ( 
 deleteSelectedButton 
 ) 
  
 { 
  
 deleteSelectedButton 
 . 
 onclick 
  
 = 
  
 () 
  
 = 
>  
 { 
  
 if 
  
 ( 
 selectedFeatureId 
 ) 
  
 { 
  
 draw 
 . 
 removeFeatures 
 ([ 
 selectedFeatureId 
 ]); 
  
 } 
  
 else 
  
 { 
  
 const 
  
 features 
  
 = 
  
 draw 
 . 
 getSnapshot 
 (); 
  
 if 
  
 ( 
 features 
 . 
 length 
 > 
 0 
 ) 
  
 { 
  
 const 
  
 lastFeature 
  
 = 
  
 features 
 [ 
 features 
 . 
 length 
  
 - 
  
 1 
 ]; 
  
 if 
  
 ( 
 lastFeature 
 . 
 id 
 ) 
  
 { 
  
 draw 
 . 
 removeFeatures 
 ([ 
 lastFeature 
 . 
 id 
 ]); 
  
 } 
  
 } 
  
 } 
  
 }; 
  
 } 
  
 const 
  
 undoButton 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'undo-button' 
 ); 
  
 if 
  
 ( 
 undoButton 
 ) 
  
 { 
  
 undoButton 
 . 
 onclick 
  
 = 
  
 () 
  
 = 
>  
 { 
  
 if 
  
 ( 
 history 
 . 
 length 
 > 
 1 
 ) 
  
 { 
  
 redoHistory 
 . 
 push 
 ( 
 history 
 . 
 pop 
 ()); 
  
 const 
  
 snapshotToRestore 
  
 = 
  
 history 
 [ 
 history 
 . 
 length 
  
 - 
  
 1 
 ]; 
  
 console 
 . 
 log 
 ( 
 "Restoring snapshot (undo):" 
 , 
  
 snapshotToRestore 
 ); 
  
 isRestoring 
  
 = 
  
 true 
 ; 
  
 draw 
 . 
 clear 
 (); 
  
 draw 
 . 
 addFeatures 
 ( 
 snapshotToRestore 
 ); 
  
 setTimeout 
 (() 
  
 = 
>  
 { 
  
 isRestoring 
  
 = 
  
 false 
 ; 
  
 }, 
  
 0 
 ); 
  
 } 
  
 }; 
  
 } 
  
 const 
  
 redoButton 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'redo-button' 
 ); 
  
 if 
  
 ( 
 redoButton 
 ) 
  
 { 
  
 redoButton 
 . 
 onclick 
  
 = 
  
 () 
  
 = 
>  
 { 
  
 if 
  
 ( 
 redoHistory 
 . 
 length 
 > 
 0 
 ) 
  
 { 
  
 const 
  
 snapshot 
  
 = 
  
 redoHistory 
 . 
 pop 
 (); 
  
 console 
 . 
 log 
 ( 
 "Restoring snapshot (redo):" 
 , 
  
 snapshot 
 ); 
  
 history 
 . 
 push 
 ( 
 snapshot 
 ); 
  
 isRestoring 
  
 = 
  
 true 
 ; 
  
 draw 
 . 
 clear 
 (); 
  
 draw 
 . 
 addFeatures 
 ( 
 snapshot 
 ); 
  
 setTimeout 
 (() 
  
 = 
>  
 { 
  
 isRestoring 
  
 = 
  
 false 
 ; 
  
 }, 
  
 0 
 ); 
  
 } 
  
 }; 
  
 } 
  
 }); 
  
 function 
  
 rotateFeature 
 ( 
 feature 
 , 
  
 angle 
 ) 
  
 { 
  
 const 
  
 newFeature 
  
 = 
  
 JSON 
 . 
 parse 
 ( 
 JSON 
 . 
 stringify 
 ( 
 feature 
 )); 
  
 const 
  
 coordinates 
  
 = 
  
 newFeature 
 . 
 geometry 
 . 
 coordinates 
 ; 
  
 const 
  
 center 
  
 = 
  
 getCenter 
 ( 
 coordinates 
 ); 
  
 const 
  
 rotatedCoordinates 
  
 = 
  
 coordinates 
 . 
 map 
 ( 
 ring 
  
 = 
>  
 { 
  
 return 
  
 ring 
 . 
 map 
 ( 
 point 
  
 = 
>  
 { 
  
 const 
  
 x 
  
 = 
  
 point 
 [ 
 0 
 ] 
  
 - 
  
 center 
 [ 
 0 
 ]; 
  
 const 
  
 y 
  
 = 
  
 point 
 [ 
 1 
 ] 
  
 - 
  
 center 
 [ 
 1 
 ]; 
  
 const 
  
 newX 
  
 = 
  
 x 
  
 * 
  
 Math 
 . 
 cos 
 ( 
 angle 
  
 * 
  
 Math 
 . 
 PI 
  
 / 
  
 180 
 ) 
  
 - 
  
 y 
  
 * 
  
 Math 
 . 
 sin 
 ( 
 angle 
  
 * 
  
 Math 
 . 
 PI 
  
 / 
  
 180 
 ); 
  
 const 
  
 newY 
  
 = 
  
 x 
  
 * 
  
 Math 
 . 
 sin 
 ( 
 angle 
  
 * 
  
 Math 
 . 
 PI 
  
 / 
  
 180 
 ) 
  
 + 
  
 y 
  
 * 
  
 Math 
 . 
 cos 
 ( 
 angle 
  
 * 
  
 Math 
 . 
 PI 
  
 / 
  
 180 
 ); 
  
 return 
  
 [ 
 newX 
  
 + 
  
 center 
 [ 
 0 
 ], 
  
 newY 
  
 + 
  
 center 
 [ 
 1 
 ]]; 
  
 }); 
  
 }); 
  
 newFeature 
 . 
 geometry 
 . 
 coordinates 
  
 = 
  
 rotatedCoordinates 
 ; 
  
 return 
  
 newFeature 
 ; 
  
 } 
  
 function 
  
 getCenter 
 ( 
 coordinates 
 ) 
  
 { 
  
 let 
  
 x 
  
 = 
  
 0 
 ; 
  
 let 
  
 y 
  
 = 
  
 0 
 ; 
  
 let 
  
 count 
  
 = 
  
 0 
 ; 
  
 coordinates 
 . 
 forEach 
 ( 
 ring 
  
 = 
>  
 { 
  
 ring 
 . 
 forEach 
 ( 
 point 
  
 = 
>  
 { 
  
 x 
  
 += 
  
 point 
 [ 
 0 
 ]; 
  
 y 
  
 += 
  
 point 
 [ 
 1 
 ]; 
  
 count 
 ++ 
 ; 
  
 }); 
  
 }); 
  
 return 
  
 [ 
 x 
  
 / 
  
 count 
 , 
  
 y 
  
 / 
  
 count 
 ]; 
  
 } 
  
 document 
 . 
 addEventListener 
 ( 
 'keydown' 
 , 
  
 ( 
 event 
 ) 
  
 = 
>  
 { 
  
 if 
  
 ( 
 event 
 . 
 key 
  
 === 
  
 'r' 
 && 
 selectedFeatureId 
 ) 
  
 { 
  
 const 
  
 features 
  
 = 
  
 draw 
 . 
 getSnapshot 
 (); 
  
 const 
  
 selectedFeature 
  
 = 
  
 features 
 . 
 find 
 ( 
 f 
  
 = 
>  
 f 
 . 
 id 
  
 === 
  
 selectedFeatureId 
 ); 
  
 if 
  
 ( 
 selectedFeature 
 ) 
  
 { 
  
 const 
  
 newFeature 
  
 = 
  
 rotateFeature 
 ( 
 selectedFeature 
 , 
  
 15 
 ); 
  
 draw 
 . 
 addFeatures 
 ([ 
 newFeature 
 ]); 
  
 } 
  
 } 
  
 }); 
  
 }); 
  
 } 
  
 catch 
  
 ( 
 e 
 ) 
  
 { 
  
 console 
 . 
 error 
 ( 
 "Error loading Google Maps API:" 
 , 
  
 e 
 ); 
  
 } 
 }). 
 catch 
 ( 
 e 
  
 = 
>  
 { 
  
 console 
 . 
 error 
 ( 
 "Error loading Google Maps API:" 
 , 
  
 e 
 ); 
 }); 
  

JavaScript

 import 
  
 { 
  
 Loader 
  
 } 
  
 from 
  
 '@googlemaps/js-api-loader' 
 ; 
 import 
  
 { 
  
 TerraDraw 
 , 
  
 TerraDrawSelectMode 
 , 
  
 TerraDrawPointMode 
 , 
  
 TerraDrawLineStringMode 
 , 
  
 TerraDrawPolygonMode 
 , 
  
 TerraDrawRectangleMode 
 , 
  
 TerraDrawCircleMode 
 , 
  
 TerraDrawFreehandMode 
  
 } 
  
 from 
  
 'terra-draw' 
 ; 
 import 
  
 { 
  
 TerraDrawGoogleMapsAdapter 
  
 } 
  
 from 
  
 'terra-draw-google-maps-adapter' 
 ; 
 const 
  
 colorPalette 
  
 = 
  
 [ 
  
 "#E74C3C" 
 , 
  
 "#FF0066" 
 , 
  
 "#9B59B6" 
 , 
  
 "#673AB7" 
 , 
  
 "#3F51B5" 
 , 
  
 "#3498DB" 
 , 
  
 "#03A9F4" 
 , 
  
 "#00BCD4" 
 , 
  
 "#009688" 
 , 
  
 "#27AE60" 
 , 
  
 "#8BC34A" 
 , 
  
 "#CDDC39" 
 , 
  
 "#F1C40F" 
 , 
  
 "#FFC107" 
 , 
  
 "#F39C12" 
 , 
  
 "#FF5722" 
 , 
  
 "#795548" 
 ]; 
 const 
  
 getRandomColor 
  
 = 
  
 () 
  
 = 
>  
 colorPalette 
 [ 
 Math 
 . 
 floor 
 ( 
 Math 
 . 
 random 
 () 
  
 * 
  
 colorPalette 
 . 
 length 
 )]; 
 function 
  
 processSnapshotForUndo 
 ( 
 snapshot 
 ) 
  
 { 
  
 // console.log("Processing snapshot for undo:", snapshot); 
  
 return 
  
 snapshot 
 . 
 map 
 ( 
 feature 
  
 = 
>  
 { 
  
 const 
  
 newFeature 
  
 = 
  
 JSON 
 . 
 parse 
 ( 
 JSON 
 . 
 stringify 
 ( 
 feature 
 )); 
  
 if 
  
 ( 
 newFeature 
 . 
 properties 
 . 
 mode 
  
 === 
  
 'rectangle' 
 ) 
  
 { 
  
 // console.log("Processing rectangle for undo:", newFeature); 
  
 newFeature 
 . 
 geometry 
 . 
 type 
  
 = 
  
 'Polygon' 
 ; 
  
 newFeature 
 . 
 properties 
 . 
 mode 
  
 = 
  
 'polygon' 
 ; 
  
 } 
  
 else 
  
 if 
  
 ( 
 newFeature 
 . 
 properties 
 . 
 mode 
  
 === 
  
 'circle' 
 ) 
  
 { 
  
 // console.log("Processing circle for undo:", newFeature); 
  
 newFeature 
 . 
 geometry 
 . 
 type 
  
 = 
  
 'Polygon' 
 ; 
  
 // The radius is already in properties, so we just need to ensure the mode is correct for re-creation 
  
 newFeature 
 . 
 properties 
 . 
 mode 
  
 = 
  
 'circle' 
 ; 
  
 } 
  
 return 
  
 newFeature 
 ; 
  
 }); 
 } 
 function 
  
 setupModeButtons 
 () 
  
 { 
  
 const 
  
 modeUI 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'mode-ui' 
 ); 
  
 if 
  
 ( 
 ! 
 modeUI 
 ) 
  
 { 
  
 return 
 ; 
  
 } 
  
 const 
  
 modeButtons 
  
 = 
  
 { 
  
 'select-mode' 
 : 
  
 'select' 
 , 
  
 'point-mode' 
 : 
  
 'point' 
 , 
  
 'linestring-mode' 
 : 
  
 'linestring' 
 , 
  
 'polygon-mode' 
 : 
  
 'polygon' 
 , 
  
 'rectangle-mode' 
 : 
  
 'rectangle' 
 , 
  
 'circle-mode' 
 : 
  
 'circle' 
 , 
  
 'freehand-mode' 
 : 
  
 'freehand' 
 , 
  
 'clear-mode' 
 : 
  
 'static' 
  
 }; 
  
 for 
  
 ( 
 const 
  
 buttonId 
  
 in 
  
 modeButtons 
 ) 
  
 { 
  
 const 
  
 button 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 buttonId 
 ); 
  
 if 
  
 ( 
 button 
 ) 
  
 { 
  
 button 
 . 
 onclick 
  
 = 
  
 () 
  
 = 
>  
 { 
  
 setActiveButton 
 ( 
 buttonId 
 ); 
  
 const 
  
 modeName 
  
 = 
  
 modeButtons 
 [ 
 buttonId 
 ]; 
  
 if 
  
 ( 
 ! 
 draw 
 ) 
  
 { 
  
 return 
 ; 
  
 } 
  
 if 
  
 ( 
 modeName 
  
 === 
  
 'static' 
 ) 
  
 { 
  
 draw 
 . 
 clear 
 (); 
  
 draw 
 . 
 setMode 
 ( 
 'static' 
 ); 
  
 } 
  
 else 
  
 if 
  
 ( 
 modeName 
 ) 
  
 { 
  
 draw 
 . 
 setMode 
 ( 
 modeName 
 ); 
  
 } 
  
 }; 
  
 } 
  
 } 
 } 
 function 
  
 setActiveButton 
 ( 
 buttonId 
 ) 
  
 { 
  
 const 
  
 buttons 
  
 = 
  
 document 
 . 
 querySelectorAll 
 ( 
 '.mode-button' 
 ); 
  
 const 
  
 resizeButton 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'resize-button' 
 ); 
  
 const 
  
 isResizeActive 
  
 = 
  
 resizeButton 
 ? 
 . 
 classList 
 . 
 contains 
 ( 
 'active' 
 ); 
  
 buttons 
 . 
 forEach 
 ( 
 button 
  
 = 
>  
 { 
  
 if 
  
 ( 
 button 
 . 
 id 
  
 !== 
  
 'resize-button' 
 ) 
  
 { 
  
 button 
 . 
 classList 
 . 
 remove 
 ( 
 'active' 
 ); 
  
 } 
  
 }); 
  
 const 
  
 activeButton 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 buttonId 
 ); 
  
 if 
  
 ( 
 activeButton 
 ) 
  
 { 
  
 activeButton 
 . 
 classList 
 . 
 add 
 ( 
 'active' 
 ); 
  
 } 
  
 if 
  
 ( 
 isResizeActive 
 ) 
  
 { 
  
 resizeButton 
 ? 
 . 
 classList 
 . 
 add 
 ( 
 'active' 
 ); 
  
 } 
 } 
 function 
  
 initUI 
 () 
  
 { 
  
 setActiveButton 
 ( 
 'point-mode' 
 ); 
 } 
 let 
  
 map 
 ; 
 let 
  
 draw 
 ; 
 let 
  
 currentMode 
  
 = 
  
 'static' 
 ; 
 let 
  
 history 
  
 = 
  
 []; 
 let 
  
 redoHistory 
  
 = 
  
 []; 
 let 
  
 selectedFeatureId 
  
 = 
  
 null 
 ; 
 let 
  
 isRestoring 
  
 = 
  
 false 
 ; 
 let 
  
 resizingEnabled 
  
 = 
  
 false 
 ; 
 let 
  
 debounceTimeout 
 ; 
 const 
  
 loader 
  
 = 
  
 new 
  
 Loader 
 ({ 
  
 apiKey 
 : 
  
 "AIzaSyA6myHzS10YXdcazAFalmXvDkrYCp5cLc8" 
 , 
  
 version 
 : 
  
 "weekly" 
 , 
  
 libraries 
 : 
  
 [ 
 "maps" 
 , 
  
 "drawing" 
 , 
  
 "marker" 
 ] 
 }); 
 loader 
 . 
 load 
 (). 
 then 
 ( 
 async 
  
 () 
  
 = 
>  
 { 
  
 try 
  
 { 
  
 const 
  
 { 
  
 Map 
  
 } 
  
 = 
  
 await 
  
 google 
 . 
 maps 
 . 
 importLibrary 
 ( 
 "maps" 
 ); 
  
 const 
  
 { 
  
 LatLngBounds 
  
 } 
  
 = 
  
 await 
  
 google 
 . 
 maps 
 . 
 importLibrary 
 ( 
 "core" 
 ); 
  
 const 
  
 { 
  
 Data 
  
 } 
  
 = 
  
 await 
  
 google 
 . 
 maps 
 . 
 importLibrary 
 ( 
 "maps" 
 ); 
  
 const 
  
 mapOptions 
  
 = 
  
 { 
  
 center 
 : 
  
 { 
  
 lat 
 : 
  
 48.862 
 , 
  
 lng 
 : 
  
 2.342 
  
 }, 
  
 zoom 
 : 
  
 12 
 , 
  
 mapId 
 : 
  
 'c306b3c6dd3ed8d9' 
 , 
  
 // raster '6a17c323f461e521', 
  
 mapTypeId 
 : 
  
 'roadmap' 
 , 
  
 zoomControl 
 : 
  
 false 
 , 
  
 tilt 
 : 
  
 45 
 , 
  
 mapTypeControl 
 : 
  
 true 
 , 
  
 clickableIcons 
 : 
  
 false 
 , 
  
 streetViewControl 
 : 
  
 false 
 , 
  
 fullscreenControl 
 : 
  
 false 
 , 
  
 }; 
  
 const 
  
 mapDiv 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 "map" 
 ); 
  
 map 
  
 = 
  
 new 
  
 Map 
 ( 
 mapDiv 
 , 
  
 mapOptions 
 ); 
  
 map 
 . 
 addListener 
 ( 
 "click" 
 , 
  
 () 
  
 = 
>  
 { 
  
 if 
  
 ( 
 draw 
 ) 
  
 { 
  
 console 
 . 
 log 
 ( 
 "Current draw mode on map click:" 
 , 
  
 draw 
 . 
 getMode 
 ()); 
  
 } 
  
 }); 
  
 map 
 . 
 addListener 
 ( 
 "projection_changed" 
 , 
  
 () 
  
 = 
>  
 { 
  
 draw 
  
 = 
  
 new 
  
 TerraDraw 
 ({ 
  
 adapter 
 : 
  
 new 
  
 TerraDrawGoogleMapsAdapter 
 ({ 
  
 map 
 , 
  
 lib 
 : 
  
 google 
 . 
 maps 
 , 
  
 coordinatePrecision 
 : 
  
 9 
  
 }), 
  
 modes 
 : 
  
 [ 
  
 new 
  
 TerraDrawSelectMode 
 ({ 
  
 flags 
 : 
  
 { 
  
 polygon 
 : 
  
 { 
  
 feature 
 : 
  
 { 
  
 draggable 
 : 
  
 true 
 , 
  
 rotateable 
 : 
  
 true 
 , 
  
 coordinates 
 : 
  
 { 
  
 midpoints 
 : 
  
 true 
 , 
  
 draggable 
 : 
  
 true 
 , 
  
 deletable 
 : 
  
 true 
 , 
  
 }, 
  
 }, 
  
 }, 
  
 linestring 
 : 
  
 { 
  
 feature 
 : 
  
 { 
  
 draggable 
 : 
  
 true 
 , 
  
 rotateable 
 : 
  
 true 
 , 
  
 coordinates 
 : 
  
 { 
  
 midpoints 
 : 
  
 true 
 , 
  
 draggable 
 : 
  
 true 
 , 
  
 deletable 
 : 
  
 true 
 , 
  
 }, 
  
 }, 
  
 }, 
  
 point 
 : 
  
 { 
  
 feature 
 : 
  
 { 
  
 draggable 
 : 
  
 true 
 , 
  
 rotateable 
 : 
  
 true 
 , 
  
 }, 
  
 }, 
  
 rectangle 
 : 
  
 { 
  
 feature 
 : 
  
 { 
  
 draggable 
 : 
  
 true 
 , 
  
 rotateable 
 : 
  
 true 
 , 
  
 coordinates 
 : 
  
 { 
  
 midpoints 
 : 
  
 true 
 , 
  
 draggable 
 : 
  
 true 
 , 
  
 deletable 
 : 
  
 true 
 , 
  
 }, 
  
 }, 
  
 }, 
  
 circle 
 : 
  
 { 
  
 feature 
 : 
  
 { 
  
 draggable 
 : 
  
 true 
 , 
  
 rotateable 
 : 
  
 true 
 , 
  
 coordinates 
 : 
  
 { 
  
 midpoints 
 : 
  
 true 
 , 
  
 draggable 
 : 
  
 true 
 , 
  
 deletable 
 : 
  
 true 
 , 
  
 }, 
  
 }, 
  
 }, 
  
 freehand 
 : 
  
 { 
  
 feature 
 : 
  
 { 
  
 draggable 
 : 
  
 true 
 , 
  
 rotateable 
 : 
  
 true 
 , 
  
 coordinates 
 : 
  
 { 
  
 midpoints 
 : 
  
 true 
 , 
  
 draggable 
 : 
  
 true 
 , 
  
 deletable 
 : 
  
 true 
 , 
  
 }, 
  
 }, 
  
 }, 
  
 }, 
  
 }), 
  
 new 
  
 TerraDrawPointMode 
 ({ 
  
 editable 
 : 
  
 true 
 , 
  
 styles 
 : 
  
 { 
  
 pointColor 
 : 
  
 getRandomColor 
 () 
  
 }, 
  
 }), 
  
 new 
  
 TerraDrawLineStringMode 
 ({ 
  
 editable 
 : 
  
 true 
 , 
  
 styles 
 : 
  
 { 
  
 lineStringColor 
 : 
  
 getRandomColor 
 () 
  
 }, 
  
 }), 
  
 new 
  
 TerraDrawPolygonMode 
 ({ 
  
 editable 
 : 
  
 true 
 , 
  
 styles 
 : 
  
 (() 
  
 = 
>  
 { 
  
 const 
  
 color 
  
 = 
  
 getRandomColor 
 (); 
  
 return 
  
 { 
  
 fillColor 
 : 
  
 color 
 , 
  
 outlineColor 
 : 
  
 color 
 , 
  
 }; 
  
 })(), 
  
 }), 
  
 new 
  
 TerraDrawRectangleMode 
 ({ 
  
 styles 
 : 
  
 (() 
  
 = 
>  
 { 
  
 const 
  
 color 
  
 = 
  
 getRandomColor 
 (); 
  
 return 
  
 { 
  
 fillColor 
 : 
  
 color 
 , 
  
 outlineColor 
 : 
  
 color 
 , 
  
 }; 
  
 })(), 
  
 }), 
  
 new 
  
 TerraDrawCircleMode 
 ({ 
  
 styles 
 : 
  
 (() 
  
 = 
>  
 { 
  
 const 
  
 color 
  
 = 
  
 getRandomColor 
 (); 
  
 return 
  
 { 
  
 fillColor 
 : 
  
 color 
 , 
  
 outlineColor 
 : 
  
 color 
 , 
  
 }; 
  
 })(), 
  
 }), 
  
 new 
  
 TerraDrawFreehandMode 
 ({ 
  
 styles 
 : 
  
 (() 
  
 = 
>  
 { 
  
 const 
  
 color 
  
 = 
  
 getRandomColor 
 (); 
  
 return 
  
 { 
  
 fillColor 
 : 
  
 color 
 , 
  
 outlineColor 
 : 
  
 color 
 , 
  
 }; 
  
 })(), 
  
 }), 
  
 ], 
  
 }); 
  
 draw 
 . 
 start 
 (); 
  
 draw 
 . 
 on 
 ( 
 'ready' 
 , 
  
 () 
  
 = 
>  
 { 
  
 console 
 . 
 log 
 ( 
 "TerraDraw is ready!" 
 ); 
  
 initUI 
 (); 
  
 setupModeButtons 
 (); 
  
 draw 
 . 
 setMode 
 ( 
 'point' 
 ); 
  
 currentMode 
  
 = 
  
 'point' 
 ; 
  
 setActiveButton 
 ( 
 'point-mode' 
 ); 
  
 draw 
 . 
 on 
 ( 
 "select" 
 , 
  
 ( 
 id 
 ) 
  
 = 
>  
 { 
  
 // console.log(`Feature selected: ${id}`); 
  
 if 
  
 ( 
 selectedFeatureId 
 && 
 selectedFeatureId 
  
 !== 
  
 id 
 ) 
  
 { 
  
 draw 
 . 
 deselectFeature 
 ( 
 selectedFeatureId 
 ); 
  
 } 
  
 selectedFeatureId 
  
 = 
  
 id 
 ; 
  
 }); 
  
 draw 
 . 
 on 
 ( 
 "deselect" 
 , 
  
 () 
  
 = 
>  
 { 
  
 // console.log("Feature deselected"); 
  
 selectedFeatureId 
  
 = 
  
 null 
 ; 
  
 }); 
  
 history 
 . 
 push 
 ( 
 processSnapshotForUndo 
 ( 
 draw 
 . 
 getSnapshot 
 ())); 
  
 // Push initial empty state 
  
 draw 
 . 
 on 
 ( 
 "change" 
 , 
  
 ( 
 ids 
 , 
  
 type 
 ) 
  
 = 
>  
 { 
  
 if 
  
 ( 
 isRestoring 
 ) 
  
 { 
  
 return 
 ; 
  
 } 
  
 if 
  
 ( 
 debounceTimeout 
 ) 
  
 { 
  
 clearTimeout 
 ( 
 debounceTimeout 
 ); 
  
 } 
  
 debounceTimeout 
  
 = 
  
 window 
 . 
 setTimeout 
 (() 
  
 = 
>  
 { 
  
 const 
  
 snapshot 
  
 = 
  
 draw 
 . 
 getSnapshot 
 (); 
  
 const 
  
 processedSnapshot 
  
 = 
  
 processSnapshotForUndo 
 ( 
 snapshot 
 ); 
  
 const 
  
 filteredSnapshot 
  
 = 
  
 processedSnapshot 
 . 
 filter 
 (( 
 f 
 ) 
  
 = 
>  
 ! 
 f 
 . 
 properties 
 . 
 midPoint 
 && 
 ! 
 f 
 . 
 properties 
 . 
 selectionPoint 
 ); 
  
 history 
 . 
 push 
 ( 
 filteredSnapshot 
 ); 
  
 redoHistory 
  
 = 
  
 []; 
  
 }, 
  
 200 
 ); 
  
 }); 
  
 const 
  
 exportButton 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'export-button' 
 ); 
  
 if 
  
 ( 
 exportButton 
 ) 
  
 { 
  
 exportButton 
 . 
 onclick 
  
 = 
  
 () 
  
 = 
>  
 { 
  
 const 
  
 features 
  
 = 
  
 draw 
 . 
 getSnapshot 
 (); 
  
 const 
  
 geojson 
  
 = 
  
 { 
  
 type 
 : 
  
 "FeatureCollection" 
 , 
  
 features 
 : 
  
 features 
 , 
  
 }; 
  
 const 
  
 data 
  
 = 
  
 JSON 
 . 
 stringify 
 ( 
 geojson 
 , 
  
 null 
 , 
  
 2 
 ); 
  
 const 
  
 blob 
  
 = 
  
 new 
  
 Blob 
 ([ 
 data 
 ], 
  
 { 
  
 type 
 : 
  
 "text/plain" 
  
 }); 
  
 const 
  
 url 
  
 = 
  
 URL 
 . 
 createObjectURL 
 ( 
 blob 
 ); 
  
 const 
  
 link 
  
 = 
  
 document 
 . 
 createElement 
 ( 
 "a" 
 ); 
  
 link 
 . 
 href 
  
 = 
  
 url 
 ; 
  
 link 
 . 
 download 
  
 = 
  
 "drawing.geojson" 
 ; 
  
 link 
 . 
 click 
 (); 
  
 URL 
 . 
 revokeObjectURL 
 ( 
 url 
 ); 
  
 }; 
  
 } 
  
 const 
  
 uploadButton 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'upload-button' 
 ); 
  
 const 
  
 uploadInput 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'upload-input' 
 ); 
  
 if 
  
 ( 
 uploadButton 
 && 
 uploadInput 
 ) 
  
 { 
  
 uploadButton 
 . 
 onclick 
  
 = 
  
 () 
  
 = 
>  
 { 
  
 uploadInput 
 . 
 click 
 (); 
  
 }; 
  
 uploadInput 
 . 
 onchange 
  
 = 
  
 ( 
 event 
 ) 
  
 = 
>  
 { 
  
 const 
  
 file 
  
 = 
  
 event 
 . 
 target 
 . 
 files 
 ? 
 .[ 
 0 
 ]; 
  
 if 
  
 ( 
 file 
 ) 
  
 { 
  
 const 
  
 reader 
  
 = 
  
 new 
  
 FileReader 
 (); 
  
 reader 
 . 
 onload 
  
 = 
  
 ( 
 e 
 ) 
  
 = 
>  
 { 
  
 try 
  
 { 
  
 const 
  
 geojson 
  
 = 
  
 JSON 
 . 
 parse 
 ( 
 e 
 . 
 target 
 ? 
 . 
 result 
 ); 
  
 if 
  
 ( 
 geojson 
 . 
 type 
  
 === 
  
 "FeatureCollection" 
 ) 
  
 { 
  
 draw 
 . 
 addFeatures 
 ( 
 geojson 
 . 
 features 
 ); 
  
 } 
  
 else 
  
 { 
  
 alert 
 ( 
 "Invalid GeoJSON file: must be a FeatureCollection." 
 ); 
  
 } 
  
 } 
  
 catch 
  
 ( 
 error 
 ) 
  
 { 
  
 alert 
 ( 
 "Error parsing GeoJSON file." 
 ); 
  
 } 
  
 }; 
  
 reader 
 . 
 readAsText 
 ( 
 file 
 ); 
  
 } 
  
 }; 
  
 } 
  
 const 
  
 resizeButton 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'resize-button' 
 ); 
  
 if 
  
 ( 
 resizeButton 
 ) 
  
 { 
  
 resizeButton 
 . 
 onclick 
  
 = 
  
 () 
  
 = 
>  
 { 
  
 resizingEnabled 
  
 = 
  
 ! 
 resizingEnabled 
 ; 
  
 resizeButton 
 . 
 classList 
 . 
 toggle 
 ( 
 'active' 
 , 
  
 resizingEnabled 
 ); 
  
 const 
  
 flags 
  
 = 
  
 { 
  
 polygon 
 : 
  
 { 
  
 feature 
 : 
  
 { 
  
 draggable 
 : 
  
 true 
 , 
  
 coordinates 
 : 
  
 { 
  
 resizable 
 : 
  
 resizingEnabled 
  
 ? 
  
 'center' 
  
 : 
  
 undefined 
 , 
  
 draggable 
 : 
  
 ! 
 resizingEnabled 
  
 } 
  
 } 
  
 }, 
  
 linestring 
 : 
  
 { 
  
 feature 
 : 
  
 { 
  
 draggable 
 : 
  
 true 
 , 
  
 coordinates 
 : 
  
 { 
  
 resizable 
 : 
  
 resizingEnabled 
  
 ? 
  
 'center' 
  
 : 
  
 undefined 
 , 
  
 draggable 
 : 
  
 ! 
 resizingEnabled 
  
 } 
  
 } 
  
 }, 
  
 rectangle 
 : 
  
 { 
  
 feature 
 : 
  
 { 
  
 draggable 
 : 
  
 true 
 , 
  
 coordinates 
 : 
  
 { 
  
 resizable 
 : 
  
 resizingEnabled 
  
 ? 
  
 'center' 
  
 : 
  
 undefined 
 , 
  
 draggable 
 : 
  
 ! 
 resizingEnabled 
  
 } 
  
 } 
  
 }, 
  
 circle 
 : 
  
 { 
  
 feature 
 : 
  
 { 
  
 draggable 
 : 
  
 true 
 , 
  
 coordinates 
 : 
  
 { 
  
 resizable 
 : 
  
 resizingEnabled 
  
 ? 
  
 'center' 
  
 : 
  
 undefined 
 , 
  
 draggable 
 : 
  
 ! 
 resizingEnabled 
  
 } 
  
 } 
  
 }, 
  
 freehand 
 : 
  
 { 
  
 feature 
 : 
  
 { 
  
 draggable 
 : 
  
 true 
 , 
  
 coordinates 
 : 
  
 { 
  
 resizable 
 : 
  
 resizingEnabled 
  
 ? 
  
 'center' 
  
 : 
  
 undefined 
 , 
  
 draggable 
 : 
  
 ! 
 resizingEnabled 
  
 } 
  
 } 
  
 }, 
  
 }; 
  
 console 
 . 
 log 
 ( 
 "Updating flags:" 
 , 
  
 flags 
 ); 
  
 draw 
 . 
 updateModeOptions 
 ( 
 'select' 
 , 
  
 { 
  
 flags 
  
 }); 
  
 }; 
  
 } 
  
 const 
  
 deleteSelectedButton 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'delete-selected-button' 
 ); 
  
 if 
  
 ( 
 deleteSelectedButton 
 ) 
  
 { 
  
 deleteSelectedButton 
 . 
 onclick 
  
 = 
  
 () 
  
 = 
>  
 { 
  
 if 
  
 ( 
 selectedFeatureId 
 ) 
  
 { 
  
 draw 
 . 
 removeFeatures 
 ([ 
 selectedFeatureId 
 ]); 
  
 } 
  
 else 
  
 { 
  
 const 
  
 features 
  
 = 
  
 draw 
 . 
 getSnapshot 
 (); 
  
 if 
  
 ( 
 features 
 . 
 length 
 > 
 0 
 ) 
  
 { 
  
 const 
  
 lastFeature 
  
 = 
  
 features 
 [ 
 features 
 . 
 length 
  
 - 
  
 1 
 ]; 
  
 if 
  
 ( 
 lastFeature 
 . 
 id 
 ) 
  
 { 
  
 draw 
 . 
 removeFeatures 
 ([ 
 lastFeature 
 . 
 id 
 ]); 
  
 } 
  
 } 
  
 } 
  
 }; 
  
 } 
  
 const 
  
 undoButton 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'undo-button' 
 ); 
  
 if 
  
 ( 
 undoButton 
 ) 
  
 { 
  
 undoButton 
 . 
 onclick 
  
 = 
  
 () 
  
 = 
>  
 { 
  
 if 
  
 ( 
 history 
 . 
 length 
 > 
 1 
 ) 
  
 { 
  
 redoHistory 
 . 
 push 
 ( 
 history 
 . 
 pop 
 ()); 
  
 const 
  
 snapshotToRestore 
  
 = 
  
 history 
 [ 
 history 
 . 
 length 
  
 - 
  
 1 
 ]; 
  
 console 
 . 
 log 
 ( 
 "Restoring snapshot (undo):" 
 , 
  
 snapshotToRestore 
 ); 
  
 isRestoring 
  
 = 
  
 true 
 ; 
  
 draw 
 . 
 clear 
 (); 
  
 draw 
 . 
 addFeatures 
 ( 
 snapshotToRestore 
 ); 
  
 setTimeout 
 (() 
  
 = 
>  
 { 
  
 isRestoring 
  
 = 
  
 false 
 ; 
  
 }, 
  
 0 
 ); 
  
 } 
  
 }; 
  
 } 
  
 const 
  
 redoButton 
  
 = 
  
 document 
 . 
 getElementById 
 ( 
 'redo-button' 
 ); 
  
 if 
  
 ( 
 redoButton 
 ) 
  
 { 
  
 redoButton 
 . 
 onclick 
  
 = 
  
 () 
  
 = 
>  
 { 
  
 if 
  
 ( 
 redoHistory 
 . 
 length 
 > 
 0 
 ) 
  
 { 
  
 const 
  
 snapshot 
  
 = 
  
 redoHistory 
 . 
 pop 
 (); 
  
 console 
 . 
 log 
 ( 
 "Restoring snapshot (redo):" 
 , 
  
 snapshot 
 ); 
  
 history 
 . 
 push 
 ( 
 snapshot 
 ); 
  
 isRestoring 
  
 = 
  
 true 
 ; 
  
 draw 
 . 
 clear 
 (); 
  
 draw 
 . 
 addFeatures 
 ( 
 snapshot 
 ); 
  
 setTimeout 
 (() 
  
 = 
>  
 { 
  
 isRestoring 
  
 = 
  
 false 
 ; 
  
 }, 
  
 0 
 ); 
  
 } 
  
 }; 
  
 } 
  
 }); 
  
 function 
  
 rotateFeature 
 ( 
 feature 
 , 
  
 angle 
 ) 
  
 { 
  
 const 
  
 newFeature 
  
 = 
  
 JSON 
 . 
 parse 
 ( 
 JSON 
 . 
 stringify 
 ( 
 feature 
 )); 
  
 const 
  
 coordinates 
  
 = 
  
 newFeature 
 . 
 geometry 
 . 
 coordinates 
 ; 
  
 const 
  
 center 
  
 = 
  
 getCenter 
 ( 
 coordinates 
 ); 
  
 const 
  
 rotatedCoordinates 
  
 = 
  
 coordinates 
 . 
 map 
 ( 
 ring 
  
 = 
>  
 { 
  
 return 
  
 ring 
 . 
 map 
 ( 
 point 
  
 = 
>  
 { 
  
 const 
  
 x 
  
 = 
  
 point 
 [ 
 0 
 ] 
  
 - 
  
 center 
 [ 
 0 
 ]; 
  
 const 
  
 y 
  
 = 
  
 point 
 [ 
 1 
 ] 
  
 - 
  
 center 
 [ 
 1 
 ]; 
  
 const 
  
 newX 
  
 = 
  
 x 
  
 * 
  
 Math 
 . 
 cos 
 ( 
 angle 
  
 * 
  
 Math 
 . 
 PI 
  
 / 
  
 180 
 ) 
  
 - 
  
 y 
  
 * 
  
 Math 
 . 
 sin 
 ( 
 angle 
  
 * 
  
 Math 
 . 
 PI 
  
 / 
  
 180 
 ); 
  
 const 
  
 newY 
  
 = 
  
 x 
  
 * 
  
 Math 
 . 
 sin 
 ( 
 angle 
  
 * 
  
 Math 
 . 
 PI 
  
 / 
  
 180 
 ) 
  
 + 
  
 y 
  
 * 
  
 Math 
 . 
 cos 
 ( 
 angle 
  
 * 
  
 Math 
 . 
 PI 
  
 / 
  
 180 
 ); 
  
 return 
  
 [ 
 newX 
  
 + 
  
 center 
 [ 
 0 
 ], 
  
 newY 
  
 + 
  
 center 
 [ 
 1 
 ]]; 
  
 }); 
  
 }); 
  
 newFeature 
 . 
 geometry 
 . 
 coordinates 
  
 = 
  
 rotatedCoordinates 
 ; 
  
 return 
  
 newFeature 
 ; 
  
 } 
  
 function 
  
 getCenter 
 ( 
 coordinates 
 ) 
  
 { 
  
 let 
  
 x 
  
 = 
  
 0 
 ; 
  
 let 
  
 y 
  
 = 
  
 0 
 ; 
  
 let 
  
 count 
  
 = 
  
 0 
 ; 
  
 coordinates 
 . 
 forEach 
 ( 
 ring 
  
 = 
>  
 { 
  
 ring 
 . 
 forEach 
 ( 
 point 
  
 = 
>  
 { 
  
 x 
  
 += 
  
 point 
 [ 
 0 
 ]; 
  
 y 
  
 += 
  
 point 
 [ 
 1 
 ]; 
  
 count 
 ++ 
 ; 
  
 }); 
  
 }); 
  
 return 
  
 [ 
 x 
  
 / 
  
 count 
 , 
  
 y 
  
 / 
  
 count 
 ]; 
  
 } 
  
 document 
 . 
 addEventListener 
 ( 
 'keydown' 
 , 
  
 ( 
 event 
 ) 
  
 = 
>  
 { 
  
 if 
  
 ( 
 event 
 . 
 key 
  
 === 
  
 'r' 
 && 
 selectedFeatureId 
 ) 
  
 { 
  
 const 
  
 features 
  
 = 
  
 draw 
 . 
 getSnapshot 
 (); 
  
 const 
  
 selectedFeature 
  
 = 
  
 features 
 . 
 find 
 ( 
 f 
  
 = 
>  
 f 
 . 
 id 
  
 === 
  
 selectedFeatureId 
 ); 
  
 if 
  
 ( 
 selectedFeature 
 ) 
  
 { 
  
 const 
  
 newFeature 
  
 = 
  
 rotateFeature 
 ( 
 selectedFeature 
 , 
  
 15 
 ); 
  
 draw 
 . 
 addFeatures 
 ([ 
 newFeature 
 ]); 
  
 } 
  
 } 
  
 }); 
  
 }); 
  
 } 
  
 catch 
  
 ( 
 e 
 ) 
  
 { 
  
 console 
 . 
 error 
 ( 
 "Error loading Google Maps API:" 
 , 
  
 e 
 ); 
  
 } 
 }). 
 catch 
 ( 
 e 
  
 = 
>  
 { 
  
 console 
 . 
 error 
 ( 
 "Error loading Google Maps API:" 
 , 
  
 e 
 ); 
 }); 
  

CSS

 html 
 , 
 body 
  
 { 
  
 height 
 : 
  
 100 
 % 
 ; 
  
 margin 
 : 
  
 0 
 ; 
  
 padding 
 : 
  
 0 
 ; 
  
 font-family 
 : 
  
 Arial 
 , 
  
 sans-serif 
 ; 
 } 
 # 
 map 
  
 { 
  
 height 
 : 
  
 100 
 % 
 ; 
  
 width 
 : 
  
 100 
 % 
 ; 
 } 
 # 
 mode-ui 
  
 { 
  
 position 
 : 
  
 absolute 
 ; 
  
 top 
 : 
  
 10 
 px 
 ; 
  
 right 
 : 
  
 10 
 px 
 ; 
  
 background 
 : 
  
 white 
 ; 
  
 padding 
 : 
  
 10 
 px 
 ; 
  
 border-radius 
 : 
  
 5 
 px 
 ; 
  
 box-shadow 
 : 
  
 0 
  
 0 
  
 10 
 px 
  
 rgba 
 ( 
 0 
 , 
  
 0 
 , 
  
 0 
 , 
  
 0.2 
 ); 
  
 z-index 
 : 
  
 1000 
 ; 
  
 display 
 : 
  
 flex 
 ; 
  
 flex-direction 
 : 
  
 column 
 ; 
 } 
 # 
 mode-ui 
  
 button 
  
 { 
  
 margin 
 : 
  
 5 
 px 
  
 0 
 ; 
  
 cursor 
 : 
  
 pointer 
 ; 
 } 
 . 
 mode-button 
  
 { 
  
 width 
 : 
  
 30 
 px 
 ; 
  
 height 
 : 
  
 30 
 px 
 ; 
  
 border 
 : 
  
 1 
 px 
  
 solid 
  
 #ccc 
 ; 
  
 background-color 
 : 
  
 white 
 ; 
  
 padding 
 : 
  
 2 
 px 
 ; 
  
 box-sizing 
 : 
  
 border-box 
 ; 
 } 
 . 
 mode-button 
  
 img 
  
 { 
  
 width 
 : 
  
 100 
 % 
 ; 
  
 height 
 : 
  
 100 
 % 
 ; 
  
 display 
 : 
  
 block 
 ; 
  
 user-select 
 : 
  
 none 
 ; 
 } 
 /* Active state for shape modes */ 
 . 
 mode-button 
 . 
 active 
  
 { 
  
 background-color 
 : 
  
 #e0e0e0 
 ; 
  
 /* light grey */ 
 } 
 /* Special buttons default state */ 
 # 
 select-mode 
 , 
 # 
 clear-mode 
 , 
 # 
 delete-selected-button 
 , 
 # 
 undo-button 
 , 
 # 
 redo-button 
 , 
 # 
 export-button 
 , 
 # 
 upload-button 
 , 
 # 
 resize-button 
  
 { 
  
 background-color 
 : 
  
 #000000 
 ; 
 } 
 /* Special buttons icon default state */ 
 # 
 select-mode 
  
 img 
 , 
 # 
 clear-mode 
  
 img 
 , 
 # 
 delete-selected-button 
  
 img 
 , 
 # 
 undo-button 
  
 img 
 , 
 # 
 redo-button 
  
 img 
 , 
 # 
 export-button 
  
 img 
 , 
 # 
 upload-button 
  
 img 
 , 
 # 
 resize-button 
  
 img 
  
 { 
  
 filter 
 : 
  
 brightness 
 ( 
 0 
 ) 
  
 invert 
 ( 
 1 
 ); 
 } 
 /* Special buttons active/click states */ 
 # 
 select-mode 
 . 
 active 
  
 { 
  
 background-color 
 : 
  
 #A9A9A9 
 ; 
  
 /* dark grey */ 
 } 
 # 
 clear-mode 
 : 
 active 
 , 
 # 
 delete-selected-button 
 : 
 active 
 , 
 # 
 undo-button 
 : 
 active 
 , 
 # 
 redo-button 
 : 
 active 
 , 
 # 
 export-button 
 : 
 active 
 , 
 # 
 upload-button 
 : 
 active 
 , 
 # 
 resize-button 
 . 
 active 
  
 { 
  
 background-color 
 : 
  
 #A9A9A9 
 ; 
  
 /* dark grey */ 
 } 
  

HTML

<html>
<head>
    <title>Terra Draw with Google Maps API Sample</title>
    <link rel="stylesheet" href="./style.css">
    <!-- Terra Draw CSS (if any needed, add here) -->
</head>
<body>
    <!-- Map Container -->
    <div id="map"></div>

    <!-- Top-right mode selection UI -->
    <div id="mode-ui">
        <button id="point-mode" class="mode-button" title="Point"><img src="./img/point.svg" alt="Point" draggable="false"></button>
        <button id="linestring-mode" class="mode-button" title="Linestring"><img src="./img/polyline.svg" alt="Linestring" draggable="false"></button>
        <button id="polygon-mode" class="mode-button active" title="Polygon"><img src="./img/polygon.png" alt="Polygon" draggable="false"></button>
        <button id="rectangle-mode" class="mode-button" title="Rectangle"><img src="./img/rectangle.svg" alt="Rectangle" draggable="false"></button>
        <button id="circle-mode" class="mode-button" title="Circle"><img src="./img/circle.svg" alt="Circle" draggable="false"></button>
        <button id="freehand-mode" class="mode-button" title="Freehand"><img src="./img/freehand.svg" alt="Freehand" draggable="false"></button>
        <button id="select-mode" class="mode-button" title="Select"><img src="./img/select.svg" alt="Select" draggable="false"></button>
        <button id="resize-button" class="mode-button" title="Resize"><img src="./img/resize.svg" alt="Resize" draggable="false"></button>
        <button id="clear-mode" class="mode-button" title="Clear"><img src="./img/delete.svg" alt="Clear" draggable="false"></button>
        <button id="delete-selected-button" class="mode-button" title="Clear last or Selected"><img src="./img/delete-selected.svg" alt="Delete Selected" draggable="false"></button>
        <button id="undo-button" class="mode-button" title="Undo"><img src="./img/undo.svg" alt="Undo" draggable="false"></button>
        <button id="redo-button" class="mode-button" title="Redo"><img src="./img/redo.svg" alt="Redo" draggable="false"></button>
        <button id="export-button" class="mode-button" title="Export"><img src="./img/download.svg" alt="Export" draggable="false"></button>
        <button id="upload-button" class="mode-button" title="Upload"><img src="./img/upload.svg" alt="Upload" draggable="false"></button>
        <input type="file" id="upload-input" style="display: none;" accept=".geojson,.json">
    </div>

    <script type="module" src="./index.ts"></script>
    <!-- Google Maps API is loaded by the Loader in index.ts -->
</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 
 / 
 map 
 - 
 drawing 
 - 
 terradraw 
 
  
  npm 
  
 i 
 
  
  npm 
  
 start 
 

Key Steps for Integration

  1. Load Libraries: Include the Google Maps JavaScript API script, followed by the Terra Draw core and google-maps scripts. If you are using script tags:
 <script src="https://unpkg.com/terra-draw@latest/dist/terra-draw.umd.js"></script>

<script src="https://unpkg.com/terra-draw-google-maps-adapter@latest/dist/terra-draw-google-maps-adapter.umd.js"></script> 
  1. Initialize Map: Create your standard google.maps.Map instance.

  2. Create Adapter: Instantiate TerraDrawGoogleMapsAdapter , passing it the google.maps library and your map instance to connect them.

  3. Create TerraDraw: Create a TerraDraw instance, providing the adapter and an array of the drawing modes you want to support.

  4. Activate Drawing: Call draw.start() to enable the tool, then draw.setMode('polygon') to select a drawing shape.

  5. Capture Data: Listen to the draw.on('change', callback) event to get an array of all drawn features as GeoJSON .

Design a Mobile Site
View Site in Mobile | Classic
Share by: