H3 Detect Flight Holding Pattern(Python)

Loading...

H3 Detect Flight Holding Pattern (Python)

Using DBR 11.2 with built-in H3 API support for spatial and blended analysis of flight paths.
We are focusing on a single flight for easy explainability and portable data but scale is what the Databricks is built for, so the more data the better!

AIRCRAFT: Boeing 717-2BD | REGISTRATION: N971AT


Author: Michael Johns mjohns@databricks.com
Last Modified: 13 SEP 2022

Show code
%pip install keplergl --quiet
Python interpreter will be restarted. Python interpreter will be restarted.
from keplergl import KeplerGl

# Import Databricks functions to get H3
from pyspark.databricks.sql.functions import *

import pyspark.sql.functions as F
from pyspark.sql.functions import col

from pyspark.sql.types import *
from pyspark.sql import Window

import pandas as pd
import pprint

from typing import *

pp = pprint.PrettyPrinter(indent=4)

Flight Track Data

225 events available for this flight, emitted every 15 seconds from Atlanta (ATL) to Washington DC (IAD).

Show code
columns --> ['time_edt', 'latitude', 'longitude', 'course', 'kts', 'mph', 'feet', 'rate', 'reporting_facility'] [ [ 'Fri 05:41:28 PM', '33.6350', '-84.4410', '272', '169', '194', '1575', 'Level', 'FlightAware ADS-B (KATL)'], [ 'Fri 05:41:44 PM', '33.6356', '-84.4564', '273', '173', '199', '2275', '1734 Climbing', 'FlightAware ADS-B (KPDK)'], [ 'Fri 05:42:00 PM', '33.6348', '-84.4726', '256', '188', '216', '2500', '797 Climbing', 'FlightAware ADS-B (KPDK)'], [ 'Fri 05:42:16 PM', '33.6283', '-84.4897', '244', '207', '238', '2700', '844 Climbing', 'FlightAware ADS-B (KMGE)'], [ 'Fri 05:42:32 PM', '33.6213', '-84.5060', '243', '222', '255', '2950', '1292 Climbing', 'FlightAware ADS-B (KATL)']]

Zoom-In on Holding Pattern

See the weather related event

Show code

Flight Track DataFrame

Use spark windowing to apply rank over events based on time_edt.

df = (
    spark.createDataFrame(data, cols)
        .withColumn("dt_aug", F.concat(F.lit("2021-05-28 "),F.substring("time_edt",5,8)))
        .withColumn("dt_edt", F.to_timestamp("dt_aug"))
        .withColumn("dt_rank", F.rank().over(Window.orderBy('dt_edt')))
    .selectExpr(
        "dt_rank", "time_edt", "cast(latitude as double)", "cast(longitude as double)", "cast(course as int)",
        "cast(kts as int)", "cast(mph as int)", "cast(feet as int)", "rate", "reporting_facility"
    )
    .orderBy("dt_rank")
)

display(df)
 
