FlightAware获取航迹数据,修正及入库。

数据获取

1.航迹数据从FlightAware - 航班跟踪/航班状态/飞行跟踪网站上获取。

2.点击地图上的一架飞机,进入到航班信息页面。

3.完整航迹数据一般从Past Flights里取;点击View Track Log,获取轨迹报表。

​ 4.直接复制即可,复制到Excel表。

表格修正

1.航班对应的机场编号和中文名称,要自己手动添加。 

2.表头添加总行数

​ 3.表格内部有白格就添加NULL

4.米数部分如果有空白,要手动修正连贯;头尾数据空白,填上合适的值。

数据入库

数据导入用C#读取Excel表;导入到postgres。

表结构

表格类和点集类
namespace ImportAirs
{
    internal class AircraftTrailTable
    {
        public Guid id { get; set; }
        /// <summary>
        /// 创建时间
        /// </summary>
        public DateTimeOffset creationTime { get; set; }
        /// <summary>
        /// 更新时间
        /// </summary>
        public DateTimeOffset updateTime { get; set; }
        /// <summary>
        /// 备注
        /// </summary>
        public string remarks { get; set; }
        /// <summary>
        /// 途径点集合
        /// </summary>
        public List<WayPointSet> wayPointSet = new List<WayPointSet>();
        /// <summary>
        /// 起点
        /// </summary>
        public string startingPoint { get; set; }
        /// <summary>
        /// 终点
        /// </summary>
        public string endPoint { get; set; }
    }

    public class WayPointSet
    {
        public double time { get; set; }
        public double longitude { get; set; }
        public double latitude { get; set; }
        public double altitude { get; set; }
    }
}

主代码

1.这里要自己根据表格的sheet数量设定循环数。

2.整体代码

using ImportAirs;
using Npgsql;
using NPOI.SS.Formula.Functions;
using NPOI.SS.UserModel;
using System.Collections.Generic;


string conStr = "Host=localhost;Port=5432;Username=postgres;Password=123456;Database=yyfz";
NpgsqlConnection conn = new NpgsqlConnection(conStr);



string importExcelPath = "C:\\Users\\10412\\Desktop\\trail\\" + "trail.xlsx";
//System.IO.Directory.GetCurrentDirectory() + "\\" + "亚洲飞机航迹数据.xlsx";//"D:\\数据\\航班信息\\云南昆明.xlsx";//System.IO.Directory.GetCurrentDirectory() + "\\3dModels\\" + fileName;
//string exportExcelPath = "E:\\export.xlsx";

IWorkbook workbook = WorkbookFactory.Create(importExcelPath);

