在機場使用的空管系統中,飛機的速度矢量線差不多是這樣的:
模擬飛機飛行時的速度矢量線,這里就大概做個類似效果:
什么叫速度矢量線呢,個人理解就是根據飛機當前速度和航向預測它在未來一段時間內的飛機軌跡,以此來監測飛機是否偏離。
如何運行代碼已經在上一篇博客《動態加載JSON數據模擬航跡線》講過了。
在這個模擬DEMO中,主要存在四個圖層,分別為地圖底圖、航跡線圖層、速度矢量線圖層、飛機圖層(我用的點代替飛機圖標),因為我自身有一個需求就是控制航跡和速度矢量線的顯示與隱藏,這跟本文沒多大關系。
技術准備
常規的數學公式都知道,路程 = 速度 * 時間,在地圖上的一些相關計算,查閱了一些資料,並翻譯了相關的計算博文《(譯)計算距離、方位以及更多經緯度之間的點》,並Google到了一個相關的技術博客,只不過這是基於OL 2的一些例子,但是我從中得到了一些靈感。
1.實現
在上一篇博文的代碼基礎上添加,相關都做了詳細注釋
2.圖層以及樣式
/** * @name: 相關樣式和圖層:點 航跡線 飛機 標牌 * @test: test font * @msg: * @param {type} * @return: */ //樣式 let point_style = new ol.style.Style({//預測點 image:new ol.style.Circle({ radius:2, snapToPixel:false, fill:new ol.style.Fill({ color:'#333' }), stroke:new ol.style.Stroke({ color:"#333", }) }) }) let route_style = new ol.style.Style({//航跡線 stroke:new ol.style.Stroke({ width:2, color: 'yellow' }), zIndex:2 }); let vel_style = new ol.style.Style({//速度矢量線 stroke:new ol.style.Stroke({ color: '#333', width: 1, }) }); //創建軌跡線 let trackLine = new ol.geom.LineString([]); //速度矢量線 let velLine = new ol.geom.LineString([]); //矢量圖層層 let aircfaftLayer = new ol.layer.Vector({//飛機圖層 source:new ol.source.Vector(), updateWhileInteracting: true, updateWhileAnimating: true }) let flightTrackLayer = new ol.layer.Vector({//航跡線圖層 source:new ol.source.Vector({ features:[ new ol.Feature({ geometry:trackLine }) ] }), style:route_style, updateWhileInteracting: true }); let velLayer = new ol.layer.Vector({//速度矢量線圖層
3.初始化地圖
/* * @name: 初始化地圖 * @description: * @param {type} none * @return: */ let center = new ol.proj.fromLonLat([104.06250000000001, 30.65681556429287]); //地圖 let map = new ol.Map({ //圖層順序自下而上 layers: [ new ol.layer.Tile({ source: new ol.source.OSM({ }) }),flightTrackLayer,velLayer,aircfaftLayer, ], renderer: 'canvas', target: 'map', view: new ol.View({ center: center, zoom: 6 }) }); /** * @name: 飛機 標牌 樣式 * @description: * @param {type} none * @return: */ //標牌疊加層 + 指引線 let markerEl = document.getElementById('geo-marker');//標牌 let marker = new ol.Overlay({ positioning: 'bottom-center', stopEvent: false, dragging: false, offset: [0, -10], element: markerEl, stopEvent: false }); map.addOverlay(marker); //飛機樣式 以點代替飛機圖片 function createGoodStyle() { return new ol.style.Style({ image:new ol.style.Circle({//點狀模擬飛機 radius:6, snapToPixel:false, fill:new ol.style.Fill({ color:'yellow' }), stroke:new ol.style.Stroke({ color:"#333", width:2 }) }) }); } //設置地圖中心 let centerAir = val => { map.getView().setCenter(val); }
4.加載數據
/** * @name: 加載數據 * @description: * @param {type} none * @return: */ //添加飛機 + 更新位置 const KTS2KPH = 1.85200;//1 knot (kt) = 1.85200 kilometer per hour (kph) const NM2KM = 1.852; const pred_secs = 5 * 60; //5分鍾 const bearing = 90; //航向 let coords = [], speedMH = [], flightData, intervalId, interval = 1000, i = 0; let from , to, distance_whole, tiem_whole, distance_cur, point_cur, point_next; const url = './data/openflights/vel.json'; let theAirplane = new ol.Feature([]); $('.startAnimate').click(() => { //加載坐標數據 $.ajax({ url: url, dataType: 'json', success: function (response) { console.log("飛行開始"); flightData = response.data; coords = flightData.map(obj => {//坐標集合 return obj[0]; }) speedMH = flightData.map(obj => {//速度集合 return obj[1]; }) //飛機 theAirplane.setId(response.aircraftNum); theAirplane.setStyle(createGoodStyle()); aircfaftLayer.getSource().addFeature(theAirplane); //預測點 let vel_point = new ol.Feature([]); vel_point.setStyle(point_style); velLayer.getSource().addFeature(vel_point); //模擬飛行 intervalId = setInterval(() => { let position; position = ol.proj.fromLonLat(coords[i]); //標牌 marker.setPosition(position); markerEl.innerHTML = response.aircraftNum;//簡標牌 //飛機 theAirplane.setGeometry(new ol.geom.Point(position)); //航跡 let point = new ol.proj.transform(coords[i], 'EPSG:4326', 'EPSG:3857'); trackLine.appendCoordinate(point); //以飛機當前位置為地圖中心 centerAir(position); //速度矢量線 from = new LatLon(coords[0][1], coords[0][0]);//起點 to = new LatLon(coords[coords.length - 1][1], coords[coords.length - 1][0]);//終點 // console.log(from, to); distance_whole = from.distanceTo(to) / 1000;// km 總里程 tiem_whole = distance_whole / speedMH[i] / KTS2KPH;// hour 總耗時 // console.log(distance_whole, tiem_whole); point_cur = new LatLon(coords[i][1], coords[i][0]);//當前坐標 distance_cur = KTS2KPH * speedMH[i] * ( pred_secs * interval / 3600); //已經過里程 point_next = point_cur.destinationPoint(distance_cur, bearing); //預測5分鍾后所在的坐標點 // console.log( distance_whole, tiem_whole, distance_cur, point_next); let pointCur = ol.proj.fromLonLat([point_cur._lon, point_cur._lat]); let pointNext = ol.proj.fromLonLat([point_next._lon, point_next._lat]); let pointNextArray = Array(); pointNextArray.push(pointNext); console.log(pointNextArray); //預測點 vel_point.setGeometry(new ol.geom.Point(pointNext)); //速度矢量線 let velFeature = new ol.Feature(velLine); velFeature.setStyle(vel_style); velLine.setCoordinates([pointCur, pointNext]); //繪制速度矢量線 velLayer.getSource().addFeature(velFeature); i++; if (i === flightData.length) { clearInterval(intervalId); console.log("飛行結束"); } }, interval); } }) })
5.json數據
json數據來源於arc.js,該例子選取的是成都—上海的坐標數據,速度是我自己隨意添加的:
{
"ID": "1",
"aircraftNum": "B000",
"data": [
[[104.06250000000001, 30.65681556429287], "284"],
[[104.23659653944907, 30.67396833485058], "285"],
[[104.4107544999246, 30.690888911014596], "285"],
[[104.58497310591778, 30.70757705503652], "286"],
[[104.75925157857333, 30.724032532190993], "284"],
[[104.93358913572729, 30.740255110788784], "286"],
[[105.10798499194534, 30.75624456218971], "287"],
[[105.28243835856125, 30.772000660815337], "288"],
[[105.45694844371592, 30.787523184161603], "288"],
[[105.63151445239656, 30.80281191281125], "287"],
[[105.80613558647657, 30.817866630446186], "288"],
[[105.98081104475536, 30.8326871238596], "287"],
[[106.15554002299895, 30.847273182967992], "287"],
[[106.33032171398055, 30.861624600823], "286"],
[[106.50515530752187, 30.875741173623137], "285"],
[[106.68003999053437, 30.889622700725297], "287"],
[[106.85497494706121, 30.90326898465615], "285"],
[[107.02995935831927, 30.916679831123393], "288"],
[[107.20499240274177, 30.929855049026738], "287"],
[[107.38007325602092, 30.942794450468945], "286"],
[[107.55520109115115, 30.95549785076639], "285"],
[[107.73037507847249, 30.967965068459744], "287"],
[[107.90559438571445, 30.98019592532436], "286"],
[[108.08085817803996, 30.992190246380456], "288"],
[[108.25616561808987, 31.003947859903253], "287"],
[[108.43151586602752, 31.01546859743276], "285"],
[[108.60690807958395, 31.026752293783623], "286"],
[[108.7823414141029, 31.0377987870545], "285"],
[[108.9578150225866, 31.0486079186376], "287"],
[[109.13332805574153, 31.059179533227734], "285"],
[[109.30887966202457, 31.069513478831404], "286"],
[[109.48446898768944, 31.079609606775563], "285"],
[[109.66009517683327, 31.089467771716325], "287"],
[[109.83575737144373, 31.099087831647413], "285"],
[[110.011454711446, 31.108469647908397], "287"],
[[110.18718633475038, 31.11761308519283], "288"],
[[110.36295137729994, 31.126518011556165], "289"],
[[110.53874897311843, 31.135184298423425], "287"],
[[110.7145782543585, 31.14361182059676], "286"],
[[110.89043835135004, 31.151800456262833], "285"],
[[111.06632839264884, 31.1597500869999], "286"],
[[111.24224750508553, 31.16746059778481], "287"],
[[111.4181948138144, 31.17493187699975], "288"],
[[111.5941694423629, 31.182163816438862], "289"],
[[111.77017051268102, 31.18915631131456], "290"],
[[111.94619714519094, 31.195909260263747], "288"],
[[112.1222484588369, 31.202422565353807], "288"],
[[112.29832357113521, 31.208696132088367], "288"],
[[112.47442159822452, 31.21472986941292], "288"],
[[112.65054165491617, 31.220523689720157], "287"],
[[112.82668285474469, 31.226077508855244], "287"],
[[113.00284431001862, 31.231391246120737], "288"],
[[113.17902513187131, 31.236464824281384], "287"],
[[113.35522443031194, 31.241298169568736], "286"],
[[113.53144131427662, 31.245891211685535], "285"],
[[113.70767489167979, 31.250243883809823], "285"],
[[113.88392426946552, 31.254356122599024], "285"],
[[114.0601885536591, 31.258227868193615], "286"],
[[114.23646684941869, 31.26185906422076], "285"],
[[114.41275826108706, 31.265249657797618], "287"],
[[114.58906189224348, 31.268399599534526], "287"],
[[114.76537684575561, 31.271308843537938], "286"],
[[114.94170222383167, 31.27397734741316], "287"],
[[115.11803712807243, 31.276405072266883], "288"],
[[115.2943806595235, 31.27859198270948], "288"],
[[115.47073191872758, 31.28053804685718], "288"],
[[115.64709000577683, 31.282243236333912], "288"],
[[115.82345402036526, 31.28370752627301], "288"],
[[115.99982306184107, 31.284930895318737], "288"],
[[116.17619622925929, 31.285913325627515], "288"],
[[116.35257262143426, 31.28665480286904], "288"],
[[116.52895133699217, 31.28715531622708], "288"],
[[116.70533147442355, 31.28741485840016], "287"],
[[116.8817121321361, 31.28743342560202], "288"],
[[117.0580924085071, 31.28721101756178], "288"],
[[117.23447140193603, 31.286747637524012], "287"],
[[117.41084821089731, 31.286043292248515], "287"],
[[117.58722193399282, 31.285097992009906], "287"],
[[117.76359167000443, 31.283911750597046], "287"],
[[117.93995651794664, 31.282484585312172], "286"],
[[118.11631557711907, 31.280816516969885], "286"],
[[118.29266794715906, 31.278907569895903], "286"],
[[118.46901272809394, 31.276757771925578], "286"],
[[118.64534902039362, 31.27436715440231], "286"],
[[118.82167592502275, 31.271735752175562], "286"],
[[118.99799254349321, 31.268863603598895], "286"],
[[119.17429797791613, 31.26575075052759], "285"],
[[119.35059133105422, 31.262397238316204], "285"],
[[119.52687170637368, 31.258803115815805], "285"],
[[119.70313820809623, 31.254968435371172], "285"],
[[119.87938994125103, 31.250893252817523], "285"],
[[120.05562601172639, 31.2465776274773], "285"],
[[120.2318455263214, 31.242021622156606], "285"],
[[120.40804759279759, 31.237225303141468], "285"],
[[120.58423131993031, 31.232188740193873], "285"],
[[120.76039581756008, 31.226912006547636], "285"],
[[120.93654019664363, 31.221395178904057], "284"],
[[121.11266356930511, 31.215638337427364], "284"],
[[121.28876504888679, 31.20964156573994], "284"],
[[121.46484375, 31.203404950917395], "283"]
]
}
后續要完成的就是拖拽飛機標牌以及控制圖層的顯示與隱藏
查看源碼:GitHub