dt_rank
time_edt
latitude
longitude
course
kts
mph
feet
rate
reporting_facility
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1
Fri 05:41:28 PM
33.635
-84.441
272
169
194
1575
Level
FlightAware ADS-B (KATL)
2
Fri 05:41:44 PM
33.6356
-84.4564
273
173
199
2275
1734 Climbing
FlightAware ADS-B (KPDK)
3
Fri 05:42:00 PM
33.6348
-84.4726
256
188
216
2500
797 Climbing
FlightAware ADS-B (KPDK)
4
Fri 05:42:16 PM
33.6283
-84.4897
244
207
238
2700
844 Climbing
FlightAware ADS-B (KMGE)
5
Fri 05:42:32 PM
33.6213
-84.506
243
222
255
2950
1292 Climbing
FlightAware ADS-B (KATL)
6
Fri 05:42:52 PM
33.6108
-84.5302
244
240
276
3475
1849 Climbing
FlightAware ADS-B (KATL)
7
Fri 05:43:15 PM
33.5994
-84.5589
244
248
285
4275
1923 Climbing
FlightAware ADS-B (KATL)
8
Fri 05:43:31 PM
33.5885
-84.5753
222
255
293
4725
2109 Climbing
FlightAware ADS-B (KPDK)
9
Fri 05:43:47 PM
33.5704
-84.5836
188
259
298
5400
2488 Climbing
FlightAware ADS-B (KATL)
10
Fri 05:44:12 PM
33.5399
-84.5826
179
266
306
6425
2394 Climbing
FlightAware ADS-B (KATL)
11
Fri 05:44:34 PM
33.5127
-84.5837
182
269
310
7275
2211 Climbing
FlightAware ADS-B (KFGU)
12
Fri 05:44:50 PM
33.4916
-84.58
162
281
323
7825
2118 Climbing
FlightAware ADS-B (KATL)
13
Fri 05:45:08 PM
33.4722
-84.564
134
295
339
8475
1875 Climbing
FlightAware ADS-B (KATL)
14
Fri 05:45:26 PM
33.4593
-84.5386
113
308
354
8950
1853 Climbing
FlightAware ADS-B (KPDK)
15
Fri 05:45:42 PM
33.4562
-84.5104
89
320
368
9525
1904 Climbing
FlightAware ADS-B (KATL)
16
Fri 05:46:18 PM
33.4561
-84.4448
91
334
384
10600
1477 Climbing
FlightAware ADS-B (KATL)
17
Fri 05:46:48 PM
33.4556
-84.3886
91
356
410
11150
1269 Climbing
FlightAware ADS-B (KATL)
18
Fri 05:47:10 PM
33.4586
-84.3436
78
368
423
11700
1819 Climbing
FlightAware ADS-B (KRYY)
19
Fri 05:47:35 PM
33.4734
-84.2964
63
372
428
12575
1943 Climbing
FlightAware ADS-B (KCCO)
20
Fri 05:48:11 PM
33.5031
-84.2307
62
379
436
13675
1500 Climbing
FlightAware ADS-B (KATL)
21
Fri 05:48:41 PM
33.5273
-84.1757
62
399
459
14225
1000 Climbing
FlightAware ADS-B (KLZU)
Showing all 225 rows.
def display_kepler(kmap:KeplerGl, height=800, width=1200) -> None:
    """
    Convenience function to render map in kepler.gl
    - use this when cannot directly render or
      want to go beyond the %%mosaic_kepler magic.
    """
    displayHTML(
        kmap
            ._repr_html_()
            .decode("utf-8")
            .replace(".height||400", f".height||{height}")
            .replace(".width||400", f".width||{width}")
    )
Show code
'map_1_config' ran...

Plot Flight Events in Kepler.gl

Holding pattern happens over events having dt_rank 141 to 181 (hover over points to see tooltip).

map_1 = KeplerGl(height=600, config=map_1_config)
map_1.add_data(data=df.toPandas(), name="flight_track")
display_kepler(map_1)
User Guide: https://docs.kepler.gl/docs/keplergl-jupyter
display(df.filter("dt_rank >= 141").filter("dt_rank <= 181"))
 
