Public
Edited
May 12
Insert cell
Insert cell
// 导入 vega-datasets
datasets = require("vega-datasets")
Insert cell
// 加载地震数据
raw_earthquakes = await datasets["earthquakes.json"]()
Insert cell
// 数据预处理,将 GeoJSON 特征转化为扁平结构
earthquakes = raw_earthquakes.features.map(feature => ({
mag: feature.properties.mag,
time: new Date(feature.properties.time), // 转换为JavaScript日期对象
place: feature.properties.place,
longitude: feature.geometry.coordinates[0],
latitude: feature.geometry.coordinates[1],
depth: feature.geometry.coordinates[2],
tsunami: feature.properties.tsunami,
type: feature.properties.type
}))
Insert cell
// 地震总数
earthquakeCount = earthquakes.length
Insert cell
// 平均震级
magnitudeAvg = d3.mean(earthquakes, d => d.mag).toFixed(2)
Insert cell
// 最大震级
magnitudeMax = d3.max(earthquakes, d => d.mag).toFixed(1)
Insert cell
// 平均深度
depthAvg = d3.mean(earthquakes, d => d.depth).toFixed(2)
Insert cell
// 最大深度
depthMax = d3.max(earthquakes, d => d.depth).toFixed(1)
Insert cell
// 最早时间
timeMin = d3.min(earthquakes, d => d.time).toLocaleDateString('zh-CN')
Insert cell
// 最晚时间
timeMax = d3.max(earthquakes, d => d.time).toLocaleDateString('zh-CN')
Insert cell
Insert cell
Insert cell
topojson = require("topojson-client@3")

Insert cell
// 加载世界地图数据
land = await fetch("https://cdn.jsdelivr.net/npm/world-atlas@2/land-110m.json").then(response => response.json())
Insert cell
landFeature = topojson.feature(land, land.objects.land)
Insert cell
// 添加经度滑块控制
viewof longitude = Inputs.range([-180, 180], {
label: "经度",
value: 0,
step: 1
})
Insert cell
Plot.plot({
title: "一周内全球地震分布",
subtitle: "点大小表示震级,颜色表示深度",
projection: {type: "orthographic", rotate: [-longitude, -30]},
width: 900,
height: 500,
r: {range: [2, 15]},
color: {
type: "quantize",
legend: true,
scheme: "YlOrRd",
label: "深度 (km)"
},
marks: [
Plot.sphere({fill: "#e6f3ff"}),
Plot.geo(landFeature, {
fill: "currentColor", fillOpacity: 0.2}
),
Plot.dot(earthquakes, {
x: "longitude",
y: "latitude",
r: d => Math.pow(2, d.mag) / 2,
fill: "depth",
title: d => `${d.place}\n震级: ${d.mag}\n深度: ${d.depth.toFixed(1)}km\n时间: ${d.time.toLocaleString('zh-CN')}`
})
]
})
Insert cell
### 分析发现:

从地图中可以清晰地看到,地震主要集中在太平洋"环火圈"地区,包括:
- 日本、菲律宾和印度尼西亚地区
- 美国西海岸(尤其是加利福尼亚州)
- 中美洲和南美洲的西海岸

这一分布与地球板块边界高度重合,证实了板块构造理论。环太平洋地区是多个板块相互碰撞和摩擦的区域,因此地震活动频繁。值得注意的是,大部分高震级地震(点较大)也出现在这些区域。
Insert cell
Insert cell
// 震级与深度关系的散点图
Plot.plot({
title: "地震震级与深度关系图",
subtitle: "分析深度与震级的相关性",
width: 800,
height: 500,
grid: true,
x: {
label: "深度 (km)",
domain: [0, d3.max(earthquakes, d => d.depth) + 10],
},
y: {
label: "震级",
domain: [0, d3.max(earthquakes, d => d.mag) + 0.5],
},
color: {
scheme: "turbo",
legend: true,
label: "震级"
},
marks: [
Plot.dot(earthquakes, {
x: "depth",
y: "mag",
fill: "mag",
r: 4,
opacity: 0.7,
title: d => `${d.place}\n震级: ${d.mag}\n深度: ${d.depth.toFixed(1)}km`
}),
Plot.lineY(
// 计算移动平均趋势线的数据点
d3.rollups(
earthquakes,
v => d3.mean(v, d => d.mag),
d => Math.floor(d.depth / 20) * 20
).map(([depth, mag]) => ({depth, mag}))
.sort((a, b) => a.depth - b.depth),
{
x: "depth",
y: "mag",
stroke: "red",
strokeWidth: 2,
curve: "catmull-rom" // 使用 Catmull-Rom 曲线
}
)
]
})
Insert cell
Insert cell
Insert cell
// 按小时统计地震次数
earthquakesByHour = d3.rollup(
earthquakes,
v => v.length,
d => d.time.getHours()
)

Insert cell
// 转换为数组以便于绘图
hourlyData = Array.from(earthquakesByHour, ([hour, count]) => ({hour, count}))
.sort((a, b) => a.hour - b.hour)
Insert cell
// 每小时地震频次柱状图
Plot.plot({
title: "24小时地震频次分布",
width: 800,
height: 400,
grid: true,
x: {
label: "小时 (0-23)",
tickFormat: d => `${d}:00`
},
y: {
label: "地震次数"
},
marks: [
Plot.barY(hourlyData, {
x: "hour",
y: "count",
fill: "steelblue",
title: d => `${d.hour}:00 - ${d.hour+1}:00\n地震次数: ${d.count}`
}),
// 添加平均线
Plot.ruleY([d3.mean(hourlyData, d => d.count)], {
stroke: "red",
strokeWidth: 2,
strokeDasharray: "4,4",
title: "平均值"
})
]
})
Insert cell
// 按日期统计地震次数
earthquakesByDay = d3.rollup(
earthquakes,
v => v.length,
d => d3.timeDay.floor(d.time)
)


Insert cell
// 转换为数组以便于绘图
dailyData = Array.from(earthquakesByDay, ([day, count]) => ({day, count}))
.sort((a, b) => a.day - b.day)
Insert cell
// 每天地震频次柱状图
Plot.plot({
title: "每日地震频次分布",
width: 800,
height: 400,
grid: true,
x: {
label: "日期",
tickFormat: d => d.toLocaleDateString('zh-CN')
},
y: {
label: "地震次数"
},
marks: [
Plot.barY(dailyData, {
x: "day",
y: "count",
fill: "orange",
title: d => `${d.day.toLocaleDateString('zh-CN')}\n地震次数: ${d.count}`
}),
// 添加平均线
Plot.ruleY([d3.mean(dailyData, d => d.count)], {
stroke: "red",
strokeWidth: 2,
strokeDasharray: "4,4",
title: "平均值"
})
]
})
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more