PR

【Google Earth】GPSフライトログを使って飛行機のパイロット目線の映像を作る

GPSフライトログのCSVファイルをもとに、simplekmlを使ってGoogleEarthで飛行機のパイロット目線の映像を作るPythonプログラムについて説明する

スポンサーリンク

はじめに

フライトログのCSVファイルをもとに、simplekmlを使ってGoogleEarthに飛行軌跡をプロットするPythonプログラムについて説明する

Explore Google Earth
Grab the helm and go on an adventure in Google Earth.

最終的にはこんな感じになる

↓使用するCSVファイルはこれ

longitudelatitudepress_alt
02.56126449.023190
12.56119749.02318-0.74282
22.56111749.02318-1.09067
32.56102249.02318-0.91195
42.56091149.02318-0.07291
52.56078349.023191.558092
62.56063649.023194.10623
72.56046949.023197.685852
82.56028249.023212.39631
92.56007549.023218.31815
102.55984749.0232125.50977
112.55959949.0232134.00473
122.55933249.0232143.80982
132.55904749.023254.90391
142.55874649.0231967.23769
152.5584349.0231880.73434
162.55810249.0231695.29103
172.55776549.02314110.7813
182.5574249.02311127.0579
192.55707149.02308143.9571
202.5567249.02304161.3022
212.55636949.02301178.9084
222.55602249.02297196.5876
232.55567949.02293214.1533
242.55534249.0229231.4247
252.55501449.02287248.2316
262.55469449.02284264.4177
272.55438449.02283279.8445
282.55408349.02282294.3936
292.55379249.02283307.9685
302.55350949.02285320.4962
312.55323449.02288331.9272
322.55296549.02293342.2353
332.55270149.023351.4167
342.5524449.02308359.488
352.5521849.02317366.4843
362.5519249.02328372.4565
372.55165949.02339377.4686
382.55139449.02352381.5941
392.55112549.02366384.9137
402.55085149.02379387.5116
412.55057149.02393389.4731
422.55028549.02407390.8818
432.54999349.0242391.8177
442.54969549.02433392.3552
452.54939349.02444392.5617
462.54908749.02455392.4969
472.54877749.02464392.2123
482.54846749.02472391.7511
492.54815549.02479391.1485
502.54784549.02485390.4324
512.54753649.02489389.6243
522.54723149.02492388.74
532.54692849.02494387.7913
542.5466349.02496386.7866
552.54633749.02497385.732
562.54604849.02499384.6329
572.54576349.025383.494
582.54548349.02503382.3205
592.54520549.02506381.1182
602.54493149.02511379.8943
612.54465949.02517378.6567
622.54438949.02524377.4146
632.5441249.02534376.1777
642.54385349.02546374.9561
652.54358749.02559373.7599
662.54332349.02575372.5983
672.54306249.02593371.4798
682.54280549.02612370.4114
692.54255549.02633369.3982
702.54231349.02656368.4433
712.54208249.0268367.5479
722.54186549.02704366.7109
732.54166549.0273365.9294
742.54148649.02755365.1989
752.5413349.02781364.5136
762.54120249.02806363.8667
772.54110449.02831363.2513
782.54103849.02855362.6603
792.54100749.02877362.0872
802.54101349.02899361.5266
812.54105749.02919360.9743
822.54113849.02938360.4276
832.54125649.02955359.8852
842.5414149.02971359.3476
852.54159949.02985358.8167
862.54181849.02997358.2956
872.54206649.03008357.7884
882.54233749.03017357.2996
892.54262849.03025356.8337
902.54293549.03031356.395
912.54325249.03037355.9868
922.54357549.03041355.6112
932.543949.03044355.2688
942.54422249.03047354.9585
952.54453849.03049354.6773
962.54484549.0305354.4201
972.5451449.03051354.1803
982.54542149.03051353.9499
992.54568849.03051353.7193
1002.5459449.03051353.4786
1012.54617849.03051353.2174
1022.54640249.0305352.9258
1032.54661449.0305352.5947
1042.54681749.0305352.2161
1052.54701249.0305351.7841
1062.54720249.0305351.2947
1072.54738949.0305350.7463
1082.54757749.03051350.14
1092.54776849.03052349.4793
1102.54796349.03054348.7699
1112.54816549.03056348.0199
1122.54837649.03058347.2389
1132.54859549.03061346.4379
1142.54882349.03064345.6284
1152.5490649.03067344.8222
1162.54930749.03071344.0305
1172.5495649.03074343.2636
1182.54982149.03078342.5299
1192.55008649.03081341.8362
1202.55035549.03085341.1865
1212.55062549.03088340.5825
1222.55089549.0309340.0233
1232.55116449.03093339.5051
1242.5514349.03094339.0222
1252.55169249.03096338.5664
1262.55194949.03096338.1283
1272.55220149.03096337.6969
1282.55244849.03096337.2612
1292.55269149.03095336.8099
1302.55292849.03094336.3326
1312.55316249.03092335.8198
1322.55339449.03091335.2641
1332.55362349.0309334.6598
1342.55385149.03088334.0037
1352.55407849.03088333.2952
1362.55430649.03088332.536
1372.55453349.03089331.7302
1382.55476149.03091330.8841
1392.55498949.03094330.0057
1402.55521549.03099329.1044
1412.55543849.03105328.1902
1422.55565749.03112327.2736
1432.55586949.03122326.3645
1442.55607249.03132325.4722
1452.55626449.03145324.6044
1462.55644149.03158323.7674
1472.55660149.03174322.9653
1482.55674249.0319322.1999
1492.5568649.03208321.4709
1502.55695449.03226320.7759
1512.55702149.03245320.1103
1522.5570649.03265319.468
1532.55707149.03285318.8415
1542.55705249.03304318.2226
1552.55700449.03324317.6026
1562.55692749.03343316.9731
1572.55682349.03362316.3266
1582.55669349.0338315.6563
1592.55653849.03397314.9574
1602.55636149.03413314.2267
1612.55616549.03427313.4631
1622.55595149.03441312.6675
1632.55572349.03453311.8429
1642.55548249.03463310.9943
1652.55523149.03473310.1281
1662.55497349.0348309.2521
1672.55470849.03487308.3748
1682.55443849.03492307.5048
1692.55416549.03496306.651
1702.55388949.03498305.8212
1712.55361249.035305.0221
1722.55333349.035304.2589
1732.55305249.03499303.5351
1742.55276949.03498302.8519
1752.55248649.03495302.2084
1762.552249.03492301.6016
1772.55191249.03489301.0265
1782.55162249.03485300.4763
1792.5513349.03481299.9428
1802.55103549.03477299.4169
1812.55073949.03473298.8889
1822.55044249.03469298.3491
1832.55014449.03466297.7886
1842.54984649.03464297.199
1852.54954849.03461296.5739
1862.54925249.0346295.9082
1872.54895849.0346295.1991
1882.54866749.0346294.4462
1892.54837949.03461293.6509
1902.54809649.03463292.8173
1912.54781649.03465291.951
1922.54754149.03468291.0596
1932.5472749.03471290.1519
1942.54700349.03475289.2376
1952.5467449.03479288.3268
1962.54647949.03482287.4293
1972.54622149.03485286.5544
1982.54596449.03487285.7102
1992.54570849.03488284.9033
2002.54545349.03487284.1383
2012.54519849.03485283.418
2022.54494549.03482282.7426
2032.54469249.03476282.1103
2042.54444249.03469281.517
2052.54419649.03459280.9567
2062.54395549.03447280.4218
2072.54372349.03434279.9034
2082.54350149.03418279.3918
2092.54329349.034278.8767
2102.54310249.03381278.3484
2112.54293149.03361277.7975
2122.54278549.03339277.2158
2132.54266649.03317276.5966
2142.54257749.03294275.935
2152.54252249.03271275.2282
2162.54250249.03248274.4755
2172.54251949.03226273.6783
2182.54257449.03205272.8406
2192.54266649.03185271.9678
2202.54279649.03167271.0675
2212.54296149.03149270.1483
2222.54315949.03134269.2202
2232.54338749.0312268.2933
2242.54364149.03108267.3783
2252.54391749.03098266.4851
2262.54420949.03089265.6232
2272.54451449.03082264.8006
2282.54482649.03076264.024
2292.5451449.03071263.2984
2302.54545249.03068262.6265
2312.54575849.03065262.0091
2322.54605449.03063261.445
2332.54633749.03062260.9306
2342.54660649.03062260.4607
2352.5468649.03061260.0279
2362.54709849.03062259.6238
2372.54732149.03062259.2383
2382.54753149.03063258.8608
2392.54772949.03064258.4798
2402.54791949.03065258.0838
2412.54810449.03066257.6614
2422.54828849.03068257.2018
2432.54847349.0307256.6948
2442.54866349.03073256.1312
2452.54886249.03075255.5033
2462.54907249.03078254.8048
2472.54929449.03081254.0308
2482.5495349.03084253.1784
2492.54978149.03086252.2462
2502.55004549.03088251.2349
2512.55032249.0309250.1467
2522.55060949.03091248.9855
2532.55090249.0309247.7569
2542.55119849.03088246.468
2552.55149349.03085245.1271
2562.55178149.03079243.7439
2572.55205749.03072242.3286
2582.55231749.03063240.8924
2592.55255549.03051239.4471
2602.55276649.03037238.0044
2612.55294749.03021236.576
2622.55309449.03003235.1735
2632.55320349.02983233.8075
2642.55327549.02962232.4879
2652.55330749.0294231.2235
2662.553349.02918230.0215
2672.55325549.02895228.8876
2682.55317549.02873227.8254
2692.55306249.02852226.8368
2702.55292149.02833225.9215
2712.55275649.02816225.0772
2722.55257349.02801224.2995
2732.55237649.02789223.582
2742.55217249.02781222.9165
2752.55196649.02776222.2935
2762.55176349.02774221.702
2772.55156949.02777221.1303
2782.5513949.02782220.5662
2792.55122849.02792219.9974
2802.55108849.02804219.412
2812.55097349.02819218.7992
2822.55088549.02836218.149
2832.55082749.02854217.4534
2842.55079949.02874216.7063
2852.55080149.02895215.9035
2862.55083349.02915215.0435
2872.55089649.02934214.1272
2882.55098749.02953213.1575
2892.55110549.02969212.1401
2902.55124949.02984211.0823
2912.55141649.02997209.9934
2922.55160549.03007208.8839
2932.55181349.03015207.765
2942.55203849.03021206.6488
2952.55227849.03024205.5467
2962.55252949.03026204.4701
2972.5527949.03026203.4289
2982.55305749.03025202.4317
2992.55332949.03023201.4853
3002.55360349.03021200.5946
3012.55387549.0302199.7621
3022.55414349.03019198.9881
3032.55440449.0302198.2707
3042.55465449.03022197.6059
3052.55489149.03027196.9882
3062.55511249.03033196.4103
3072.55531249.03042195.8639
3082.55548949.03054195.3404
3092.5556449.03067194.8306
3102.55576149.03083194.3258
3112.55585249.03101193.8178
3122.55590949.03119193.2996
3132.5559349.03139192.7653
3142.55591649.03159192.2106
3152.55586549.0318191.6326
3162.55577849.03199191.0303
3172.55565649.03217190.4039
3182.55550249.03233189.7548
3192.55531749.03247189.0858
3202.55510549.03259188.4
3212.5548749.03267187.7009
3222.55461749.03272186.9921
3232.55435249.03274186.2768
3242.55407949.03272185.5572
3252.55380549.03266184.8347
3262.55353549.03257184.1096
3272.55327649.03245183.3808
3282.55303349.0323182.646
3292.55281149.03212181.9017
3302.55261649.03193181.1435
3312.55245149.03171180.3666
3322.55231949.03149179.5657
3332.55222549.03126178.736
3342.55216849.03103177.8731
3352.55215249.0308176.9738
3362.55217449.03057176.0367
3372.55223549.03036175.062
3382.55233349.03016174.0521
3392.55246749.02998173.0118
3402.55263249.02982171.9483
3412.55282549.02967170.8709
3422.55304449.02955169.7906
3432.55328349.02945168.7202
3442.5535449.02936167.6734
3452.55380949.02929166.6641
3462.55408849.02924165.7058
3472.55437349.02921164.8111
3482.5546649.02919163.9903
3492.55494749.02918163.2515
3502.55523249.02918162.5993
3512.55551249.02918162.0346
3522.55578849.02919161.5543
3532.55605849.02921161.151
3542.55632249.02923160.8132
3552.55658149.02925160.5253
3562.55683549.02927160.2683
3572.55708549.02929160.0205
3582.55733249.02931159.7581
3592.55757849.02933159.4562
3602.55782449.02935159.0901
3612.55807149.02938158.6364
3622.55832149.0294158.0737
3632.55857549.02943157.3844
3642.55883349.02945156.5548
3652.55909749.02948155.5768
3662.55936649.02951154.4475
3672.55964249.02955153.1706
3682.55992349.02958151.7554
3692.56021149.02962150.2175
3702.56050349.02966148.5778
3712.560849.0297146.862
3722.561149.02974145.0992
3732.56140449.02977143.3215
3742.56170949.02981141.5617
3752.56201649.02985139.8529
3762.56232349.02988138.2261
3772.56262949.02991136.7096
3782.56293549.02993135.3273
3792.56323949.02995134.0976
3802.56354149.02997133.0327
3812.56384249.02998132.138
3822.5641449.02998131.4114
3832.56443749.02998130.844
3842.56473349.02997130.42
3852.56502749.02996130.1172
3862.5653249.02995129.9085
3872.56561349.02993129.7627
3882.56590649.02991129.646
3892.56619949.02989129.5234
3902.56649349.02987129.3606
3912.56678749.02985129.1252
3922.56708249.02983128.7883
3932.56737749.02982128.3259
3942.56767349.0298127.7199
3952.5679749.02979126.9587
3962.56826549.02978126.0384
3972.5685649.02977124.9622
3982.56885449.02977123.7407
3992.56914549.02976122.3911
4002.56943249.02975120.9366
4012.56971649.02974119.4055
4022.56999549.02973117.8293
4032.57026949.02971116.2418
4042.57053549.02969114.6774
4052.57079549.02965113.1694
4062.57104649.02961111.7485
4072.57128849.02955110.4418
4082.57152149.02948109.2711
4092.57174349.02939108.2523
4102.57195549.02928107.3946
4112.57215549.02916106.7001
4122.57234349.02902106.1642
4132.57251949.02886105.7751
4142.57268349.02868105.5153
4152.57283349.02848105.3616
4162.5729749.02826105.2868
4172.57309249.02803105.2605
4182.573249.02779105.2504
4192.57329349.02753105.2241
4202.57337149.02727105.1498
4212.57343249.02699104.9976
4222.57347749.02672104.7408
4232.57350449.02644104.3564
4242.57351449.02617103.8259
4252.57350649.02591103.1355
4262.57347949.02565102.2762
4272.57343449.0254101.2434
4282.57336949.02517100.0371
4292.57328649.0249598.66064
4302.57318349.0247597.12018
4312.57306149.0245795.42404
4322.57292149.0244193.58165
4332.57276349.0242791.60278
4342.57258849.0241489.49672
4352.57239549.0240487.27171
4362.57218849.0239684.93443
4372.57196549.0238982.48972
4382.5717349.0238379.94056
4392.57148249.0237977.28817
4402.57122449.0237774.53242
4412.57095749.0237571.67234
4422.57068249.0237368.70683
4432.57040149.0237365.63538
4442.57011649.0237262.45895
4452.56982749.0237259.18078
4462.56953749.0237255.80708
4472.56924649.0237152.34767
4482.56895549.0237148.81642
4492.56866649.023745.23147
4502.56837949.0236941.61519
4512.56809549.0236737.99392
4522.56781449.0236534.39742
4532.56753849.0236430.85811
4542.56726649.0236127.41008
4552.56699949.0235924.08795
4562.56673749.0235720.92557
4572.5664849.0235517.95478
4582.56622849.0235215.20407
4592.56598149.023512.69742
4602.56573949.0234910.45322
4612.56550349.023478.483496
4622.56527249.023466.793307
4632.56504649.023455.380534
4642.56482749.023444.235935
4652.56461349.023443.343557
4662.56440549.023442.681455
4672.56420449.023432.22271
4682.56400949.023431.936681
4692.56382249.023431.790431
4702.56364249.023431.75028
4712.5634749.023431.783363
4722.56330649.023421.859156
4732.56315149.023411.950854
4742.56300449.02342.036537
4752.56286749.023392.100067
4762.56273849.023382.131643
4772.56261849.023362.127998
4782.56250749.023342.092205
4792.56240549.023322.03311
4802.56231149.02331.964406
4812.56222649.023281.903398
4822.56214849.023261.869526
4832.56207749.023241.882721
4842.56201249.023221.961688
4852.56195449.02322.122211
4862.56190249.023192.375588
4872.56185449.023182.727292
4882.56181149.023173.175941
4892.56177349.023163.712667
4902.56173749.023164.320932
4912.56170649.023164.976838
4922.56167749.023165.649931
4932.56165149.023176.304511
4942.56162749.023176.901382
4952.56160449.023187.400002
4962.56158449.023187.760931
4972.56156549.023197.948487
4982.56154649.023197.933473
4992.56152749.02327.695859
5002.56150849.02327.227279
5012.56148749.02326.533209
5022.56146449.02325.634715
5032.56143749.02324.569646
5042.56140549.02323.3932
5052.56136649.023192.177796
5062.5613249.023191.012213

