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

visualization.mddocs/

0

# Visualization

1

2

Data visualization component for displaying heatmaps and other visual representations of geographic data on Google Maps. Requires the visualization library.

3

4

## Capabilities

5

6

### HeatmapLayer

7

8

Component for displaying heatmap visualizations of geographic data points with density-based coloring.

9

10

```javascript { .api }

11

/**

12

* Heatmap layer component for data visualization

13

* Requires: visualization library in Google Maps API URL

14

*/

15

class HeatmapLayer extends Component<HeatmapLayerProps> {

16

/** Returns the heatmap data array */

17

getData(): google.maps.MVCArray<google.maps.LatLng | google.maps.visualization.WeightedLocation>;

18

}

19

20

interface HeatmapLayerProps {

21

// Default props

22

defaultData?: google.maps.MVCArray<google.maps.LatLng | google.maps.visualization.WeightedLocation> | Array<google.maps.LatLng | google.maps.visualization.WeightedLocation>;

23

defaultOptions?: google.maps.visualization.HeatmapLayerOptions;

24

25

// Controlled props

26

data?: google.maps.MVCArray<google.maps.LatLng | google.maps.visualization.WeightedLocation> | Array<google.maps.LatLng | google.maps.visualization.WeightedLocation>;

27

options?: google.maps.visualization.HeatmapLayerOptions;

28

}

29

```

30

31

**Usage Example:**

32

33

```javascript

34

import HeatmapLayer from "react-google-maps/lib/components/visualization/HeatmapLayer";

35

36

const HeatmapVisualization = () => {

37

// Sample data points for San Francisco crime data

38

const [heatmapData] = useState([

39

{ location: new google.maps.LatLng(37.782551, -122.445368), weight: 0.5 },

40

{ location: new google.maps.LatLng(37.782745, -122.444586), weight: 2 },

41

{ location: new google.maps.LatLng(37.782842, -122.443688), weight: 3 },

42

{ location: new google.maps.LatLng(37.782919, -122.442815), weight: 2 },

43

{ location: new google.maps.LatLng(37.782992, -122.442112), weight: 3 },

44

{ location: new google.maps.LatLng(37.783100, -122.441461), weight: 0.5 },

45

{ location: new google.maps.LatLng(37.783206, -122.440829), weight: 2 },

46

{ location: new google.maps.LatLng(37.783273, -122.440324), weight: 3 },

47

{ location: new google.maps.LatLng(37.783316, -122.440023), weight: 3 },

48

{ location: new google.maps.LatLng(37.783357, -122.439794), weight: 2 },

49

// ... more data points

50

]);

51

52

const [heatmapOptions, setHeatmapOptions] = useState({

53

radius: 20,

54

opacity: 0.6,

55

gradient: [

56

'rgba(0, 255, 255, 0)',

57

'rgba(0, 255, 255, 1)',

58

'rgba(0, 191, 255, 1)',

59

'rgba(0, 127, 255, 1)',

60

'rgba(0, 63, 255, 1)',

61

'rgba(0, 0, 255, 1)',

62

'rgba(0, 0, 223, 1)',

63

'rgba(0, 0, 191, 1)',

64

'rgba(0, 0, 159, 1)',

65

'rgba(0, 0, 127, 1)',

66

'rgba(63, 0, 91, 1)',

67

'rgba(127, 0, 63, 1)',

68

'rgba(191, 0, 31, 1)',

69

'rgba(255, 0, 0, 1)'

70

]

71

});

72

73

return (

74

<div>

75

{/* Heatmap controls */}

76

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

77

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

78

<label style={{ marginRight: '10px' }}>

79

Radius: {heatmapOptions.radius}

80

<input

81

type="range"

82

min="10"

83

max="50"

84

value={heatmapOptions.radius}

85

onChange={(e) => setHeatmapOptions(prev => ({

86

...prev,

87

radius: parseInt(e.target.value)

88

}))}

89

style={{ marginLeft: '10px' }}

90

/>

91

</label>

92

93

<label style={{ marginLeft: '20px' }}>

94

Opacity: {heatmapOptions.opacity}

95

<input

96

type="range"

97

min="0.1"

98

max="1"

99

step="0.1"

100

value={heatmapOptions.opacity}

101

onChange={(e) => setHeatmapOptions(prev => ({

102

...prev,

103

opacity: parseFloat(e.target.value)

104

}))}

105

style={{ marginLeft: '10px' }}

106

/>

107

</label>

108

</div>

109

</div>

110

111

<GoogleMap

112

defaultZoom={13}

113

defaultCenter={{ lat: 37.7831, lng: -122.4421 }}

114

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

115

>

116

<HeatmapLayer

117

data={heatmapData}

118

options={heatmapOptions}

119

/>

120

</GoogleMap>

121

</div>

122

);

123

};

124

```

