公共交通データ：FlightAware API を使って，飛行データを取得しよう．

## 航空機に関するデータについて

アクセスできる航空のデータについて整理します。

### 航空会社のオープンデータ

##### 公共交通オープンデータ API
https://www.odpt.org/overview/

対象路線:国内線
対象航空会社:JAL, ANA
取得可能なデータ項目
- フライト時刻表
- 出発時刻
- 到着時刻

##### 民間のデータ収集会社のデータ
**FlightAware API**
https://ja.flightaware.com/

対象路線:世界中の全路線
対象航空会社:世界中の全航空会社
取得可能なデータ項目
- AllAirlines
- AllAirports
- GetFlightID
- GetHistoricalTrack

など多数。

**FlightRader24 API**
公式なAPIはなし。

非公式なAPI(pythonのライブラリ)はあるが、利用する際には利用規約をご自身で確認の上、ご利用ください。

https://pypi.org/project/FlightRadarAPI/

## フライトトラッカー とは

ADS-Bの周波数を受信して、飛行機の位置情報を可視化しているサービスの１つです。

代表的なフライトトラッカーのサイトとして、
Flight Aware（アメリカ） や FlightRader24（スウェーデン） などがあります。

[FlightAware vs FlightRadar24: Which is Better? 2024](https://viraltalky.com/flightaware-vs-flightradar24/) By: AuthorViralTalky によると以下のような違いがあります。

「Flightradar24は主にADS-B技術を使用しており、特にアプリの使いやすさや3Dビュー、拡張現実機能などが評価されています。一方、FlightAwareはレーダーデータとADS-Bを組み合わせて使用し、過去のフライトデータやフライトスケジュール情報に強みがあります。」　


私は２つともスマホにインストールしており、どちらにもお世話になっています。

APIを使いたい際には、Flight Awareがおすすめです！
Flight Aware APIでは、自宅などに受信機を設置すると、データ収集の見返りとして「Enterprise User」の会員になれ、APIが無料で利用できます。
興味のある方はぜひ設置しましょう。



:::note info
受信機の設置方法についてはこちらを御覧ください。

FlightAware PiAware ADS-B受信機を自作してみませんか？
https://ja.flightaware.com/adsb/piaware/build/
:::



![FlighAware_home.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274029/d4ddb4ac-f8c0-94a6-1fd8-b79ab425c07c.png)



## Flight Aware API (AeroAPI) とは

「AeroAPIは、ソフトウェア開発者に様々なFlightAwareのフライトデータへのアクセスを許可するシンプルなクエリーベースのAPIです。ユーザーは、現在のデータや履歴データを入手できます。AeroAPIは、正確かつ実用的なデータを届けるRESTful APIです。ForesightTMの導入により、お客様は米国内における航空会社の予想到着時刻の半分以上を司るデータにアクセスできます。」


![FlightAwareAPI.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274029/d1b056f7-4d5a-2889-95ae-89c7c85eac86.jpeg)

左は個人でデータ取得、右は企業などが大規模にデータを取得する際に利用するという違いがあります。
今回は、この左側のFlightXMLというAPIサービスを利用します。


:::note info
FlightAware のAPI（概要・プラン・値段など）
~~https://flightaware.com/commercial/flightxml/~~
https://ja.flightaware.com/commercial/aeroapi/


APIマニュアル（各APIの定義,Input,Resultなど）
~~http://flightxml.flightaware.com/soap/FlightXML2/doc#~~
https://flightaware.com/aeroapi/portal/documentation#overview
:::


# FlightAware API を使ってみよう。

実際に API を使ってみましょう。
その際に以下の２つが必要になります．

・FlightAwareのAPI
　→[Your FlightAware API Keys](https://flightaware.com/commercial/flightxml/key)　のページで API を取得
・FlightAwareのユーザーネーム
　→アカウントを作成する際に，自分で決めたものです．


ではさっそくやってみましょう．


## Flight Number と Tail number の違い

Flight Number： NH171 のようにフライトごとに割り当てられている番号です。
NH171 であれば、サンディエゴ 12:25発-成田 15:05着のフライトであると特定できます。

ただFlight Number だけでは、いつのどの飛行機までか特定できません。

tail number は、登録番号で JA828A のように機体ごとに割り当てられている番号です。
Flight Number/tail number と 出発時間（出発予定時間 OR 実際の出発時間） を組み合わせると、
いつのどのフライトかひとつに特定できます。

今回利用する Flight Numberは Googleで検索するとすぐにできます。

![image_0000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274029/37bba6b9-7464-bd27-2ac2-c58fca67a8d8.png)



## API: GetFlightID
GetFlightIDでは，faFlightID というFlightAwareが割り当てるフライトごとのIDを取得することができます．

次に紹介する GetHistoricalTrack の情報を取得する際に、faFlightID をinputと設定するのでとても大事なステップです。


GetFlightIDでのInput は、飛行機の登録番号と出発時間（予定時間または実際の出発時間）の情報が必要になります。
また出発時間は、Unix Time で表示された時間である必要があります．

Unix Timeを調べるには、[Epoch Converter](https://www.epochconverter.com/)の Epoch & Unix Timestamp Conversion Tools が便利です。

正確な出発時間調べるの大変だしうまくいくか不安ですよね。
なので、基本は出発予定時間を使いましょう。また、時間は、分単位までで大丈夫です。

例：日本時間の 「2019年 1月 1日 1時 1分 0秒」 を変換すると以下のように Unix Timeで返ってきます。

　Epoch timestamp: 1546272060

それでは準備ができたら，以下のプログラムを参考にfaFlightIDを取得してみましょう．

```Python:GetFlightID.py

import requests

#以下２つはFlightAwareユーザーであることの承認に使われます。
username = "your_flightaware_username"  # FlightAware登録時のusername
apiKey = "your_flightaware_apikey"      # FlightAwareのAPI

fxmlUrl = "https://flightxml.flightaware.com/json/FlightXML2/"


inputs = {'ident':'NH171','departureTime':'1566433007'} #FlightNumberとUnixTimeでの現地出発時間
response = requests.get(fxmlUrl + "GetFlightID", params=inputs, auth=(username, apiKey))


if response.status_code == 200:
    print (response.json())
else:
    print ("Error executing request")

```

## API: GetHistoricalTrack

過去の飛行データを取得するためのAPIです．
ちなみに，公式サポートに問い合わせたところ，（エンタープライズアカウントであっても，APIでの過去のデータ取得は）14日前までしか取得できないということでした．

さきほど取得したfaFlightIDをInputとして，以下のプログラムを処理してみましょう．

```Python:GetHistoricalTrack.py

import requests
import json
import pandas as pd

username = "your_flightaware_username"
apiKey = "your_flightaware_apikey"
fxmlUrl = "https://flightxml.flightaware.com/json/FlightXML2/"

# GetFlightID または、　InFlightInfo で取得したFlightAware固有のfaFlightIDを使用する。
registration = {'faFlightID':'ANA171-1560778504-airline-0082'}
response = requests.get(fxmlUrl + "GetHistoricalTrack", params=registration, auth=(username, apiKey))

if response.status_code == 200:
    print (response.json())
else:
    print ("Error executing request")

#JSON形式のデータを取得
json_data = response.json()

#dumpは、dict型を str型にする。
TrackResult=json.dumps(json_data["GetHistoricalTrackResult"])

#loadsはdict型にする。
TrackResult_dict=json.loads(TrackResult)

data=json.dumps(TrackResult_dict["data"])

#pandasでjsonデータを読み込む。
df = pd.read_json(data)

#CSV形式で出力
csv_data = df.to_csv("out/HistricalTrack_01.csv")

```

このような形で、データを整形することができました。

単位は、groundspeed は　knot,
altitude は 100feet です！

<img width="720" alt="スクリーンショット 2019-09-02 13.05.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274029/53df43a5-b3ae-2e1d-9737-bd4402a31360.png">

## プログラムのアップデート

以下の点を修正いたしました。
- 一連のプログラムで取得したFlightIDを用いて、HistricalTrackを取得
- 日付データからUNIX TIMEへの変換
- CSV形式で出力時にINDEXをFALSE
- altitude を *100 して単位をfeetに変更
- コマンドライン引数の設定

コマンドの実行例
python3 [ファイル名] [flight_number] [日時] [UTCとの時差]
`python3 GetFlightID_HistricalTrack.py NH985 2022-04-18-06-25 +9`

```Python:GetFlightID_HistricalTrack.py
import sys
import requests
from datetime import datetime, timezone, timedelta
import json
import pandas as pd

# コマンドライン引数の設定
# ex. python3 GetFlightID_HistricalTrack.py NH6 2022-05-12-17-00 +9
args = sys.argv
print(args[1])
print(args[2])
print(args[3])

flight_number = args[1]
date = args[2]

# JSTとの時差
timeoffset=9-float(args[3])
hour_diff = timedelta(hours=timeoffset)
print(hour_diff)

# 日付変換(LocalTime)
dt = datetime.strptime(date, "%Y-%m-%d-%H-%M")  # strptime(): 文字列から日付、時間への変換
print("local time is %s" % dt)  # ex.2021-12-01 00:00:00

# 日本時間に換算
dt_jst = dt+hour_diff
print("JST is %s" % dt_jst)  # ex.2021-12-01 00:00:00

# timestamp_jst(システムのlocaltimeを検知して、UTCへ)
# localtime(JST)をUTCのepoch_timestampに変更している。
epoch_timestamp = int(dt_jst.timestamp())

print("epoch_timestamp is %s" % epoch_timestamp)


# APIの基礎情報
username = "your_flightaware_username"
apiKey = "your_flightaware_apikey"
fxmlUrl = "https://flightxml.flightaware.com/json/FlightXML2/"

# FlightIDの取得
print("Getting Flight ID...")
print("Flight Number  is {0}".format(flight_number))

inputs = {'ident': flight_number, 'departureTime': epoch_timestamp}
response = requests.get(fxmlUrl + "GetFlightID",
                        params=inputs, auth=(username, apiKey))

if response.status_code == 200:
    flightID = format(response.json()['GetFlightIDResult'])
    print("Flight ID  is {0}".format(flightID))
else:
    print("Error executing request")

# HistricalTrackの取得
print("Getting Histrical Track Data")
registration = {'faFlightID': flightID}
response = requests.get(fxmlUrl + "GetHistoricalTrack",
                        params=registration, auth=(username, apiKey))

if response.status_code == 200:
    print("GetHistoricalTrack Success!")
else:
    print("Error executing request")

json_data = response.json()

# データの整形
# JSONファイルを辞書として読み込み
TrackResult = json.dumps(json_data["GetHistoricalTrackResult"])
# JSON文字列を辞書に変換
TrackResult_dict = json.loads(TrackResult)
# JSONファイルを辞書として読み込み
data = json.dumps(TrackResult_dict["data"])

# csvデータの出力
# pandas.DataFrameとして読み込み
df = pd.read_json(data)
df['altitude_feet'] = df['altitude'] * 100
# csvで出力
csv_data = df.to_csv(
    "out/HistricalTrack_{0}_{1}.csv".format(flight_number, date), index=False)

print("Process Successfully Done!!!")


```
ChatGPT を使ってもう少し整理してみました。

```Python:GetFlightID_HistricalTrack.py
# 変更した点

# コマンドライン引数の不要なprintステートメントを削除
# 日時のJSTへの変換とエポックタイムスタンプの変換の組み合わせ
# PEP 8のスタイルガイドに準拠するため、変数名をsnake_caseに変更しました。
# APIリクエストのエラー処理を簡略化し、エラーが発生した場合はプログラムを終了するようにしました。
# JSONデータから文字列、辞書への不要な変換を削除しました。
# 出力ファイル名の書式を変更し、入力引数との整合性を図りました。
# CSVデータの作成と出力がワンステップで行えます。

import sys
import requests
from datetime import datetime, timezone, timedelta
import json
import pandas as pd

# Parse command line arguments
flight_number, date, time_offset = sys.argv[1:]
time_diff = timedelta(hours=9 - float(time_offset))

# Convert date and time to JST
dt_jst = datetime.strptime(date, "%Y-%m-%d-%H-%M") + time_diff

# Convert JST to epoch timestamp
epoch_timestamp = int(dt_jst.timestamp())

# API credentials and endpoint
username = "your_flightaware_username"
api_key = "your_flightaware_apikey"
fxml_url = "https://flightxml.flightaware.com/json/FlightXML2/"

# Get flight ID
inputs = {'ident': flight_number, 'departureTime': epoch_timestamp}
response = requests.get(fxml_url + "GetFlightID", params=inputs, auth=(username, api_key))
if response.status_code != 200:
    print("Error executing request")
    sys.exit()
flight_id = response.json()['GetFlightIDResult']

# Get historical track data
registration = {'faFlightID': flight_id}
response = requests.get(fxml_url + "GetHistoricalTrack", params=registration, auth=(username, api_key))
if response.status_code != 200:
    print("Error executing request")
    sys.exit()

# Process and output data
track_data = response.json()['GetHistoricalTrackResult']['data']
df = pd.DataFrame(track_data)
df['altitude_feet'] = df['altitude'] * 100
csv_data = df.to_csv(f"out/HistoricalTrack_{flight_number}_{date}.csv", index=False)

print("Process Successfully Done!!!")


```



## （番外編）イレギュラー時の飛行データについて


:::note info
イレギュラー時の飛行データについて

インシデントなどがあった際のデータが取得できるのかを
調査してみました。

結果としては、
墜落事故など重大なインシデントのデータなどは取得できませんでした。
例えば、亡くなった方がいないような急降下が発生したなどのフライトは取得することができました。
:::

## 取得したデータを可視化してみよう

https://kepler.gl/

から、インストラクションに従ってファイルをアップロードしてください。

![00625404-D4BD-4D05-93B8-5AF78B8D86F0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274029/54a9f72e-cad0-e6a3-57cc-7423b5db6423.png)

自動で可視化されます。

![0842119E-DB80-4236-AA01-B40364817C10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274029/9c013c21-890f-baac-192d-ebe86d14ced8.png)

## 注意したいポイント

FlightAware PiAware ADS-B Ground Stationの設定に関する注意ポイントを紹介します。

https://ja.flightaware.com/adsb/piaware/build/

私がハマってしまい、数ヶ月悩んでいた部分です。

:::note info
2.4GHzの11チャンネル以下に設定すること
:::

:::note info
Wifi(2.4Ghz)のパスワードに、数字、文字以外のキャラクターがある際には、パスワードをダブルクォーテーションで囲む必要がある。
:::

:::note info
自動での関連付けがうまく行かない場合、手動でもPiAwareを登録することができます。

「PiAwareを手動で関連付けするには、ルーターの管理画面でIPアドレスを検索し、同じネットワーク上で割り当てられたIPアドレスにアクセスしてください。PiAwareデバイスを関連付けするためのリンクが表示されます。」
:::

手動でも関連付けができるんですね！！！

ちなみに、以下のような画面が現れ、
「Go to SkyAware Map」の上のリンク（Claim this feeder to ...）をクリックすると、PiAwareとの関連付けが行われます。
![8AC1357B-9812-4FF6-B3C4-0B557EAD0B0C.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274029/11cab28b-4e19-59ac-5028-05bebb2ff169.png)



## まとめ
最後までお読みいただきありがとうございます。

うまくいきましたら，ぜひ「いいね」おねがいしまーす．