conn.Open();
for (int h = 0; h < 1; h++)
{
    ISheet sheet = workbook.GetSheetAt(h);//获取第一个工作薄

    string insertStr = $"INSERT INTO public.\"aircraftTrailTable_copy1\"" +
    $"( \"id\", \"creationTime\",\"updateTime\",\"startingPoint\",\"endPoint\",\"remarks\",\"wayPointSet\") " +
    $"VALUES(@id,@creationTime,@updateTime,@startingPoint,@endPoint,@remarks,@wayPointSet)";

    NpgsqlCommand cmd = new NpgsqlCommand(insertStr, conn);


    AircraftTrailTable cameraParameters = new AircraftTrailTable();

    string str = ((IRow)sheet.GetRow(0)).Cells[0].ToString();
    int number = Convert.ToInt32(((IRow)sheet.GetRow(0)).Cells[0].ToString());

    cameraParameters.startingPoint = ((IRow)sheet.GetRow(1)).Cells[0].ToString();
    cameraParameters.endPoint = ((IRow)sheet.GetRow(number - 2)).Cells[0].ToString();
    cameraParameters.remarks = ((IRow)sheet.GetRow(number - 1)).Cells[0].ToString();



    for (int i = 2; i < number - 2; i++)
    {



        IRow row = (IRow)sheet.GetRow(i);//获取第一行

        if (!(row.Cells[0].ToString()).Contains("Gap in available data"))
        {

            WayPointSet wayPointSet = new WayPointSet();

            wayPointSet.longitude = Convert.ToDouble(row.Cells[2].ToString());
            wayPointSet.latitude = Convert.ToDouble(row.Cells[1].ToString());
            //string strss = row.Cells[6].ToString();//Estimated
            wayPointSet.altitude = Convert.ToDouble(row.Cells[6].ToString());

            cameraParameters.wayPointSet.Add(wayPointSet);
        }


    }



    string[] array = new string[cameraParameters.wayPointSet.Count];

    Guid insertId = Guid.NewGuid();
    cmd.Parameters.AddWithValue("@id", insertId);
    cmd.Parameters.AddWithValue("@creationTime", DateTime.UtcNow);
    cmd.Parameters.AddWithValue("@updateTime", DateTime.UtcNow);
    cmd.Parameters.AddWithValue("@startingPoint", cameraParameters.startingPoint);
    cmd.Parameters.AddWithValue("@endPoint", cameraParameters.endPoint);
    cmd.Parameters.AddWithValue("@remarks", cameraParameters.remarks);


    for (int i = 0; i < cameraParameters.wayPointSet.Count; i++)
    {
     array[i] = cameraParameters.wayPointSet[i].longitude.ToString() + "," + cameraParameters.wayPointSet[i].latitude.ToString() + "," + cameraParameters.wayPointSet[i].altitude.ToString();
    }

    cmd.Parameters.AddWithValue("@wayPointSet", array);
    cmd.ExecuteNonQuery();

    //string paht = @"C:\Users\hy\Desktop\新建文件夹 (4)\chongQingBeiJingPath.json";
}
conn.Close();

Console.WriteLine("成功!");

算法修正

算法修正主要是为了去除航迹中的骤变点,包括经纬度修正和高度修正;用C#实现。

经纬度修正

1.飞行过程中三点向量夹角大于等于90°,判断轨迹发生急转。

2.坐标及角度余弦获取代码

        double Ax = Convert.ToDouble(b[0]) - Convert.ToDouble(a[0]);
        double Ay = Convert.ToDouble(b[1]) - Convert.ToDouble(a[1]);

        double Bx = Convert.ToDouble(c[0]) - Convert.ToDouble(b[0]);
        double By = Convert.ToDouble(c[1]) - Convert.ToDouble(b[1]);

        double cos = (Ax * Bx + Ay * By) / (Math.Sqrt(Ax * Ax + Ay * Ay) * Math.Sqrt(Bx * Bx + By * By));
        double angle = Math.Acos(cos);

高度修正

1.提升或降低中间点的高度,使高度平滑。

//高度修改
if (Convert.ToDouble(b[2]) < Convert.ToDouble(a[2]) && Convert.ToDouble(b[2]) < Convert.ToDouble(c[2])|| Convert.ToDouble(b[2]) > Convert.ToDouble(a[2]) && Convert.ToDouble(b[2]) > Convert.ToDouble(c[2]))
            {
                b[2] = Convert.ToString((Convert.ToDouble(a[2])+ Convert.ToDouble(c[2]))/4);
            }

代码部分

表格类和点集类

沿用数据导入部分的。

点类
namespace ConsoleApp1
{
    internal class PointD
    {

        public double X { get; set; }
        public double Y { get; set; }

        public PointD(double x, double y)
        {
            X = x;
            Y = y;
        }

        public PointD() { }

    }
}
数据库操作类
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Npgsql;

namespace ConsoleApp1
{
    internal class CommRatePgSql
    {
        //测试AppSetting操作
        static string ConnectionString = "Host=localhost;Port=5432;Username=postgres;Password=123456;Database=yyfz;"; //AppSetting.app(new string[] { "AppSettings", "ConnectionStrings" });