dt_rank
time_edt
latitude
longitude
course
kts
mph
feet
rate
reporting_facility
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
141
Fri 06:45:01 PM
38.6923
-78.5408
73
369
425
12850
-1525 Descending
FlightAware ADS-B (KAGC)
142
Fri 06:45:31 PM
38.7071
-78.4774
74
348
400
12250
-975 Descending
FlightAware ADS-B (KIAD)
143
Fri 06:46:01 PM
38.7202
-78.4223
73
342
394
11875
-725 Descending
FlightAware ADS-B (KIAD)
144
Fri 06:46:31 PM
38.7343
-78.3623
74
342
394
11525
-765 Descending
FlightAware ADS-B (KDCA)
145
Fri 06:46:50 PM
38.7373
-78.3263
94
337
388
11250
-512 Descending
FlightAware ADS-B (KJYO)
146
Fri 06:47:12 PM
38.7253
-78.2887
122
323
372
11175
-115 Descending
FlightAware ADS-B (KJYO)
147
Fri 06:47:29 PM
38.7063
-78.2662
149
302
348
11175
Level
FlightAware ADS-B (KJYO)
148
Fri 06:47:47 PM
38.6825
-78.2593
178
284
327
11175
Level
FlightAware ADS-B (KDCA)
149
Fri 06:48:05 PM
38.6613
-78.2687
211
270
311
11175
Level
FlightAware ADS-B (KJYO)
150
Fri 06:48:22 PM
38.6486
-78.2883
241
266
306
11175
Level
FlightAware ADS-B (KBWI)
151
Fri 06:48:39 PM
38.6439
-78.3136
262
266
306
11175
-27 Descending
FlightAware ADS-B (KDCA)
152
Fri 06:49:18 PM
38.6411
-78.3772
268
262
302
11150
Level
FlightAware ADS-B (KIAD)
153
Fri 06:49:35 PM
38.636
-78.4012
248
261
300
11175
27 Climbing
FlightAware ADS-B (KJPN)
154
Fri 06:50:14 PM
38.6142
-78.4551
243
262
302
11175
-26 Descending
FlightAware ADS-B (KSHD)
155
Fri 06:50:32 PM
38.6097
-78.4826
275
269
310
11150
Level
FlightAware ADS-B (KRMN)
156
Fri 06:50:48 PM
38.6165
-78.506
302
279
321
11175
44 Climbing
FlightAware ADS-B (KCHO)
157
Fri 06:51:06 PM
38.6362
-78.5265
329
295
339
11175
Level
FlightAware ADS-B (KHGR)
158
Fri 06:51:22 PM
38.6563
-78.5316
355
312
359
11175
Level
FlightAware ADS-B (KSHD)
159
Fri 06:51:40 PM
38.683
-78.5226
25
328
377
11175
Level
FlightAware ADS-B (KDCA)
160
Fri 06:51:56 PM
38.7016
-78.5026
49
338
389
11175
Level
FlightAware ADS-B (KFDK)
161
Fri 06:52:18 PM
38.7156
-78.4611
76
339
390
11175
Level
FlightAware ADS-B (KJYO)
Showing all 41 rows.

Here is a reduced-size screenshot of what is rendered with kepler in the imported notebook.

Show code

Resolution for H3 Grids

If a plane is flying at ~483 kph (300 mph), then it covers ~8 km (5 mi) per minute or ~4 km (2.5 mi) per 30 seconds or 2 km (1.25 mi) per 15 seconds which is near the interval of flight data reporting. H3 resolutions around 8 at .737 km^2 would be useful for straight track distances; however, since we are looking at holding patterns, will use resolution 6 for the initial analysis.

H3 Resolution Average Hexagon Area (km2) Average Hexagon Edge Length (km) Number of unique indexes
0 4,250,546.8477000 1,107.712591000 122
1 607,220.9782429 418.676005500 842
2 86,745.8540347 158.244655800 5,882
3 12,392.2648621 59.810857940 41,162
4 1,770.3235517 22.606379400 288,122
5 252.9033645 8.544408276 2,016,842
6 36.1290521 3.229482772 14,117,882
7 5.1612932 1.220629759 98,825,162
8 0.7373276 0.461354684 691,776,122
9 0.1053325 0.174375668 4,842,432,842
10 0.0150475 0.065907807 33,897,029,882
11 0.0021496 0.024910561 237,279,209,162
12 0.0003071 0.009415526 1,660,954,464,122
13 0.0000439 0.003559893 11,626,681,248,842
14 0.0000063 0.001348575 81,386,768,741,882
15 0.0000009 0.000509713 569,707,381,193,162

From https://www.programmersought.com/article/30995709682/

