This examples combines a React Map Chart with a Heatmap to show the distribution of earthquakes around the world.
drawExample.ts
index.tsx
theme.ts
1import {
2 FastLineRenderableSeries,
3 HeatmapColorMap,
4 HeatmapLegend,
5 MouseWheelZoomModifier,
6 NumberRange,
7 NumericAxis,
8 SciChartSurface,
9 UniformHeatmapDataSeries,
10 UniformHeatmapRenderableSeries,
11 XyDataSeries,
12 ZoomExtentsModifier,
13 ZoomPanModifier,
14} from "scichart";
15import { appTheme } from "../../../theme";
16
17/** Earthquake data structure from CSV */
18interface EarthquakeData {
19 latitude: number;
20 longitude: number;
21 magnitude: number;
22 depth: number;
23}
24
25/** Base URL for fetching data files - handles both local and production environments */
26const baseUrl =
27 typeof window !== "undefined" &&
28 !window.location.hostname.includes("scichart.com") &&
29 !window.location.hostname.includes("localhost")
30 ? "https://www.scichart.com/demo"
31 : "";
32
33/**
34 * Creates a heatmap visualization of earthquake data overlaid on a world map.
35 * The heatmap displays earthquake magnitudes using a color gradient from black (no activity)
36 * to red (highest magnitude).
37 */
38export const drawExample = async (rootElement: string | HTMLDivElement) => {
39 const { sciChartSurface, wasmContext } = await SciChartSurface.create(rootElement, {
40 theme: appTheme.SciChartJsTheme,
41 });
42
43 // World coordinate bounds (standard lat/lon ranges)
44 const worldBounds = {
45 minLat: -90,
46 maxLat: 90,
47 minLon: -180,
48 maxLon: 180,
49 };
50
51 // Configure axes with world coordinate bounds (hidden for clean map appearance)
52 sciChartSurface.xAxes.add(
53 new NumericAxis(wasmContext, {
54 isVisible: false,
55 visibleRange: new NumberRange(worldBounds.minLon, worldBounds.maxLon),
56 })
57 );
58 sciChartSurface.yAxes.add(
59 new NumericAxis(wasmContext, {
60 isVisible: false,
61 visibleRange: new NumberRange(worldBounds.minLat, worldBounds.maxLat),
62 })
63 );
64
65 // Heatmap resolution
66 const heatmapWidth = 600;
67 const heatmapHeight = 400;
68
69 // Magnitude range for color mapping (Richter scale)
70 const colorPaletteMin = 0;
71 const colorPaletteMax = 10;
72
73 // Load earthquake data and world map outlines
74 const earthquakeData = await fetchEarthquakeData();
75 const convertedData = await fetch(baseUrl + "worldConverted.json").then((response) => response.json());
76
77 // Define coordinate bounds for the world map data
78 const minX = -180;
79 const maxX = 180;
80 const minY = -90;
81 const maxY = 90;
82
83 // Transform world map coordinates to match the chart's coordinate system
84 const transformedOutlines = convertedData.map((d: any) => {
85 return d.outline.map((point: number[]) => {
86 const scaledX =
87 worldBounds.minLon + ((point[0] - minX) / (maxX - minX)) * (worldBounds.maxLon - worldBounds.minLon);
88 const scaledY =
89 worldBounds.minLat + ((point[1] - minY) / (maxY - minY)) * (worldBounds.maxLat - worldBounds.minLat);
90 return [scaledX, scaledY];
91 });
92 });
93
94 // Create line series for country/continent outlines
95 const outlinesSC = transformedOutlines.map((outline: number[][]) => {
96 const xVals = outline.map((d: number[]) => d[0]);
97 const yVals = outline.map((d: number[]) => d[1]);
98
99 return new FastLineRenderableSeries(wasmContext, {
100 dataSeries: new XyDataSeries(wasmContext, {
101 xValues: xVals,
102 yValues: yVals,
103 }),
104 stroke: "#ffffff",
105 strokeThickness: 2,
106 opacity: 1,
107 });
108 });
109
110 sciChartSurface.renderableSeries.add(...outlinesSC);
111
112 // Generate heatmap from earthquake data
113 const initialZValues = generateEarthquakeHeatmap(earthquakeData, heatmapWidth, heatmapHeight);
114
115 // Reverse Y-axis to match geographic orientation (north at top)
116 const reversedZValues = initialZValues.slice().reverse();
117
118 // Create heatmap data series with world coordinate mapping
119 const heatmapDataSeries = new UniformHeatmapDataSeries(wasmContext, {
120 zValues: reversedZValues,
121 xStart: worldBounds.minLon,
122 xStep: (worldBounds.maxLon - worldBounds.minLon) / heatmapWidth,
123 yStart: worldBounds.minLat,
124 yStep: (worldBounds.maxLat - worldBounds.minLat) / heatmapHeight,
125 });
126
127 // Add heatmap with semi-transparent overlay
128 sciChartSurface.renderableSeries.add(
129 new UniformHeatmapRenderableSeries(wasmContext, {
130 dataSeries: heatmapDataSeries,
131 useLinearTextureFiltering: false,
132 opacity: 0.5,
133 colorMap: new HeatmapColorMap({
134 minimum: colorPaletteMin,
135 maximum: colorPaletteMax,
136 gradientStops: [
137 { offset: 1, color: "#FF0000" }, // Red - highest magnitude
138 { offset: 0.8, color: "#FF4500" }, // Orange-red
139 { offset: 0.6, color: "#FFA500" }, // Orange
140 { offset: 0.4, color: "#FFFF00" }, // Yellow
141 { offset: 0.2, color: "#90EE90" }, // Light green
142 { offset: 0, color: "#000000" }, // Black - no activity
143 ],
144 }),
145 })
146 );
147
148 // Add interactive chart modifiers
149 sciChartSurface.chartModifiers.add(new ZoomPanModifier({ enableZoom: true }));
150 sciChartSurface.chartModifiers.add(new ZoomExtentsModifier());
151 sciChartSurface.chartModifiers.add(new MouseWheelZoomModifier());
152
153 return { sciChartSurface };
154};
155
156/**
157 * Creates a heatmap legend showing the magnitude color scale.
158 */
159export const drawHeatmapLegend = async (rootElement: string | HTMLDivElement) => {
160 const { heatmapLegend, wasmContext } = await HeatmapLegend.create(rootElement, {
161 theme: {
162 ...appTheme.SciChartJsTheme,
163 sciChartBackground: appTheme.DarkIndigo + "BB",
164 loadingAnimationBackground: appTheme.DarkIndigo + "BB",
165 },
166 yAxisOptions: {
167 isInnerAxis: true,
168 labelStyle: {
169 fontSize: 12,
170 color: appTheme.ForegroundColor,
171 },
172 axisBorder: {
173 borderRight: 1,
174 color: appTheme.ForegroundColor + "77",
175 },
176 majorTickLineStyle: {
177 color: appTheme.ForegroundColor,
178 tickSize: 6,
179 strokeThickness: 1,
180 },
181 minorTickLineStyle: {
182 color: appTheme.ForegroundColor,
183 tickSize: 3,
184 strokeThickness: 1,
185 },
186 },
187 colorMap: {
188 minimum: 0,
189 maximum: 10,
190 gradientStops: [
191 { offset: 1, color: "#FF0000" }, // Red - highest magnitude
192 { offset: 0.8, color: "#FF4500" }, // Orange-red
193 { offset: 0.6, color: "#FFA500" }, // Orange
194 { offset: 0.4, color: "#FFFF00" }, // Yellow
195 { offset: 0.2, color: "#90EE90" }, // Light green
196 { offset: 0, color: "#0001FF" }, // Blue - no activity
197 ],
198 },
199 });
200
201 return { sciChartSurface: heatmapLegend.innerSciChartSurface.sciChartSurface };
202};
203
204/** Fetches earthquake data from CSV file */
205async function fetchEarthquakeData(): Promise<EarthquakeData[]> {
206 try {
207 const response = await fetch(baseUrl + "earthquakes-23k.csv");
208 const csvText = await response.text();
209 return parseEarthquakeCSV(csvText);
210 } catch (error) {
211 console.error("Error fetching earthquake data:", error);
212 return [];
213 }
214}
215
216/** Parses CSV text into earthquake data objects */
217function parseEarthquakeCSV(csvText: string): EarthquakeData[] {
218 const lines = csvText.trim().split("\n");
219 const earthquakes: EarthquakeData[] = [];
220
221 // CSV format: Date,Latitude,Longitude,Magnitude (skip header row)
222 for (let i = 1; i < lines.length; i++) {
223 const columns = lines[i].split(",");
224
225 if (columns.length >= 4) {
226 const latitude = parseFloat(columns[1]);
227 const longitude = parseFloat(columns[2]);
228 const magnitude = parseFloat(columns[3]);
229
230 // Validate geographic bounds and magnitude
231 if (
232 !isNaN(latitude) &&
233 !isNaN(longitude) &&
234 !isNaN(magnitude) &&
235 latitude >= -90 &&
236 latitude <= 90 &&
237 longitude >= -180 &&
238 longitude <= 180 &&
239 magnitude >= 0
240 ) {
241 earthquakes.push({
242 latitude,
243 longitude,
244 magnitude,
245 depth: 0, // Not available in this dataset
246 });
247 }
248 }
249 }
250
251 return earthquakes;
252}
253
254/** Generates a heatmap grid from earthquake data */
255function generateEarthquakeHeatmap(earthquakes: EarthquakeData[], width: number, height: number): number[][] {
256 // Initialize grid with zeros
257 const grid: number[][] = [];
258 for (let y = 0; y < height; y++) {
259 grid.push(new Array(width).fill(0));
260 }
261
262 if (earthquakes.length === 0) {
263 return grid;
264 }
265
266 // World map bounds
267 const minLat = -90;
268 const maxLat = 90;
269 const minLon = -180;
270 const maxLon = 180;
271
272 // Map each earthquake to a grid cell
273 earthquakes.forEach((earthquake) => {
274 // Convert lat/lon to grid coordinates
275 const x = Math.floor(((earthquake.longitude - minLon) / (maxLon - minLon)) * width);
276 const y = Math.floor(((maxLat - earthquake.latitude) / (maxLat - minLat)) * height); // Flip Y for proper orientation
277
278 if (x >= 0 && x < width && y >= 0 && y < height) {
279 // Use maximum magnitude when multiple earthquakes fall in the same cell
280 grid[y][x] = Math.max(grid[y][x], earthquake.magnitude);
281 }
282 });
283
284 return smoothHeatmap(grid, width, height);
285}
286
287/** Applies a 3x3 averaging filter to smooth the heatmap */
288function smoothHeatmap(grid: number[][], width: number, height: number): number[][] {
289 const smoothed: number[][] = [];
290
291 for (let y = 0; y < height; y++) {
292 const row: number[] = [];
293 for (let x = 0; x < width; x++) {
294 let sum = 0;
295 let count = 0;
296
297 // Average with neighboring cells (3x3 kernel)
298 for (let dy = -1; dy <= 1; dy++) {
299 for (let dx = -1; dx <= 1; dx++) {
300 const ny = y + dy;
301 const nx = x + dx;
302
303 if (ny >= 0 && ny < height && nx >= 0 && nx < width) {
304 sum += grid[ny][nx];
305 count++;
306 }
307 }
308 }
309
310 row.push(count > 0 ? sum / count : 0);
311 }
312 smoothed.push(row);
313 }
314
315 return smoothed;
316}
317This example demonstrates how to create a Heatmap visualization of earthquake data overlaid on a world map using SciChart.js.
FastLineRenderableSeries with white strokesUniformHeatmapRenderableSeries displays earthquake magnitudes with a color gradient from black (no activity) through green, yellow, orange to red (highest magnitude)
Create a React Histogram Chart with custom texture fills and patterns. Try the SciChartReact wrapper component for seamless React integration today.