        /// <summary>
        /// 查询并返回结果集DataTable(无参数)
        /// </summary>
        /// <param name="sql"></param>
        /// <returns></returns>
        public static DataTable ExecuteQuery(string sql)
        {
            //string connStr = "Host=10.21.1.153;Port=5432;Username=postgres;Password=123456;Database=yyfz";
            NpgsqlConnection sqlConn = new NpgsqlConnection(ConnectionString);
            DataTable ds = new DataTable();
            try
            {
                using (NpgsqlDataAdapter sqldap = new NpgsqlDataAdapter(sql, sqlConn))
                {
                    sqldap.Fill(ds);
                }
                return ds;
            }
            catch (System.Exception ex)
            {
                throw ex;
            }
        }

  

        public static List<T> TableToListModel<T>(DataTable dt) where T : new()
        {
            // 定义集合    
            List<T> ts = new List<T>();

            // 获得此模型的类型   
            Type type = typeof(T);
            string tempName = "";

            foreach (DataRow dr in dt.Rows)
            {
                T t = new T();
                // 获得此模型的公共属性      
                PropertyInfo[] propertys = t.GetType().GetProperties();
                foreach (PropertyInfo pi in propertys)
                {
                    tempName = pi.Name;  // 检查DataTable是否包含此列    

                    if (dt.Columns.Contains(tempName))
                    {
                        // 判断此属性是否有Setter      
                        if (!pi.CanWrite) continue;

                        object value = dr[tempName];
                        if (value != DBNull.Value)
                            pi.SetValue(t, value, null);
                    }
                }
                ts.Add(t);
            }
            return ts;
        }
    }
}
主代码

从数据库查询waypoint点集列表,每三个点进行一次处理,然后重新插入。

using System.Collections.Generic;
using ConsoleApp1;
using Microsoft.VisualBasic;
using Npgsql;

string conStr = "Host=localhost;Port=5432;Username=postgres;Password=123456;Database=yyfz";
NpgsqlConnection conn = new NpgsqlConnection(conStr);

string selectSql = "SELECT id, remarks, \"wayPointSet\", \"startingPoint\", \"endPoint\", \"creationTime\", \"updateTime\", type\r\n\tFROM public.\"aircraftTrailTable\";";

conn.Open();
List<AircraftTrailTable> aircraftTrailTable = CommRatePgSql.TableToListModel<AircraftTrailTable>(CommRatePgSql.ExecuteQuery(selectSql));

int affectedRows = 0;
for (int i = 0; i < aircraftTrailTable.Count; i++)
{
    Boolean flag = false;
    string trailName = aircraftTrailTable[i].remarks;

    string[]  wayPointSet = aircraftTrailTable[i].wayPointSet.ToArray();
    List<String> pointSetList = new List<String>(wayPointSet);

    int index = 0;
    while(index< pointSetList.Count-1)
    {
        if(index <=0)
        {
            index++;
          continue;
        }

        String[] a = pointSetList[index - 1].Split(',');
        String[] b = pointSetList[index].Split(',');
        String[] c = pointSetList[index + 1].Split(',');

        //String[] a = "2,0,0".Split(',');
        //String[] b = "0,0,0".Split(',');
        //String[] c = "2,2,0".Split(',');

        double Ax = Convert.ToDouble(b[0]) - Convert.ToDouble(a[0]);
        double Ay = Convert.ToDouble(b[1]) - Convert.ToDouble(a[1]);

        double Bx = Convert.ToDouble(c[0]) - Convert.ToDouble(b[0]);
        double By = Convert.ToDouble(c[1]) - Convert.ToDouble(b[1]);

        double cos = (Ax * Bx + Ay * By) / (Math.Sqrt(Ax * Ax + Ay * Ay) * Math.Sqrt(Bx * Bx + By * By));
        double angle = Math.Acos(cos);


        if (angle>=Math.PI/2)
        {
            //夹角移除
            pointSetList.RemoveAt(index);
            flag = true;
        }
        else
        {
            //高度修改
            if (Convert.ToDouble(b[2]) < Convert.ToDouble(a[2]) && Convert.ToDouble(b[2]) < Convert.ToDouble(c[2])|| Convert.ToDouble(b[2]) > Convert.ToDouble(a[2]) && Convert.ToDouble(b[2]) > Convert.ToDouble(c[2]))
            {
                b[2] = Convert.ToString((Convert.ToDouble(a[2])+ Convert.ToDouble(c[2]))/4);
                pointSetList[index] = b[0]+","+b[1]+","+b[2];
            }
            index++;
            flag = true;
        }
    }

    if (flag)
    {
        affectedRows++;
    }
    String insertWay = "{";
    pointSetList.ForEach(x => { insertWay += '"'+ x + '"'+','; });
    insertWay = insertWay.TrimEnd(',');
    insertWay = insertWay + "}";

    String updateSql = "UPDATE public.\"aircraftTrailTable\" SET \"wayPointSet\"= '" + insertWay + "'WHERE remarks ='"+ trailName +"';";
    CommRatePgSql.ExecuteQuery(updateSql);
}