経度,緯度,高度が10進法で記録されている
度分秒と十進度の相互換算

↓ソースコードはここ

mtkbirdman.com/igc at master · mtkbirdman/mtkbirdman.com
mtk_birdman's blog @mtk_birdman. Contribute to mtkbirdman/mtkbirdman.com development by creating an account on GitHub.

↓公式ドキュメントはこれ

(飛行経路の描画)

Linestring Tutorial — SIMPLEKML 1.3.6 documentation
Geometries — SIMPLEKML 1.3.6 documentation

(フライト映像の作成)

Touring in KML  |  Keyhole Markup Language  |  Google for Developers
Tour — SIMPLEKML 1.3.6 documentation

それではいってみよう

simplekmlのインポート

simplekmlをインポートする

PS C:\Users\mtkbirdman\Desktop\igc> pip install simplekml
Collecting simplekml
  Downloading https://files.pythonhosted.org/packages/15/e4/c333a93b7e3346437ad1ff42b8e362b853eb405ad6243ab6163f9af2a460/simplekml-1.3.6.tar.gz (52kB)
    100% |████████████████████████████████| 61kB 5.1MB/s
Installing collected packages: simplekml
  Running setup.py install for simplekml ... done
Successfully installed simplekml-1.3.6

pipを使えば特に問題なく実行できる