Build a React Gantt Chart with SciChart. View the demo for horizontal bars, rounded corners and data labels to show project timelines and task completion.

Create a React Choropleth map, a type of thematic map where areas are shaded or patterned in proportion to the value of a variable being represented.

Create a React Multi-Layer Map Example, using FastTriangleRenderableSeries with GeoJSON data-points using a constrained delaunay triangulation algorithm.

Bring annual comparison data to life with the React Animated Bar Chart example from SciChart. This demo showcases top 10 tennis players from 1990 to 2024.

View the React Vector Field Plot example from SciChart, including dynamic vector generation, gradient-colored segments, and interactive zoom/pan. Try demo.

Build a React Waterfall Chart with dynamic coloring, multi-line data labels and responsive design, using the SciChartReact component for seamless integration.

Try the React Box Plot Chart example for React-friendly chart lifecycle management, dynamic sub-surface positioning, and custom styling. Try the demo now.

Create React Triangle Meshes with the Triangle Series from SciChart. This demo supports strip mode, list mode and the drawing of polygons. View the example.

Create a React Treemap Chart to define rectangle positions based on total value. Use SciChart FastRectangleRenderableSeries and d3-hierarchy.js layouts.

Demonstrating the capability of SciChart.js to create a JavaScript Audio Analyzer Bars and visualize the Fourier-Transform of an audio waveform in realtime.

View the React Linear Gauge Chart example to combine rectangles & annotations. Create a linear gauge dashboard with animated indicators and custom scales.

The React Order of Rendering example gives you full control of the draw order of series and annotations for charts. Try SciChart's advanced customizations.

Build Responsive React HTML Annotations with SciChart. Use the advanced CSS container queries for responsive text layout and custom design. View demo now.

React HTML Chart Control example demonstrates advanced HTML annotation integration and how to render HTML components within charts. Try the SciChart demo.

Explore SciChart's Polar Interactivity Modifiers including zooming, panning, and cursor tracking. Try the demo to trial the Polar Chart Behavior Modifiers.