or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

addons.mdadvanced.mdcore-components.mddrawing.mdhocs.mdindex.mdlayers.mdplaces.mdshapes.mdvisualization.md

drawing.mddocs/

0

# Drawing Tools

1

2

Google Maps drawing tools component for creating and editing geometric shapes interactively on the map. Enables users to draw markers, polylines, polygons, circles, and rectangles.

3

4

## Capabilities

5

6

### DrawingManager

7

8

Component that provides interactive drawing tools for creating shapes on the map.

9

10

```javascript { .api }

11

/**

12

* Drawing manager component for interactive shape creation

13

*/

14

class DrawingManager extends Component<DrawingManagerProps> {

15

/** Returns the current drawing mode */

16

getDrawingMode(): google.maps.drawing.OverlayType;

17

}

18

19

interface DrawingManagerProps {

20

// Default props

21

defaultDrawingMode?: google.maps.drawing.OverlayType;

22

defaultOptions?: google.maps.drawing.DrawingManagerOptions;

23

24

// Controlled props

25

drawingMode?: google.maps.drawing.OverlayType;

26

options?: google.maps.drawing.DrawingManagerOptions;

27

28

// Event handlers

29

onCircleComplete?(c: google.maps.Circle): void;

30

onMarkerComplete?(m: google.maps.Marker): void;

31

onOverlayComplete?(e: google.maps.drawing.OverlayCompleteEvent): void;

32

onPolygonComplete?(p: google.maps.Polygon): void;

33

onPolylineComplete?(p: google.maps.Polyline): void;

34

onRectangleComplete?(r: google.maps.Rectangle): void;

35

}

36

```

37

38

**Usage Example:**

39

40