ソースコード

ソースコード全文はこれ

https://github.com/mtkbirdman/mtkbirdman.com/blob/master/igc/IGCfileToGoogleEarthTour.py

import os
import numpy as np
import pandas as pd
import simplekml

# Import CSV file from current directory
path = '2023-02-22-XNA-9AD179D6D6F04DDD95474658245E6D4F-01'+'.csv'
df_B = pd.read_csv(os.path.join(os.getcwd(),path),header=0,index_col=0)

# Convert dataframe to list of tuple
tuple_B =  [tuple(x) for x in df_B[['longitude','latitude','press_alt']].values]

# Convert longitude/latitude to the distance from the point of beginning
dt = 1
lat = np.radians(df_B.latitude.values-df_B.latitude.values[0])*6378137
lng = np.radians(df_B.longitude.values-df_B.longitude.values[0])*6378137
alt = df_B.press_alt.values

# Calculate the angles of yaw (heading), pitch (tilt) and roll from the coordinate
df_B['heading'] = 90
df_B['tilt'] = 90
df_B['roll'] = 0
for n in np.arange(len(df_B)):
  if dt-1 < n and n < len(df_B)-dt:
    a = lat[n+dt]-lat[n-dt]
    b = lng[n+dt]-lng[n-dt]
    c = lng[n-dt]*lat[n+dt]-lng[n+dt]*lat[n-dt]
    d = np.sqrt(a*a+b*b)
    e = np.abs(-a*lng[n]+b*lat[n]+c)/d
    # Calculate turn radius and rate
    R = (d*d+4*e*e)/(8*e)
    Omega = np.arcsin((d/2)/R)/dt
    # Calculate yaw, pitch and roll
    df_B.loc[n,'heading'] = -np.rad2deg(np.arctan2(a,b))-(-90)
    df_B.loc[n,'tilt'] = 90+np.rad2deg(np.arctan2(alt[n+dt]-alt[n-dt],d))
    df_B.loc[n,'roll'] = np.rad2deg(np.arctan2(R*Omega*Omega,9.81))
    # Modification
    df_B.loc[n,'heading'] = df_B.heading[n]-360 if df_B.heading[n] > 180 else df_B.heading[n]
    df_B.loc[n,'roll'] = -df_B.roll[n] if -a*lng[n]+b*lat[n]+c < 0 else df_B.roll[n]