125

126

## Google Maps Visualization API Setup

127

128

### API Requirements

129

130

The visualization components require the visualization library to be loaded:

131

132

```javascript

133

// Include 'visualization' in the libraries parameter

134

const googleMapURL = `https://maps.googleapis.com/maps/api/js?key=${API_KEY}&libraries=places,visualization`;

135

136

const MapWithVisualization = withScriptjs(withGoogleMap(() => (

137

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

138

<HeatmapLayer data={heatmapData} />

139

</GoogleMap>

140

)));

141

142

<MapWithVisualization

143

googleMapURL={googleMapURL}

144

loadingElement={<div style={{ height: "100%" }} />}

145

containerElement={<div style={{ height: "400px" }} />}

146

mapElement={<div style={{ height: "100%" }} />}

147

/>

148

```

149

150

## Heatmap Configuration Options

151

152

### Data Formats

153

154

The heatmap accepts data in multiple formats:

155

156

```javascript

157

// Simple LatLng array

158

const simpleData = [

159

new google.maps.LatLng(37.782551, -122.445368),

160

new google.maps.LatLng(37.782745, -122.444586),

161

new google.maps.LatLng(37.782842, -122.443688)

162

];

163

164

// Weighted locations with intensity values

165

const weightedData = [

166

{ location: new google.maps.LatLng(37.782551, -122.445368), weight: 0.5 },

167

{ location: new google.maps.LatLng(37.782745, -122.444586), weight: 2.0 },

168

{ location: new google.maps.LatLng(37.782842, -122.443688), weight: 3.0 }

169

];

170

171

// Using plain objects (will be converted to LatLng)

172

const objectData = [

173

{ lat: 37.782551, lng: -122.445368 },

174

{ lat: 37.782745, lng: -122.444586 },

175

{ lat: 37.782842, lng: -122.443688 }

176

];

177

```

178

179

### Styling Options

180

181

Customize heatmap appearance through options:

182

183

```javascript

184

const heatmapOptions = {

185

// Point radius in pixels

186

radius: 20,

187

188

// Layer opacity (0-1)

189

opacity: 0.6,

190

191

// Maximum intensity for color mapping

192

maxIntensity: 100,

193

194

// Dissipating effect (points fade over time)

195

dissipating: true,

196

197

// Custom color gradient

198

gradient: [

199

'rgba(0, 255, 255, 0)', // Transparent cyan

200

'rgba(0, 255, 255, 1)', // Cyan

201

'rgba(0, 191, 255, 1)', // Light blue

202

'rgba(0, 127, 255, 1)', // Blue

203

'rgba(0, 63, 255, 1)', // Dark blue

204

'rgba(0, 0, 255, 1)', // Pure blue

205

'rgba(0, 0, 223, 1)', // Dark blue

206

'rgba(0, 0, 191, 1)', // Darker blue

207

'rgba(0, 0, 159, 1)', // Even darker blue

208

'rgba(0, 0, 127, 1)', // Very dark blue

209

'rgba(63, 0, 91, 1)', // Purple-blue

210

'rgba(127, 0, 63, 1)', // Purple

211

'rgba(191, 0, 31, 1)', // Red-purple

212

'rgba(255, 0, 0, 1)' // Red

213

]

214

};

