在机场使用的空管系统中,飞机的速度矢量线差不多是这样的:
模拟飞机飞行时的速度矢量线,这里就大概做个类似效果:
什么叫速度矢量线呢,个人理解就是根据飞机当前速度和航向预测它在未来一段时间内的飞机轨迹,以此来监测飞机是否偏离。
如何运行代码已经在上一篇博客《动态加载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({ 2, color: 'yellow' }), zIndex:2 }); let vel_style = new ol.style.Style({//速度矢量线 stroke:new ol.style.Stroke({ color: '#333', 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", 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