H3 Resolution 6 DataFrame df_hex6

  • h3_longlatash3 to get latitude and longitude H3 cell
  • h3_centeraswkt to get the centroid of the H3 cell as WKT (Well Known Text)
df_hex6 =  (
    df
        .withColumn("h3_resolution", F.lit(6))
        .withColumn("hex_id", h3_longlatash3("longitude", "latitude", 6))
        .withColumn("hex_centroid", h3_centeraswkt("hex_id"))
    .select(*df.columns,"hex_id","hex_centroid","h3_resolution")
)

display(df_hex6)
 
dt_rank
time_edt
latitude
longitude
course
kts
mph
feet
rate
reporting_facility
hex_id
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1
Fri 05:41:28 PM
33.635
-84.441
272
169
194
1575
Level
FlightAware ADS-B (KATL)
604691931238236200
2
Fri 05:41:44 PM
33.6356
-84.4564
273
173
199
2275
1734 Climbing
FlightAware ADS-B (KPDK)
604691931238236200
3
Fri 05:42:00 PM
33.6348
-84.4726
256
188
216
2500
797 Climbing
FlightAware ADS-B (KPDK)
604691931238236200
4
Fri 05:42:16 PM
33.6283
-84.4897
244
207
238
2700
844 Climbing
FlightAware ADS-B (KMGE)
604691925466873900
5
Fri 05:42:32 PM
33.6213
-84.506
243
222
255
2950
1292 Climbing
FlightAware ADS-B (KATL)
604691925466873900
6
Fri 05:42:52 PM
33.6108
-84.5302
244
240
276
3475
1849 Climbing
FlightAware ADS-B (KATL)
604691929359188000
7
Fri 05:43:15 PM
33.5994
-84.5589
244
248
285
4275
1923 Climbing
FlightAware ADS-B (KATL)
604691929359188000
8
Fri 05:43:31 PM
33.5885
-84.5753
222
255
293
4725
2109 Climbing
FlightAware ADS-B (KPDK)
604691929359188000
9
Fri 05:43:47 PM
33.5704
-84.5836
188
259
298
5400
2488 Climbing
FlightAware ADS-B (KATL)
604691928956534800
10
Fri 05:44:12 PM
33.5399
-84.5826
179
266
306
6425
2394 Climbing
FlightAware ADS-B (KATL)
604691928956534800
11
Fri 05:44:34 PM
33.5127
-84.5837
182
269
310
7275
2211 Climbing
FlightAware ADS-B (KFGU)
604691929493405700
12
Fri 05:44:50 PM
33.4916
-84.58
162
281
323
7825
2118 Climbing
FlightAware ADS-B (KATL)
604691929761841200
13
Fri 05:45:08 PM
33.4722
-84.564
134
295
339
8475
1875 Climbing
FlightAware ADS-B (KATL)
604691929761841200
14
Fri 05:45:26 PM
33.4593
-84.5386
113
308
354
8950
1853 Climbing
FlightAware ADS-B (KPDK)
604691917816463400
15
Fri 05:45:42 PM
33.4562
-84.5104
89
320
368
9525
1904 Climbing
FlightAware ADS-B (KATL)
604691917816463400
16
Fri 05:46:18 PM
33.4561
-84.4448
91
334
384
10600
1477 Climbing
FlightAware ADS-B (KATL)
604691917145374700
17
Fri 05:46:48 PM
33.4556
-84.3886
91
356
410
11150
1269 Climbing
FlightAware ADS-B (KATL)
604691917413810200
Showing all 225 rows.
Show code
'map_4_config' ran...

Plot H3 Resolution 6 with MPH > 275 + Count > 3 (Red)

df_hex6_dense = (
    df_hex6
        .filter("mph > 275")
        .groupBy("hex_id")
        .count()
        .filter("count > 3")
        .orderBy(F.desc("count"))
)