# Insert values to beginning and end of dataframe
df_B.loc[0:dt,'heading'] = df_B.heading[dt]
df_B.loc[0:dt,'tilt'] = df_B.tilt[dt]
df_B.loc[len(df_B)-dt:,'heading'] = df_B.heading[len(df_B)-dt-1]
df_B.loc[len(df_B)-dt:,'tilt'] = df_B.tilt[len(df_B)-dt-1]

## Create an instance of Kml
kml = simplekml.Kml(open=1)

# Create a linestring
linestring = kml.newlinestring(name="A Sloped Line")
linestring.coords = tuple_B
linestring.altitudemode = simplekml.AltitudeMode.relativetoground
linestring.extrude = 0
linestring.style.linestyle.width = 3
linestring.style.linestyle.color = simplekml.Color.red

# Create a tour and attach a playlist to it
tour = kml.newgxtour(name="Take-Off!")
playlist = tour.newgxplaylist()

n=0
for _ in df_B.iterrows():
    # Attach a gx:FlyTo to the playlist
    flyto = playlist.newgxflyto()
    flyto.camera.longitude = df_B.longitude[n]
    flyto.camera.latitude = df_B.latitude[n]
    flyto.camera.altitude = df_B.press_alt[n]+1.
    flyto.camera.heading = df_B.heading[n]
    flyto.camera.tilt = df_B.tilt[n]
    flyto.camera.roll = df_B.roll[n]
    flyto.gxduration = 1.
    flyto.gxflytomode = 'smooth'
    n+=1

