【D3.js in Action 3 精译_040】4.4 D3 弧形图的绘制方法

当前内容所在位置:

  • 第四章 直线、曲线与弧线的绘制 ✔️
    • 4.1 坐标轴的创建(上篇)
      • 4.1.1 D3 中的边距约定(中篇)
      • 4.1.2 坐标轴的生成(中篇)
        • 4.1.2.1 比例尺的声明(中篇)
        • 4.1.2.2 坐标轴的添加(下篇)
        • 4.1.2.3 轴标签的添加(下篇)
    • 4.2 D3 折线图的绘制
      • 4.2.1 直线生成工具的使用
      • 4.2.2 对数据点作曲线插值处理
    • 4.3 D3 面积图的绘制
      • 4.3.1 面积图生成工具的用法
      • 4.3.2 用标签提高图表的可读性
    • 4.4 D3 弧形图的绘制 ✔️
      • 4.4.1 D3 中的极坐标系 ✔️
      • 4.4.2 圆弧生成器的使用 ✔️
      • 4.4.3 圆弧形心的计算 ✔️
      • 4.4.4 人物专访:Francis Gagnon、Patricia Angkiriwang 和 Olivia Gélinas
    • 4.5 本章小结

文章目录

    • 4.4 D3 弧形图的绘制 Drawing arcs
      • 4.4.1 D3 中的极坐标系 The polar coordinate system
      • 4.4.2 圆弧生成器的使用 Using the arc generator
      • 4.4.3 圆弧形心的计算 Calculating the centroid of an arc

《D3.js in Action》全新第三版封面

《D3.js in Action》全新第三版封面

译者按
终于来到了第四章的最后一节——弧形图的绘制。有了前面几节的知识储备,看似云遮雾绕的 D3 弧形图绘制也可以立马手到擒来。这也是 D3 可视化的另一个强大之处——掌握相关基础知识后可以快速触类旁通其他图表类型!

4.4 D3 弧形图的绘制 Drawing arcs

在本章最后一节,一起再来看看 D3 弧形图的绘制方法。弧形是数据可视化中的常见形状,经常用于绘制饼图、旭日图(sunburst diagrams)和南丁格尔玫瑰图(Nightingale rose charts)等,用于呈现目标数量与总量之间的关系。此外还可以在自定义的径向可视化作品(custom radial visualizations)中见到它们的身影。