map_4 = KeplerGl(height=600, config=map_4_config)
map_4.add_data(data = df_hex6.select(*df.columns, h3_h3tostring("hex_id").alias("hex_id"), "hex_centroid", "h3_resolution").toPandas(), name = "hex6_flight")
map_4.add_data(data = df_hex6_dense.select(h3_h3tostring("hex_id").alias("hex_id"),"count").toPandas(), name = "hex6_dense_flight")
display_kepler(map_4)
User Guide: https://docs.kepler.gl/docs/keplergl-jupyter

Evaluation: At H3 Resolution 6, there is generally 1 - 3 points per polygon; however, there are 7 entries where MPH > 275 having > 3 points and all are within the holding pattern range (141 - 181). The benefit of this is more potential to directly detect any repeat passes of an aircraft through the same polygon.

Here is a reduced-size screenshot of what is rendered with kepler in the imported notebook.

Show code

K-Ring Variation Example

The above approach would capture more data if h3.hexring(<h3_id>,1) were used as data would not be lost at the boundary of a cell, so that is done below.

df_hex6_kring = (
    df_hex6
        .filter("mph > 275")
        .withColumn("k_rings_1", h3_hexring("hex_id", 1)) #h3_kring_udf("hex_id",F.lit(1)))
        .select("*", F.explode("k_rings_1").alias("k_ring_1"))
        .drop("k_rings_1", "hex_centroid")
        .orderBy("dt_rank")
)
print(f"count? {df_hex6_kring.count():,}")
display(df_hex6_kring.limit(100))
count? 1,158
 
dt_rank
time_edt
latitude
longitude
course
kts
mph
feet
rate
reporting_facility
hex_id
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
6
Fri 05:42:52 PM
33.6108
-84.5302
244
240
276
3475
1849 Climbing
FlightAware ADS-B (KATL)
604691929359188000
6
Fri 05:42:52 PM
33.6108
-84.5302
244
240
276
3475
1849 Climbing
FlightAware ADS-B (KATL)
604691929359188000
6
Fri 05:42:52 PM
33.6108
-84.5302
244
240
276
3475
1849 Climbing
FlightAware ADS-B (KATL)
604691929359188000
6
Fri 05:42:52 PM
33.6108
-84.5302
244
240
276
3475
1849 Climbing
FlightAware ADS-B (KATL)
604691929359188000
6
Fri 05:42:52 PM
33.6108
-84.5302
244
240
276
3475
1849 Climbing
FlightAware ADS-B (KATL)
604691929359188000
6
Fri 05:42:52 PM
33.6108
-84.5302
244
240
276
3475
1849 Climbing
FlightAware ADS-B (KATL)
604691929359188000
7
Fri 05:43:15 PM
33.5994
-84.5589
244
248
285
4275
1923 Climbing
FlightAware ADS-B (KATL)
604691929359188000
7
Fri 05:43:15 PM
33.5994
-84.5589
244
248
285
4275
1923 Climbing
FlightAware ADS-B (KATL)
604691929359188000
7
Fri 05:43:15 PM
33.5994
-84.5589
244
248
285
4275
1923 Climbing
FlightAware ADS-B (KATL)
604691929359188000
7
Fri 05:43:15 PM
33.5994
-84.5589
244
248
285
4275
1923 Climbing
FlightAware ADS-B (KATL)
604691929359188000
7
Fri 05:43:15 PM
33.5994
-84.5589
244
248
285
4275
1923 Climbing
FlightAware ADS-B (KATL)
604691929359188000
7
Fri 05:43:15 PM
33.5994
-84.5589
244
248
285
4275
1923 Climbing
FlightAware ADS-B (KATL)
604691929359188000
8
Fri 05:43:31 PM
33.5885
-84.5753
222
255
293
4725
2109 Climbing
FlightAware ADS-B (KPDK)
604691929359188000
8
Fri 05:43:31 PM
33.5885
-84.5753
222
255
293
4725
2109 Climbing
FlightAware ADS-B (KPDK)
604691929359188000
8
Fri 05:43:31 PM
33.5885
-84.5753
222
255
293
4725
2109 Climbing
FlightAware ADS-B (KPDK)
604691929359188000
8
Fri 05:43:31 PM
33.5885
-84.5753
222
255
293
4725
2109 Climbing
FlightAware ADS-B (KPDK)
604691929359188000
8
Fri 05:43:31 PM
33.5885
-84.5753
222
255
293
4725
2109 Climbing
FlightAware ADS-B (KPDK)
604691929359188000
8
Fri 05:43:31 PM
33.5885
-84.5753
222
255
293
4725
2109 Climbing
FlightAware ADS-B (KPDK)
604691929359188000
9
Fri 05:43:47 PM
33.5704
-84.5836
188
259
298
5400
2488 Climbing
FlightAware ADS-B (KATL)
604691928956534800
9
Fri 05:43:47 PM
33.5704
-84.5836
188
259
298
5400
2488 Climbing
FlightAware ADS-B (KATL)
604691928956534800
9
Fri 05:43:47 PM
33.5704
-84.5836
188
259
298
5400
2488 Climbing
FlightAware ADS-B (KATL)
604691928956534800
9
Fri 05:43:47 PM
33.5704
-84.5836
188
259
298
5400
2488 Climbing
FlightAware ADS-B (KATL)
604691928956534800
9
Fri 05:43:47 PM
33.5704
-84.5836
188
259
298
5400
2488 Climbing
FlightAware ADS-B (KATL)
604691928956534800
9
Fri 05:43:47 PM
33.5704
-84.5836
188
259
298
5400
2488 Climbing
FlightAware ADS-B (KATL)
604691928956534800
Showing all 100 rows.

