基于HTML5 WebGL 与 GIS 的智慧机场大数据可视化分析
大數(shù)據(jù),人工智能,工業(yè)物聯(lián)網(wǎng),5G 已經(jīng)或者正在潛移默化地改變著我們的生活。在信息技術(shù)快速發(fā)展的時(shí)代,誰能抓住數(shù)據(jù)的核心,利用有效的方法對(duì)數(shù)據(jù)做數(shù)據(jù)挖掘和數(shù)據(jù)分析,從數(shù)據(jù)中發(fā)現(xiàn)趨勢(shì),誰就能做到精準(zhǔn)控制,實(shí)時(shí)分析,有的放矢,從而獲取更快速、更平穩(wěn)、更長(zhǎng)遠(yuǎn)地發(fā)展。在航空領(lǐng)域,機(jī)場(chǎng)、航班和航線信息是至關(guān)重要的數(shù)據(jù),本文將介紹 以 HT 為平臺(tái),應(yīng)用 JavaScript、HTML5、GIS 等技術(shù)開發(fā)的全球航線實(shí)例。
主界面
動(dòng)態(tài)案例預(yù)覽地址:https://www.hightopo.com/demos/index.html
飛機(jī)及飛機(jī)陰影動(dòng)畫
?
場(chǎng)景搭建
本實(shí)例的場(chǎng)景包括 3D 和 2D 場(chǎng)景兩部分,分別是通過 HT 的 3D 和 2D 編輯器構(gòu)建,該編輯工具基于 HTML5 技術(shù)開發(fā),易于上手,而且預(yù)定義了許多圖元類型,用戶可以無編碼快速可視化搭建各種 3D/2D 場(chǎng)景。3D 場(chǎng)景效果如下:
?
2D 面板部分主要包括左側(cè)航線表格,右側(cè)風(fēng)暴實(shí)時(shí)數(shù)據(jù)表格以及底部的信息面板。左側(cè)航線表格展示了不同大洲的航線信息,大洲可以通過底部的左側(cè)按鈕進(jìn)行切換;右測(cè)風(fēng)暴信息是模擬生成,實(shí)時(shí)更新;底部信息欄包括大洲按鈕及航線詳細(xì)信息。面板截圖:
本實(shí)例的場(chǎng)景包括 3D 和 2D 場(chǎng)景兩部分,分別是通過 HT 的 3D 和 2D 編輯器構(gòu)建,該編輯工具基于 HTML5 技術(shù)開發(fā),易于上手,而且預(yù)定義了許多圖元類型,用戶可以無編碼快速可視化搭建各種 3D/2D 場(chǎng)景。3D 場(chǎng)景效
航線來源及機(jī)場(chǎng)位置的計(jì)算
實(shí)例的機(jī)場(chǎng)和航線源數(shù)據(jù)來自于開源網(wǎng)站 openflights.org。拿到原始數(shù)據(jù)之后,我們首先對(duì)機(jī)場(chǎng)和航線數(shù)據(jù)進(jìn)行了初步處理將其存為 JSON 文件。處理后的機(jī)場(chǎng)數(shù)據(jù)格式如下,每個(gè)域?qū)?yīng)的信息依次是緯度、經(jīng)度、海拔、機(jī)場(chǎng)簡(jiǎn)稱、大洲、國(guó)家、地區(qū)和機(jī)場(chǎng)名字。
[ [-9.443380356,147.2200012, 146,"POM","OC","PG","PG-NCD","Port Moresby"],????[63.98500061,-22.60560036,?171,"KEF","EU","IS","IS-2","Reykjavík"], [36.001741,117.63201,0,"CN-0083","AS","CN","CN-U-A",""],?????…?]處理后的航線數(shù)據(jù)片段格式如下,以第一條信息為例,航線的起始機(jī)場(chǎng)為 MIA,能夠抵達(dá)的機(jī)場(chǎng)包括["3201:PUJ","24:MSY","24:MVD","24:NAS","24:ORF","24:PHL","24:PTP","24:PTY","24:RIC","24:SAL","24:SAN","24:SDQ","24:SFO","1299:AMS"]。
{ "MIA":["3201:PUJ","24:MSY","24:MVD","24:NAS","24:ORF","24:PHL","24:PTP","24:PTY","24:RIC","24:SAL","24:SAN","24:SDQ","24:SFO","1299:AMS"], "HKG":["3021:SIN","1683:MNL","2994:ICN","15999:PVG","24:JFK","24:LAX","24:NRT","24:SFO","330:YVR","218:KIX","576:KUL","1680:SGN","328:POM"], "SJU":["3029:SXM","3029:TPA"], …}通過對(duì)處理后的機(jī)場(chǎng)、航線數(shù)據(jù)分析,可以看出機(jī)場(chǎng)位置是生成航線的基礎(chǔ)。在處理后的機(jī)場(chǎng)數(shù)據(jù)中,已經(jīng)具備了機(jī)場(chǎng)的經(jīng)緯度信息,因此問題的關(guān)鍵點(diǎn)在于如何將經(jīng)緯度轉(zhuǎn)換為球體坐標(biāo),轉(zhuǎn)換代碼如下:
// 將經(jīng)緯度轉(zhuǎn)換為球體位置getSpherePos(radius, longitude, latitude) { let ang1 = Math.PI * (longitude - 90) / 180; let ang2 = Math.PI * latitude / 180; let x, y, z; let s_r = radius; x = s_r * Math.sin(ang1) * Math.cos(ang2); y = s_r * Math.cos(ang1) * Math.cos(ang2); z = s_r * Math.sin(ang2); return [x, y, z];}對(duì)所有機(jī)場(chǎng)數(shù)據(jù)循環(huán)處理,計(jì)算每個(gè)機(jī)場(chǎng)的球體坐標(biāo),并將坐標(biāo)信息與其它既有的機(jī)場(chǎng)信息保存于全局?jǐn)?shù)組中。
航線生成
在生成航線時(shí),使用了 ht.Polyline 類型,該類型支持三維空間點(diǎn)描述,而且結(jié)合 segments 參數(shù),實(shí)現(xiàn)了從二維平面曲線延伸到三維空間曲線的效果。在本實(shí)例中,根據(jù)航線的起點(diǎn)和終點(diǎn)的位置,利用向量運(yùn)算構(gòu)造出中間的控制點(diǎn),生成貝塞爾曲線來渲染航線。航線創(chuàng)建并添加到 DataModel (通過 add 函數(shù))之后, 調(diào)用 setHost(host) 函數(shù)將其吸附到地球,這樣地球在移動(dòng)或者旋轉(zhuǎn)時(shí),航線也會(huì)隨之變化。以下為創(chuàng)建一條航線的代碼實(shí)現(xiàn):
/** * 根據(jù)航線起點(diǎn),終點(diǎn)位置創(chuàng)建航線(貝塞爾曲線)?*?@param?{Object}?start 起點(diǎn)機(jī)場(chǎng)信息?* @param {Object} end 終點(diǎn)機(jī)場(chǎng)信息 */createEdge(start, end) { let edge; let distance = ht.Default.getDistance(start.point, end.point); let ratio = distance / this.radius; let v1 = new ht.Math.Vector3(start.point); let v2 = new ht.Math.Vector3(end.point); let v3 = v1.clone().add(v2).setLength(distance / 2); let v4 = v3.clone().add(v2); v3.add(v1); edge = new ht.Polyline(); ????//?此處設(shè)置edge樣式和屬性的代碼省略 edge.setPoints([ { x: start.point[0], y: start.point[2], e: start.point[1] }, { x: v3.x, y: v3.z, e: v3.y }, { x: v4.x, y: v4.z, e: v4.y }, { x: end.point[0], y: end.point[2], e: end.point[1] }, ]); // 設(shè)置segments參數(shù) edge.setSegments([1, 4]); // 添加到DataModel this.dm3d.add(edge); ????//?吸附到this.earth edge.setHost(this.earth);}這部分的難點(diǎn)在于如何根據(jù)航線的起點(diǎn)和終點(diǎn)位置構(gòu)造中間控制點(diǎn)來生成貝塞爾曲線。下面的示意圖演示了代碼中向量的計(jì)算及各個(gè)向量變量的變化。
對(duì)所有航線數(shù)據(jù)循環(huán)處理,調(diào)用創(chuàng)建航線的 createEdge(start, end) 函數(shù),就能完成所有航線的繪制生成。如圖所示:
2D/3D互動(dòng)畫線
在文章的第二幅圖中,有一條黃色的線。這條線的起點(diǎn)對(duì)應(yīng)著表格中選中的航線,終點(diǎn)對(duì)應(yīng)著 3D 空間的航線。當(dāng)點(diǎn)擊表格中某條航線時(shí),如何生成一條線,跨越 2D 和 3D 空間呢?本實(shí)例的思路是獲取 3D 空間的位置坐標(biāo) p3 后,調(diào)用 g3d.toViewPosition 獲取二維屏幕坐標(biāo) p,之后通過調(diào)用 g2d.getLogicalPoint 得到 2D 坐標(biāo),這個(gè)坐標(biāo)就是終點(diǎn)的位置。以下是獲取終點(diǎn)位置的代碼實(shí)現(xiàn):
// 獲取定位線的終點(diǎn) -- 3D 球體中選中航線對(duì)應(yīng)的位置getLineEnd() { let p3 = this.g3d.getLineOffset(this.selectedEdge, this.g3d.getLineLength(this.selectedEdge) * 0.5); let p = g3d.toViewPosition([p3.point.x, p3.point.y, p3.point.z]); p = this.g2d.getLogicalPoint(p); this.endPoint = p;}線的起點(diǎn)位置代碼如下,分別計(jì)算起點(diǎn)的橫坐標(biāo)和縱坐標(biāo)。???????
// 獲取定位線的起點(diǎn) -- 航線表格對(duì)應(yīng)的位置getLineStart() { let offset = this.table.a('ht.translateY'); let lineStartPoint = {}; let height = this.table.getHeight(); let origY = this.table.p().y - height / 2 + this.table.a('ht.headHeight') + this.table.a("ht.rowHeight") / 2; lineStartPoint.x = this.table.p().x + this.table.getWidth() / 2; lineStartPoint.y = origY + this.rowIndex * this.table.a("ht.rowHeight") + offset; this.startPoint = lineStartPoint;}飛機(jī),飛機(jī)陰影動(dòng)畫及光源移動(dòng)
在表格中選中某條航線或者雙擊地球上某條航線時(shí),飛機(jī)將會(huì)沿著航線飛行,飛機(jī)上方有光源移動(dòng),下方有飛機(jī)陰影移動(dòng)。這部分使用了 HT 內(nèi)置的 startAni 函數(shù)啟動(dòng)動(dòng)畫。在 startAni 函數(shù)中,action 函數(shù)必須提供,實(shí)現(xiàn)動(dòng)畫過程中的屬性變化;finishFunc 為動(dòng)畫結(jié)束后調(diào)用的函數(shù)。一個(gè)簡(jiǎn)單的動(dòng)畫例子如下:
???????
ht.Default.startAnim({ frames: 60, interval: 16, finishFunc: function() { console.log('finish'); }, action: function(t) { console.log(t); }});以下為本 Demo 中的 action 函數(shù),該函數(shù)完成了動(dòng)畫過程中飛機(jī)、光源及飛機(jī)陰影的移動(dòng),飛機(jī)姿態(tài)調(diào)整和旋轉(zhuǎn)。???????
action: function (v, t) { let offset = that.g3d.getLineOffset(that.selectedEdge, length * v);//偏移量 let p1 = offset.point; // 3d坐標(biāo) let tangent = offset.tangent; // 切線方向 let direction = new ht.Math.Vector3(tangent); let vp1 = new ht.Math.Vector3(p1); direction.multiplyScalar(0.1); direction.add(vp1); direction.setLength(direction.length() + 2); vp1.setLength(vp1.length() + 2); that.airPlane.p3(vp1.x, vp1.y, vp1.z); that.airPlane.setRotationMode('yxz'); that.airPlane.lookAtX([0, 0, 0], 'bottom'); that.airPlane.lookAtX([direction.x, direction.y, direction.z], 'front'); lightP = new ht.Math.Vector3(p1); lightP.setLength(that.radius * 2); that.spotLight.p3(lightP.x, lightP.y, lightP.z); direction.setLength(that.radius); lightP.setLength(that.radius); that.planeShadow.p3(lightP.x, lightP.y, lightP.z); that.planeShadow.setRotationMode('yxz'); that.planeShadow.lookAtX([0, 0, 0], 'back'); that.planeShadow.lookAtX([direction.x, direction.y, direction.z], 'right');}衛(wèi)星動(dòng)畫
實(shí)例中,衛(wèi)星按照橢圓軌道圍繞地球旋轉(zhuǎn),Logo 和光暈又圍繞衛(wèi)星旋轉(zhuǎn)。橢圓軌道的計(jì)算方式采用的是參數(shù)方程。假設(shè)橢圓的半長(zhǎng)軸和半短軸的長(zhǎng)度分別為 a 和 b,分別以半長(zhǎng)軸和半短軸做橢圓的內(nèi)切圓和外切圓。通過下圖可以看出橢圓上任意一點(diǎn) A 與內(nèi)切圓上的 A1 點(diǎn)有相同的縱坐標(biāo),與外切圓上的 A2 點(diǎn)有相同的橫坐標(biāo),所以A點(diǎn)的坐標(biāo)就可以描述為 (a * cosθ,b * sinθ),其中θ是橢圓內(nèi)切圓或者外切圓的圓心角。
Logo 和光暈的旋轉(zhuǎn)使用了 3D 旋轉(zhuǎn)函數(shù),具體使用方法可以參照 HT 3D 手冊(cè)中的 3D 旋轉(zhuǎn)函數(shù)部分。衛(wèi)星動(dòng)畫的代碼實(shí)現(xiàn)如下所示:
// 衛(wèi)星及Logo的旋轉(zhuǎn)startSat() { let dm = this.dm3d; let a = 1226; // 橢圓半長(zhǎng)軸 let b = 698; // 橢圓半短軸 let x, y, z; y = 0; let sat_ang = 0; // 衛(wèi)星初始角度 let logo_ang = 0; // logo 初始角度 setInterval(() => { sat_ang = sat_ang + this.satelliteSpeed; logo_ang = logo_ang + 0.01 x = a * Math.cos(-sat_ang); // 衛(wèi)星當(dāng)前 x 軸坐標(biāo) z = b * Math.sin(-sat_ang); // 衛(wèi)星當(dāng)前 z 軸坐標(biāo) y = x * Math.sin(Math.PI * 16 / 180); // 衛(wèi)星當(dāng)前 y 軸坐標(biāo) x = x * Math.cos(Math.PI * 16 / 180); // 衛(wèi)星軌道面沿 z 軸旋轉(zhuǎn)之后的新的 x 軸坐標(biāo) this.sat.p3(x, y, z); this.logo.setRotationY(logo_ang); this.logo.setRotationZ(28 / 180 * Math.PI); this.logo.setRotationMode('yzx'); this.sat_p.setRotationY(logo_ang); this.sat_p.setRotationZ(-35 / 180 * Math.PI); this.sat_p.setRotationMode('yzx'); }, 16.7);}風(fēng)暴動(dòng)畫
風(fēng)暴動(dòng)畫使用 setInterval() 方法重復(fù)調(diào)用風(fēng)暴動(dòng)畫部分,模擬風(fēng)暴的移動(dòng),風(fēng)暴變大及變小。風(fēng)暴變大及變小的實(shí)現(xiàn)思路是設(shè)置兩個(gè) Flag 來判斷風(fēng)暴變大或者變小,風(fēng)暴變大時(shí),不斷加大風(fēng)暴在 x,y,z 軸方向的長(zhǎng)度,并利用 setSize3d 函數(shù)賦值;風(fēng)暴變小時(shí),不斷減小風(fēng)暴在 x,y,z 軸方向的長(zhǎng)度,并利用 setSize3d 函數(shù)賦值。風(fēng)暴的移動(dòng)代碼實(shí)現(xiàn)如下:
// 風(fēng)暴動(dòng)畫startStorm() { let s_ang = 0; let s_ang2 = 0; let s_x, s_y, s_z; let s_r = 380.07; setInterval(() => { s_ang = s_ang + 0.002; s_ang2 = s_ang2 + 0.002; s_x = s_r * Math.sin(s_ang) * Math.cos(s_ang2); s_z = s_r * Math.cos(s_ang) * Math.cos(s_ang2); s_y = s_r * Math.sin(s_ang2); this.storm.p3(s_x, s_y, s_z); this.storm.lookAtX([0, 0, 0], 'bottom'); this.storm.setRotationMode('yzx'); this.storm.setRotationY(s_ang * 20); }, 60);}性能優(yōu)化
?為帶來更好的用戶體驗(yàn),本實(shí)例還進(jìn)行了一系列的優(yōu)化,使得實(shí)例的運(yùn)行更加流暢,美觀。
分批顯示航線
在該實(shí)例中共有2486條航線,如果一次性顯示在地球上,加上各種樣式,那么不但加載速度非常緩慢,而且可能會(huì)因?yàn)閮?nèi)存過大而導(dǎo)致程序崩潰。因此,本實(shí)例采用了分批加載航線的方式,來提高系統(tǒng)性能。具體實(shí)現(xiàn)思路是在初次加載時(shí),設(shè)置一個(gè)名稱為 display_flag 的樣式來控制航線的顯示與否,然后每隔一定時(shí)間(本 Demo 中是每隔30s)更新一次航線。相關(guān)代碼如下:
???????
this.maxDisplayCount = 300; // 30s更新一次航線this.MAX_DISPLAY_COUNT = 6;edge.s({ // 創(chuàng)建航線時(shí) 'display_flag': parseInt(Math.random() * 10) % this.MAX_DISPLAY_COUNT,}); start() { this.edgeTimer = setInterval(() => { this.edges.forEach((val) => { let showFlag = this.checkStormDistance(val); showFlag = showFlag && (val.s('display_flag') == this.displayFlag); val.s('3d.visible', showFlag) }); this.displayCount++; if (this.displayCount > this.maxDisplayCount) { this.displayFlag = (this.displayFlag + 1) % this.MAX_DISPLAY_COUNT; this.displayCount = 0; } }, 100);}Polyline resolution動(dòng)態(tài)改變
HT 通過微分段的方式實(shí)現(xiàn)曲線,參數(shù) shape3d.resolution 用來控制曲線微分段數(shù),這個(gè)參數(shù)決定 3D 圖形精度,數(shù)值越大曲線越均勻,但同時(shí)會(huì)影響性能。在本 Demo 中,為防止飛機(jī)抖動(dòng) shape3d.resolution 設(shè)置為60。但是這樣設(shè)置之后,性能影響會(huì)很大,因此我們采用了動(dòng)態(tài)調(diào)整 resolution 的方式,根據(jù)航線是否被選中動(dòng)態(tài)調(diào)整,提高性能。代碼如下。在 updateResolution 中也需要調(diào)用 g3d.invalidateCachedGeometry(data)? 來重置 geometry,更新方法見 Polyline cache 以及更新方法”部分。
// 動(dòng)態(tài)改變r(jià)esolutionupdateResolution(isRestore) { if (!this.selectedEdge) { // 沒有航線被選中 return; } let res, thickness; let len = this.g3d.getLineLength(this.selectedEdge); if (isRestore) { // 需要恢復(fù)默認(rèn)值 res = 30; thickness = 0.7; } else { res = len / 200 * 30; if (res < 60) { res = 60; } thickness = 5; } this.selectedEdge.s('shape3d.resolution', res); this.selectedEdge.setThickness(thickness);}Polyline cache?以及更新方法
如前所述,本 Demo 中創(chuàng)建了2486條航線,每條航線都是一個(gè) ht.polyLine 類型的 3D 曲線。為提高性能,在創(chuàng)建航線時(shí),將其屬性 geometry.cache 設(shè)置為 true。在后續(xù) polyLine 的屬性(例如 points, segments, width)發(fā)生變化時(shí),使用 g3d.invalidateCachedGeometry(data) 來重置 geometry。
// 創(chuàng)建航線時(shí)設(shè)置屬性edge.s({ 'geometry.cache': true}); let ui = g3d.getData3dUI(this.selectedEdge);ui.shapeModel = ui.info = null;this.g3d.invalidateData(this.selectedEdge);有效大洲中心添加輔助定位用的立方體
在有效的大洲中心位置添加一個(gè)輔助定位用的立方體,當(dāng)點(diǎn)擊大洲按鈕時(shí),使用 flyTo() 函數(shù)調(diào)整球體視角。
2D/3D互動(dòng)畫線調(diào)用setTimeout
當(dāng) 2D/3D 定位線顯示在面板后,用戶每次移動(dòng)界面,定位線都需要重新計(jì)算和繪制。考慮到移動(dòng)界面觸發(fā)這個(gè)事件的頻率非常高,如果每次都響應(yīng),那么程序?qū)?huì)變得非常繁忙,出現(xiàn)卡頓現(xiàn)象;甚至可能造成事件丟失的情況,比如出現(xiàn)用戶已經(jīng)停止了移動(dòng),線卻沒有畫到位的現(xiàn)象。因此使用 setTimout 保證更新的最短間隔為 50ms,去掉不必要的更新。當(dāng)然這個(gè)間隔可以根據(jù)實(shí)際情況調(diào)整,以降低視覺上的遲鈍感。
this.updateTimer = setTimeout(() => { this.updateTimer = null; if (this.selectedEdge == null) { // 沒有航線被選中 return; } this.getLineEnd(); // 計(jì)算2D/3D 定位線的終點(diǎn) this.updateLine(true); // 繪制定位線}, 50);有了 2D 和 3D 場(chǎng)景,按照文中介紹的思路和邏輯,就可以完成動(dòng)畫的生成,航線數(shù)據(jù)加載,航線可視化,飛機(jī)態(tài)勢(shì)可視化和暴數(shù)據(jù)的實(shí)時(shí)顯示,整個(gè)過程其樂無窮。
?
基于航空大數(shù)據(jù),在本實(shí)例中提到的數(shù)據(jù)分析和可視化的基礎(chǔ)上,還可以挖掘更多的應(yīng)用場(chǎng)景,比如航班運(yùn)行數(shù)據(jù)可視化,飛機(jī)數(shù)據(jù)實(shí)時(shí)展示,航班歷史數(shù)據(jù)分析,應(yīng)急航線調(diào)度等。如果想了解更多工業(yè)互聯(lián)網(wǎng) 2D, 3D 可視化應(yīng)用案例,可參考《分享數(shù)百個(gè) HT 工業(yè)互聯(lián)網(wǎng) 2D 3D 可視化應(yīng)用案例之 2019 篇》,或者關(guān)注公眾號(hào)更多資訊。
?
?
總結(jié)
以上是生活随笔為你收集整理的基于HTML5 WebGL 与 GIS 的智慧机场大数据可视化分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: steelray project vie
- 下一篇: 2017年html5行业报告,云适配发布