```javascript

41

import DrawingManager from "react-google-maps/lib/components/drawing/DrawingManager";

42

43

const DrawingMap = () => {

44

const [drawingMode, setDrawingMode] = useState(null);

45

const [shapes, setShapes] = useState([]);

46

47

const onOverlayComplete = (event) => {

48

const newShape = {

49

id: Date.now(),

50

type: event.type,

51

overlay: event.overlay

52

};

53

54

setShapes(prevShapes => [...prevShapes, newShape]);

55

56

// Disable drawing mode after completing a shape

57

setDrawingMode(null);

58

};

59

60

const deleteShape = (shapeId) => {

61

setShapes(prevShapes => {

62

const shapeToDelete = prevShapes.find(shape => shape.id === shapeId);

63

if (shapeToDelete) {

64

shapeToDelete.overlay.setMap(null); // Remove from map

65

}

66

return prevShapes.filter(shape => shape.id !== shapeId);

67

});

68

};

69

70

return (

71

<div>

72

{/* Drawing controls */}

73

<div style={{ padding: '10px', backgroundColor: '#f5f5f5' }}>

74

<button

75

onClick={() => setDrawingMode(google.maps.drawing.OverlayType.MARKER)}

76

style={{ margin: '0 5px', padding: '8px 12px' }}

77

>

78

Draw Marker

79

</button>

80

<button

81

onClick={() => setDrawingMode(google.maps.drawing.OverlayType.POLYLINE)}

82

style={{ margin: '0 5px', padding: '8px 12px' }}

83

>

84

Draw Line

85

</button>

86

<button

87

onClick={() => setDrawingMode(google.maps.drawing.OverlayType.POLYGON)}

88

style={{ margin: '0 5px', padding: '8px 12px' }}

89

>

90

Draw Polygon

91

</button>

92

<button

93

onClick={() => setDrawingMode(google.maps.drawing.OverlayType.CIRCLE)}

94

style={{ margin: '0 5px', padding: '8px 12px' }}

95

>

96

Draw Circle

97

</button>

98

<button

99

onClick={() => setDrawingMode(google.maps.drawing.OverlayType.RECTANGLE)}

100

style={{ margin: '0 5px', padding: '8px 12px' }}

101

>

102

Draw Rectangle

103

</button>

104

<button

105

onClick={() => setDrawingMode(null)}

106

style={{ margin: '0 5px', padding: '8px 12px', backgroundColor: '#ff6b6b' }}

107

>

108

Stop Drawing

109

</button>

110

</div>

111

112

<GoogleMap

113

defaultZoom={10}

114

defaultCenter={{ lat: 37.7749, lng: -122.4194 }}

115

style={{ height: '500px' }}

116

>

117

<DrawingManager

118

drawingMode={drawingMode}

119

options={{

120

drawingControl: true,

121

drawingControlOptions: {

122

position: google.maps.ControlPosition.TOP_CENTER,

123

drawingModes: [

124

google.maps.drawing.OverlayType.MARKER,

125

google.maps.drawing.OverlayType.CIRCLE,

126

google.maps.drawing.OverlayType.POLYGON,

127

google.maps.drawing.OverlayType.POLYLINE,

128

google.maps.drawing.OverlayType.RECTANGLE,

129

],

130

},

131

markerOptions: {

132

icon: 'https://maps.google.com/mapfiles/ms/icons/blue-dot.png',

133

},

134

circleOptions: {

135

fillColor: '#ffff00',

136

fillOpacity: 0.3,

137

strokeWeight: 2,

138

clickable: false,

139

editable: true,

140

zIndex: 1,

141

},

142

polygonOptions: {

143

fillColor: '#ff0000',

144

fillOpacity: 0.3,

145

strokeWeight: 2,

146

clickable: false,

147

editable: true,

148

zIndex: 1,

149

},

150

polylineOptions: {

151

strokeColor: '#0000ff',

152

strokeWeight: 3,

153

clickable: false,

154

editable: true,

155

zIndex: 1,

156

},

157

rectangleOptions: {

158

fillColor: '#00ff00',

159

fillOpacity: 0.3,

160

strokeWeight: 2,

161

clickable: false,

162

editable: true,

163

zIndex: 1,

164

},

165

}}

166

onOverlayComplete={onOverlayComplete}

167

onMarkerComplete={(marker) => {

168

console.log('Marker created at:', marker.getPosition().toJSON());

169

}}

170

onCircleComplete={(circle) => {

171

console.log('Circle created with radius:', circle.getRadius());

172

}}

173

onPolygonComplete={(polygon) => {

174

console.log('Polygon created with', polygon.getPath().getLength(), 'vertices');

175

}}

176

onPolylineComplete={(polyline) => {

177

console.log('Polyline created with', polyline.getPath().getLength(), 'points');

178

}}

179

onRectangleComplete={(rectangle) => {

180

console.log('Rectangle created with bounds:', rectangle.getBounds().toJSON());

181

}}

182

/>

183

</GoogleMap>

184

185

{/* Shape list */}

186

<div style={{ padding: '10px' }}>

187

<h3>Created Shapes ({shapes.length})</h3>

188

{shapes.map((shape) => (

189

<div key={shape.id} style={{

190

display: 'flex',

191

justifyContent: 'space-between',

192

alignItems: 'center',

193

padding: '8px',

194

border: '1px solid #ddd',

195

margin: '4px 0'

196

}}>

197

<span>{shape.type} (ID: {shape.id})</span>

198

<button

199

onClick={() => deleteShape(shape.id)}

200

style={{ backgroundColor: '#ff6b6b', color: 'white', border: 'none', padding: '4px 8px' }}

201

>

202

Delete

203

</button>

204

</div>

205

))}

206

</div>

207

</div>

208

);

209

};

210

```

211

212

## Drawing Manager Configuration

213

214

### Drawing Modes

215

216

Available drawing modes from `google.maps.drawing.OverlayType`:

217

218

```javascript

219

const drawingModes = {

220

MARKER: google.maps.drawing.OverlayType.MARKER,

221

CIRCLE: google.maps.drawing.OverlayType.CIRCLE,

222

POLYGON: google.maps.drawing.OverlayType.POLYGON,

223

POLYLINE: google.maps.drawing.OverlayType.POLYLINE,

224

RECTANGLE: google.maps.drawing.OverlayType.RECTANGLE

225

};

226

```

227

228

### Shape Styling Options

229

230

Configure the appearance of each shape type:

231

232

