The following example shows how to add a drawing layer to a map using [Terra
Draw](https://github.com/JamesLMilner/terra-draw) JavaScript libraries. The layer relies on [OverlayView](https://developers.google.com/maps/documentation/javascript/reference/overlay-view.md.txt#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](https://geojson.org/) objects.

<br />

> [!NOTE]
> **Note:** Terra Draw is a third-party product, and is not supported by Google.

<iframe src="https://maps-docs-team.web.app/samples/map-drawing-terradraw/dist/" allow="fullscreen; "></iframe>

### TypeScript

```typescript
import {
    TerraDraw,
    TerraDrawSelectMode,
    TerraDrawPointMode,
    TerraDrawLineStringMode,
    TerraDrawPolygonMode,
    TerraDrawRectangleMode,
    TerraDrawCircleMode,
    TerraDrawFreehandMode,
    GeoJSONStoreFeatures,
} 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: GeoJSONStoreFeatures[]
): GeoJSONStoreFeatures[] {
    // console.log("Processing snapshot for undo:", snapshot);
    return snapshot.map((feature) => {
        const newFeature = JSON.parse(
            JSON.stringify(feature)
        ) as GeoJSONStoreFeatures;

        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: Record<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];

                // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
                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;
const history: GeoJSONStoreFeatures[][] = [];
let redoHistory: GeoJSONStoreFeatures[][] = [];
let selectedFeatureId: string | null = null;
let isRestoring = false;
let resizingEnabled = false;
let debounceTimeout: number | undefined;

async function init() {
    try {
        const { Map } = await google.maps.importLibrary('maps');

        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')!;
        map = new Map(mapDiv, mapOptions);

        map.addListener('click', () => {
            // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
            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');
                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', () => {
                    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,
                        };
                        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 as HTMLInputElement).click();
                    };

                    (uploadInput as HTMLInputElement).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
                                    ) as {
                                        type: string;
                                        features: GeoJSONStoreFeatures[];
                                    };
                                    if (geojson.type === 'FeatureCollection') {
                                        draw.addFeatures(geojson.features);
                                    } else {
                                        alert(
                                            'Invalid GeoJSON file: must be a FeatureCollection.'
                                        );
                                    }
                                } catch {
                                    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) {
                            const popped = history.pop();
                            if (popped) {
                                redoHistory.push(popped);
                            }
                            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: GeoJSONStoreFeatures,
                angle: number
            ): GeoJSONStoreFeatures {
                const newFeature = JSON.parse(
                    JSON.stringify(feature)
                ) as GeoJSONStoreFeatures;
                const coordinates = (
                    newFeature.geometry as { coordinates: number[][][] }
                ).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: number[][][]): number[] {
                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);
    }
}

void init();
```

### JavaScript

```javascript
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;
const history = [];
let redoHistory = [];
let selectedFeatureId = null;
let isRestoring = false;
let resizingEnabled = false;
let debounceTimeout;

async function init() {
    try {
        const { Map } = 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');
                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', () => {
                    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,
                        };
                        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 {
                                    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) {
                            const popped = history.pop();
                            if (popped) {
                                redoHistory.push(popped);
                            }
                            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);
    }
}

void init();
```

### CSS

```css
html,
body {
    height: 100%;
    margin: 0;
    padding: 0;
    font-family: Arial, sans-serif;
}
#map {
    height: 100%;
    width: 100%;
}
#mode-ui {
    position: absolute;
    top: 10px;
    right: 10px;
    background: white;
    padding: 10px;
    border-radius: 5px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
    z-index: 1000;
    display: flex;
    flex-direction: column;
}
#mode-ui button {
    margin: 5px 0;
    cursor: pointer;
}

.mode-button {
    width: 30px;
    height: 30px;
    border: 1px solid #ccc;
    background-color: white;
    padding: 2px;
    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
<html>
    <head>
        <title>Terra Draw with Google Maps API Sample</title>
        <link rel="stylesheet" href="./style.css" />
        <script type="module" src="./index.ts"></script>
        <script>
            // prettier-ignore
            (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: "GOOGLE_MAPS_API_KEY"
            });
        </script>
    </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="https://maps-docs-team.web.app/samples/map-drawing-terradraw/dist/img/point.svg" alt="Point" draggable="false" />
            </button>
            <button id="linestring-mode" class="mode-button" title="Linestring">
                <img
                    src="https://maps-docs-team.web.app/samples/map-drawing-terradraw/dist/img/polyline.svg"
                    alt="Linestring"
                    draggable="false" />
            </button>
            <button
                id="polygon-mode"
                class="mode-button active"
                title="Polygon">
                <img src="https://maps-docs-team.web.app/samples/map-drawing-terradraw/dist/img/polygon.png" alt="Polygon" draggable="false" />
            </button>
            <button id="rectangle-mode" class="mode-button" title="Rectangle">
                <img
                    src="https://maps-docs-team.web.app/samples/map-drawing-terradraw/dist/img/rectangle.svg"
                    alt="Rectangle"
                    draggable="false" />
            </button>
            <button id="circle-mode" class="mode-button" title="Circle">
                <img src="https://maps-docs-team.web.app/samples/map-drawing-terradraw/dist/img/circle.svg" alt="Circle" draggable="false" />
            </button>
            <button id="freehand-mode" class="mode-button" title="Freehand">
                <img
                    src="https://maps-docs-team.web.app/samples/map-drawing-terradraw/dist/img/freehand.svg"
                    alt="Freehand"
                    draggable="false" />
            </button>
            <button id="select-mode" class="mode-button" title="Select">
                <img src="https://maps-docs-team.web.app/samples/map-drawing-terradraw/dist/img/select.svg" alt="Select" draggable="false" />
            </button>
            <button id="resize-button" class="mode-button" title="Resize">
                <img src="https://maps-docs-team.web.app/samples/map-drawing-terradraw/dist/img/resize.svg" alt="Resize" draggable="false" />
            </button>
            <button id="clear-mode" class="mode-button" title="Clear">
                <img src="https://maps-docs-team.web.app/samples/map-drawing-terradraw/dist/img/delete.svg" alt="Clear" draggable="false" />
            </button>
            <button
                id="delete-selected-button"
                class="mode-button"
                title="Clear last or Selected">
                <img
                    src="https://maps-docs-team.web.app/samples/map-drawing-terradraw/dist/img/delete-selected.svg"
                    alt="Delete Selected"
                    draggable="false" />
            </button>
            <button id="undo-button" class="mode-button" title="Undo">
                <img src="https://maps-docs-team.web.app/samples/map-drawing-terradraw/dist/img/undo.svg" alt="Undo" draggable="false" />
            </button>
            <button id="redo-button" class="mode-button" title="Redo">
                <img src="https://maps-docs-team.web.app/samples/map-drawing-terradraw/dist/img/redo.svg" alt="Redo" draggable="false" />
            </button>
            <button id="export-button" class="mode-button" title="Export">
                <img src="https://maps-docs-team.web.app/samples/map-drawing-terradraw/dist/img/download.svg" alt="Export" draggable="false" />
            </button>
            <button id="upload-button" class="mode-button" title="Upload">
                <img src="https://maps-docs-team.web.app/samples/map-drawing-terradraw/dist/img/upload.svg" alt="Upload" draggable="false" />
            </button>
            <input
                type="file"
                id="upload-input"
                style="display: none"
                accept=".geojson,.json" />
        </div>
    </body>
</html>
```

### Clone Sample


Git and Node.js are required to run this sample locally. Follow these [instructions](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
to install Node.js and NPM. You will also need a [Google Maps API key](https://developers.google.com/maps/documentation/javascript/get-api-key.md.txt).


The following commands clone the repository, install dependencies, and copy the environment file.

```javascript
  git clone https://github.com/googlemaps-samples/js-api-samples.git
  cd js-api-samples
  npm i
  cp .env.template .env
```


Once you copy the environment file, make sure to open the `.env` file and replace `<Insert your own development API key here>` with your actual API key before starting the sample.

```javascript
  cd samples/map-drawing-terradraw
  npm start
```

### Key Steps for Integration

<br />

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:

```html
<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`.