Skip to content

D3.js - 地理图形

D3.js 提供了强大的工具用于可视化地理数据,常用于显示人口密度、天气模式或选举结果等信息的地图。D3 中用于地理空间数据的关键模块包括用于投影和路径生成的 d3-geo,以及常用于处理 TopoJSON 数据的 topojson-client。

D3 地理功能的核心组件是:

  • GeoJSON/TopoJSON: 编码地理数据结构的标准格式。
  • 投影 (d3-geo): 将球面坐标(经度、纬度)转换为适用于屏幕显示的二维笛卡尔坐标系的函数。
  • 路径生成器 (d3-geo): 接受 GeoJSON 几何数据和投影,用于生成 SVG 路径数据(即 <path> 元素的 d 属性)或渲染到 Canvas 的函数。

地理路径生成器,使用 d3.geoPath() 创建,用于将 GeoJSON 特征转换为 SVG 路径字符串或将其渲染到 HTML5 Canvas 上。它需要一个投影来执行坐标转换。

// 1. 创建一个投影 (例如,Mercator)
const projection = d3.geoMercator()
.scale(100) // 示例缩放比例
.translate([width / 2, height / 2]); // 地图居中
// 2. 创建一个地理路径生成器并将其关联到投影
const pathGenerator = d3.geoPath()
.projection(projection);
// 3. 将路径生成器与 GeoJSON 数据一起使用
svg.selectAll("path")
.data(geoJsonFeatures) // 一个 GeoJSON feature 对象的数组
.enter()
.append("path")
.attr("d", pathGenerator); // pathGenerator(d) 返回 SVG 路径字符串

例如,要从一个名为 statesGeoJson 的 GeoJSON FeatureCollection 中绘制某个国家的州/省份界线:

svg.append("g")
.selectAll("path")
.data(statesGeoJson.features)
.join("path")
.attr("d", pathGenerator)
.attr("fill", "steelblue")
.attr("stroke", "white");

投影将球面多边形几何数据(经度、纬度)转换为平面多边形几何数据(x, y 坐标)。D3 提供了各种各样的投影类型,每种在面积、形状、距离或方向的变形方面具有不同的特性。

  • 方位角投影 (Azimuthal): 将球面直接投影到平面上(例如,d3.geoOrthographic()、d3.geoStereographic())。
  • 圆锥投影 (Conic): 将球面投影到圆锥上,然后展开圆锥(例如,d3.geoAlbers()、d3.geoConicEqualArea())。
  • 圆柱投影 (Cylindrical): 将球面投影到圆柱上,然后展开圆柱(例如,d3.geoMercator()、d3.geoEquirectangular())。
  • 组合投影 (Composite): 组合多个投影(例如,用于美国本土、阿拉斯加和夏威夷的 d3.geoAlbersUsa())。

要创建一个新的投影,请调用特定的投影函数:

const projection = d3.geoMercator(); // 创建一个 Mercator 投影
// 常用的配置方法:
projection
.scale(number) // 设置投影的缩放因子
.translate([x, y]) // 设置投影的平移偏移量(地图在像素空间中的中心)
.center([lon, lat]) // 设置投影的中心点(经度、纬度)
.rotate([lambda, phi, gamma]) // 设置投影的三轴旋转

一些流行的投影:

  • d3.geoOrthographic(): 显示一个半球,如同从太空俯瞰。
  • d3.geoGnomonic(): 将大圆投影为直线。
  • d3.geoEquirectangular(): 一种简单的圆柱投影,常用于栅格数据。
  • d3.geoMercator(): 常用于网络地图;局部保持角度不变,但在高纬度地区面积变形严重。
  • d3.geoAlbersUsa(): 一种专门用于美国的组合投影。

我们来创建一个国家(例如印度)的地图,使用 TopoJSON 数据。TopoJSON 是 GeoJSON 的一个扩展,它编码了拓扑结构,通常可以减小文件大小。

步骤 1: 包含 D3.js 和 TopoJSON 客户端库。您需要一个 TopoJSON 文件(例如,india-states.topojson)。

<!-- In your HTML -->
<script src="https://d3js.org/d3.v7.min.js"></script>
<script src="https://unpkg.com/topojson-client@3"></script> <!-- topojson-client library -->

步骤 2: 定义 SVG 尺寸和投影。

const width = 600;
const height = 500;
const projection = d3.geoMercator()
.center([82, 22]) // 印度的大致中心
.scale(800) // 调整比例以适应
.translate([width / 2, height / 2]);
const pathGenerator = d3.geoPath().projection(projection);

步骤 3: 创建 SVG 容器。

const svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.style("border", "1px solid black");

步骤 4: 加载 TopoJSON 数据并渲染地图。我们使用 d3.json(),它返回一个 Promise 对象。

// Assume 'india-states.topojson' is available at this path
d3.json("india-states.topojson").then(function(topology) {
// Convert TopoJSON to GeoJSON features
// 'states' is the name of the object in your TopoJSON file that contains geometries
const states = topojson.feature(topology, topology.objects.states);
svg.append("g")
.selectAll("path")
.data(states.features)
.join("path")
.attr("d", pathGenerator)
.attr("fill", (d) => {
// Example: Color Tamil Nadu (TN) differently if ID is available
if (d.id === "TN") return "red";
return "#ccc";
})
.attr("stroke", "#333")
.attr("stroke-width", 0.5);
}).catch(function(error) {
console.error("Error loading or processing TopoJSON:", error);
});

步骤 5: (可选)添加 CSS 样式。

/* CSS (可选) */
path {
/* 路径的默认样式,除非被 .attr 覆盖 */
}
path:hover {
fill: orange; /* 示例悬停效果 */
}

关于 TopoJSON 数据 (india-states.topojson) 的说明:这个文件包含地理数据。topology.objects.states 部分指的是 TopoJSON 文件中一个特定的命名对象。该文件结构的一个高度精简的示例:

{
"type": "Topology",
"transform": { /* scale and translate info */ },
"objects": {
"states": { // This key 'states' is used in topojson.feature
"type": "GeometryCollection",
"geometries": [
{ "type": "Polygon", "arcs": [[...]], "id": "AP", "properties": { "name": "Andhra Pradesh" } },
{ "type": "MultiPolygon", "arcs": [[...]], "id": "TN", "properties": { "name": "Tamil Nadu" } },
// ... more state geometries
]
}
// ... other objects like 'districts' could be here
},
"arcs": [ /* array of shared line segments */ ]
}

这个示例演示了加载地理数据,使用 topojson.feature 进行转换,并使用 D3 的路径生成器和投影进行渲染。具体的属性(d.id、d.properties.name)取决于您使用的 GeoJSON/TopoJSON 文件。