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