Console.WriteLine(affectedRows);

python爬虫

从FlightAware网站上直接复制的轨迹数据是飞机运行的真实轨迹,因而会有折返和白点出现。      可以用python抓取航班预定的轨迹线路,都是平滑的。

url拼接

1.拼接携带航班代码的url,获取历史起止机场。

url = f"https://flightaware.com/live/flight/{planecode}/history"

2.从url的标签页面中获取飞机起止机场,并拼接到url1中。

 url1 = "https://flightaware.com"
        page = requests.get(url)
        soup = BeautifulSoup(page.content, 'html.parser')
        tr_tags = soup.find_all('tr')
        for tag in tr_tags:
            if('data-target' in tag.attrs):
                count = tag.attrs.get("data-target").count('/')-1
                index = 0
                while index< len(tag.attrs.get("data-target")):
                    if(tag.attrs.get("data-target")[index]=='/'):
                      count = count-1
                      index = index+1
                      if(count ==0):
                          url1 = url +"/" +tag.attrs.get("data-target")[index:len(tag.attrs.get("data-target"))]
                          break
                    else:
                        index = index+1

3.从url1标签页面“trackpollGlobals”关键字中获取token,拼接得到含有完整预定航线的api_url。   解析获得完整数据。

        page = requests.get(url1)
        soup = BeautifulSoup(page.content, 'html.parser')
        script_tags = soup.find_all('script')
        for tag in script_tags:
          if "trackpollGlobals" in tag.text:
            
            match = re.search('"TOKEN":"(.*?)"', tag.text)
        
            token = match.group(1)
        
            api_url = f"https://flightaware.com/ajax/trackpoll.rvt?token={token}&locale=en_US&summary=1"
        
            page = requests.get(api_url)
        
            data = json.loads(page.text)

获取航班号列表

FlightAware主网站上有大量航班号,抓取形成航班号列表;                                                            通过循环语句多次调用爬虫代码,实现批量抓取和存Excel。

航班号抓取代码
import requests
import json


url = "https://flightaware.com/ajax/vicinity_aircraft.rvt?&minLon=-122.86468863487244&minLat=37.6171875&maxLon=-114.70250129699707&maxLat=90&token=2613fc20c59cbfc105c8bd09abb8b79fb41bf15b"

page = requests.get(url)
data = json.loads(page.text)

res = list()

#for i in range(len(data.get("features"))):
for i in range(100):
    res.append(data.get("features")[i].get("properties").get("ident"))

print(res)

整体代码

# -*- coding: gbk -*-
from asyncio.windows_events import NULL
from re import findall
import requests
import json
import response
import re
from bs4 import BeautifulSoup
import xlwt