215

```

216

217

## Advanced Visualization Patterns

218

219

### Dynamic Heatmap Data

220

221

Update heatmap data based on user interactions or time:

222

223

```javascript

224

const DynamicHeatmap = () => {

225

const [allData, setAllData] = useState([]); // Complete dataset

226

const [filteredData, setFilteredData] = useState([]);

227

const [timeRange, setTimeRange] = useState({ start: 0, end: 24 });

228

229

useEffect(() => {

230

// Load data from API

231

fetchHeatmapData().then(data => {

232

setAllData(data);

233

setFilteredData(data);

234

});

235

}, []);

236

237

const filterDataByTime = (start, end) => {

238

const filtered = allData.filter(point => {

239

const hour = point.timestamp.getHours();

240

return hour >= start && hour <= end;

241

});

242

setFilteredData(filtered);

243

};

244

245

return (

246

<div>

247

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

248

<label>

249

Time Range: {timeRange.start}:00 - {timeRange.end}:00

250

</label>

251

<input

252

type="range"

253

min="0"

254

max="23"

255

value={timeRange.start}

256

onChange={(e) => {

257

const start = parseInt(e.target.value);

258

setTimeRange(prev => ({ ...prev, start }));

259

filterDataByTime(start, timeRange.end);

260

}}

261

/>

262

<input

263

type="range"

264

min="0"

265

max="23"

266

value={timeRange.end}

267

onChange={(e) => {

268

const end = parseInt(e.target.value);

269

setTimeRange(prev => ({ ...prev, end }));

270

filterDataByTime(timeRange.start, end);

271

}}

272

/>

273

</div>

274

275

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

276

<HeatmapLayer

277

data={filteredData.map(point => ({

278

location: new google.maps.LatLng(point.lat, point.lng),

279

weight: point.intensity

280

}))}

281

/>

282

</GoogleMap>

283

</div>

284

);

285

};

286

```

287

288

### Multi-Category Heatmaps

289

290

Display different categories of data with toggleable layers:

291

292

```javascript

293

const MultiCategoryHeatmap = () => {

294

const [categories] = useState({

295

crime: { color: ['rgba(255,0,0,0)', 'rgba(255,0,0,1)'], data: crimeData },

296

traffic: { color: ['rgba(0,255,0,0)', 'rgba(0,255,0,1)'], data: trafficData },

297

business: { color: ['rgba(0,0,255,0)', 'rgba(0,0,255,1)'], data: businessData }

298

});

299

300

const [visibleCategories, setVisibleCategories] = useState({

301

crime: true,

302

traffic: false,

303

business: false

304

});

305

306

const toggleCategory = (categoryName) => {

307

setVisibleCategories(prev => ({

308

...prev,

309

[categoryName]: !prev[categoryName]

310

}));

311

};

312

313

return (

314

<div>

315

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

316

{Object.keys(categories).map(categoryName => (

317

<label key={categoryName} style={{ marginRight: '20px' }}>

318

<input

319

type="checkbox"

320

checked={visibleCategories[categoryName]}

321

onChange={() => toggleCategory(categoryName)}

322

/>

323

{categoryName.charAt(0).toUpperCase() + categoryName.slice(1)}

324

</label>

325

))}

326

</div>

327

328

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

329

{Object.entries(categories).map(([categoryName, category]) =>

330

visibleCategories[categoryName] && (

331

<HeatmapLayer

332

key={categoryName}

333

data={category.data}

334

options={{

335

radius: 25,

336

opacity: 0.6,

337

gradient: category.color

338

}}

339

/>

340

)

341

)}

342

</GoogleMap>

343

</div>

344

);

345

};