与折线图和面积图一样,弧形图也是通过 SVG 路径元素绘制的。可能您已经猜到了,D3 内置了一个便捷的弧形生成工具专门用于计算弧形路径的 d 属性。在详细介绍弧形生成器之前,先要做好示例项目的相应准备工作。本节要绘制的弧形图,即本章最开始介绍的图 4.1 中右边的“降水天数”环形图,也可以在该图形的线上版本进行查看(详见 http://mng.bz/5orB)。其中蓝色弧线表示 2021 年纽约市全年降水天数的百分比(35%),灰色弧线则表示无降水天数。

首先打开 arc.js 文件,本节后续实现逻辑都将写到这里。同往常一样,这里需要加载一个数据集,即 data 文件夹下的降水量数据文件 daily_precipitation.csv。查看该 CSV 文件,会看到当中只包含两列,其中 date 列为 2021 年的每一天,而 total_precip_in 列则为每一天的总降水量,单位为英寸。

在下面的代码段中,我们使用 d3.csv() 读取数据集,并利用格式转换函数 d3.autoType 得到正确的日期和数字;然后链式调用 Promisethen 方法,并将得到的数据集输出到控制台:

d3.csv("./data/daily_precipitation.csv", d3.autoType).then(data => {
  console.log("precipitation data", data);
});

注意

这里不会展开讨论 d3.csv() 方法的细节,相关内容详见第三章,或者参考本章 4.1 小节了解 d3.autoType 函数的用法。

如果查看控制台中的数据,就会看到日期和数字的格式都是正确的。接下来就可以将处理好的数据集传入 drawArc() 函数。该函数已经事先在 arcs.js 中定义好了(译注:只是不完整):

d3.csv("../data/daily_precipitation.csv", d3.autoType).then(data => {
  console.log("precipitation data", data);
  drawArc(data);
});

drawArc() 函数中,可以添加一个新的 SVG 容器,如以下代码所示。先指定 SVG 容器的宽高为 300px,并添加到一个 ID 为 arcdiv 元素中。该 ID 值已在 index.html 文件中声明。这里利用第一章介绍的知识将 SVG 元素设为响应式容器:忽略其 widthheight 属性,直接指定 viewBox 属性并传入指定的宽高。这样,SVG 容器就能自适应父容器的大小,并保持恒定纵横比不变。最后,再将 SVG 容器的选择集赋给常量 svg 备用:

const pieChartWidth = 300;
const pieChartHeight = 300;
const svg = d3.select("#arc")
  .append("svg")
  .attr("viewBox", [0, 0, pieChartWidth, pieChartHeight]);

4.4.1 D3 中的极坐标系 The polar coordinate system

回顾 4.1 小节介绍的内容,当时的图表是绘制在一个 SVG 分组元素中并通过平移到达容器指定位置;而本节的实现策略不太一样,由于无需为坐标轴和标签预留足够空间,因此边距约定环节可以省略。但与目前创建的所有可视化效果不同,弧形图是基于极坐标系绘制的,并非之前的直角坐标系。

如图 4.24 所示,SVG 容器的坐标系为直角坐标系(也称笛卡尔坐标系)。它通过两个相互垂直的维度 xy 来描述二维平面空间中的位置;而平面极坐标系也有两个维度:半径与角度。其中半径为坐标原点与空间中某个点之间的距离,而角度则基于 12 点方向沿顺时针算得。这种空间坐标的描述方式在处理圆弧时特别管用。

图 4.24 直角坐标系的两个维度相互垂直,而极坐标系通过半径和角度来描述空间中的方位

【图 4.24 直角坐标系的两个维度相互垂直,而极坐标系通过半径和角度来描述空间中的方位】

由于元素在极坐标系中相对于原点定位,因此我们可以说,待绘制的弧形可视化效果的原点位于 SVG 容器的中心,如图 4.25 所示。

图 4.25 通过在 SVG 容器的分组元素中绘制弧形图,然后平移至容器正中,可以很方便地实现弧形图的绘制。此时各圆弧相对于图表中心定位,也就是极坐标系的原点位置。

【图 4.25 通过在 SVG 容器的分组元素中绘制弧形图,然后平移至容器正中,可以很方便地实现弧形图的绘制。此时各圆弧相对于图表中心定位,也就是极坐标系的原点位置。】

以下代码片段将在 SVG 容器内添加一个分组元素,然后平移至容器正中,并将该选择集赋给常量 innerChart

const innerChart = svg
  .append("g")
    .attr("transform", `translate(${pieChartWidth/2}, ${pieChartHeight/2})`);

要绘制圆弧,还需要计算弧形图中降水天数对应的角度值。使用 D3 创建饼图或环形图时,通常使用饼图布局生成工具(即 pie layout generator)来处理相关计算,该生成器工具将在下一章中重点介绍。本节只涉及两条弧线,因此计算起来很简单。

首先,通过数据集的 length 属性,我们知道了 2021 年全年数据点的总天数为 365 天。接着筛选出降水量大于 0 的天数,即 126 天,从而得到当年有降水记录的天数。最后用降水天数除以总天数,得到 35% 的占比:

const numberOfDays = data.length;
const numberOfDaysWithPrecipitation = data.filter(d => 
  d.total_precip_in > 0).length;
const percentageDaysWithPrecipitation =   
  Math.round(numberOfDaysWithPrecipitation / numberOfDays * 100);

接下来需要计算降水天数对应的角度,用占比乘以 360 度(即一整圈的度数),得到 126 度。用角度进行计算更加直观,但使用时需转换为弧度值,即用角度值乘以 π/180,算得弧度约为 2.2。最后将该结果赋给常量 angleDaysWithPrecipitation_rad

之所以需要转换,是因为后续会用到的弧形生成器需要弧度制单位而非角度单位。根据经验,在处理角度时 JavaScript 通常使用弧度制,而 CSS 样式表中则采用角度制计量:

const angleDaysWithPrecipitation_deg = percentageDaysWithPrecipitation * 
  360 / 100;
const angleDaysWithPrecipitation_rad = angleDaysWithPrecipitation_deg * 
  Math.PI / 180;

4.4.2 圆弧生成器的使用 Using the arc generator

终于讲到最有意思的部分:圆弧的生成了!首先需要像折线图和面积图那样,声明一个圆弧生成器(arc generator)。圆弧生成器函数 d3.arc() 位于 d3-shape 模块;本例中需要两个关键的访问器函数(accessor functions):圆弧的内圆半径和外圆半径,分别通过 innerRadius() 函数和 outerRadius() 函数进行设置,传入参数分别为 80px120px。如果内圆半径为 0,则会得到一个饼图。

const arcGenerator = d3.arc()
  .innerRadius(80)
  .outerRadius(120);

如图 4.26 所示,可以调用访问器函数 padAngle() 设置圆弧的填充角来定制圆弧形状。该函数接受一个弧度单位的角度值。这里用的是 0.02 弧度,相当于 1° 多一点点。此外还可以调用 cornerRadius() 函数对圆弧的拐角作圆化处理,其参数为一个基于像素的数字。该访问器函数的效果于 CSS 中的 border-radius 属性类似:

const arcGenerator = d3.arc()
  .innerRadius(80)
  .outerRadius(120)
  .padAngle(0.02)
  .cornerRadius(6);

图 4.26 圆弧生成器利用多个访问器函数计算弧形的 d 属性。图中声明生成器时分别指定了内圆半径、外圆半径、填充角及拐角半径。添加路径元素时需要传入每个圆弧的起始角与终止角。

【图 4.26 圆弧生成器利用多个访问器函数计算弧形的 d 属性。图中声明生成器时分别指定了内圆半径、外圆半径、填充角及拐角半径。添加路径元素时需要传入每个圆弧的起始角与终止角。】

您也许会问,为什么不使用访问器函数来处理圆弧覆盖的角度呢?本例中的角度值是手动计算的,因此在添加路径时将其直接传给圆弧生成器会更简单。在下一章中,我们还将介绍另一些不同的情况。

下面来添加代表降水天数的弧线。在以下代码片段中,先往内部图表的选择集内添加一个 path 元素;接着通过调用前面声明的圆弧生成器来指定其 d 属性。

注意观察,绘制圆弧时需要向生成器中传入一个包含起始角与终止角的配置对象:起始角 startAngle 的值为 0,对应 12 点钟方向;终止角 endAngle 的值为前面算好的降水天数对应的弧度值。最后指定圆弧的填充色为青蓝色 #6EB7C2

innerChart
  .append("path")
    .attr("d", () => {
      return arcGenerator({
        startAngle: 0,
        endAngle: angleDaysWithPrecipitation_rad
      });
    })
    .attr("fill", "#6EB7C2");

同理可添加第二段圆弧。此时弧线从上一条圆弧的终止位置开始,并在整个圆的终止处结束(对应的弧度为 2*Pi)。接着再给第二段圆弧指定填充色 #DCE2E2,即更接近灰色的颜色,表明这些日子没有降水:

innerChart
  .append("path")
    .attr("d", () => {
      return arcGenerator({
        startAngle: angleDaysWithPrecipitation_rad,
        endAngle: 2 * Math.PI
      });
    })
    .attr("fill", "#DCE2E2");

图 4.27 用以展示降水天数与无降水天数对比情况的弧线图效果

【图 4.27 用以展示降水天数与无降水天数对比情况的弧线图效果】

保存项目后,本地页面的弧形图应该与图 4.27 中的版本类似。您可以自行尝试变换传入圆弧生成器各访问器函数中的参数值,例如半径和拐角尺寸,观察它们对圆弧外观的影响。

如图 4.28 所示,弧形图的绘制过程与折线图及面积图类似,主要区别在于,弧线在平面空间中的位置坐标是通过极坐标描述的,并非之前的直角坐标系。这一点在圆弧生成器的访问函数中也有所体现。

图 4.28 弧形图的绘制步骤

【图 4.28 弧形图的绘制步骤】

4.4.3 圆弧形心的计算 Calculating the centroid of an arc

饼图和环形图近期在数据可视化领域可谓负面新闻不断,主要原因时人们发现肉眼并不擅长估算角度所代表的比例大小。不过,在包含少量数据类别时,这些图表的表现还是可圈可点的;但我们绝对可以通过添加标签来提高它们的可读性,这就是本节要实现的目标。

在表示降水天数的圆弧上,我们希望添加一个 “35%” 的标签,即之前算出的降水天数占比。该标签的最佳位置应该在圆弧的中心处,即形心(centroid)位置(也叫质心,即 center of mass)。该位置坐标可通过圆弧生成器计算得到。

在下面的代码片段中,我们调用了此前声明的圆弧生成器上的方法。此时,起始角与终止角也可以通过链式调用 startAngle()endAngle() 这两个访问器函数进行设置,最后再链式调用形心计算方法 centroid(),得到该圆弧的中点坐标:

const centroid = arcGenerator
  .startAngle(0)
  .endAngle(angleDaysWithPrecipitation_rad)
  .centroid();

将中点坐标输出到控制台,可以看到形心为包含两个坐标值的数组组成,分别为形心的水平与垂直坐标值,本例即为 [89, -45]

在以下代码片段中,我们向内部图表选择集添加了一个 text 元素来创建文本标签。为了让标签值带有百分号 %,这里用到了 D3 的格式化方法 d3.format(".0%"),然后在后面的括号中传入指定的数值。该方法可以轻松实现数字的各种格式化表示,如货币、百分比及指数形式;也可以为数字添加指定的后缀,如表示百万的 M 以及表示微米数量级的 μ。具体的格式转换列表详见 d3-format 模块的官方文档。

接着,我们使用 centroid() 方法返回的坐标数组完成 xy 属性的设置。注意,这里还指定了 text-anchor 属性和 dominant-baseline 属性,以确保文本标签在水平与垂直方向均相对 xy 属性居中渲染。

最后,将标签的字体颜色改为白色,粗细设为 500,以提高标签的可读性。保存项目后,带文本标签的弧形图效果应该与图 4.29 一致:

图 4.29 带文本标签的弧形图最终效果

【图 4.29 带文本标签的弧形图最终效果】

innerChart
  .append("text")
    .text(d => d3.format(".0%")(percentageDaysWithPrecipitation/100))
    .attr("x", centroid[0])
    .attr("y", centroid[1])
    .attr("text-anchor", "middle")
    .attr("dominant-baseline", "middle")
    .attr("fill", "white")
    .style("font-weight", 500);

至此,我们已经掌握了 D3 折线图、面积图以及弧形图的绘制方法。下一章,我们将利用布局生成工具(layout generators)让这些图形的绘制再提高一个档次!



另附:专栏文章连载期间 完全免费,后续 不排除 调整为收费专栏。对 D3.js 感兴趣、或者想要从零开始彻底掌握 D3 的朋友们强烈建议及时关注本专栏,一起学习交流,共同进步!

目前译好的其他章节内容如下(可进入专栏查看详情):

  • 第一部分 D3.js 基础知识
    • 第一章 D3.js 简介(已完结)
      • 1.1 何为 D3.js?
      • 1.2 D3 生态系统——入门须知
      • 1.3 数据可视化最佳实践(上)
      • 1.3 数据可视化最佳实践(下)
      • 1.4 本章小结
    • 第二章 DOM 的操作方法(已完结)
      • 2.1 第一个 D3 可视化图表
      • 2.2 环境准备
      • 2.3 用 D3 选中页面元素
      • 2.4 向选择集添加元素
      • 2.5 用 D3 设置与修改元素属性
      • 2.6 用 D3 设置与修改元素样式
      • 2.7 本章小结
    • 第三章 数据的处理(已完结)
      • 3.1 理解数据
      • 3.2 准备数据
      • 3.3 将数据绑定到 DOM 元素
        • 3.3.1 利用数据给 DOM 属性动态赋值
      • 3.4 让数据适应屏幕
        • 3.4.1 比例尺简介(上篇)
        • 3.4.2 线性比例尺(中篇)
          • 3.4.2.1 基于 Mocha 测试 D3 线性比例尺(DIY 实战)
        • 3.4.3 分段比例尺(下篇)
          • 3.4.3.1 使用 Observable 在线绘制 D3 条形图(DIY 实战)
      • 3.5 加注图表标签(上篇)
        • 3.5.1 人物专访:Krisztina Szűcs(下篇)
      • 3.6 本章小结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/919519.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Flink监控checkpoint

Flink的web界面提供了一个选项卡来监控作业的检查点。这些统计信息在任务终止后也可用。有四个选项卡可以显示关于检查点的信息:概述(Overview)、历史(History)、摘要(Summary)和配置(Configuration)。下面依次来看这几个选项。 Overview Tab Overview选项卡列出了以…

跨平台WPF框架Avalonia教程 十五

ListBox 列表框 列表框从元素源集合中显示多行元素,并允许选择单个或多个。 列表中的元素可以组合、绑定和模板化。 列表的高度会扩展以适应所有元素,除非特别设置(使用高度属性),或由容器控件设置,例如…

【Redis】Redis实现的消息队列

一、用list实现【这是数据类型所以支持持久化】 消息基于redis存储不会因为受jvm内存上限的限制,支持消息的有序性,基于redis的持久化机制,只支持单一消费者订阅,无法避免消息丢失。 二、用PubSub【这不是数据类型,是…

从0-1训练自己的数据集实现火焰检测

随着工业、建筑、交通等领域的快速发展,火灾作为一种常见的灾难性事件,对生命财产安全造成了严重威胁。为了提高火灾的预警能力,减少火灾损失,火焰检测技术应运而生,成为火灾监控和预防的有效手段之一。 传统的火灾检测方法,如烟雾探测器、温度传感器等,存在响应时间慢…

【vmware+ubuntu16.04】ROS学习_博物馆仿真克隆ROS-Academy-for-Beginners软件包处理依赖报错问题

首先安装git 进入终端,输入sudo apt-get install git 安装后,创建一个工作空间名为tutorial_ws, 输入 mkdir tutorial_ws#创建工作空间 cd tutorial_ws#进入 mkdir src cd src git clone https://github.com/DroidAITech/ROS-Academy-for-Be…

13. Linux上CUDA的安装

这个专栏记录我学习/科研过程中遇到的一些小问题以及解决方案,一些问题可能比较蠢请见谅。自用,仅供参考。 ------------------------------------------------------------------------------------ Linux上CUDA的安装 在安装之前,首先要确…

ThinkPHP6门面(Facade)

门面 门面(Facade) 门面为容器中的(动态)类提供了一个静态调用接口,相比于传统的静态方法调用, 带来了更好的可测试性和扩展性,你可以为任何的非静态类库定义一个facade类。 系统已经为大部分…

(计算机毕设)基于SpringBoot+Vue的房屋租赁系统的设计与实现

博主可接毕设设计!!! 各种毕业设计源码只要是你有的题目我这里都有源码 摘 要 社会的发展和科学技术的进步,互联网技术越来越受欢迎。网络计算机的生活方式逐渐受到广大人民群众的喜爱,也逐渐进入了每个用户的使用。互…

[Admin] Dashboard Filter for Mix Report Types

Background RevOps team has built a dashboard for sales team to track team members’ performance, but they’re blocked by how to provide a manager view based on sales’ hierarchy. Therefore, they seek for dev team’s help to clear their blocker. From foll…

游戏引擎学习第19天

介绍 这段内容描述了开发者在进行游戏开发时,对于音频同步和平台层的理解和调整的过程。以下是更详细的复述: 开发者表达了他希望今天继续进行的工作内容。他提到,昨天他讲解了一些关于音频的内容,今天他想稍微深入讲解一下他正…

高阶云服务-ELB+AS

ELBAS 弹性负载均衡弹性伸缩 原来1台web服务器不满足相应,现部署多台提供相同服务; 由于多个服务器多个ip该如何提供给应用呢? 引申出负载均衡(HAProxy,LVS01四层,Nginx七层) 防单点故障做主备…

STM32设计防丢防摔智能行李箱-分享

目录 目录 前言 一、本设计主要实现哪些很“开门”功能? 二、电路设计原理图 1.电路图采用Altium Designer进行设计: 2.实物展示图片 三、程序源代码设计 四、获取资料内容 前言 随着科技的不断发展,嵌入式系统、物联网技术、智能设备…

Mysql中REPLACE INTO详解及和INSERT INTO的区别

前言 我们在进行数据库操作的时候,经常会遇到这样的场景: 首先判断数据是否存在;如果不存在,则插入;如果存在,则更新。 博主之前是是Java来进行逻辑判断,例如: 看起来似乎也很简洁…

基于Java Springboot发艺美发店管理系统

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术:Html、Css、Js、Vue、Element-ui 数据库:MySQL 后端技术:Java、Spring Boot、MyBatis 三、运行环境 开发工具:IDEA/eclipse 数据…

element ui 走马灯一页展示多个数据实现

element ui 走马灯一页展示多个数据实现 element ui 走马灯一页展示多个数据实现 element ui 走马灯一页展示多个数据实现 主要是对走马灯的数据的操作,先看js处理 let list [{ i: 1, name: 1 },{ i: 2, name: 2 },{ i: 3, name: 3 },{ i: 4, name: 4 },]let newL…

ACM招新赛<赛后题解与反思总结>③

问题 A: 数数(Easy) 题目描述 某一天,工作室需要统计人数,现在小劉开始数人数,但是由于小劉数学极差,因此他数数的时候总是会忘记数数字8,也就是说他数完7以后,就会直接数数字9,直观的讲就是数字…

网络安全:我们的安全防线

在数字化时代,网络安全已成为国家安全、经济发展和社会稳定的重要组成部分。网络安全不仅仅是技术问题,更是一个涉及政治、经济、文化、社会等多个层面的综合性问题。从宏观到微观,网络安全的重要性不言而喻。 宏观层面:国家安全与…

八、鸿蒙开发-网络请求、应用级状态管理

提示:本文根据b站尚硅谷2024最新鸿蒙开发HarmonyOS4.0鸿蒙NEXT星河版零基础教程课整理 链接指引 > 尚硅谷2024最新鸿蒙开发HarmonyOS4.0鸿蒙NEXT星河版零基础教程 文章目录 一、网络请求1.1 申请网络访问权限1.2 安装axios库1.2.1 配置环境变量1.2.2 第二步&…

【MySQL实战45讲笔记】基础篇——MySQL 的基础架构

目录 1. MySQL 的基础架构1.1 连接器1.2 查询缓存1.3 分析器1.4 优化器1.5 执行器 1. MySQL 的基础架构 我们由一个问题引入对MySQL 基础架构的学习:一条SQL查询语句是如何执行的? mysql> select * from T where ID10;通过分析学习MySQL…

计算机网络 (5)数据通信的基础知识

前言 数据通信是一种以信息处理技术和计算机技术为基础的通信方式,它通过数据通信系统将数据以某种信号方式从一处传送到另一处,为计算机网络的应用和发展提供了技术支持和可靠的通信环境,是现代通信技术的关键部分。 一、数据通信的基本概念…