# Attach a gx:Wait to the playlist to give the gx:AnimatedUpdate time to finish
wait = playlist.newgxwait(gxduration=3)

# Save to file
kml.save(os.path.join(os.getcwd(),path[:-4]+".kml"))

ひとつずつ解説していく

# Import CSV file from current directory

# Import CSV file from current directory
path = '2023-02-22-XNA-9AD179D6D6F04DDD95474658245E6D4F-01'+'.csv'
df_B = pd.read_csv(os.path.join(os.getcwd(),'csv',path),header=0,index_col=0)

# Convert dataframe to list of tuple
tuple_B =  [tuple(x) for x in df_B[['longitude','latitude','press_alt']].values]

CSVファイルをpd.read_csv()でデータフレームdf_Bとして読み込み,リスト内法表記を使ってタプルのリストtuple_Bに変換している

タプルのリストはGoogle Earthに飛行軌跡をプロットするのに必要になる

pandas.read_csv — pandas 1.5.3 documentation

# Convert longitude/latitude to the distance from the point of beginning

# Convert longitude/latitude to the distance from the point of beginning
dt = 1
lat = np.radians(df_B.latitude.values-df_B.latitude.values[0])*6378137
lng = np.radians(df_B.longitude.values-df_B.longitude.values[0])*6378137
alt = df_B.press_alt.values