def trailpachong(traillist):
    flag = 0
    workBook = xlwt.Workbook(encoding='utf-8')
    for i in range(len(traillist)):
        planecode = traillist[i]
        url = f"https://flightaware.com/live/flight/{planecode}/history"
        
        url1 = "https://flightaware.com"
        page = requests.get(url)
        soup = BeautifulSoup(page.content, 'html.parser')
        tr_tags = soup.find_all('tr')
        for tag in tr_tags:
            if('data-target' in tag.attrs):
                count = tag.attrs.get("data-target").count('/')-1
                index = 0
                while index< len(tag.attrs.get("data-target")):
                    if(tag.attrs.get("data-target")[index]=='/'):
                      count = count-1
                      index = index+1
                      if(count ==0):
                          url1 = url +"/" +tag.attrs.get("data-target")[index:len(tag.attrs.get("data-target"))]
                          break
                    else:
                        index = index+1
                   
        
        page = requests.get(url1)
        soup = BeautifulSoup(page.content, 'html.parser')
        script_tags = soup.find_all('script')
        for tag in script_tags:
          if "trackpollGlobals" in tag.text:
            
            match = re.search('"TOKEN":"(.*?)"', tag.text)
        
            token = match.group(1)
        
            api_url = f"https://flightaware.com/ajax/trackpoll.rvt?token={token}&locale=en_US&summary=1"
        
            page = requests.get(api_url)
        
            data = json.loads(page.text)
            index1 = page.text.find("flights")
            index2 = page.text.find("activityLog")
            flightname = page.text[index1+11:index2-4]

        if(data.get("flights").get(flightname).get("origin").get("friendlyLocation")is None):
            origin = data.get("flights").get(flightname).get("origin").get("icao")
        else:
            origin = data.get("flights").get(flightname).get("origin").get("friendlyLocation")+"_"+data.get("flights").get(flightname).get("origin").get("icao")

        if(data.get("flights").get(flightname).get("destination").get("friendlyLocation")is None):
            destination = data.get("flights").get(flightname).get("destination").get("icao")
        else:
            destination = data.get("flights").get(flightname).get("destination").get("friendlyLocation")+"_"+data.get("flights").get(flightname).get("destination").get("icao")

        if(len(data.get("flights").get(flightname).get("waypoints"))<1):
            continue
        waypoints = data.get("flights").get(flightname).get("waypoints")
        flag = 1
        #插入Excel
        sheet = workBook.add_sheet(f"sheet{i+1}")
        head = [len(waypoints)+3,'纬度','经度','航向','节','公里/小时','米','爬升率','报告设施']
        head2 = [origin,'NULL','NULL','NULL','NULL','NULL',0,'NULL','NULL']
        tail = [destination,'NULL','NULL','NULL','NULL','NULL','NULL','NULL','NULL']
        tail2 = [origin+'--'+destination,'NULL','NULL','NULL','NULL','NULL','NULL','NULL','NULL']

        for i in head:
            sheet.write(0,head.index(i),i)

        for i in range(9):
            sheet.write(1,i,head2[i])

        colc = len(waypoints) -1
        
        for i in range(colc):
         sheet.write(i+2,0,1111)
         sheet.write(i+2,3,1111)
         sheet.write(i+2,4,1111)
         sheet.write(i+2,5,1111)
         sheet.write(i+2,7,1111)
         sheet.write(i+2,8,1111)

        for i in range(colc):
            jingdu = waypoints[i][0]
            weidu = waypoints[i][1]
            sheet.write(i+2,1,weidu)
            sheet.write(i+2,2,jingdu)

        for i in range(colc):
            if i == 0:
              sheet.write(i+2,6,0)
            elif i == 1:
              sheet.write(i+2,6,500)
            elif i == 2:
              sheet.write(i+2,6,1000)
            elif i == 3:
              sheet.write(i+2,6,5000)
            elif i == colc-4:
              sheet.write(i+2,6,5000)
            elif i == colc-3:
              sheet.write(i+2,6,1000)
            elif i == colc-2:
              sheet.write(i+2,6,500)
            elif i == colc-1:
              sheet.write(i+2,6,0)
            else:
              sheet.write(i+2,6,10000)

        for i in range(9):
            sheet.write(len(waypoints) +1,i,tail[i])

        for i in range(9):
            sheet.write(len(waypoints) +2,i,tail2[i])

    if(flag==0):
        print("数据为空,导入失败")
        return
    savePath = 'C:\\Users\\10412\\Desktop\\trail\\trailnew.xlsx'
    workBook.save(savePath)
    print("导入完成")

traillist = ['WJA1554', 'SWA734', 'EVA691', 'EJA237', 'ASA97', 'N80168', 'SKW3429']


trailpachong(traillist)

数据部署

导出数据表脚本,进行部署和转存。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值