虽然数据可视化领域有很多成熟、界面友好、功能强大的软件产品(例如Tableau、VIDI、NodeXL等),但是借助Processing我们可以基于Java语言框架进行丰富多元的可视化编程,熟悉了Processing也可以说是上一学期Topics课程的最大收获,以另一种方式将数据重新组织、统计并以可视化界面展现出来。今天再看其他资料时碰巧看到XX对Processing的支持,颇感亲切,所以决定把上学期Topics的作业过程记录下来。
可视化内容是从Databank下载的217个国家、6个地区的7项统计数据。要求从通过数据可视化看出每个指标的变化趋势、所选不同国家之间的对比情况、不同时间段数据变化、不同地区所包含国家的统计数据对比等。
– Electricity production (kWh)
– Electricity production from renewable sources (kWh)
– Electric power consumption (kWh)
– Electric power consumption (kWh per capita)
– CO2 emissions (metric tons per capita)
– CO2 emissions (kt)
– Population, total
数据文件如下:
最终可视化结果如下:
源码如下:
1 import processing.core.*; 2 import processing.data.*; 3 import processing.event.*; 4 import processing.opengl.*; 5 6 import controlP5.*; 7 import java.lang.Math; 8 9 import java.util.HashMap; 10 import java.util.ArrayList; 11 import java.io.File; 12 import java.io.BufferedReader; 13 import java.io.PrintWriter; 14 import java.io.InputStream; 15 import java.io.OutputStream; 16 import java.io.IOException; 17 18 public class TCS1 extends PApplet { 19 20 ControlP5 cp5; 21 22 23 FloatTable data, dataRegion; 24 float dataMin, dataMax; 25 float volumnMax; 26 27 float plotX1, plotY1; 28 float plotX2, plotY2; 29 float labelX, labelY; 30 31 int yearMin, yearMax; 32 int[] years; 33 int yearInterval = 3; 34 35 36 int showRow = 6; 37 int showRegionRow = 0; 38 39 int yearStartCol = 8; 40 41 //Integrator[] interpolators; 42 43 PFont plotFont; 44 DropdownList dropCountry, dropCate, dropRegion; 45 46 boolean isNormalOn = true; 47 boolean isGroupOn = false; 48 boolean isCompareOn = false; 49 50 51 int groupMode = 1; 52 static final int GROUP_MODE_REGIONS = 1; 53 static final int GROUP_MODE_DECADES = 2; 54 55 IntList listCompare = new IntList(); 56 int[][] compareColors = new int[300][3]; 57 int cateCompare = 0; 58 59 int[] decadeYearIndex = new int[4]; 60 String[] decadeYearStr = new String[4]; 61 62 public void setup() { 63 size(1200, 750); 64 65 66 cp5 = new ControlP5(this); 67 data = new FloatTable("data.tsv"); 68 dataRegion = new FloatTable("region.tsv"); 69 70 years = PApplet.parseInt(data.getColumnNames()); 71 72 yearMin = years[yearStartCol]; 73 yearMax = years[years.length - 1]; 74 75 //init decade year 76 for (int i=7; i>3; i--) { 77 decadeYearStr[7-i] = data.getColumnNames()[i]; 78 decadeYearIndex[7-i] = 7-i; 79 } 80 81 82 83 84 interpolators = new Integrator[data.getColumnCount()]; 85 for(int col=yearStartCol; col=weishu; i--) { 86 maxStr += "0"; 87 eachStr += "0"; 88 } 89 eachStr += "0" + firstNum; 90 maxStr += firstNum + ""; 91 } 92 else { 93 for (int i=-1; i>=weishu+1; i--) { 94 maxStr += "0"; 95 eachStr += "0"; 96 } 97 eachStr += "0" + firstNum; 98 maxStr += firstNum + ""; 99 } 100 } 101 else if (weishu == 0) { 102 if (firstNum != 10) { 103 maxStr = "0."+firstNum; 104 eachStr = "0.0" + firstNum; 105 } 106 else { 107 maxStr = firstNum / 10 + ""; 108 eachStr = "0." + firstNum / 10; 109 } 110 } 111 else if (weishu == 1) { 112 if (firstNum != 10) { 113 maxStr = firstNum + ""; 114 eachStr = "0." + firstNum; 115 } 116 else { 117 maxStr = firstNum + ""; 118 eachStr = firstNum / 10 + ""; 119 } 120 } 121 else { 122 maxStr += firstNum + ""; 123 eachStr += firstNum + ""; 124 for (int i=1; i= 10) { 125 if (weishu == 0) { 126 volumnNumbers[i] = currentFirstNum / 100 + ""; 127 } 128 else { 129 volumnNumbers[i] = currentFirstNum / pow(10, 2-weishu) + ""; 130 } 131 } 132 else if (PApplet.parseFloat(currentFirstNum) / 10 >= 1) { 133 volumnNumbers[i] += "0."; 134 for (int j=-1; j>weishu-1;j--) { 135 volumnNumbers[i] += "0"; 136 } 137 volumnNumbers[i] += currentFirstNum; 138 } 139 else { 140 volumnNumbers[i] += "0."; 141 for (int j=-1; j>weishu-2;j--) { 142 volumnNumbers[i] += "0"; 143 } 144 volumnNumbers[i] += currentFirstNum; 145 } 146 } 147 else if (weishu == 1) { 148 if (PApplet.parseFloat(currentFirstNum) / 10 >= 1) { 149 150 volumnNumbers[i] = (currentFirstNum / 10) + "." + (currentFirstNum - ((currentFirstNum / 10) * 10)); 151 } 152 else { 153 volumnNumbers[i] = "0."+currentFirstNum; 154 } 155 } 156 else { 157 volumnNumbers[i] = currentFirstNum + ""; 158 for (int j=0; j 0) { 159 if (v > 100) { 160 return String.format("%.0f", v); 161 } 162 else if (v > 10) { 163 return String.format("%.2f", v); 164 } 165 else if (v > 1) { 166 return String.format("%.3f", v); 167 } 168 else { 169 return String.format("%.4f", v); 170 } 171 } 172 return ""; 173 } 174 175 176 public void drawDataPoints(int row) { 177 for (int col=yearStartCol; col3; col--) { 178 if (data.isValid(row, col)) { 179 float value = data.getFloat(row, col); 180 System.out.println(value); 181 float x = map(decadeYearIndex[7-col], -0.7f, 3.7f, plotX1, plotX2); 182 float y = map(value, 0, volumnMax, plotY2, plotY1); 183 184 rect(x-(recWidth / 2), y, recWidth, plotY2 - y); 185 186 text(value, x, y - 20); 187 } 188 } 189 } 190 191 192 public void drawDataLine(int row) { 193 beginShape(); 194 //stroke(23, 23, 23); 195 for (int col=yearStartCol; col= data.length)) { 196 throw new RuntimeException("There is no row " + rowIndex); 197 } 198 if ((col < 0) || (col >= data[rowIndex].length)) { 199 throw new RuntimeException("Row " + rowIndex + " does not have a column " + col); 200 } 201 // end training wheels 202 203 return data[rowIndex][col]; 204 } 205 206 207 public boolean isValid(int row, int col) { 208 //System.out.println("data[6].length" + data[6].length); 209 if (row < 0) return false; 210 if (row >= rowCount) return false; 211 //if (col >= columnCount) return false; 212 if (col >= data[row].length) return false; 213 if (col < 0) return false; 214 if(data[row][col] == 0) return false; 215 return !Float.isNaN(data[row][col]); 216 } 217 218 219 public float getColumnMin(int col) { 220 float m = Float.MAX_VALUE; 221 for (int row = 0; row < rowCount; row++) { 222 if (isValid(row, col)) { 223 if (data[row][col] < m) { 224 m = data[row][col]; 225 } 226 } 227 } 228 return m; 229 } 230 231 232 public float getColumnMax(int col) { 233 float m = -Float.MAX_VALUE; 234 for (int row = 0; row < rowCount; row++) { 235 if (isValid(row, col)) { 236 if (data[row][col] > m) { 237 m = data[row][col]; 238 } 239 } 240 } 241 return m; 242 } 243 244 245 public float getRowMin(int row) { 246 float m = Float.MAX_VALUE; 247 for (int col = 0; col < columnCount; col++) { 248 if (isValid(row, col)) { 249 if (data[row][col] < m) { 250 m = data[row][col]; 251 } 252 } 253 } 254 return m; 255 } 256 257 258 public float getRowMax(int row) { 259 float m = -Float.MAX_VALUE; 260 for (int col = 0; col < columnCount; col++) { 261 if (isValid(row, col)) { 262 if (data[row][col] > m) { 263 m = data[row][col]; 264 } 265 } 266 } 267 return m; 268 } 269 270 271 public float getTableMin() { 272 float m = Float.MAX_VALUE; 273 for (int row = 0; row < rowCount; row++) { 274 for (int col = 0; col < columnCount; col++) { 275 if (isValid(row, col)) { 276 if (data[row][col] < m) { 277 m = data[row][col]; 278 } 279 } 280 } 281 } 282 return m; 283 } 284 285 286 public float getTableMax() { 287 float m = -Float.MAX_VALUE; 288 for (int row = 0; row < rowCount; row++) { 289 for (int col = 0; col < columnCount; col++) { 290 if (isValid(row, col)) { 291 if (data[row][col] > m) { 292 m = data[row][col]; 293 } 294 } 295 } 296 } 297 return m; 298 } 299 300 public boolean hasData(int row, int startCol) { 301 boolean hasData = false; 302 for (int col = startCol; col < columnCount; col++) { 303 //System.out.println("row="+row+" col="+col+" isValid="+isValid(row, col)); 304 if (isValid(row, col)) { 305 hasData = true; 306 break; 307 } 308 } 309 return hasData; 310 } 311 312 313 public float getRowMin(int row, int startCol) { 314 float m = Float.MAX_VALUE; 315 for (int col = startCol; col < columnCount; col++) { 316 317 //System.out.println("row="+row+" col="+col+" isValid="+isValid(row, col)); 318 if (isValid(row, col)) { 319 if (data[row][col] < m) { 320 m = data[row][col]; 321 } 322 } 323 } 324 return m; 325 } 326 327 public float getRowMax(int row, int startCol) { 328 float m = -Float.MAX_VALUE; 329 for (int col = startCol; col < columnCount; col++) { 330 //System.out.println("row="+row+" col="+col+" isValid="+isValid(row, col)); 331 if (isValid(row, col)) { 332 if (data[row][col] > m) { 333 m = data[row][col]; 334 } 335 } 336 } 337 return m; 338 } 339 340 public float getRowMin(int row, int startCol, int endCol) { 341 float m = Float.MAX_VALUE; 342 for (int col = startCol; col < endCol + 1; col++) { 343 344 //System.out.println("row="+row+" col="+col+" isValid="+isValid(row, col)); 345 if (isValid(row, col)) { 346 if (data[row][col] < m) { 347 m = data[row][col]; 348 } 349 } 350 } 351 return m; 352 } 353 354 public float getRowMax(int row, int startCol, int endCol) { 355 float m = -Float.MAX_VALUE; 356 for (int col = startCol; col < endCol + 1; col++) { 357 //System.out.println("row="+row+" col="+col+" isValid="+isValid(row, col)); 358 if (isValid(row, col)) { 359 if (data[row][col] > m) { 360 m = data[row][col]; 361 } 362 } 363 } 364 return m; 365 } 366 } 367 class Integrator { 368 369 final float DAMPING = 0.5f; 370 final float ATTRACTION = 0.2f; 371 372 float value; 373 float vel; 374 float accel; 375 float force; 376 float mass = 1; 377 378 float damping = DAMPING; 379 float attraction = ATTRACTION; 380 boolean targeting; 381 float target; 382 383 384 Integrator() { } 385 386 387 Integrator(float value) { 388 this.value = value; 389 } 390 391 392 Integrator(float value, float damping, float attraction) { 393 this.value = value; 394 this.damping = damping; 395 this.attraction = attraction; 396 } 397 398 399 public void set(float v) { 400 value = v; 401 } 402 403 404 public void update() { 405 if (targeting) { 406 force += attraction * (target - value); 407 } 408 409 accel = force / mass; 410 vel = (vel + accel) * damping; 411 value += vel; 412 413 force = 0; 414 } 415 416 417 public void target(float t) { 418 targeting = true; 419 target = t; 420 } 421 422 423 public void noTarget() { 424 targeting = false; 425 } 426 } 427 static public void main(String[] passedArgs) { 428 String[] appletArgs = new String[] { "TCS1" }; 429 if (passedArgs != null) { 430 PApplet.main(concat(appletArgs, passedArgs)); 431 } else { 432 PApplet.main(appletArgs); 433 } 434 } 435 }