初始化创建画布_使用HTML5,画布和开放数据创建全球降水(雨)可视化
初始化創建畫布
 我目前正在為Three.js編寫下一本書,其中一章涉及可視化開放數據。 在尋找可以使用的數據時,我遇到了來自NOAA的一組數據。 通過此站點,您可以以網格格式下載一組全世界的每月降水報告。 因此,我下載了它們,然后開始處理數據以查看其外觀和使用方式。 在本文中,我不會向您展示基于Three.js的結果,但是我將為您提供一個快速概述,如何獲得最初用于調試目的的格式: 
 
在此圖像中,您可以看到2012年7月全球對月降水量的對數。我還創建了一個簡單的站點來顯示此動畫以及正在運行的動畫。
因此,您需要做什么才能將可以從NOAA站點下載的集轉換為可視的內容。
- 下載并轉換NetCDF格式。
 - 加載生成的CSV文件
 - 將CSV數據處理到世界網格中
 - 動畫化兩個月之間的過渡
 - 作為獎勵:還可以創建圖例以顯示什么顏色表示什么
 
但是,首先,我們需要獲取數據。
下載并轉換NetCDF格式
我們需要做的第一件事就是獲取數據。 我使用了以下鏈接:您可以在其中定義要下載的數據范圍。 在此示例中,我使用了2012年1月至2012年12月的范圍,并選擇了創建子集而不繪制圖的選項。
但是,下載它的格式不能直接用作我們基于HTML5畫布的可視化的輸入。 您可以使用ncdump-json創建一個JSON文件,但是仍然需要能夠解釋它,因此我選擇了另一種方法。 我剛剛編寫了一個簡單的Java程序,將NetCDF格式轉換為簡單的CSV文件。
我使用了以下Maven依賴項:
<dependencies><dependency><groupId>edu.ucar</groupId><artifactId>netcdf</artifactId><version>4.2.20</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.4</version></dependency></dependencies>并使用以下一段Java代碼:
public class NetCDFDump {public static void main(String[] args) throws IOException, InvalidRangeException {String year = "2012";NetcdfFile nc = NetcdfFile.open("src/main/resources/X84.31.143.145.44.1.47.49.nc");Variable precip = nc.findVariable("precip");// use the shapes to create an arrayint[] shapes = precip.getShape();// month, lat, lonfloat[][][] data = new float[shapes[0]][shapes[1]][shapes[2]];// iterate over 12 (or 11) monthsint[] pos = new int[3];int[] shape = {1,1,1};for (int i = 0 ; i < shapes[0] ; i++) {pos[0]=i;for (int lat = 0 ; lat < shapes[1]; lat++) {pos[1] = lat;for (int lon = 0 ; lon < shapes[2]; lon++) {pos[2] = lon;Array result = precip.read(pos, shape);data[pos[0]][pos[1]][pos[2]] = result.getFloat(0);}}}// output data like this// month, lat, lon, humidityfloat[][] combined = new float[data[0].length][data[0][0].length];for (int m = 0 ; m < data.length ; m++) {File outputM = new File(year + "-out-" + m + ".csv");for (int lat = 0 ; lat < data[m].length ; lat++) {for (int lon = 0 ; lon < data[m][lat].length; lon++) {float value = data[m][lat][lon];if (value > -1000) {combined[lat][lon]+=value;} else {combined[lat][lon]+=-1000;}// write the string for outputfileStringBuffer bOut = new StringBuffer();bOut.append(m);bOut.append(',');bOut.append(lat);bOut.append(',');bOut.append(lon);bOut.append(',');bOut.append(value);bOut.append('\n');// write to month fileFileUtils.write(outputM,bOut,true);}}}// now process the combinedFile outputM = new File(year + "-gem.csv");for (int i = 0; i < combined.length; i++) {for (int j = 0; j < combined[0].length; j++) {StringBuffer bOut = new StringBuffer();bOut.append(i);bOut.append(',');bOut.append(j);bOut.append(',');bOut.append(combined[i][j]/data.length);bOut.append('\n');FileUtils.write(outputM, bOut, true);}}} }我不會詳細介紹正在發生的事情,但是這段代碼會生成許多文件,每個文件一個月,其中一個包含平均值。
每月以以下格式顯示
... 0,65,78,32.65 0,65,79,35.09 0,65,80,31.14 0,65,81,42.7 0,65,82,49.57 ...這些值分別表示:月份,緯度,經度和降水。 對于平均值,除了省略第一個條目外,它看起來幾乎相同。
... 59,94,59.874165 59,95,65.954994 59,96,57.805836 ...現在,我們已經獲得了易于使用的格式的數據,可以使用它來創建可視化。
加載生成的CSV文件
要加載文件,我們只使用一個簡單的XMLHttpRequest,如下所示:
// create an XMLHttpRequest to get the datavar xmlhttp = new XMLHttpRequest();xmlhttp.onreadystatechange = function() {if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {var coords = CSVToArray(xmlhttp.responseText,",");// and process each of the coordinates...}}// make the call and use the callback to process the resultxmlhttp.open("GET", "location/of/the/file", true);xmlhttp.send();現在,coords變量包含所有坐標,并為每個坐標顯示值。 實際上,將其轉換為畫布非常容易。
將CSV數據處理到世界網格中
在XMLHttpRequest的回調中,我們檢查是否已接收到數據并將其轉換為一組坐標。 我們唯一需要做的就是將這些坐標轉換為畫布上的可視化圖像。
var coords = CSVToArray(xmlhttp.responseText,",");coords.forEach(function(point) {var offset = 0;if (point.length > 3) {offset = 1;}if (parseFloat(point[2+offset]) >= 0) {var lat = parseInt(point[0+offset]);var lon = parseInt(point[1+offset]);var value = parseFloat(point[2+offset]);if (value > max) max = value;// lat is from 0 to 180// lon is from 0 to 360var x = canvas.width/360*((lon)-180);if (x<=0) {x=canvas.width-(x*-1);}var y = canvas.height/180*lat;if (value >= 0) {context.beginPath();context.rect(x,y,4,4);context.fillStyle = scale(value).hex();context.fill();}}});如您所見,非常簡單的代碼就是我們將位置取下來,將它們轉換為畫布上的X和Y坐標,并創建具有特定顏色的小方塊。 為了生成顏色,我們使用Chroma.js比例尺。
var scale = chroma.scale(['red' , 'yellow', 'green', 'blue']).domain([1,1700], 100, 'log');此調用創建從紅色到黃色到綠色到藍色的色標。 值的范圍是1到1700,分為100步,并使用對數刻度。 這將產生以下圖像(這次是2012年1月的降水:
由于我們擁有所有月份的數據,因此我們現在可以輕松創建簡單的動畫。
動畫化兩個月之間的過渡
對于動畫,我們將創建類似于以下電影中所示的內容,其中我們在各個月份之間緩慢過渡:
只需將圖像彼此疊加顯示并更改不透明度,即可輕松創建此動畫。 因此,首先設置一些css,它將大部分圖像隱藏起來,然后將它們全部放在另一個頂部。
#cf {position:relative;margin:0 auto;height: 700px;}#cf img {position:absolute;left:0;width: 1600px;}現在我們可以添加圖像,并使用“ bottom”類僅顯示第一個圖像:
<div id="cf"><img id="img-1" class="top" src="./assets/images/2012-01-perc.png" /><img id="img-2" class="bottom" src="./assets/images/2012-02-perc.png" /><img id="img-3" class="bottom" src="./assets/images/2012-03-perc.png" /><img id="img-4" class="bottom" src="./assets/images/2012-04-perc.png" /><img id="img-5" class="bottom" src="./assets/images/2012-05-perc.png" /><img id="img-6" class="bottom" src="./assets/images/2012-06-perc.png" /><img id="img-7" class="bottom" src="./assets/images/2012-07-perc.png" /><img id="img-8" class="bottom" src="./assets/images/2012-08-perc.png" /><img id="img-9" class="bottom" src="./assets/images/2012-09-perc.png" /><img id="img-10" class="bottom" src="./assets/images/2012-10-perc.png" /><img id="img-11" class="bottom" src="./assets/images/2012-11-perc.png" /><img id="img-12" class="bottom" src="./assets/images/2012-12-perc.png" /> </div>現在,我們只需要一些JavaScript即可將所有內容捆綁在一起:
var month=[];month[0]="January";month[1]="February";month[2]="March";month[3]="April";month[4]="May";month[5]="June";month[6]="July";month[7]="August";month[8]="September";month[9]="October";month[10]="November";month[11]="December";var allTweens;init();animate();function init() {// create a chain of tweensallTweens = setupTweens(12);allTweens[0].start();}function setupTweens(imageCount) {var tweens = [];for (var i = 0 ; i < imageCount ; i++) {var tween = new TWEEN.Tween( { opac: 0, image: i, max: imageCount } ).to( { opac: 100 }, 2500 ).easing( TWEEN.Easing.Linear.None ).onUpdate( function () {// on update, lower the opacity of image i and update the opacity of// image i+1;var currentImage = document.getElementById('img-'+(this.image+1));if (this.image == imageCount -1) {var nextImage = document.getElementById('img-'+1);} else {var nextImage = document.getElementById('img-'+(this.image+2));}currentImage.style.opacity = 1- this.opac / 100;nextImage.style.opacity = this.opac / 100;} );tween.onComplete(function() {document.getElementById('title-2012').textContent = "Showing precipitation: " + month[this.image] + " " + 2012;// Set the inner variable to 0.this.opac = 0;// we're done, restartif (this.max-1 == this.image) {allTweens[0].start();}});// connect to each anotherif (i > 0) {tweens[i-1].chain(tween);}tweens.push(tween);tweens[0].repeat();}return tweens;}function animate() {requestAnimationFrame(animate);TWEEN.update();}在這里,我們使用tween.js設置圖像之間的過渡。
作為獎勵:還可以創建圖例以顯示什么顏色表示什么
在動畫中,您可以在底部看到圖例。 此圖例創建為簡單的畫布,另存為圖像。 為了完整起見,此處顯示執行此操作的代碼:
var canvas = document.createElement("canvas");canvas.width = 435;canvas.height = 30;var context = canvas.getContext('2d');var domains = scale.domain();document.body.appendChild(canvas);// from 1 to 1700for (var i = 0 ; i < domains.length ; i++) {context.beginPath();context.rect(10+i*4,0,4,20);console.log(domains[i]);context.fillStyle = scale(domains[i]).hex();context.fill();}context.fillStyle = 'black';context.fillText("0 mm", 0, 30);context.fillText(Math.round(domains[25]) + " mm", 100, 30);context.fillText(Math.round(domains[50]) + " mm", 200, 30);context.fillText(Math.round(domains[75]) + " mm", 300, 30);context.fillText("1700 mm", 390, 30); 在這里,我們只使用我們更容易看到的比例,并遍歷各個域以創建彩色圖例。 
翻譯自: https://www.javacodegeeks.com/2014/02/create-global-precipitation-rain-visualizations-with-html5-canvas-and-open-data.html
初始化創建畫布
總結
以上是生活随笔為你收集整理的初始化创建画布_使用HTML5,画布和开放数据创建全球降水(雨)可视化的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: gddr6显卡是独立显卡吗(gddr6和
 - 下一篇: 修订和不变性