Skip to content

Commit e705080

Browse files
authored
Add FAA airspace data sets (#496)
1 parent 277cf21 commit e705080

File tree

6 files changed

+112
-69
lines changed

6 files changed

+112
-69
lines changed

src/traffic/data/faa/__init__.py

Lines changed: 30 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,33 @@
1-
import importlib
2-
import json
3-
import logging
4-
from pathlib import Path
5-
from typing import Any, Dict
6-
7-
import httpx
8-
9-
import pandas as pd
10-
11-
from ... import cache_expiration, cache_path
12-
from .. import client
13-
14-
__all__ = list(p.stem[1:] for p in Path(__file__).parent.glob("_[a-z]*py"))
15-
16-
_log = logging.getLogger(__name__)
17-
18-
19-
class ADDS_FAA_OpenData:
20-
id_: str
21-
filename: str
22-
website = "https://adds-faa.opendata.arcgis.com/datasets/{}"
23-
json_url = "https://opendata.arcgis.com/datasets/{}.geojson"
24-
25-
def __init__(self) -> None:
26-
self.cache_file = cache_path / self.filename
27-
self.website = self.website.format(self.id_)
28-
self.json_url = self.json_url.format(self.id_)
29-
30-
def download_data(self) -> None:
31-
_log.warning(
32-
f"Downloading data from {self.website}. Please check terms of use."
33-
)
34-
c = client.get(self.json_url)
35-
c.raise_for_status()
36-
json_contents = c.json()
37-
with self.cache_file.open("w") as fh:
38-
json.dump(json_contents, fh)
39-
40-
def json_contents(self) -> Dict[str, Any]:
41-
if self.cache_file.exists():
42-
last_modification = (self.cache_file).lstat().st_mtime
43-
delta = pd.Timestamp("now") - pd.Timestamp(last_modification * 1e9)
44-
45-
if cache_expiration is not None and delta > cache_expiration:
46-
try:
47-
self.download_data()
48-
except httpx.TransportError:
49-
pass
50-
else:
51-
self.download_data()
52-
53-
with self.cache_file.open("r") as fh:
54-
json_contents = json.load(fh)
55-
56-
return json_contents # type: ignore
57-
58-
1+
from typing import Any
2+
3+
from ._airspaces import (
4+
Airspace_Boundary,
5+
Class_Airspace,
6+
Prohibited_Airspace,
7+
Route_Airspace,
8+
Special_Use_Airspace,
9+
)
10+
from ._ats_route import Ats_Route
11+
from ._designated_points import Designated_Points
12+
from ._navaid_components import Navaid_Components
13+
14+
__all__ = [
15+
"Airspace_Boundary",
16+
"Ats_Route",
17+
"Class_Airspace",
18+
"Designated_Points",
19+
"Navaid_Components",
20+
"Prohibited_Airspace",
21+
"Route_Airspace",
22+
"Special_Use_Airspace",
23+
]
24+
25+
26+
# this is here for compatibility with the <=2.12.0 API to allow for stuff like
27+
# `from traffic.data.faa import class_airspace`
5928
def __getattr__(name: str) -> Any:
60-
if name in __all__:
61-
mod = importlib.import_module("._" + name, package="traffic.data.faa")
62-
return getattr(mod, name.title())()
29+
for cls in __all__:
30+
if cls.lower() == name.lower():
31+
return globals()[cls]()
6332

6433
raise AttributeError()
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import json
2+
import logging
3+
from typing import Any, Dict
4+
5+
import httpx
6+
7+
import pandas as pd
8+
9+
from ... import cache_expiration, cache_path
10+
from .. import client
11+
12+
_log = logging.getLogger(__name__)
13+
14+
15+
class ADDS_FAA_OpenData:
16+
id_: str
17+
filename: str
18+
website = "https://adds-faa.opendata.arcgis.com/datasets/{}"
19+
json_url = "https://opendata.arcgis.com/datasets/{}.geojson"
20+
21+
def __init__(self) -> None:
22+
self.cache_file = cache_path / self.filename
23+
self.website = self.website.format(self.id_)
24+
self.json_url = self.json_url.format(self.id_)
25+
26+
def download_data(self) -> None:
27+
_log.warning(
28+
f"Downloading data from {self.website}. Please check terms of use."
29+
)
30+
c = client.get(self.json_url)
31+
c.raise_for_status()
32+
json_contents = c.json()
33+
with self.cache_file.open("w") as fh:
34+
json.dump(json_contents, fh)
35+
36+
def json_contents(self) -> Dict[str, Any]:
37+
if self.cache_file.exists():
38+
last_modification = (self.cache_file).lstat().st_mtime
39+
delta = pd.Timestamp("now") - pd.Timestamp(last_modification * 1e9)
40+
41+
if cache_expiration is not None and delta > cache_expiration:
42+
try:
43+
self.download_data()
44+
except httpx.TransportError:
45+
pass
46+
else:
47+
self.download_data()
48+
49+
with self.cache_file.open("r") as fh:
50+
json_contents = json.load(fh)
51+
52+
return json_contents # type: ignore

src/traffic/data/faa/_airspace_boundary.py renamed to src/traffic/data/faa/_airspaces.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,12 @@
99
from shapely.ops import orient
1010

1111
from ...core.airspace import Airspace, Airspaces, ExtrudedPolygon
12-
from . import ADDS_FAA_OpenData
12+
from ._adds_faa_opendata import ADDS_FAA_OpenData
1313

1414
_log = logging.getLogger(__name__)
1515

1616

17-
class Airspace_Boundary(ADDS_FAA_OpenData, Airspaces):
18-
id_ = "67885972e4e940b2aa6d74024901c561_0"
19-
filename = "faa_airspace_boundary.json"
20-
17+
class FAA_Airspace(ADDS_FAA_OpenData, Airspaces):
2118
def __init__(self, data: gpd.GeoDataFrame | None = None) -> None:
2219
super().__init__()
2320
self.data = data
@@ -78,3 +75,28 @@ def back(self) -> Dict[str, Airspace]:
7875
airspaces[name] = airspace
7976

8077
return airspaces
78+
79+
80+
class Airspace_Boundary(FAA_Airspace):
81+
id_ = "67885972e4e940b2aa6d74024901c561_0"
82+
filename = "faa_airspace_boundary.json"
83+
84+
85+
class Class_Airspace(FAA_Airspace):
86+
id_ = "c6a62360338e408cb1512366ad61559e_0"
87+
filename = "faa_class_airspace.json"
88+
89+
90+
class Special_Use_Airspace(FAA_Airspace):
91+
id_ = "dd0d1b726e504137ab3c41b21835d05b_0"
92+
filename = "faa_special_use_airspace.json"
93+
94+
95+
class Route_Airspace(FAA_Airspace):
96+
id_ = "8bf861bb9b414f4ea9f0ff2ca0f1a851_0"
97+
filename = "faa_route_airspace.json"
98+
99+
100+
class Prohibited_Airspace(FAA_Airspace):
101+
id_ = "354ee0c77484461198ebf728a2fca50c_0"
102+
filename = "faa_prohibited_airspace.json"

src/traffic/data/faa/_ats_route.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Any, Dict
22

3-
from . import ADDS_FAA_OpenData
3+
from ._adds_faa_opendata import ADDS_FAA_OpenData
44

55

66
class Ats_Route(ADDS_FAA_OpenData):

src/traffic/data/faa/_designated_points.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pandas as pd
22

33
from ..basic.navaid import Navaids
4-
from . import ADDS_FAA_OpenData
4+
from ._adds_faa_opendata import ADDS_FAA_OpenData
55

66

77
class FAA_Designated_Points(Navaids):

src/traffic/data/faa/_navaid_components.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pandas as pd
22

33
from ..basic.navaid import Navaids
4-
from . import ADDS_FAA_OpenData
4+
from ._adds_faa_opendata import ADDS_FAA_OpenData
55

66

77
class Navaid_Components(ADDS_FAA_OpenData):

0 commit comments

Comments
 (0)