十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

D3 geoPath 完整实战指南:从 GeoJSON 到 SVG 路径与 Canvas 渲染

D3 geoPath 完整实战指南:从 GeoJSON 到 SVG 路径与 Canvas 渲染 D3 geoPath 完整实战指南从 GeoJSON 到 SVG 路径与 Canvas 渲染【免费下载链接】d3Bring data to life with SVG, Canvas and HTML. :bar_chart::chart_with_upwards_trend::tada:项目地址: https://gitcode.com/GitHub_Trending/d3/d3本篇指南基于 D3 v7 官方文档 docs/d3-geo/path.md 系统讲解 d3-geo 模块中的地理路径生成器d3.geoPath()它如何把任意 GeoJSON 几何对象转换为 SVG 路径数据字符串或 Canvas 绘制指令如何通过投影、裁剪、数字精度与点半径等配置项控制渲染行为以及area、bounds、centroid、measure等平面度量方法的正确用法。读完本篇你可以独立完成 SVG 地图与 Canvas 地图的生成、多要素批量渲染、要素标注定位并理解 D3 文档站点自带的 WorldMap.vue、UsMap.vue 两个真实地图组件的实现原理。geoPath 在 D3 地图渲染链中的位置在 D3 的地理可视化中从数据到画面要经过三步数据球面 GeoJSON→ 投影球面到平面的几何变换→ 路径平面坐标到具体画布。d3.geoPath负责最后一步也是唯一直接产出可渲染内容的环节地理路径生成器 geoPath 接收一个 GeoJSON 几何对象或 feature 对象生成 SVG path data 字符串或者渲染到 Canvas它可以与投影或变换transforms配合使用也可以不指定投影直接以原始平面坐标渲染几何。理解投影与路径的分工需要先了解 d3-geo 文档中说明的背景球面多边形的边是大圆上的测地线而非直线测地线在几乎所有投影除 gnomonic 外都会变成曲线因此 D3 采用自适应采样来平衡精度与性能同时球面多边形还存在绕向约定winding order与反子午线切割等拓扑问题。这些计算全部发生在投影的 stream 阶段geoPath拿到的已经是经过裁剪和采样的平面坐标再把它们组织成路径指令——这也是后文path.area、path.bounds等方法“遵守投影裁剪”的原因。从本仓库结构看d3-geo是d3主包的依赖之一package.json 中声明为d3-geo: ^3.1.1并通过 src/index.js 中的export * from d3-geo统一导出因此所有d3.geo*前缀的 API 都来自该模块。创建路径生成器geoPath(projection, context)创建一个带默认设置的新地理路径生成器const path d3.geoPath(projection); // 用于 SVGconst path d3.geoPath(projection, context); // 用于 Canvas两个可选参数分别对应后两节的path.projection 与path.contextprojection投影省略时为null即恒等变换输入几何不做投影、直接以原始坐标渲染context渲染上下文省略时为null即生成器返回 SVG path data 字符串传入 Canvas 2D 上下文后生成器改为调用上下文上的绘图方法。渲染几何对象path(object, ...arguments)调用生成器path(object, ...arguments) 渲染给定的object它可以是任意 GeoJSON feature 或 geometry 对象。D3 支持完整的 GeoJSON 类型谱系Point—— 单个位置MultiPoint—— 位置数组LineString—— 构成一条连续折线的位置数组MultiLineString—— 位置数组的数组构成多条折线Polygon—— 构成多边形可含孔洞的位置数组的数组MultiPolygon—— 多维位置数组构成多个多边形GeometryCollection—— 几何对象数组Feature—— 包含上述任一几何对象的要素FeatureCollection—— 要素对象数组此外还支持特殊类型Sphere它不含任何坐标用于渲染整个球面地球轮廓。这是 D3 文档站点地图组件的常用手法见 docs/components/WorldMap.vue 中的const outline {type: Sphere}直接将其传给path得到全球轮廓。多要素的两种渲染方式要显示多个 feature第一种方式是合并成一个 feature collection用一个 path 元素承载svg.append(path) .datum({type: FeatureCollection, features: features}) .attr(d, d3.geoPath());第二种方式是为每个 feature 创建独立的 path 元素svg.selectAll() .data(features) .join(path) .attr(d, d3.geoPath());性能与交互的权衡独立的 path 元素通常比单个 path 元素慢但便于逐要素着色与交互点击、mouseover反之 Canvas 渲染通常比 SVG 快但实现样式与交互的成本更高。仓库中的美国地图组件 docs/components/UsMap.vue 展示了单路径模式的典型用法把全国轮廓、州边界 mesh、县边界 mesh 分别绑定到三个path元素每个元素承载一个由 TopoJSON 转换出的 feature/mesh 对象topojson.feature/topojson.mesh的输出仍满足 GeoJSON 对象接口可直接交给path。平面度量area、bounds、centroid、measure路径生成器提供四个度量方法全部工作在投影后的平面坐标上因此返回值通常是像素单位。这组方法与 球面数学 中的geoArea、geoBounds、geoCentroid、geoLength一一对应前者是“投影后的平面值”后者是“球面上的真实值”。path.area(object)返回指定 GeoJSONobject的投影后平面面积通常为平方像素path.area(california) // 17063.1671837991 px²规则要点Point、MultiPoint、LineString、MultiLineString 几何的面积恒为零对 Polygon 与 MultiPolygon先计算外环面积再减去所有内部孔洞的面积本方法遵守投影所做的任何裁剪可参考 projection.clipAngle 与 projection.clipExtent它是 geoArea 的平面等价物。path.bounds(object)返回指定 GeoJSONobject的投影后平面包围盒通常为像素path.bounds(california) // [[18.48513821663947, 159.95146883594333], [162.7651668852596, 407.09641570706725]]包围盒表示为二维数组[[x₀, y₀], [x₁, y₁]]x₀是最小 x 坐标y₀是最小 y 坐标x₁是最大 x 坐标y₁是最大 y 坐标。典型用途是“缩放聚焦到某个要素”——例如配合 d3-zoom 计算目标 transform 的 k 与平移量。注意平面坐标系的坐标方向最小纬度通常对应最大的y值最大纬度通常对应最小的y值y 轴向下。本方法同样遵守投影裁剪是 geoBounds 的平面等价物。path.centroid(object)返回指定 GeoJSONobject的投影后平面质心通常为像素path.centroid(california) // [82.08679434495191, 288.14204870673404]典型用途包括为州或县边界添加文字标签、绘制符号地图symbol map。例如“非连通卡通地图”noncontiguous cartogram一类的实现会围绕每个州的质心对各州进行缩放。本方法遵守投影裁剪是 geoCentroid 的平面等价物。path.measure(object)返回指定 GeoJSONobject的投影后平面长度通常为像素path.measure(california) // 825.7124297512761规则要点Point 与 MultiPoint 几何长度恒为零Polygon 与 MultiPolygon 计算所有环的累计长度。同样遵守投影裁剪是 geoLength 的平面等价物。配置坐标精度path.digits(digits)如果指定了非负数digits则设置 SVG 路径字符串中坐标的小数位数const path d3.geoPath().digits(3);如果不指定参数返回当前位数默认值为 3path.digits() // 3适用前提该选项只在关联的context为null时生效即生成器用于产出 SVG path data 字符串的场景参见 SVG 规范中 PathData 一节。在 Canvas 模式下坐标直接以数值传给绘图方法digits 不起作用。实际影响是位数越多路径越精细但字符串越长、SVG 文件越大3 位小数在像素级渲染中通常是精度与体积的合理折中。设置投影path.projection(projection)如果指定了projection将当前投影设置为该投影const path d3.geoPath().projection(d3.geoAlbers());如果不指定参数返回当前投影path.projection() // a d3.geoAlbers instance默认投影为null代表恒等变换输入几何不做投影直接以原始坐标渲染。这有两种典型价值渲染预先投影好的几何pre-projected geometry——几何坐标已经是平面值跳过投影步骤可以显著提速快速渲染等距圆柱投影equirectangular。projection通常是 D3 内置的地理投影之一但实际上任何暴露了projection.stream 方法的对象都可以作为投影使用这使得自定义投影成为可能。要理解这个接口约定可以阅读 Streams 文档——投影本质上是一个“几何流变换器”geoPath正是通过调用projection.stream(context)拿到流变换再把平面坐标写入路径或绘制上下文。D3 的 transforms 提供了任意几何变换的更多例子平移、缩放、旋转、自定义仿射矩阵等。渲染到 Canvaspath.context(context)如果指定了context设置当前渲染上下文并返回路径生成器const context canvas.getContext(2d); const path d3.geoPath().context(context);context 的取值决定输出形态context为null时路径生成器返回 SVG path 字符串context非null时生成器不再返回字符串而是调用指定上下文的方法直接绘制几何。上下文需要实现 CanvasRenderingContext2D API 的以下子集这也是自定义上下文只需满足的契约context.beginPath()context.moveTo(x,y)context.lineTo(x,y)context.arc(x,y,radius,startAngle,endAngle)context.closePath()不指定参数时返回当前渲染上下文默认为null。从契约看这套接口与 d3-path 模块的路径对象是同构的d3-path提供了“生成字符串”和“调用 context”两种实现d3-geo 的路径生成器在其上叠加了地理语义球面几何分派、投影裁剪。理解这一点后你可以推断任何实现了上述五个方法的对象比如某个 SVG 路径构建器都可以作为context传入而不必局限于 Canvas。与 SVG 模式的差异小结Canvas 模式通常渲染更快尤其面对海量县界、网格线等细碎几何时但样式与交互需要借助 hit-testing 等额外手段实现这正是前文“单 path 元素 vs 多 path 元素 vs Canvas”三者权衡的完整闭环。点半径path.pointRadius(radius)如果指定了radius设置用于显示 Point 与 MultiPoint 几何的半径const path d3.geoPath().pointRadius(10);不指定参数时返回当前半径访问器path.pointRadius() // 10默认半径为 4.5。半径通常是数值常量但也可以写成函数——按 feature 计算且会接收到传给路径生成器的所有附加参数。典型场景GeoJSON 数据带有额外的属性字段时可以在半径函数内读取这些属性来让点大小随数据变化。若需要更大的表达自由度文档建议改用 d3-shape 的 symbol形状符号配合投影自行绘制点而不是依赖 geoPath 的圆形点。仓库内实战参照两个地图组件的实现细节D3 官方文档站点用 VitePress Vue 组件演示 geoPath两个组件都是上文 API 组合的完整示范。WorldMap.vueSphere 轮廓 经纬网 陆地要素docs/components/WorldMap.vue 的核心渲染逻辑const outline {type: Sphere}; const graticule d3.geoGraticule10(); // ... const path d3.geoPath(projection); svg.selectAll([nameoutline]).attr(d, path(outline)); svg.selectAll([namegraticule]).attr(d, path(graticule)); svg.selectAll([namefeature]).attr(d, path(feature));要点地球轮廓用无坐标的Sphere对象生成模板中同时渲染填充轮廓与描边轮廓两个 path前者负责背景填充、后者负责边界线避免“填充描边”互相干扰经纬网由d3.geoGraticule10()生成这印证了 path 生成器接受任何“可被投影 stream 处理”的地理对象而非仅限 GeoJSON陆地数据通过d3.json拉取 TopoJSON 后用topojson.feature转换组件还支持 pointermove 拖拽旋转投影修改projection.rotate后重新计算三条 path 的d属性展示了“更新投影 → 重新生成路径”的动态地图范式。UsMap.vue单路径承载 mesh 的批量渲染docs/components/UsMap.vue 展示单 path 元素批量渲染的模式const {statemesh, countymesh, nation} await objectsPromise; const path d3.geoPath(projection); svg.selectAll([namecountymesh]).attr(d, path(countymesh)); svg.selectAll([namestatemesh]).attr(d, path(statemesh)); svg.selectAll([namenation]).attr(d, path(nation));其中topojson.mesh(us, us.objects.counties, (a, b) a ! b (a.id / 1000 | 0) (b.id / 1000 | 0))这类调用只生成“不同要素之间”的边界线去除了相邻县之间的重复内边——对全美县界这种细碎几何mesh 模式能显著减少路径数据量正是“合并到少量 path 元素通常更快”这条经验在真实组件中的体现。适用前提与版本说明本仓库为 D3 v7 主线package.json 中version: 7.9.0engines: {node: 12}d3-geo依赖版本为^3.1.1d3-path为^3.1.0本文所有 APIgeoPath、digits、pointRadius、四个度量方法、projection、context均在 d3-geo v3 中可用文档示例中的数值如path.area(california)的结果对应文档给定的特定投影设置具体数值取决于你使用的投影与 scale构建产物由 rollup.config.js 从bundle.js打包为 UMD / ESM / min 三种格式dist/d3.js的 UMD 构建会将全部d3.geo*API 挂载到全局d3对象上。小结d3.geoPath是 d3-geo 的出口 API一个生成器实例同时承担“SVG 路径字符串生成”“Canvas 直接绘制”“投影后平面度量”三类职责。使用时记住四条主线——用geoPath(projection, context)建立生成器projection为null时按原始平面坐标渲染context为null时返回字符串path(object)接受全部 GeoJSON 类型与特殊类型Sphere多要素可合并 FeatureCollection 单路径渲染也可逐要素 join 独立路径换取交互能力area/bounds/centroid/measure给出投影后、裁剪后的平面度量用于标注、聚焦缩放与布局计算digits控制 SVG 字符串精度仅字符串模式生效默认 3pointRadius控制点符号大小默认 4.5支持函数访问器。配合 投影、Streams 与 球面数学 文档以及仓库中 docs/components/WorldMap.vue、docs/components/UsMap.vue 两个可运行的地图组件示例即可覆盖从静态地图到动态旋转地图的完整开发需求。【免费下载链接】d3Bring data to life with SVG, Canvas and HTML. :bar_chart::chart_with_upwards_trend::tada:项目地址: https://gitcode.com/GitHub_Trending/d3/d3创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表