aeronavx 0.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. aeronavx-0.3.1/LICENSE +21 -0
  2. aeronavx-0.3.1/PKG-INFO +196 -0
  3. aeronavx-0.3.1/README.md +150 -0
  4. aeronavx-0.3.1/aeronavx/__init__.py +69 -0
  5. aeronavx-0.3.1/aeronavx/api/__init__.py +3 -0
  6. aeronavx-0.3.1/aeronavx/api/server.py +180 -0
  7. aeronavx-0.3.1/aeronavx/cli/__init__.py +3 -0
  8. aeronavx-0.3.1/aeronavx/cli/main.py +221 -0
  9. aeronavx-0.3.1/aeronavx/core/__init__.py +97 -0
  10. aeronavx-0.3.1/aeronavx/core/airports.py +49 -0
  11. aeronavx-0.3.1/aeronavx/core/analytics.py +137 -0
  12. aeronavx-0.3.1/aeronavx/core/distance.py +167 -0
  13. aeronavx-0.3.1/aeronavx/core/emissions.py +96 -0
  14. aeronavx-0.3.1/aeronavx/core/geodesy.py +113 -0
  15. aeronavx-0.3.1/aeronavx/core/loader.py +281 -0
  16. aeronavx-0.3.1/aeronavx/core/routing.py +179 -0
  17. aeronavx-0.3.1/aeronavx/core/search.py +173 -0
  18. aeronavx-0.3.1/aeronavx/core/timezone.py +94 -0
  19. aeronavx-0.3.1/aeronavx/core/weather.py +97 -0
  20. aeronavx-0.3.1/aeronavx/data/airports.csv +84226 -0
  21. aeronavx-0.3.1/aeronavx/data/airports_minimal.csv +11 -0
  22. aeronavx-0.3.1/aeronavx/exceptions.py +22 -0
  23. aeronavx-0.3.1/aeronavx/models/__init__.py +3 -0
  24. aeronavx-0.3.1/aeronavx/models/airport.py +63 -0
  25. aeronavx-0.3.1/aeronavx/utils/__init__.py +54 -0
  26. aeronavx-0.3.1/aeronavx/utils/cache.py +27 -0
  27. aeronavx-0.3.1/aeronavx/utils/constants.py +30 -0
  28. aeronavx-0.3.1/aeronavx/utils/logging.py +34 -0
  29. aeronavx-0.3.1/aeronavx/utils/spatial_index.py +115 -0
  30. aeronavx-0.3.1/aeronavx/utils/units.py +86 -0
  31. aeronavx-0.3.1/aeronavx/utils/validators.py +31 -0
  32. aeronavx-0.3.1/aeronavx.egg-info/PKG-INFO +196 -0
  33. aeronavx-0.3.1/aeronavx.egg-info/SOURCES.txt +40 -0
  34. aeronavx-0.3.1/aeronavx.egg-info/dependency_links.txt +1 -0
  35. aeronavx-0.3.1/aeronavx.egg-info/entry_points.txt +2 -0
  36. aeronavx-0.3.1/aeronavx.egg-info/requires.txt +26 -0
  37. aeronavx-0.3.1/aeronavx.egg-info/top_level.txt +1 -0
  38. aeronavx-0.3.1/pyproject.toml +91 -0
  39. aeronavx-0.3.1/setup.cfg +4 -0
  40. aeronavx-0.3.1/tests/test_distance.py +58 -0
  41. aeronavx-0.3.1/tests/test_geodesy.py +67 -0
  42. aeronavx-0.3.1/tests/test_validators.py +54 -0