Only maintain rows where k_ring_1 is also in the data as hex_id.

df_hex6_join = (
    df_hex6
        .join(
            df_hex6_kring.selectExpr("dt_rank as dt_rank_k_ring", "k_ring_1"),
            col("k_ring_1") == col("hex_id")) # inner
         .filter("mph > 275") # pre-filter out mph
         .select("dt_rank", "dt_rank_k_ring", "hex_id")
            .distinct()
        .orderBy("dt_rank","dt_rank_k_ring")
)

print(f"count? {df_hex6_join.count():,}")
display(df_hex6_join)
count? 726
 
dt_rank
dt_rank_k_ring
hex_id
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
6
9
604691929359188000
6
10
604691929359188000
7
9
604691929359188000
7
10
604691929359188000
8
9
604691929359188000
8
10
604691929359188000
9
6
604691928956534800
9
7
604691928956534800
9
8
604691928956534800
9
11
604691928956534800
9
12
604691928956534800
9
13
604691928956534800
10
6
604691928956534800
10
7
604691928956534800
10
8
604691928956534800
10
11
604691928956534800
10
12
604691928956534800
Showing all 726 rows.
Show code
map_4a_config ran...

Plot H3 Resolution 6 with MPH > 275 + K-Ring Count > 20 (Red)

df_hex6_dense_kring = (
    df_hex6_join
        .groupBy("hex_id")
        .count()
        .filter("count > 20")
        .orderBy(F.desc("count"))
)

map_4a = KeplerGl(height=600, config=map_4a_config)
map_4a.add_data(data = df_hex6.select(*df.columns, h3_h3tostring("hex_id").alias("hex_id"), "hex_centroid", "h3_resolution").toPandas(), name = "hex6_flight")
map_4a.add_data(data = df_hex6_dense_kring.select(h3_h3tostring("hex_id").alias("hex_id"), "count").toPandas(), name = "hex6_kring_flight")
display_kepler(map_4a)
User Guide: https://docs.kepler.gl/docs/keplergl-jupyter

Evaluation: A number of entries collapse into the same hex_id for resolution 6 with the k-ring approach, so the counts are higher overall which is what you would want for detecting a holding pattern. At count > 20 only the holding pattern is detected.

