用Processing实现北京地铁的一天动态可视化

一、项目思路

在本次项目中,将读取各个地铁站点每分钟的进出站客流并进行动态可视化。

1.1 地图背景使用卫星地图图片,地铁站点用ellipse()展示,地铁线路用line()绘制,站点和线路的的颜色分别使用fill()和stroke()控制。

1.2 在setup()函数中完成一些初始化工作,如读取数据、读取背景图片、加载字体等。

1.3 由于每分钟提供一次数据汇总,因此需要60x24共计1440帧,在每帧的draw()中使用对应的数据重新绘制画布,用ellipse()的大小来表示客流量的多少。

二、项目实现

2.1 新建草图

2.2 新建Table变量

Table是一种类似二维数组的变量类型,定义table变量用于存储站点数据,link变量用于存储线路数据、traffic变量用于存储客流数据。
N等于1440,表示一天一共有1440分钟,interval为每帧对应的时间,即1分钟。
current为当前帧对应的时间,由于凌晨零点至五点地铁尚未运营,所以这里选择从五点开始展示。

2.3 setup()函数

编辑setup()函数以完成一些初始化工作,包括设置画布大小、启用平滑处理、设置背景颜色、设置填充色、启用圆角绘图、设置帧频等。

2.4 数据保存在data文件夹

将背景图片和地铁线路数据存放在data文件夹

2.5 loadTable()函数加载csv数据并赋值给三个Table变量

2.6 使用loadImage()函数加载背景图片并赋值给PImage变量

2.7 设置字体

使用createFont()函数生成一个字体,并使用textFont()函数将生成的字体设置为当前绘图所用的字体。

Table table;
Table link;
Table traffic;
PImage beijing;
int R;
int C;
float w = 1280;
float h = 720;
float minLng = 134.6412879;
float maxLng = 135.9199972;
float minLat = 35.9535469;
float maxLat = 36.6819062;
int interval = 1;
int N = 24 * 60 / interval;
int current = 5 * 60;

int offset = 1;

void setup(){
  size(1280, 720);
  smooth();
  background(250);
  fill(102);
  strokeCap(ROUND);
  frameRate(60);
  
  table = loadTable("stations_with_geo.csv");
  link = loadTable("links_with_geo.csv");
  traffic = loadTable("oneday_traffic.csv");
  beijing = loadImage("beijing.png");
  
  textFont(createFont("MicrosoftYaHei", 13));
}

void draw(){
  drawBackground();
  
  for (int i = 0; i < 304; i++){
    float lat = traffic.getFloat(current, i * 7);
    float lng = traffic.getFloat(current, i * 7 + 1);
    int cr = traffic.getInt(current, i * 7 + 2);
    int cg = traffic.getInt(current, i * 7 + 3);
    int cb = traffic.getInt(current, i * 7 + 4);
    float entry;
    if (offset == 0) {
      entry = traffic.getFloat(current, i * 7 + 5) * 3;
    }
    else {
      entry = traffic.getFloat(current, i * 7 + 6) * 3;
    }
    fill(cr, cg, cb, 120);
    ellipse(w*(lng-minLng)/(maxLng-minLng), h*(maxLat-lat)/(maxLat-minLat), entry, entry);
  }  
  
  // saveFrame("frames/####.tif");
  
  current = current + 1;
  if (current == N) {
    // current = 5 * 60;
    exit();
  }
}

void drawBackground(){
  image(beijing, 0, 0, 1280, 720);
  
  int bg = 250;
  // background(bg);
  
  int hour = current / 60;
  int minute = current % 60;
  fill(bg);
  textSize(30);
  if (hour < 10 && minute < 10) {
    text('0' + str(hour) + ":0" + str(minute), 950, 60);
  }
  else if (hour >= 10 && minute < 10) {
    text(str(hour) + ":0" + str(minute), 950, 60);
  }
  else if (hour < 10 && minute >= 10) {
    text('0' + str(hour) + ':' + str(minute), 950, 60);
  }
  else {
    text(str(hour) + ':' + str(minute), 950, 60);
  }
  textSize(18);
  if (offset == 0) {
    text("Entries / per 3m", 1050, 60);
  }
  else {
    text("Exits / per 3m", 1050, 60);
  }
  textSize(12);
  int tmp = 0;
  for (int i = 0; i < 10; i++){
    String s = traffic.getString(N + current - current % 10, 10 * 2 * offset + i * 2);
    int number = traffic.getInt(N + current - current % 10, 10 * 2 * offset + i * 2 + 1);
    if (number >= 20) {
      textAlign(RIGHT);
      fill(bg);
      text(s, 1020, 96 + 20 * tmp);
      textAlign(LEFT);
      text(number, 1050 + number / 5, 96 + 20 * tmp);
      noStroke();
      fill(bg, 255 - tmp * 20);
      rect(1040, 96 + 20 * tmp - 9, number / 5, 10);
      tmp += 1;
    }
  }
  
  R = link.getRowCount();
  C = link.getColumnCount();
  
  for (int i = 0; i < R; i++){
    float latO = link.getFloat(i, 1);
    float lngO = link.getFloat(i, 2);
    float latD = link.getFloat(i, 4);
    float lngD = link.getFloat(i, 5);
    int cr = link.getInt(i, 7);
    int cg = link.getInt(i, 8);
    int cb = link.getInt(i, 9);
    stroke(cr, cg, cb);
    strokeWeight(2);
    line(w*(lngO-minLng)/(maxLng-minLng), h*(maxLat-latO)/(maxLat-minLat), w*(lngD-minLng)/(maxLng-minLng), h*(maxLat-latD)/(maxLat-minLat));
  }
  
  R = table.getRowCount();
  C = table.getColumnCount();
  
  for (int i = 0; i < R; i++){
    //String name = table.getString(i, 0);
    float lat = table.getFloat(i, 1);
    float lng = table.getFloat(i, 2);
    int category = table.getInt(i, 4);
    if (category == 1) {
      fill(204);
      noStroke();
      ellipse(w*(lng-minLng)/(maxLng-minLng), h*(maxLat-lat)/(maxLat-minLat), 6, 6);
    }
    else {
      fill(204);
      noStroke();
      ellipse(w*(lng-minLng)/(maxLng-minLng), h*(maxLat-lat)/(maxLat-minLat), 3, 3);
    }
  }
}

三、总结

通过“北京地铁的一天”这一实战项目,巩固了Processing中常用的一些语法和知识点,也掌握了使用Processing实现动态可视化的基本思想:准备好不同时间对应的数据,在draw()中根据当前时间获取对应的数据,使用点、线、圆形、矩形等绘图元素进行可视化。

四、成果展示

BeiJingSubwayOnday

五、参考文献

张宏伦 [上海交通大学] 实战 上海地铁的一天动态可视化

  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

极客范儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值