後ほどオイラー角(ヨー/ピッチ/ロール)の計算に使用するため,度数表記の緯度経度をフライトの始点からの距離[m]に変換する

簡単のため地球を完全な球体だとし,地球の半径6,378,237mと三角関数の微小近似を使って計算している

# Calculate the angles of yaw (heading), pitch (tilt) and roll from the coordinate

# Calculate the angles of yaw (heading), pitch (tilt) and roll from the coordinate
df_B['heading'] = 90
df_B['tilt'] = 90
df_B['roll'] = 0
for n in np.arange(len(df_B)):
  if dt-1 < n and n < len(df_B)-dt:
    a = lat[n+dt]-lat[n-dt]
    b = lng[n+dt]-lng[n-dt]
    c = lng[n-dt]*lat[n+dt]-lng[n+dt]*lat[n-dt]
    d = np.sqrt(a*a+b*b)
    e = np.abs(-a*lng[n]+b*lat[n]+c)/d
    # Calculate turn radius and rate
    R = (d*d+4*e*e)/(8*e)
    Omega = np.arcsin((d/2)/R)/dt
    # Calculate yaw, pitch and roll
    df_B.loc[n,'heading'] = -np.rad2deg(np.arctan2(a,b))-(-90)
    df_B.loc[n,'tilt'] = 90+np.rad2deg(np.arctan2(alt[n+dt]-alt[n-dt],d))
    df_B.loc[n,'roll'] = np.rad2deg(np.arctan2(R*Omega*Omega,9.81))
    # Modification
    df_B.loc[n,'heading'] = df_B.heading[n]-360 if df_B.heading[n] > 180 else df_B.heading[n]
    df_B.loc[n,'roll'] = -df_B.roll[n] if -a*lng[n]+b*lat[n]+c < 0 else df_B.roll[n]

フライト映像をそれっぽく見せるため,フライトログからオイラー角(ヨー/ピッチ/ロール)を計算し,それに応じて画面を傾けて表示する

具体的な計算を行うために,座標系を以下のように定義する

ここで,下図の各値は以下のように計算できる

\begin{align}
&a=y_{n+1}-y_{n-1} \\\\
&b=x_{n+1}-x_{n-1} \\\\
&c=x_{n-1}y_{n+1}-x_{n+1}y_{n-1}\\\\
&d=\sqrt{\left(x_{n+1}-x_{n-1}\right)^{2}+\left(y_{n+1}-y_{n-1}\right)^{2}}=\sqrt{a^2+b^2}\\\\
&e=\frac{\left|-ax_{n}+by_{n}+c\right|}{\sqrt{a^{2}+b^{2}}}=\frac{\left|-ax_{n}+by_{n}+c\right|}{d} \\\\
&R=\frac{d^{2}+4e^{2}}{8e} \\\\
&\theta=\arcsin{\left(\frac{d/2}{R}\right)}\simeq \frac{d}{2R} \\\\
&\Omega=\frac{\theta}{\Delta t}
\end{align}

