Skip to content

Commit 365298c

Browse files
committed
swtich over to opensky API fro production
1 parent 7be6d70 commit 365298c

File tree

8 files changed

+75
-14
lines changed

8 files changed

+75
-14
lines changed

.github/workflows/deploy.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,6 @@ jobs:
5454
delay: 5
5555
env:
5656
HD_ADSBEXCHANGE_API_KEY: ${{ secrets.ADSBEXCHANGE_API_KEY }}
57+
HD_DATA_PROVIDER: ${{ secrets.DATA_PROVIDER }}
5758
HD_OPENSKY_CLIENT_ID: ${{ secrets.OPENSKY_CLIENT_ID }}
5859
HD_OPENSKY_CLIENT_SECRET: ${{ secrets.OPENSKY_CLIENT_SECRET }}

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ RUN <<EOF cat > /entrypoint.sh
3232
exec python -m local_flight_map \
3333
--app-port \${PORT:-5006} \
3434
--app-dev-mode false \
35-
--map-refresh-interval 1000
35+
--map-refresh-interval 1000 \
3636
EOF
3737
RUN chmod +x /entrypoint.sh
3838

src/local_flight_map/api/opensky/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ async def get_states_from_opensky(
120120
if bbox:
121121
bbox.validate()
122122

123-
params = {}
123+
params = {'extended': 1}
124124
if time_secs:
125125
if isinstance(time_secs, datetime):
126126
time_secs = int(time_secs.timestamp())

src/local_flight_map/ui/app/config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class MapConfig(BaseSettings):
2929
map_refresh_interval: The interval between map updates in milliseconds.
3030
data_batch_size: The number of aircraft to process in each batch.
3131
data_max_threads: The maximum number of concurrent threads for data processing.
32-
data_provider: The source of aircraft data (adsbexchange or opensky).
32+
data_provider: The source of aircraft data (adsbexchange, opensky, opensky_personal).
3333
app_port: The port number for the web application.
3434
app_dev_mode: Whether to run in development mode.
3535
"""
@@ -66,7 +66,7 @@ class MapConfig(BaseSettings):
6666
default=10,
6767
description="The maximum number of threads to use for the data"
6868
)
69-
data_provider: Literal["adsbexchange", "opensky"] = Field(
69+
data_provider: Literal["adsbexchange", "opensky", "opensky_personal"] = Field(
7070
default="adsbexchange",
7171
description="The provider of the data",
7272
)

src/local_flight_map/ui/app/data.py

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from typing import Dict, Any, Union, Tuple, Optional
77
import asyncio
8+
import traceback
89

910
from ...api import ApiClients, Location
1011
from ...api.adsbexchange import AdsbExchangeResponse
@@ -64,13 +65,41 @@ def _generate_tags(self, feature: Dict[str, Any], inplace: bool = False) -> list
6465
'callsign': properties.get('callsign'),
6566
'registration': properties.get('registration'),
6667
'altitude': properties.get('baro_altitude') or properties.get('geom_altitude'),
67-
'speed': properties.get('ground_speed'),
68+
'speed': properties.get('ground_speed') or properties.get('velocity'),
6869
'emergency': properties.get('emergency_status'),
69-
'category': properties.get('category'),
70+
'category': {
71+
0: 'no_information',
72+
1: 'no_adsb_emitter_category_information',
73+
2: 'light',
74+
3: 'small',
75+
4: 'large',
76+
5: 'high_vortex_large',
77+
6: 'heavy',
78+
7: 'high_performance',
79+
8: 'rotorcraft',
80+
9: 'glider_sailplane',
81+
10: 'lighter_than_air',
82+
11: 'parachutist_skydiver',
83+
12: 'ultralight_hangglider_paraglider',
84+
13: 'reserved',
85+
14: 'unmanned_aerial_vehicle',
86+
15: 'space_transatmospheric_vehicle',
87+
16: 'surface_vehicle_emergency_vehicle',
88+
17: 'surface_vehicle_service_vehicle',
89+
18: 'point_obstacle_includes_tethered_balloons',
90+
19: 'cluster_obstacle',
91+
20: 'line_obstacle',
92+
}.get(properties.get('category')),
93+
"position_source": {
94+
0: 'adsb',
95+
1: 'asterix',
96+
2: 'mlat',
97+
3: 'flarm',
98+
}.get(properties.get('position_source')),
7099
}
71100

72101
for key, value in optional_tags.items():
73-
if value:
102+
if value is not None:
74103
match key:
75104
case 'altitude':
76105
try:
@@ -100,6 +129,14 @@ def _generate_tags(self, feature: Dict[str, Any], inplace: bool = False) -> list
100129
else:
101130
tags.append('speed:fast')
102131

132+
case 'category':
133+
properties["category"] = value
134+
tags.append(f"category:{value}")
135+
136+
case 'position_source':
137+
properties["position_source"] = value
138+
tags.append(f"position_source:{value}")
139+
103140
case _:
104141
tags.append(f"{key}:{value}")
105142

@@ -158,13 +195,19 @@ async def resolve_route(label: str, route: str) -> Tuple[str, Optional[Dict[str,
158195
if route
159196
]
160197
):
198+
if not airport:
199+
logger.error(f"No airport found for {label}")
200+
continue
161201
for name, value in airport["properties"].items():
162202
feature["properties"][f"{label}_{name}"] = value
163203

164204
self._generate_tags(feature, inplace=True)
165205

166206
except Exception as e:
167-
logger.error(f"Error processing feature {icao24}/{callsign}: {str(e)}")
207+
logger.error(
208+
f"Error processing feature icao24:{icao24} callsign:{callsign}: "
209+
f"{str(e)}\n{traceback.format_exc()}"
210+
)
168211

169212
finally:
170213
return feature
@@ -188,6 +231,9 @@ async def get_aircrafts_geojson(self) -> Dict[str, Any]:
188231
case 'opensky':
189232
args = (0, None, self._config.map_bbox)
190233
method = self._clients.opensky_client.get_states_from_opensky
234+
case 'opensky_personal':
235+
args = (0, None, None)
236+
method = self._clients.opensky_client.get_my_states_from_opensky
191237
case _:
192238
raise ValueError(f"Invalid provider: {self._config.data_provider}")
193239

src/local_flight_map/ui/app/static/js/10_flight_marker_utils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ const FlightMarkerUtils = {
113113
}
114114

115115
const altitude = props.baro_altitude || props.geom_altitude || 0;
116-
const groundSpeed = props.ground_speed || 0;
116+
const groundSpeed = props.ground_speed || props.velocity || 0;
117117
const { vsColor, vsSymbol } = this.calculateVerticalSpeedIndicator(props);
118118
const flightLevel = altitude === 'ground' ? 0 : Math.round(altitude / 100);
119119

src/local_flight_map/ui/plugins/js/realtime_point_to_layer.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,18 @@
233233
}
234234
}
235235

236+
/**
237+
* Convert snake case to title case with spaces
238+
* @param {string} str - The string to convert
239+
* @returns {string} The converted string
240+
*/
241+
function snakeToTitleCase(str) {
242+
return str
243+
.split('_')
244+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
245+
.join(' ');
246+
}
247+
236248
/**
237249
* Create popup content for the marker
238250
* @param {Object} props - Aircraft properties
@@ -248,7 +260,7 @@
248260
const [key, value] = tag.split(':');
249261
return `
250262
<span class="tag">
251-
<span class="tag-key">${key}</span>
263+
<span class="tag-key">${snakeToTitleCase(key)}</span>
252264
<span class="tag-value">${value}</span>
253265
</span>
254266
`;
@@ -280,7 +292,7 @@
280292
<table>
281293
<tbody>`;
282294
for (const [key, value] of originProps) {
283-
const displayKey = key.replace('origin_', '');
295+
const displayKey = snakeToTitleCase(key.replace('origin_', ''));
284296
const displayValue = typeof value === 'object' && value !== null ? JSON.stringify(value, null, 2) : value;
285297
content += `
286298
<tr>
@@ -301,7 +313,7 @@
301313
<table>
302314
<tbody>`;
303315
for (const [key, value] of destinationProps) {
304-
const displayKey = key.replace('destination_', '');
316+
const displayKey = snakeToTitleCase(key.replace('destination_', ''));
305317
const displayValue = typeof value === 'object' && value !== null ? JSON.stringify(value, null, 2) : value;
306318
content += `
307319
<tr>
@@ -329,10 +341,11 @@
329341
<table>
330342
<tbody>`;
331343
for (const [key, value] of remainingProps) {
344+
const displayKey = snakeToTitleCase(key);
332345
const displayValue = typeof value === 'object' && value !== null ? JSON.stringify(value, null, 2) : value;
333346
content += `
334347
<tr>
335-
<th>${key}</th>
348+
<th>${displayKey}</th>
336349
<td>${displayValue}</td>
337350
</tr>`;
338351
}

tests/test_api_client_opensky.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ async def test_get_states_from_opensky(self, opensky_client):
114114
# Verify the API call
115115
mock_session.get.assert_called_once_with(
116116
"/api/states/all",
117-
params={}
117+
params={'extended': 1}
118118
)
119119

120120
@pytest.mark.asyncio
@@ -154,6 +154,7 @@ async def test_get_states_from_opensky_with_params(self, opensky_client):
154154
mock_session.get.assert_called_once_with(
155155
"/api/states/all",
156156
params={
157+
'extended': 1,
157158
'time': int(time.timestamp()),
158159
'icao24': 'a83547,b12345',
159160
'lamax': 41.0,

0 commit comments

Comments
 (0)