```javascript

233

const drawingOptions = {

234

drawingControl: true,

235

drawingControlOptions: {

236

position: google.maps.ControlPosition.TOP_CENTER,

237

drawingModes: [

238

google.maps.drawing.OverlayType.MARKER,

239

google.maps.drawing.OverlayType.CIRCLE,

240

google.maps.drawing.OverlayType.POLYGON,

241

google.maps.drawing.OverlayType.POLYLINE,

242

google.maps.drawing.OverlayType.RECTANGLE,

243

],

244

},

245

markerOptions: {

246

icon: {

247

url: 'https://maps.google.com/mapfiles/ms/icons/blue-dot.png',

248

scaledSize: { width: 32, height: 32 }

249

},

250

draggable: true

251

},

252

circleOptions: {

253

fillColor: '#ffff00',

254

fillOpacity: 0.3,

255

strokeColor: '#ffff00',

256

strokeWeight: 2,

257

clickable: true,

258

editable: true,

259

draggable: true,

260

zIndex: 1,

261

},

262

polygonOptions: {

263

fillColor: '#ff0000',

264

fillOpacity: 0.3,

265

strokeColor: '#ff0000',

266

strokeWeight: 2,

267

clickable: true,

268

editable: true,

269

draggable: true,

270

zIndex: 1,

271

},

272

polylineOptions: {

273

strokeColor: '#0000ff',

274

strokeWeight: 3,

275

clickable: true,

276

editable: true,

277

draggable: true,

278

zIndex: 1,

279

},

280

rectangleOptions: {

281

fillColor: '#00ff00',

282

fillOpacity: 0.3,

283

strokeColor: '#00ff00',

284

strokeWeight: 2,

285

clickable: true,

286

editable: true,

287

draggable: true,

288

zIndex: 1,

289

},

290

};

291

```

292

293

## Advanced Drawing Patterns

294

295

### Shape Data Export

296

297

Export drawn shapes as GeoJSON:

298

299

```javascript

300

const exportShapesToGeoJSON = (shapes) => {

301

const features = shapes.map(shape => {

302

let geometry;

303

304

switch (shape.type) {

305

case google.maps.drawing.OverlayType.MARKER:

306

const position = shape.overlay.getPosition();

307

geometry = {

308

type: "Point",

309

coordinates: [position.lng(), position.lat()]

310

};

311

break;

312

313

case google.maps.drawing.OverlayType.POLYLINE:

314

const path = shape.overlay.getPath().getArray();

315

geometry = {

316

type: "LineString",

317

coordinates: path.map(point => [point.lng(), point.lat()])

318

};

319

break;

320

321

case google.maps.drawing.OverlayType.POLYGON:

322

const polygonPath = shape.overlay.getPath().getArray();

323

geometry = {

324

type: "Polygon",

325

coordinates: [polygonPath.map(point => [point.lng(), point.lat()])]

326

};

327

break;

328

329

case google.maps.drawing.OverlayType.CIRCLE:

330

const center = shape.overlay.getCenter();

331

const radius = shape.overlay.getRadius();

332

// Note: GeoJSON doesn't support circles directly

333

geometry = {

334

type: "Point",

335

coordinates: [center.lng(), center.lat()],

336

properties: { radius: radius }

337

};

338

break;

339

340

case google.maps.drawing.OverlayType.RECTANGLE:

341

const bounds = shape.overlay.getBounds();

342

const ne = bounds.getNorthEast();

343

const sw = bounds.getSouthWest();

344

geometry = {

345

type: "Polygon",

346

coordinates: [[

347

[sw.lng(), sw.lat()],

348

[ne.lng(), sw.lat()],

349

[ne.lng(), ne.lat()],

350

[sw.lng(), ne.lat()],

351

[sw.lng(), sw.lat()]

352

]]

353

};

354

break;

355

}

356

357

return {

358

type: "Feature",

359

properties: {

360

id: shape.id,

361

type: shape.type

362

},

363

geometry: geometry

364

};

365

});

366

367

return {

368

type: "FeatureCollection",

369

features: features

370

};

371

};

372

```

373

374

### Drawing with Validation

375

376

Add validation rules for drawn shapes:

377

378