XY平面上において,2点(\(x_{n+1}, y_{n+1}\)),(\(x_{n-1}, y_{n-1}\))を通る直線の式は以下の式で表される

\begin{align}
&y-y_{n-1}=\frac{y_{n+1}-y_{n-1}}{x_{n+1}-x_{n-1}}\left(x-x_{n-1}\right) \\
\rightarrow & -\left(y_{n+1}-y_{n-1}\right)x+\left(x_{n+1}-x_{n-1}\right)y+\left(x_{n-1}y_{n+1}-x_{n+1}y_{n-1}\right)=0 \\
\rightarrow & -ax+by+c=0 \\\\
&\left\{\begin{array}{l}
a=y_{n+1}-y_{n-1} \\
b=x_{n+1}-x_{n-1} \\
c=x_{n-1}y_{n+1}-x_{n+1}y_{n-1}\\
\end{array}\right.
\end{align}

この直線と点(\(x_{n}, y_{n}\))の距離は,点と直線の距離の公式より以下のようになる

\begin{align}
e=\frac{\left|-ax_{n}+by_{n}+c\right|}{\sqrt{a^{2}+b^{2}}}=\frac{\left|-ax_{n}+by_{n}+c\right|}{d}
\end{align}

また,扇形の図に着目すると以下の関係が分かる

\begin{align}
&\left\{\begin{array}{l}
R\sin{\theta}=d/2 \\
R(1-\cos{\theta})=e \\
\end{array}\right.
\rightarrow \left\{\begin{array}{l}
\sin{\theta}=\frac{d/2}{R} \\
\cos{\theta}=1-\frac{e}{R} \\
\end{array}\right.
\end{align}

\(\sin{\theta}^2+\cos{\theta}^2=1\)より

\begin{align}
&\left(\frac{d/2}{R}\right)^2+\left(1-\frac{e}{R}\right)^2=1
\rightarrow R=\frac{d^2+4e^2}{8e}
\end{align}

このようにして計算した\(a,\cdots,e, R,\Omega\)を用いるとオイラー角(ヨー/ピッチ/ロール)[deg] は以下のように計算できる

\begin{align}
&\psi=\frac{180}{\pi}\arctan{\left(\frac{a}{b}\right)}-(-90) \\\\
&\theta=90+\frac{180}{\pi}\arctan{\left(\frac{z_{n+1}-z_{n-1}}{d}\right)} \\\\
&\phi=\frac{180}{\pi}\arctan{\left(\frac{R\Omega^2}{g}\right)} \\\\
\end{align}

バンク角\(\phi\)で旋回しているとき,上下の力のつり合いおよび円運動の方程式は以下のようになる

\begin{align}
&\left\{\begin{array}{l}
&L\cos{\phi}=Mg \\
&L\sin{\phi}=MR\Omega^2
\end{array}\right.
\end{align}

上式から\(L\)を消去すると次の式が得られる

\begin{align}
&\tan{\phi}=\frac{R\Omega^2}{g} \\\\
&\rightarrow \phi=\frac{180}{\pi}\arctan{\left(\frac{R\Omega^2}{g}\right)}
\end{align}

以上の計算を行ったうえでデータの一番最初と最後に値を補完する

# Insert values to beginning and end of dataframe
df_B.loc[0:dt,'heading'] = df_B.heading[dt]
df_B.loc[0:dt,'tilt'] = df_B.tilt[dt]
df_B.loc[len(df_B)-dt:,'heading'] = df_B.heading[len(df_B)-dt-1]
df_B.loc[len(df_B)-dt:,'tilt'] = df_B.tilt[len(df_B)-dt-1]

# Create a linestring

## Create an instance of Kml
kml = simplekml.Kml(open=1)

# Create a linestring
linestring = kml.newlinestring(name="A Sloped Line")
linestring.coords = tuple_B
linestring.altitudemode = simplekml.AltitudeMode.relativetoground
linestring.extrude = 0
linestring.style.linestyle.width = 3
linestring.style.linestyle.color = simplekml.Color.red

kmlのインスタンスを作成し,kml.newlinestring()を使って飛行軌跡をプロットする

simplekml.LineStringの設定は以下の通り

オプション説明
coords座標を表すタプルのリスト(経度,緯度,高度)tuple_B
altitudemode標高モードsimplekml.AltitudeMode.relativetoground
extrude地表面からの押出し(0/1)0
style.linestyle.width線の太さ3
style.linestyle.color線の色simplekml.Color.red

↓参考のドキュメント

Linestring Tutorial — SIMPLEKML 1.3.6 documentation
Geometries — SIMPLEKML 1.3.6 documentation
標高モード  |  KML(Keyhole Markup Language)  |  Google for Developers
Styles — SIMPLEKML 1.3.6 documentation

# Create a tour and attach a playlist to it

# Create a tour and attach a playlist to it
tour = kml.newgxtour(name="Take-Off!")
playlist = tour.newgxplaylist()

n=0
for _ in df_B.iterrows():
    # Attach a gx:FlyTo to the playlist
    flyto = playlist.newgxflyto()
    flyto.camera.longitude = df_B.longitude[n]
    flyto.camera.latitude = df_B.latitude[n]
    flyto.camera.altitude = df_B.press_alt[n]+1.
    flyto.camera.heading = df_B.heading[n]
    flyto.camera.tilt = df_B.tilt[n]
    flyto.camera.roll = df_B.roll[n]
    flyto.gxduration = 1.
    flyto.gxflytomode = 'smooth'
    n+=1

kml.newgxtour()でツアーを作成し,tour.newgxplaylist()でツアーの中にプレイリストを作成する

playlist.newgxflyto()で作成したプレイリストの中に目的地を追加していく

オプションは以下の通り

オプション 説明単位 例
flyto.camera.longitude緯度degdf_B.longitude[n]
flyto.camera.latitude経度degdf_B.latitude[n]
flyto.camera.altitude高度mdf_B.press_alt[n]+1.
flyto.camera.headingヨー角degdf_B.heading[n]
flyto.camera.tiltピッチ角degdf_B.tilt[n]
flyto.camera.rollロール角degdf_B.roll[n]
flyto.gxdurationフレーム間隔s1
flyto.gxflytomodeカメラの移動方法-'smooth'

flyto.gxdurationはフレームごとに時間間隔である

今回はGPSのデータ取得間隔に合わせて1.0 [s] としたが,ここをいじることで再生速度を変更することができる

カメラをぬるぬる動かすためにはflyto.gxflytomode'smooth'を指定する

↓参考

カメラ  |  KML(Keyhole Markup Language)  |  Google for Developers

# Save to file

# Attach a gx:Wait to the playlist to give the gx:AnimatedUpdate time to finish
wait = playlist.newgxwait(gxduration=3)

# Save to file
kml.save(os.path.join(os.getcwd(),path[:-4]+".kml"))

フライト後の余韻の時間を設定し,kmlファイルを保存する

以上でプログラムの解説は終了である

実行結果

VS codeなりubuntuなりにPythonの実行環境を構築して、以下のコマンドでプログラムを実行する

$: py -3.6 IGCfileToCSV.py
$: py -3.6 IGCfileToGoogleEarthTour.py

.kmlファイルが出力される

/mnt/c/Users/mtkbirdman/Desktop/igc
├── 2023-02-22-XNA-9AD179D6D6F04DDD95474658245E6D4F-01.csv
├── 2023-02-22-XNA-9AD179D6D6F04DDD95474658245E6D4F-01.igc
├── 2023-02-22-XNA-9AD179D6D6F04DDD95474658245E6D4F-01.kml
├── 2023-02-22-XNA-9AD179D6D6F04DDD95474658245E6D4F-01FFT.igc
├── IGCfileToCSV.py
└── IGCfileToGoogleEarthTour.py

IGCfileToCSV.pyはフライトログに一般的に用いられる.igcファイルからGoogleEarth用のCSVファイルを作成するプログラムである

https://github.com/mtkbirdman/mtkbirdman.com/blob/master/igc/IGCfileToCSV.py

↓参考

GoogleEarthにkmlファイルをインポートする

Google Earthを開き,左側のメニューから「Projects」を選択する

Explore Google Earth
Grab the helm and go on an adventure in Google Earth.

メニュー上部から「New Project>Import KML file from computer」を選択する

.kmlファイルを選択して開く

読み込んだ.kmlファイルの場所までびよーんと飛んでいく

「Take Off!」をクリックするとフライト動画が再生される

あとは煮るなり焼くなり好きにすればいい

↓Windows10/11の標準機能で画面録画する方法

Windowsの標準機能で画面録画をする方法|ドスパラ通販【公式】
Windowsの画面録画をする方法にはいくつかの種類があります。Windowsの標準機能を使えば特別な準備が不要で手軽に録画が可能となります。この記事では、Windowsの画面録画を標準機能のXbox Game Barで行う方法、ショートカ...

おわりに

GPSフライトログのCSVファイルをもとに、simplekmlを使ってGoogleEarthで飛行機のパイロット目線の映像を作るPythonプログラムについて説明した

↓関連記事

コメント