Here is a reduced-size screenshot of what is rendered with kepler in the imported notebook.

Show code

Window Function for Distances between Messages

H3 has an API for calculating distances. Let's first see how far we get with just Spark Windowing with distances based on H3 resolution 8.

  • h3_distance: Returns the distance in grid cells between the two indexes; km based on resolution (here).
display(
    df_hex6
        .withColumn("hex_lag", F.lag("hex_id",5).over(Window.orderBy("dt_rank")))
        .withColumn("hex_lag_dist", h3_distance("hex_id", "hex_lag"))
            .filter("mph > 275")
        .orderBy("dt_rank")
)
 
dt_rank
time_edt
latitude
longitude
course
kts
mph
feet
rate
reporting_facility
hex_id
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
6
Fri 05:42:52 PM
33.6108
-84.5302
244
240
276
3475
1849 Climbing
FlightAware ADS-B (KATL)
604691929359188000
7
Fri 05:43:15 PM
33.5994
-84.5589
244
248
285
4275
1923 Climbing
FlightAware ADS-B (KATL)
604691929359188000
8
Fri 05:43:31 PM
33.5885
-84.5753
222
255
293
4725
2109 Climbing
FlightAware ADS-B (KPDK)
604691929359188000
9
Fri 05:43:47 PM
33.5704
-84.5836
188
259
298
5400
2488 Climbing
FlightAware ADS-B (KATL)
604691928956534800
10
Fri 05:44:12 PM
33.5399
-84.5826
179
266
306
6425
2394 Climbing
FlightAware ADS-B (KATL)
604691928956534800
11
Fri 05:44:34 PM
33.5127
-84.5837
182
269
310
7275
2211 Climbing
FlightAware ADS-B (KFGU)
604691929493405700
12
Fri 05:44:50 PM
33.4916
-84.58
162
281
323
7825
2118 Climbing
FlightAware ADS-B (KATL)
604691929761841200
13
Fri 05:45:08 PM
33.4722
-84.564
134
295
339
8475
1875 Climbing
FlightAware ADS-B (KATL)
604691929761841200
14
Fri 05:45:26 PM
33.4593
-84.5386
113
308
354
8950
1853 Climbing
FlightAware ADS-B (KPDK)
604691917816463400
15
Fri 05:45:42 PM
33.4562
-84.5104
89
320
368
9525
1904 Climbing
FlightAware ADS-B (KATL)
604691917816463400
16
Fri 05:46:18 PM
33.4561
-84.4448
91
334
384
10600
1477 Climbing
FlightAware ADS-B (KATL)
604691917145374700
17
Fri 05:46:48 PM
33.4556
-84.3886
91
356
410
11150
1269 Climbing
FlightAware ADS-B (KATL)
604691917413810200
18
Fri 05:47:10 PM
33.4586
-84.3436
78
368
423
11700
1819 Climbing
FlightAware ADS-B (KRYY)
604691919963947000
19
Fri 05:47:35 PM
33.4734
-84.2964
63
372
428
12575
1943 Climbing
FlightAware ADS-B (KCCO)
604691919292858400
20
Fri 05:48:11 PM
33.5031
-84.2307
62
379
436
13675
1500 Climbing
FlightAware ADS-B (KATL)
604691919695511600
21
Fri 05:48:41 PM
33.5273
-84.1757
62
399
459
14225
1000 Climbing
FlightAware ADS-B (KLZU)
604691886677950500
22
Fri 05:49:11 PM
33.5541
-84.1146
62
418
481
14675
925 Climbing
FlightAware ADS-B (KFGU)
604691886409515000
Showing all 193 rows.

Evaluation: Speed (and therefore distance) noticeably reduced over the ~145 to ~180 events which is expected as the plane generally would go slower when in a holding pattern. The hump in the middle of the holding pattern is also somewhat reflected from the flight track overview below.

Here is a reduced-size screenshot of the bar chart rendered in the imported notebook.

Show code
Show code