```javascript

379

const ValidatedDrawingManager = () => {

380

const [shapes, setShapes] = useState([]);

381

const [errors, setErrors] = useState([]);

382

383

const validateShape = (shape, type) => {

384

const validation = { isValid: true, errors: [] };

385

386

switch (type) {

387

case google.maps.drawing.OverlayType.POLYGON:

388

const path = shape.getPath().getArray();

389

if (path.length < 3) {

390

validation.isValid = false;

391

validation.errors.push("Polygon must have at least 3 vertices");

392

}

393

394

// Check for minimum area

395

const area = google.maps.geometry.spherical.computeArea(path);

396

if (area < 1000) { // 1000 square meters

397

validation.isValid = false;

398

validation.errors.push("Polygon area must be at least 1000 sq meters");

399

}

400

break;

401

402

case google.maps.drawing.OverlayType.CIRCLE:

403

const radius = shape.getRadius();

404

if (radius < 50) { // 50 meters minimum

405

validation.isValid = false;

406

validation.errors.push("Circle radius must be at least 50 meters");

407

}

408

break;

409

410

case google.maps.drawing.OverlayType.POLYLINE:

411

const polylinePath = shape.getPath().getArray();

412

if (polylinePath.length < 2) {

413

validation.isValid = false;

414

validation.errors.push("Polyline must have at least 2 points");

415

}

416

break;

417

}

418

419

return validation;

420

};

421

422

const onOverlayComplete = (event) => {

423

const validation = validateShape(event.overlay, event.type);

424

425

if (validation.isValid) {

426

const newShape = {

427

id: Date.now(),

428

type: event.type,

429

overlay: event.overlay

430

};

431

setShapes(prevShapes => [...prevShapes, newShape]);

432

setErrors([]);

433

} else {

434

// Remove invalid shape from map

435

event.overlay.setMap(null);

436

setErrors(validation.errors);

437

}

438

};

439

440

return (

441

<div>

442

{errors.length > 0 && (

443

<div style={{

444

background: '#ffebee',

445

color: '#c62828',

446

padding: '10px',

447

margin: '10px 0'

448

}}>

449

<strong>Drawing Errors:</strong>

450

<ul>

451

{errors.map((error, index) => (

452

<li key={index}>{error}</li>

453

))}

454

</ul>

455

</div>

456

)}

457

458

<GoogleMap defaultZoom={10} defaultCenter={{ lat: 37.7749, lng: -122.4194 }}>

459

<DrawingManager

460

options={drawingOptions}

461

onOverlayComplete={onOverlayComplete}

462

/>

463

</GoogleMap>

464

</div>

465

);

466

};

467

```

468

469

### Drawing with Snapping

470

471

Enable snapping to existing shapes or grid:

472

473

```javascript

474

const SnappingDrawingManager = () => {

475

const [snapToGrid, setSnapToGrid] = useState(true);

476

const gridSize = 0.001; // ~100m grid

477

478

const snapToGridPoint = (latLng) => {

479

if (!snapToGrid) return latLng;

480

481

const lat = Math.round(latLng.lat() / gridSize) * gridSize;

482

const lng = Math.round(latLng.lng() / gridSize) * gridSize;

483

484

return new google.maps.LatLng(lat, lng);

485

};

486

487

const onOverlayComplete = (event) => {

488

if (snapToGrid) {

489

switch (event.type) {

490

case google.maps.drawing.OverlayType.MARKER:

491

const snappedPosition = snapToGridPoint(event.overlay.getPosition());

492

event.overlay.setPosition(snappedPosition);

493

break;

494

495

case google.maps.drawing.OverlayType.POLYGON:

496

const path = event.overlay.getPath();

497

for (let i = 0; i < path.getLength(); i++) {

498

const snappedPoint = snapToGridPoint(path.getAt(i));

499

path.setAt(i, snappedPoint);

500

}

501

break;

502

}

503

}

504

505

// Continue with normal shape handling...

506

};

507

508

return (

509

<div>

510

<label>

511

<input

512

type="checkbox"

513

checked={snapToGrid}

514

onChange={(e) => setSnapToGrid(e.target.checked)}

515

/>

516

Snap to Grid

517

</label>

518

519

<GoogleMap defaultZoom={15} defaultCenter={{ lat: 37.7749, lng: -122.4194 }}>

520

<DrawingManager

521

options={drawingOptions}

522

onOverlayComplete={onOverlayComplete}

523

/>

524

</GoogleMap>

525

</div>

526

);

527

};

528

```