Chapter 3. Scales, Axes and Lines(比例尺、坐标轴与线)

(接上篇:《Getting Started with D3》填坑之旅(五):第三章(上))

示例2:绘制两个地点的转门日均流量对比图

这里的转门指的是出入纽约地铁站的闸机旋转门,通过 1 人次则转数加 1。目标是将纽约两大地铁站——时代广场与大中央广场,每日进出闸机转门的平均流量(或人次数)对比情况,绘制在一张基于时间的散点图上,并进一步绘制散点折线图。

本例相当于在前一案例的基础上新增了 时间轴折线 两个知识点。操作步骤如下:

  1. 取值范围:由于两组数据共用一个绘图区,计算 x 轴与 y 轴的实际取值范围时,要把两个站点的所有数据点都考虑进 d3.extent()
  2. 比例尺
    1. 流量数 y 轴还是用线性比例尺(旧版:d3.scale.linear();新版:d3.scaleLinear());
    2. x 轴计算时间比例尺时,旧版用d3.time.scale(),新版用 d3.scaleTime()
  3. 坐标轴:y 轴与示例一相同;x 轴则使用时间轴(旧版:d3.svg.axis().scale(time_scale);新版:d3.axisBottom(time_scale));
  4. 坐标标签:无变化;
  5. 基本界面:需要创建两个 circle 组,分别代表时代广场和大中央广场,其余不变。
  6. 样式设置:给不同的站点数据设置不同的颜色加以区分。

效果如下:

代码如下:

<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"><title>Ch3 - Example 2 | Getting Started with D3</title>
</head><body><h2>Ch3 - Example 2 | Graphing Turnstile Traffic</h2><script src="/demos/js/d3.js"></script><script>let json = null;function draw(data) {"use strict";// badass visualization code goes herejson = data;// Viewport setupvar margin = 40,width = 700 - margin,height = 300 - margin;var chart = d3.select('body').append('svg').attr('width', width + margin).attr('height', height + margin).append('g').attr('class', 'chart');chart.selectAll('circle.times_square').data(data.times_square).enter().append('circle').attr('class', 'times_square');chart.selectAll('circle.grand_central').data(data.grand_central).enter().append('circle').attr('class', 'grand_central');// scales// y_scalevar count_extent = d3.extent(data.times_square.concat(data.grand_central),function(d) { return d.count });var count_scale = d3.scale.linear().domain(count_extent).range([height, margin]);// x_scalevar time_extent = d3.extent(data.times_square.concat(data.grand_central),function(d) { return d.time });var time_scale = d3.time.scale().domain(time_extent).range([margin, width]);// draw circleschart.selectAll('circle').attr('cx', function(d) { return time_scale(d.time) }).attr('cy', function(d) { return count_scale(d.count) }).attr('r', 3);// Axes// x var time_axis = d3.svg.axis().scale(time_scale);chart.append('g').attr('class', 'x axis').attr('transform', 'translate(0, '+height+')').call(time_axis);// yvar count_axis = d3.svg.axis().scale(count_scale).orient('left');chart.append('g').attr('class', 'y axis').attr('transform', 'translate('+margin+', 0)').call(count_axis);}d3.json("/demos/data/turnstile_traffic.json", draw);</script><style>.axis {font-family: Arial;font-size: 0.6em;}path {fill: none;stroke: black;stroke-width: 2px;}.tick {fill: none;stroke: black;}circle {stroke: black;stroke-width: 0.5px;}circle.times_square {fill: DeepPink;}circle.grand_central {fill: MediumSeaGreen;}</style>
</body></html>

这里和书中的示例源码相比略有调整。首先是用变量引用替换了多次从 d3.select() 引用 SVG 元素。然后是实操过程中,发现第 31 行创建的 chart 组是多余的。其实应该将所有的绘图元素都放到 chart 里,使其成为一个整体。于是有了以 chart 变量开头的微调(L33,L39,L64,L78)。

接下来引入 path 元素,将点连成线。path 的关键属性 d,其值为一组绘图指令,负责将多个点按指令顺次连成一段折线。D3 对 d 属性的取值封装了一个折线构造函数,利用该函数,只需要指定源数据和位置信息,即可快速绘制折线段。D3 的这一强大的工具函数,旧版为 d3.svg.line(),新版为 d3.line()。返回的结果函数,包含一个 x(fn)y(fn) 方法,参数都是一个访问函数 fn,负责将对应轴上的数据点,分别映射到该比例尺下的绘图区。

加入以下语句绘制折线:

// paths
var line = d3.svg.line().x(function(d) { return time_scale(d.time) }).y(function(d) { return count_scale(d.count) });// make lines
svg.append('path').attr('class', 'times_square').attr('d', line(data.times_square));
svg.append('path').attr('class', 'grand_central').attr('d', line(dat

在加上折线的 CSS 样式:

path.times_square {stroke: DeepPink;
}
path.grand_central {stroke: MediumSeaGreen;
}

得到两站点的折线散点图:

再加上与示例一类似的坐标轴标签,即可得到最终效果:

// axis labels
// y
chart.select('.y.axis').append('text').text('mean number of turnstile revolutions').attr('transform', 'rotate(90, '+ (-margin) +', 0)').attr('x', 20).attr('y', 0);
// x
chart.select('.x.axis').append('text').text('time').attr('x', function(){ return (width / 1.6) - margin }).attr('y', margin / 1.5);

有了示例一的填坑经历,示例二中的新版本改写的坑也就不再是坑了:

  1. 坐标轴标签默认不显示:需对 text 标签设置填充颜色:.attr('fill', 'black')
  2. 坐标轴标签位置偏移:y 轴标签旋转后需加入垂直平移量,具体根据实际确定(这里取 translate(155));
  3. 坐标轴刻度标签过粗:注释 CSS 样式:.tick{ stroke: black; }

连点成线后,就可以进行简单的数据分析了:时代广场的日均流量滞后于大中央广场;工作日早高峰时段的流量峰值方面,大中央广场更胜一筹……

虽然缺了点图例标识哪条线是哪个站点,不过上手阶段不必一步到位,后续章节应该有扩展的。

新版 D3 完整代码如下:

<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"><title>Ch3 - Example 2 | Getting Started with D3</title>
</head><body><h2>Ch3 - Example 2 | Graphing Turnstile Traffic</h2><script src="/demos/js/d3.v6.js"></script><script>let json = null;function draw(data) {"use strict";// badass visualization code goes herejson = data;// Viewport setupconst margin = 40,width = 700 - margin,height = 300 - margin;const chart = d3.select('body').append('svg').attr('width', width + margin).attr('height', height + margin).append('g').attr('class', 'chart');chart.selectAll('circle.times_square').data(data.times_square).join(enter => enter.append('circle').attr('class', 'times_square'));chart.selectAll('circle.grand_central').data(data.grand_central).join(enter => enter.append('circle').attr('class', 'grand_central'));// scales// y_scaleconst count_extent = d3.extent(data.times_square.concat(data.grand_central),d => d.count);const count_scale = d3.scaleLinear().domain(count_extent).range([height, margin]);// x_scaleconst time_extent = d3.extent(data.times_square.concat(data.grand_central),d => d.time);const time_scale = d3.scaleTime().domain(time_extent).range([margin, width]);// draw circlesd3.selectAll('circle').attr('cx', d => time_scale(d.time)).attr('cy', d => count_scale(d.count)).attr('r', 3);// Axes// x const time_axis = d3.axisBottom(time_scale);chart.append('g').attr('class', 'x axis').attr('transform', `translate(0, ${height})`).call(time_axis);// yconst count_axis = d3.axisLeft(count_scale);chart.append('g').attr('class', 'y axis').attr('transform', `translate(${margin}, 0)`).call(count_axis);// pathsconst line = d3.line().x(d => time_scale(d.time) ).y(d => count_scale(d.count) );// make lineschart.append('path').attr('class', 'times_square').attr('d', line(data.times_square));chart.append('path').attr('class', 'grand_central').attr('d', line(data.grand_central));// axis labels// yd3.select('.y.axis').append('text').text('mean number of turnstile revolutions').attr('fill', 'black').attr('transform', `rotate(90, ${-margin}, 0) translate(155)`).attr('x', 20).attr('y', 0);// xd3.select('.x.axis').append('text').text('time').attr('fill', 'black').attr('x', (width / 1.6) - margin).attr('y', margin / 1.5);}d3.json("/demos/data/turnstile_traffic.json").then(draw).catch(console.error);</script><style>.axis {font-family: Arial;font-size: 0.6em;}path {fill: none;stroke: black;stroke-width: 2px;}path.times_square {stroke: DeepPink;}path.grand_central {stroke: MediumSeaGreen;}.tick {fill: none;/* stroke: black; */}circle {stroke: black;stroke-width: 0.5px;}circle.times_square {fill: DeepPink;}circle.grand_central {fill: MediumSeaGreen;}</style>
</body></html>

《Getting Started with D3》填坑之旅(六):第三章(下)相关推荐

  1. React Native填坑之旅--动画篇

    React Native填坑之旅--Button篇 React Native填坑之旅--动画 React Native填坑之旅--HTTP请求篇 动画是提高用户体验不可缺少的一个元素.恰如其分的动画可 ...

  2. 一次动态代理的填坑之旅

    转载自  一次动态代理的填坑之旅 背景 想在现有的接口加上熔断降级或者限流的功能,比较好的方式是通过注解的方式,并基于动态代理进行实现,下面代码是Rhino的实现 @Rhino public clas ...

  3. Jenkins项目实战之-MacOS High Sierra自动化打包方案的填坑之旅

    前面我介绍了如何在android和iOS实现公司内部app的自动化打包构建的过程.这里写一个关于这个app自动化打包平台从想法到实践再到放机房平稳运行的一个回忆录.总的来说,在jenkins上实现an ...

  4. 【填坑之旅】手把手教你如何用AndroidStudio上传自己的library到Jcenter库

    [填坑之旅]手把手教你如何用AndroidStudio上传自己的library到Jcenter库 前言:我们在使用AndroidStudio的时候,会经常用到第三方的代码库.引入第三方库的方式无非就是 ...

  5. AppCode Updating indexes一直不停的填坑之旅

    最近在做APP安装包瘦身,找到了传说中的AppCode神器. AppCode 提供了 Inspect Code 来诊断代码,其中含有查找无用代码的功能.它可以帮助我们查找出 AppCode 中无用的类 ...

  6. 关于Echarts的填坑之旅

    正如标题所说,这是Echarts的一遍填坑,如果你是一些echart的配置的话可以阅读 http://echarts.baidu.com/opti...的官网配置信息.今天我想给大家分享的是一些我前段 ...

  7. 微信云托管-填坑之旅

    微信云开发开始收费了,一个月20块钱,贼贵,用不起.而按用量收费.资源还能复用的兄弟产品微信云托管还像还行.所以就有了一场填坑旅行. windows10下安装docker之坑 在本地开发,需要使用do ...

  8. ViewPager中显示图片和播放视频填坑之旅

    一.需求来源与实现思路 1.最近项目需求中有用到需要在ViewPager中播放视频和显示图片的功能,视频是本地视频,最开始的实现思路是ViewPager中根据当前item位置对应的是图片还是视频去初始 ...

  9. 树莓派4B+EdgeX+MQTT的填坑之旅

    最近尝试学习协议适配框架,其中EdgeX作为一款开源的边缘计算平台受到大家的青睐,于是决定一试.目标是实现基于EdgeX的基础的南北MQTT通信功能,整体实现参考了一位大神的博客: (13条消息) E ...

最新文章

  1. Struts2注册信息验证判断是否符合格式 validation
  2. 用java实现 两个工具和一个copy功能
  3. 加油python_力扣——gas station (加油站) python实现
  4. FreeChart柱状图中如何取消柱子的倒影
  5. Boost Graph
  6. 莫队(bzoj 2038: [2009国家集训队]小Z的袜子(hose))
  7. 机器学习十大经典算法——逻辑回归
  8. matlab 怎么保存,matlab怎么保存程序
  9. android 倒计时类,Android倒计时工具类
  10. java生成树形Excel_java poi导出树形结构到excel文件
  11. 谷歌浏览器弹出窗口html代码,谷歌浏览器显示通知消息JS代码
  12. 全面接入:ChatGPT杀进10个商业应用,让AI替你打工
  13. 小议费雪线性判别(Fisher Linear Discriminant Analysis)
  14. 树莓派挂载和卸载U盘或移动硬盘
  15. 虚拟机——windows安装VMware虚拟机
  16. python 警告:simplify chained comparison
  17. matlab画出给定点,已知各点之间的距离,怎么用MATLAB画出点
  18. CNN网络模型的发展概述
  19. android拨号器源代码,android拨号器...
  20. 【UE4 第一人称射击游戏】21-添加动态扩散准心

热门文章

  1. node下载安装配置淘宝镜像以及遇到的问题
  2. vue2核心以及面试题讲解(组件通信方式,分页器,防抖节流)
  3. sql导出oracle错误12154,Oracle导dmp文件ORA-12154报错解决方法
  4. 基于jsp+mysql+ssm超市蔬菜信息管理系统-计算机毕业设计
  5. web开发工具 http-server , grunt 使用
  6. 饥荒 出现error during initialization的解决方法
  7. linux on zfs,ZFS On Linux现状——稳定来临
  8. 【论文笔记】Non-Rigid Point Set Registration Networks
  9. 请问,元宇宙,时空克隆,三维视频融合,数字孪生有什么区别? 点卯-魔镜系列
  10. 小程序接入信用能力,微信花呗来了