aeronavx-0.3.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Tefik Yildiz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,196 @@
1
+ Metadata-Version: 2.4
2
+ Name: aeronavx
3
+ Version: 0.3.1
4
+ Summary: Production-grade airport data and flight geometry library
5
+ Author-email: Teyfik OZ <teyfikoz@example.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/teyfikoz/AeroNavX
8
+ Project-URL: Repository, https://github.com/teyfikoz/AeroNavX
9
+ Project-URL: Documentation, https://github.com/teyfikoz/AeroNavX/docs
10
+ Project-URL: Issues, https://github.com/teyfikoz/AeroNavX/issues
11
+ Keywords: airport,aviation,geodesy,geography,distance,routing
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering :: GIS
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=7.0; extra == "dev"
25
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
26
+ Requires-Dist: black>=23.0; extra == "dev"
27
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
28
+ Provides-Extra: full
29
+ Requires-Dist: pandas>=2.0; extra == "full"
30
+ Requires-Dist: scipy>=1.10; extra == "full"
31
+ Requires-Dist: rapidfuzz>=3.0; extra == "full"
32
+ Requires-Dist: timezonefinder>=6.0; extra == "full"
33
+ Requires-Dist: requests>=2.31; extra == "full"
34
+ Provides-Extra: api
35
+ Requires-Dist: fastapi>=0.104; extra == "api"
36
+ Requires-Dist: uvicorn[standard]>=0.24; extra == "api"
37
+ Provides-Extra: all
38
+ Requires-Dist: pandas>=2.0; extra == "all"
39
+ Requires-Dist: scipy>=1.10; extra == "all"
40
+ Requires-Dist: rapidfuzz>=3.0; extra == "all"
41
+ Requires-Dist: timezonefinder>=6.0; extra == "all"
42
+ Requires-Dist: requests>=2.31; extra == "all"
43
+ Requires-Dist: fastapi>=0.104; extra == "all"
44
+ Requires-Dist: uvicorn[standard]>=0.24; extra == "all"
45
+ Dynamic: license-file
46
+
47
+ # AeroNavX
48
+
49
+ A production-grade Python library for airport data and flight geometry calculations.
50
+
51
+ **🆕 v0.2.0:** Now with **84,000+ airports** from [OurAirports](https://ourairports.com) (MIT License)
52
+
53
+ ## Features
54
+
55
+ - 🛫 **Airport Database**: 84,000+ global airports with efficient IATA/ICAO indexing
56
+ - 📏 **Distance Calculations**: Haversine, Vincenty, and Spherical Law of Cosines
57
+ - 🌍 **Geodesy**: Bearings, midpoints, great circle paths
58
+ - 🔍 **Search**: Fuzzy name search, nearest neighbor queries, radius search
59
+ - 🛤️ **Routing**: Multi-segment routes, flight time estimation, shortest paths
60
+ - 📊 **Analytics**: Statistics by country, continent, type, and elevation
61
+ - ⏰ **Timezone Support**: Automatic timezone detection and local time
62
+ - 🌱 **Emissions**: CO2 emissions estimation per passenger
63
+ - 🌤️ **Weather**: METAR and TAF data fetching
64
+ - 💻 **CLI**: Command-line interface for quick queries
65
+ - 🌐 **REST API**: FastAPI-based web service
66
+
67
+ ## Installation
68
+
69
+ ```bash
70
+ pip install aeronavx
71
+ ```
72
+
73
+ **Or from source:**
74
+ ```bash
75
+ git clone https://github.com/teyfikoz/AeroNavX.git
76
+ cd AeroNavX
77
+ pip install -e .
78
+ ```
79
+
80
+ ## Quick Start
81
+
82
+ ```python
83
+ import aeronavx
84
+
85
+ # Get airports
86
+ ist = aeronavx.get_airport("IST")
87
+ jfk = aeronavx.get_airport("JFK")
88
+
89
+ # Calculate distance
90
+ dist_km = ist.distance_to(jfk)
91
+ print(f"Distance: {dist_km:.2f} km")
92
+
93
+ # Find nearest airports
94
+ nearest = aeronavx.nearest_airport(41.0, 29.0, n=5)
95
+
96
+ # Estimate emissions
97
+ co2 = aeronavx.estimate_co2_kg_for_segment("IST", "JFK")
98
+ print(f"CO2: {co2:.2f} kg per passenger")
99
+ ```
100
+
101
+ ### Advanced: Filtering Airports
102
+
103
+ ```python
104
+ from aeronavx.core import loader
105
+
106
+ # Load only major airports (large + medium with scheduled service)
107
+ major_airports = loader.load_airports(
108
+ include_types=['large_airport', 'medium_airport'],
109
+ scheduled_service_only=True
110
+ )
111
+ print(f"Major airports: {len(major_airports):,}") # ~3,200
112
+
113
+ # Load specific countries
114
+ us_airports = loader.load_airports(countries=['US'])
115
+ print(f"US airports: {len(us_airports):,}") # ~20,000
116
+
117
+ # Load airports with IATA codes only
118
+ iata_airports = loader.load_airports(has_iata_only=True)
119
+ print(f"IATA airports: {len(iata_airports):,}") # ~9,000
120
+ ```
121
+
122
+ ## CLI Usage
123
+
124
+ ```bash
125
+ # Calculate distance
126
+ aeronavx distance --from IST --to JFK --unit nmi
127
+
128
+ # Find nearest airports
129
+ aeronavx nearest --lat 41.0 --lon 29.0 --n 5
130
+
131
+ # Search by name
132
+ aeronavx search --name "Heathrow"
133
+
134
+ # Estimate emissions
135
+ aeronavx emissions --from IST --to LHR
136
+
137
+ # Flight time
138
+ aeronavx flight-time --from IST --to JFK
139
+ ```
140
+
141
+ ## API Server
142
+
143
+ ```bash
144
+ python -m aeronavx.api.server
145
+ ```
146
+
147
+ Then access:
148
+ - http://localhost:8000/health
149
+ - http://localhost:8000/airport/IST
150
+ - http://localhost:8000/distance?from=IST&to=JFK
151
+ - http://localhost:8000/nearest?lat=41.0&lon=29.0&n=5
152
+
153
+ ## Data
154
+
155
+ AeroNavX includes **84,000+ airports** from [OurAirports](https://ourairports.com/data/), which provides:
156
+ - ✅ **Global Coverage**: Airports, heliports, seaplane bases, and more
157
+ - ✅ **MIT License**: Free to use commercially
158
+ - ✅ **Regular Updates**: Community-maintained and updated
159
+ - ✅ **Comprehensive Data**: IATA/ICAO codes, coordinates, types, and more
160
+
161
+ **Data Attribution:**
162
+ Airport data from [OurAirports](https://ourairports.com) (David Megginson et al.) - Licensed under [MIT License](https://github.com/davidmegginson/ourairports-data)
163
+
164
+ ## Examples
165
+
166
+ See `examples/` directory for:
167
+ - `basic_distance.py`: Distance calculations
168
+ - `nearest_airports.py`: Finding nearby airports
169
+ - `routing_example.py`: Multi-segment routes
170
+ - `emissions_example.py`: CO2 estimation
171
+
172
+ ## Testing
173
+
174
+ ```bash
175
+ pytest
176
+ ```
177
+
178
+ ## Dependencies
179
+
180
+ **Required**: Python >= 3.10
181
+
182
+ **Optional**:
183
+ - `pandas`: DataFrame support
184
+ - `scipy`: Faster spatial indexing
185
+ - `rapidfuzz`: Better fuzzy search
186
+ - `timezonefinder`: Timezone support
187
+ - `fastapi`, `uvicorn`: API server
188
+ - `requests`: Weather data
189
+
190
+ ## License
191
+
192
+ MIT License
193
+
194
+ ## Contributing
195
+
196
+ Contributions welcome! Please open an issue or pull request.
@@ -0,0 +1,150 @@
1
+ # AeroNavX
2
+
3
+ A production-grade Python library for airport data and flight geometry calculations.
4
+
5
+ **🆕 v0.2.0:** Now with **84,000+ airports** from [OurAirports](https://ourairports.com) (MIT License)
6
+
7
+ ## Features
8
+
9
+ - 🛫 **Airport Database**: 84,000+ global airports with efficient IATA/ICAO indexing
10
+ - 📏 **Distance Calculations**: Haversine, Vincenty, and Spherical Law of Cosines
11
+ - 🌍 **Geodesy**: Bearings, midpoints, great circle paths
12
+ - 🔍 **Search**: Fuzzy name search, nearest neighbor queries, radius search
13
+ - 🛤️ **Routing**: Multi-segment routes, flight time estimation, shortest paths
14
+ - 📊 **Analytics**: Statistics by country, continent, type, and elevation
15
+ - ⏰ **Timezone Support**: Automatic timezone detection and local time
16
+ - 🌱 **Emissions**: CO2 emissions estimation per passenger
17
+ - 🌤️ **Weather**: METAR and TAF data fetching
18
+ - 💻 **CLI**: Command-line interface for quick queries
19
+ - 🌐 **REST API**: FastAPI-based web service
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ pip install aeronavx
25
+ ```
26
+
27
+ **Or from source:**
28
+ ```bash
29
+ git clone https://github.com/teyfikoz/AeroNavX.git
30
+ cd AeroNavX
31
+ pip install -e .
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ```python
37
+ import aeronavx
38
+
39
+ # Get airports
40
+ ist = aeronavx.get_airport("IST")
41
+ jfk = aeronavx.get_airport("JFK")
42
+
43
+ # Calculate distance
44
+ dist_km = ist.distance_to(jfk)
45
+ print(f"Distance: {dist_km:.2f} km")
46
+
47
+ # Find nearest airports
48
+ nearest = aeronavx.nearest_airport(41.0, 29.0, n=5)
49
+
50
+ # Estimate emissions
51
+ co2 = aeronavx.estimate_co2_kg_for_segment("IST", "JFK")
52
+ print(f"CO2: {co2:.2f} kg per passenger")
53
+ ```
54
+
55
+ ### Advanced: Filtering Airports
56
+
57
+ ```python
58
+ from aeronavx.core import loader
59
+
60
+ # Load only major airports (large + medium with scheduled service)
61
+ major_airports = loader.load_airports(
62
+ include_types=['large_airport', 'medium_airport'],
63
+ scheduled_service_only=True
64
+ )
65
+ print(f"Major airports: {len(major_airports):,}") # ~3,200
66
+
67
+ # Load specific countries
68
+ us_airports = loader.load_airports(countries=['US'])
69
+ print(f"US airports: {len(us_airports):,}") # ~20,000
70
+
71
+ # Load airports with IATA codes only
72
+ iata_airports = loader.load_airports(has_iata_only=True)
73
+ print(f"IATA airports: {len(iata_airports):,}") # ~9,000
74
+ ```
75
+
76
+ ## CLI Usage
77
+
78
+ ```bash
79
+ # Calculate distance
80
+ aeronavx distance --from IST --to JFK --unit nmi
81
+
82
+ # Find nearest airports
83
+ aeronavx nearest --lat 41.0 --lon 29.0 --n 5
84
+
85
+ # Search by name
86
+ aeronavx search --name "Heathrow"
87
+
88
+ # Estimate emissions
89
+ aeronavx emissions --from IST --to LHR
90
+
91
+ # Flight time
92
+ aeronavx flight-time --from IST --to JFK
93
+ ```
94
+
95
+ ## API Server
96
+
97
+ ```bash
98
+ python -m aeronavx.api.server
99
+ ```
100
+
101
+ Then access:
102
+ - http://localhost:8000/health
103
+ - http://localhost:8000/airport/IST
104
+ - http://localhost:8000/distance?from=IST&to=JFK
105
+ - http://localhost:8000/nearest?lat=41.0&lon=29.0&n=5
106
+
107
+ ## Data
108
+
109
+ AeroNavX includes **84,000+ airports** from [OurAirports](https://ourairports.com/data/), which provides:
110
+ - ✅ **Global Coverage**: Airports, heliports, seaplane bases, and more
111
+ - ✅ **MIT License**: Free to use commercially
112
+ - ✅ **Regular Updates**: Community-maintained and updated
113
+ - ✅ **Comprehensive Data**: IATA/ICAO codes, coordinates, types, and more
114
+
115
+ **Data Attribution:**
116
+ Airport data from [OurAirports](https://ourairports.com) (David Megginson et al.) - Licensed under [MIT License](https://github.com/davidmegginson/ourairports-data)
117
+
118
+ ## Examples
119
+
120
+ See `examples/` directory for:
121
+ - `basic_distance.py`: Distance calculations
122
+ - `nearest_airports.py`: Finding nearby airports
123
+ - `routing_example.py`: Multi-segment routes
124
+ - `emissions_example.py`: CO2 estimation
125
+
126
+ ## Testing
127
+
128
+ ```bash
129
+ pytest
130
+ ```
131
+
132
+ ## Dependencies
133
+
134
+ **Required**: Python >= 3.10
135
+
136
+ **Optional**:
137
+ - `pandas`: DataFrame support
138
+ - `scipy`: Faster spatial indexing
139
+ - `rapidfuzz`: Better fuzzy search
140
+ - `timezonefinder`: Timezone support
141
+ - `fastapi`, `uvicorn`: API server
142
+ - `requests`: Weather data
143
+
144
+ ## License
145
+
146
+ MIT License
147
+
148
+ ## Contributing
149
+
150
+ Contributions welcome! Please open an issue or pull request.
@@ -0,0 +1,69 @@
1
+ from .models import Airport, Runway
2
+ from .core.airports import get as get_airport, get_by_iata, get_by_icao, search_by_name as search_airports_by_name
3
+ from .core.distance import distance, distance_km, distance_mi, distance_nmi
4
+ from .core.geodesy import initial_bearing, midpoint, great_circle_path
5
+ from .core.search import nearest_airport, nearest_airports, airports_within_radius
6
+ from .core.routing import estimate_flight_time_hours as estimate_flight_time, route_distance
7
+ from .core.emissions import estimate_co2_kg_by_codes as estimate_co2_kg_for_segment
8
+ from .core.weather import get_metar, get_taf
9
+ from .core.runways import (
10
+ get_runways_by_airport,
11
+ get_longest_runway,
12
+ get_paved_runways,
13
+ )
14
+ from .core.statistics import (
15
+ get_country_stats,
16
+ get_continent_stats,
17
+ get_global_stats,
18
+ get_top_countries_by_airports,
19
+ get_top_countries_by_large_airports,
20
+ )
21
+ from .exceptions import (
22
+ AeroNavXError,
23
+ AirportNotFoundError,
24
+ InvalidAirportCodeError,
25
+ DataLoadError,
26
+ RoutingError,
27
+ WeatherDataError,
28
+ )
29
+
30
+
31
+ __version__ = "0.3.1"
32
+
33
+ __all__ = [
34
+ "Airport",
35
+ "Runway",
36
+ "get_airport",
37
+ "get_by_iata",
38
+ "get_by_icao",
39
+ "distance",
40
+ "distance_km",
41
+ "distance_mi",
42
+ "distance_nmi",
43
+ "initial_bearing",
44
+ "midpoint",
45
+ "great_circle_path",
46
+ "nearest_airport",
47
+ "nearest_airports",
48
+ "airports_within_radius",
49
+ "estimate_flight_time",
50
+ "route_distance",
51
+ "estimate_co2_kg_for_segment",
52
+ "search_airports_by_name",
53
+ "get_metar",
54
+ "get_taf",
55
+ "get_runways_by_airport",
56
+ "get_longest_runway",
57
+ "get_paved_runways",
58
+ "get_country_stats",
59
+ "get_continent_stats",
60
+ "get_global_stats",
61
+ "get_top_countries_by_airports",
62
+ "get_top_countries_by_large_airports",
63
+ "AeroNavXError",
64
+ "AirportNotFoundError",
65
+ "InvalidAirportCodeError",
66
+ "DataLoadError",
67
+ "RoutingError",
68
+ "WeatherDataError",
69
+ ]
@@ -0,0 +1,3 @@
1
+ from .server import app, run_server
2
+
3
+ __all__ = ["app", "run_server"]
@@ -0,0 +1,180 @@
1
+ from typing import Optional
2
+ from fastapi import FastAPI, HTTPException, Query
3
+ from fastapi.responses import JSONResponse
4
+
5
+ from ..core.airports import get
6
+ from ..core.distance import distance
7
+ from ..core.search import nearest_airports, search_airports_by_name
8
+ from ..core.routing import estimate_flight_time_hours
9
+ from ..core.emissions import estimate_co2_kg_by_codes
10
+ from ..exceptions import AeroNavXError
11
+
12
+
13
+ app = FastAPI(
14
+ title="AeroNavX API",
15
+ description="Airport and flight geometry utilities",
16
+ version="0.1.0"
17
+ )
18
+
19
+
20
+ @app.get("/health")
21
+ async def health():
22
+ return {"status": "ok", "service": "AeroNavX API"}
23
+
24
+
25
+ @app.get("/airport/{code}")
26
+ async def get_airport(
27
+ code: str,
28
+ code_type: str = Query("auto", regex="^(iata|icao|auto)$")
29
+ ):
30
+ try:
31
+ airport = get(code, code_type=code_type)
32
+
33
+ if airport is None:
34
+ raise HTTPException(status_code=404, detail=f"Airport not found: {code}")
35
+
36
+ return airport.as_dict()
37
+
38
+ except AeroNavXError as e:
39
+ raise HTTPException(status_code=400, detail=str(e))
40
+
41
+
42
+ @app.get("/distance")
43
+ async def calculate_distance(
44
+ from_code: str = Query(..., alias="from"),
45
+ to_code: str = Query(..., alias="to"),
46
+ code_type: str = Query("auto", regex="^(iata|icao|auto)$"),
47
+ model: str = Query("haversine", regex="^(haversine|slc|vincenty)$"),
48
+ unit: str = Query("km", regex="^(km|mi|nmi)$")
49
+ ):
50
+ try:
51
+ from_airport = get(from_code, code_type=code_type)
52
+ to_airport = get(to_code, code_type=code_type)
53
+
54
+ if from_airport is None:
55
+ raise HTTPException(status_code=404, detail=f"Origin airport not found: {from_code}")
56
+
57
+ if to_airport is None:
58
+ raise HTTPException(status_code=404, detail=f"Destination airport not found: {to_code}")
59
+
60
+ dist = distance(
61
+ from_airport.latitude_deg,
62
+ from_airport.longitude_deg,
63
+ to_airport.latitude_deg,
64
+ to_airport.longitude_deg,
65
+ model=model,
66
+ unit=unit
67
+ )
68
+
69
+ return {
70
+ "from": from_airport.as_dict(),
71
+ "to": to_airport.as_dict(),
72
+ "distance": dist,
73
+ "unit": unit,
74
+ "model": model
75
+ }
76
+
77
+ except AeroNavXError as e:
78
+ raise HTTPException(status_code=400, detail=str(e))
79
+
80
+
81
+ @app.get("/nearest")
82
+ async def find_nearest(
83
+ lat: float = Query(..., ge=-90, le=90),
84
+ lon: float = Query(..., ge=-180, le=180),
85
+ n: int = Query(5, ge=1, le=100)
86
+ ):
87
+ try:
88
+ airports = nearest_airports(lat, lon, n=n)
89
+
90
+ return {
91
+ "query": {"lat": lat, "lon": lon},
92
+ "count": len(airports),
93
+ "airports": [a.as_dict() for a in airports]
94
+ }
95
+
96
+ except AeroNavXError as e:
97
+ raise HTTPException(status_code=400, detail=str(e))
98
+
99
+
100
+ @app.get("/search")
101
+ async def search(
102
+ q: str = Query(..., min_length=1),
103
+ limit: int = Query(20, ge=1, le=100)
104
+ ):
105
+ try:
106
+ airports = search_airports_by_name(q, limit=limit)
107
+
108
+ return {
109
+ "query": q,
110
+ "count": len(airports),
111
+ "airports": [a.as_dict() for a in airports]
112
+ }
113
+
114
+ except AeroNavXError as e:
115
+ raise HTTPException(status_code=400, detail=str(e))
116
+
117
+
118
+ @app.get("/flight-time")
119
+ async def flight_time(
120
+ from_code: str = Query(..., alias="from"),
121
+ to_code: str = Query(..., alias="to"),
122
+ speed_kts: float = Query(450.0, ge=100, le=1000)
123
+ ):
124
+ try:
125
+ from_airport = get(from_code, code_type="auto")
126
+ to_airport = get(to_code, code_type="auto")
127
+
128
+ if from_airport is None:
129
+ raise HTTPException(status_code=404, detail=f"Origin airport not found: {from_code}")
130
+
131
+ if to_airport is None:
132
+ raise HTTPException(status_code=404, detail=f"Destination airport not found: {to_code}")
133
+
134
+ time_hours = estimate_flight_time_hours(from_airport, to_airport, speed_kts=speed_kts)
135
+
136
+ hours = int(time_hours)
137
+ minutes = int((time_hours - hours) * 60)
138
+
139
+ return {
140
+ "from": from_airport.as_dict(),
141
+ "to": to_airport.as_dict(),
142
+ "speed_kts": speed_kts,
143
+ "time_hours": time_hours,
144
+ "time_formatted": f"{hours}h {minutes}m"
145
+ }
146
+
147
+ except AeroNavXError as e:
148
+ raise HTTPException(status_code=400, detail=str(e))
149
+
150
+
151
+ @app.get("/emissions")
152
+ async def emissions(
153
+ from_code: str = Query(..., alias="from"),
154
+ to_code: str = Query(..., alias="to"),
155
+ model: str = Query("haversine", regex="^(haversine|slc|vincenty)$")
156
+ ):
157
+ try:
158
+ co2_kg = estimate_co2_kg_by_codes(from_code, to_code, code_type="auto", model=model)
159
+
160
+ from_airport = get(from_code, code_type="auto")
161
+ to_airport = get(to_code, code_type="auto")
162
+
163
+ return {
164
+ "from": from_airport.as_dict(),
165
+ "to": to_airport.as_dict(),
166
+ "co2_kg_per_passenger": co2_kg,
167
+ "model": model
168
+ }
169
+
170
+ except AeroNavXError as e:
171
+ raise HTTPException(status_code=400, detail=str(e))
172
+
173
+
174
+ def run_server(host: str = "0.0.0.0", port: int = 8000):
175
+ import uvicorn
176
+ uvicorn.run(app, host=host, port=port)
177
+
178
+
179
+ if __name__ == "__main__":
180
+ run_server()
@@ -0,0 +1,3 @@
1
+ from .main import main
2
+
3
+ __all__ = ["main"]