346

```

347

348

### Real-time Heatmap Updates

349

350

Stream real-time data to update heatmap visualization:

351

352

```javascript

353

const RealtimeHeatmap = () => {

354

const [heatmapData, setHeatmapData] = useState([]);

355

const [isStreaming, setIsStreaming] = useState(false);

356

357

useEffect(() => {

358

let interval;

359

360

if (isStreaming) {

361

interval = setInterval(() => {

362

// Simulate real-time data updates

363

const newPoint = {

364

location: new google.maps.LatLng(

365

37.7749 + (Math.random() - 0.5) * 0.1,

366

-122.4194 + (Math.random() - 0.5) * 0.1

367

),

368

weight: Math.random() * 5

369

};

370

371

setHeatmapData(prevData => {

372

const updatedData = [...prevData, newPoint];

373

// Keep only last 100 points for performance

374

return updatedData.slice(-100);

375

});

376

}, 1000);

377

}

378

379

return () => {

380

if (interval) clearInterval(interval);

381

};

382

}, [isStreaming]);

383

384

return (

385

<div>

386

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

387

<button onClick={() => setIsStreaming(!isStreaming)}>

388

{isStreaming ? 'Stop' : 'Start'} Real-time Updates

389

</button>

390

<span style={{ marginLeft: '20px' }}>

391

Data Points: {heatmapData.length}

392

</span>

393

</div>

394

395

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

396

<HeatmapLayer

397

data={heatmapData}

398

options={{

399

radius: 30,

400

opacity: 0.8,

401

dissipating: true,

402

maxIntensity: 10

403

}}

404

/>

405

</GoogleMap>

406

</div>

407

);

408

};

409

```

410

411

### Heatmap with Data Analysis

412

413

Combine heatmap with statistical analysis:

414

415

```javascript

416

const AnalyticalHeatmap = () => {

417

const [heatmapData, setHeatmapData] = useState([]);

418

const [statistics, setStatistics] = useState({});

419

420

const analyzeData = (data) => {

421

const weights = data.map(point => point.weight || 1);

422

const total = weights.reduce((sum, weight) => sum + weight, 0);

423

const average = total / weights.length;

424

const max = Math.max(...weights);

425

const min = Math.min(...weights);

426

427

return {

428

totalPoints: data.length,

429

totalWeight: total.toFixed(2),

430

averageWeight: average.toFixed(2),

431

maxWeight: max.toFixed(2),

432

minWeight: min.toFixed(2)

433

};

434

};

435

436

useEffect(() => {

437

if (heatmapData.length > 0) {

438

setStatistics(analyzeData(heatmapData));

439

}

440

}, [heatmapData]);

441

442

return (

443

<div style={{ display: 'flex' }}>

444

<div style={{ flex: 1 }}>

445

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

446

<HeatmapLayer data={heatmapData} />

447

</GoogleMap>

448

</div>

449

450

<div style={{ width: '300px', padding: '20px', backgroundColor: '#f5f5f5' }}>

451

<h3>Data Analysis</h3>

452

<table style={{ width: '100%' }}>

453

<tbody>

454

<tr>

455

<td>Total Points:</td>

456

<td>{statistics.totalPoints || 0}</td>

457

</tr>

458

<tr>

459

<td>Total Weight:</td>

460

<td>{statistics.totalWeight || 0}</td>

461

</tr>

462

<tr>

463

<td>Average Weight:</td>

464

<td>{statistics.averageWeight || 0}</td>

465

</tr>

466

<tr>

467

<td>Max Weight:</td>

468

<td>{statistics.maxWeight || 0}</td>

469

</tr>

470

<tr>

471

<td>Min Weight:</td>

472

<td>{statistics.minWeight || 0}</td>

473

</tr>

474

</tbody>

475

</table>

476

</div>

477

</div>

478

);

479

};

480

```