windborne 1.1.2__py3-none-any.whl → 1.1.3__py3-none-any.whl
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.
- windborne/__init__.py +2 -0
- windborne/cli.py +17 -1
- windborne/data_api.py +60 -10
- windborne/forecasts_api.py +7 -43
- windborne/{cyclone_formatting.py → track_formatting.py} +75 -23
- {windborne-1.1.2.dist-info → windborne-1.1.3.dist-info}/METADATA +1 -1
- windborne-1.1.3.dist-info/RECORD +13 -0
- windborne-1.1.2.dist-info/RECORD +0 -13
- {windborne-1.1.2.dist-info → windborne-1.1.3.dist-info}/WHEEL +0 -0
- {windborne-1.1.2.dist-info → windborne-1.1.3.dist-info}/entry_points.txt +0 -0
- {windborne-1.1.2.dist-info → windborne-1.1.3.dist-info}/top_level.txt +0 -0
windborne/__init__.py
CHANGED
@@ -14,6 +14,7 @@ from .data_api import (
|
|
14
14
|
get_flying_missions,
|
15
15
|
get_mission_launch_site,
|
16
16
|
get_predicted_path,
|
17
|
+
get_flight_path
|
17
18
|
)
|
18
19
|
|
19
20
|
# Import Forecasts API functions
|
@@ -50,6 +51,7 @@ __all__ = [
|
|
50
51
|
"get_flying_missions",
|
51
52
|
"get_mission_launch_site",
|
52
53
|
"get_predicted_path",
|
54
|
+
"get_flight_path",
|
53
55
|
|
54
56
|
"get_point_forecasts",
|
55
57
|
"get_initialization_times",
|
windborne/cli.py
CHANGED
@@ -14,6 +14,7 @@ from . import (
|
|
14
14
|
get_flying_missions,
|
15
15
|
get_mission_launch_site,
|
16
16
|
get_predicted_path,
|
17
|
+
get_flight_path,
|
17
18
|
|
18
19
|
get_point_forecasts,
|
19
20
|
get_initialization_times,
|
@@ -125,6 +126,11 @@ def main():
|
|
125
126
|
prediction_parser.add_argument('mission_id', help='Mission ID')
|
126
127
|
prediction_parser.add_argument('output', nargs='?', help='Output file')
|
127
128
|
|
129
|
+
# Get Flight Path Command
|
130
|
+
flight_path_parser = subparsers.add_parser('flight-path', help='Get traveled flight path')
|
131
|
+
flight_path_parser.add_argument('mission_id', help='Mission ID')
|
132
|
+
flight_path_parser.add_argument('output', nargs='?', help='Output file')
|
133
|
+
|
128
134
|
####################################################################################################################
|
129
135
|
# FORECASTS API FUNCTIONS
|
130
136
|
####################################################################################################################
|
@@ -377,8 +383,18 @@ def main():
|
|
377
383
|
elif args.command == 'predict-path':
|
378
384
|
get_predicted_path(
|
379
385
|
mission_id=args.mission_id,
|
380
|
-
output_file=args.output
|
386
|
+
output_file=args.output,
|
387
|
+
print_result=(not args.output)
|
388
|
+
)
|
389
|
+
|
390
|
+
elif args.command == 'flight-path':
|
391
|
+
get_flight_path(
|
392
|
+
mission_id=args.mission_id,
|
393
|
+
output_file=args.output,
|
394
|
+
print_result=(not args.output)
|
381
395
|
)
|
396
|
+
|
397
|
+
|
382
398
|
####################################################################################################################
|
383
399
|
# FORECASTS API FUNCTIONS CALLED
|
384
400
|
####################################################################################################################
|
windborne/data_api.py
CHANGED
@@ -7,6 +7,7 @@ import json
|
|
7
7
|
from .api_request import make_api_request
|
8
8
|
from .observation_formatting import format_little_r, convert_to_netcdf
|
9
9
|
from .utils import to_unix_timestamp, save_arbitrary_response, print_table
|
10
|
+
from .track_formatting import save_track
|
10
11
|
|
11
12
|
DATA_API_BASE_URL = "https://sensor-data.windbornesystems.com/api/v1"
|
12
13
|
|
@@ -551,6 +552,9 @@ def get_flying_missions(output_file=None, print_results=False):
|
|
551
552
|
url = f"{DATA_API_BASE_URL}/missions.json"
|
552
553
|
flying_missions_response = make_api_request(url)
|
553
554
|
flying_missions = flying_missions_response.get("missions", [])
|
555
|
+
for mission in flying_missions:
|
556
|
+
if mission.get('number'):
|
557
|
+
mission['name'] = f"W-{mission['number']}"
|
554
558
|
|
555
559
|
# Display currently flying missions only if we are in cli and we don't save info in file
|
556
560
|
if print_results:
|
@@ -617,7 +621,8 @@ def get_mission_launch_site(mission_id=None, output_file=None, print_result=Fals
|
|
617
621
|
|
618
622
|
return response.get('launch_site')
|
619
623
|
|
620
|
-
|
624
|
+
|
625
|
+
def get_predicted_path(mission_id=None, output_file=None, print_result=False):
|
621
626
|
"""
|
622
627
|
Fetches the predicted flight path for a given mission.
|
623
628
|
Displays currently flying missions if the provided mission ID is invalid.
|
@@ -625,7 +630,7 @@ def get_predicted_path(mission_id=None, output_file=None):
|
|
625
630
|
Args:
|
626
631
|
mission_id (str): The ID of the mission to fetch the prediction for.
|
627
632
|
output_file (str): Optional path to save the response data.
|
628
|
-
|
633
|
+
print_result (bool): Whether to print the results in the CLI.
|
629
634
|
|
630
635
|
Returns:
|
631
636
|
list: The API response containing the predicted flight path data.
|
@@ -635,25 +640,70 @@ def get_predicted_path(mission_id=None, output_file=None):
|
|
635
640
|
return
|
636
641
|
|
637
642
|
# Check if provided mission ID belong to a flying mission
|
638
|
-
|
639
|
-
flying_missions = flying_missions_response.get("missions", [])
|
643
|
+
flying_missions = get_flying_missions()
|
640
644
|
|
641
|
-
|
645
|
+
mission = None
|
646
|
+
for candidate in flying_missions:
|
647
|
+
if candidate.get('id') == mission_id or candidate.get('name') == mission_id:
|
648
|
+
mission = candidate
|
649
|
+
break
|
650
|
+
|
651
|
+
if mission is None:
|
642
652
|
print(f"Provided mission ID '{mission_id}' does not belong to a mission that is currently flying.")
|
643
653
|
|
644
654
|
# Display currently flying missions
|
645
655
|
if flying_missions:
|
646
656
|
print("\nCurrently flying missions:\n")
|
647
657
|
|
648
|
-
print_table(flying_missions, keys=['
|
658
|
+
print_table(flying_missions, keys=['id', 'name'], headers=['Mission ID', 'Mission Name'])
|
649
659
|
else:
|
650
660
|
print("No missions are currently flying.")
|
651
661
|
return
|
652
662
|
|
653
|
-
url = f"{DATA_API_BASE_URL}/missions/{
|
663
|
+
url = f"{DATA_API_BASE_URL}/missions/{mission.get('id')}/prediction.json"
|
654
664
|
response = make_api_request(url)
|
655
665
|
|
666
|
+
if response is None:
|
667
|
+
return
|
668
|
+
|
656
669
|
if output_file:
|
657
|
-
|
658
|
-
|
659
|
-
|
670
|
+
name = mission.get('name', mission_id)
|
671
|
+
save_track(output_file, {name: response['prediction']}, time_key='time')
|
672
|
+
|
673
|
+
if print_result:
|
674
|
+
print("Predicted flight path\n")
|
675
|
+
print_table(response['prediction'], keys=['time', 'latitude', 'longitude', 'altitude'], headers=['Time', 'Latitude', 'Longitude', 'Altitude'])
|
676
|
+
|
677
|
+
return response.get('prediction')
|
678
|
+
|
679
|
+
|
680
|
+
def get_flight_path(mission_id=None, output_file=None, print_result=False):
|
681
|
+
"""
|
682
|
+
Fetches the flight path for a given mission.
|
683
|
+
|
684
|
+
Args:
|
685
|
+
mission_id (str): The ID of the mission to fetch the flight path for.
|
686
|
+
output_file (str): Optional path to save the response data.
|
687
|
+
print_result (bool): Whether to print the results in the CLI.
|
688
|
+
|
689
|
+
Returns:
|
690
|
+
list: The API response containing the flight path.
|
691
|
+
"""
|
692
|
+
if not mission_id:
|
693
|
+
print("A mission id is required to get a flight path")
|
694
|
+
return
|
695
|
+
|
696
|
+
url = f"{DATA_API_BASE_URL}/missions/{mission_id}/flight_data.json"
|
697
|
+
response = make_api_request(url)
|
698
|
+
|
699
|
+
if response is None:
|
700
|
+
return
|
701
|
+
|
702
|
+
if output_file:
|
703
|
+
save_track(output_file, {mission_id: response['flight_data']}, time_key='transmit_time')
|
704
|
+
|
705
|
+
if print_result:
|
706
|
+
print("Flight path\n")
|
707
|
+
print_table(response['flight_data'], keys=['transmit_time', 'latitude', 'longitude', 'altitude'], headers=['Time', 'Latitude', 'Longitude', 'Altitude'])
|
708
|
+
|
709
|
+
return response.get('flight_data')
|
windborne/forecasts_api.py
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
import requests
|
2
|
-
import json
|
3
2
|
|
4
3
|
from .utils import (
|
5
4
|
parse_time,
|
@@ -7,22 +6,14 @@ from .utils import (
|
|
7
6
|
print_table
|
8
7
|
)
|
9
8
|
|
10
|
-
from .api_request import
|
11
|
-
|
12
|
-
)
|
13
|
-
|
14
|
-
from .cyclone_formatting import (
|
15
|
-
save_track_as_geojson,
|
16
|
-
save_track_as_gpx,
|
17
|
-
save_track_as_kml,
|
18
|
-
save_track_as_little_r
|
19
|
-
)
|
9
|
+
from .api_request import make_api_request
|
10
|
+
from .track_formatting import save_track
|
20
11
|
|
21
12
|
FORECASTS_API_BASE_URL = "https://forecasts.windbornesystems.com/api/v1"
|
22
13
|
FORECASTS_GRIDDED_URL = f"{FORECASTS_API_BASE_URL}/gridded"
|
23
14
|
FORECASTS_HISTORICAL_URL = f"{FORECASTS_API_BASE_URL}/gridded/historical"
|
24
15
|
FORECASTS_TCS_URL = f"{FORECASTS_API_BASE_URL}/tropical_cyclones"
|
25
|
-
TCS_SUPPORTED_FORMATS = ('.csv', '.json', '.geojson', '.gpx', '.kml', 'little_r')
|
16
|
+
TCS_SUPPORTED_FORMATS = ('.csv', '.json', '.geojson', '.gpx', '.kml', '.little_r')
|
26
17
|
|
27
18
|
|
28
19
|
# Point forecasts
|
@@ -263,11 +254,7 @@ def get_tropical_cyclones(initialization_time=None, basin=None, output_file=None
|
|
263
254
|
response = make_api_request(FORECASTS_TCS_URL, params=params)
|
264
255
|
|
265
256
|
if output_file:
|
266
|
-
if
|
267
|
-
print("You have to provide a filetype for your output file.")
|
268
|
-
print_tc_supported_formats()
|
269
|
-
exit (4)
|
270
|
-
elif not output_file.lower().endswith(TCS_SUPPORTED_FORMATS):
|
257
|
+
if not output_file.lower().endswith(TCS_SUPPORTED_FORMATS):
|
271
258
|
print("Unsupported file format.")
|
272
259
|
print_tc_supported_formats()
|
273
260
|
exit(44)
|
@@ -276,37 +263,14 @@ def get_tropical_cyclones(initialization_time=None, basin=None, output_file=None
|
|
276
263
|
# make_api_request covers 403, 404, 502, HTTP, Connections Errors
|
277
264
|
# If we pass all of these and we get an empty dictionary ==> there are no active TCs
|
278
265
|
print("There are no active tropical cyclones for your request\n")
|
279
|
-
print("We didn't save any file on your machine.")
|
280
266
|
# It's pointless to save an empty file
|
281
267
|
# save_response_to_file() will throw error on saving {}
|
282
268
|
elif response is None:
|
283
269
|
print("-------------------------------------------------------")
|
284
270
|
print("You are too quick!\nThe tropical cyclone data for initialization time are not uploaded yet.")
|
285
|
-
print('You may check again in a few
|
286
|
-
|
287
|
-
|
288
|
-
flattened_data = []
|
289
|
-
for cyclone_id, tracks in response.items():
|
290
|
-
for track in tracks:
|
291
|
-
track_data = {
|
292
|
-
'cyclone_id': cyclone_id,
|
293
|
-
'latitude': track['latitude'],
|
294
|
-
'longitude': track['longitude'],
|
295
|
-
'time': track['time']
|
296
|
-
}
|
297
|
-
flattened_data.append(track_data)
|
298
|
-
save_arbitrary_response(output_file, {'prediction': flattened_data}, csv_data_key='prediction')
|
299
|
-
elif output_file.lower().endswith('.json'):
|
300
|
-
# Direct save for JSON
|
301
|
-
save_arbitrary_response(output_file, response)
|
302
|
-
elif output_file.lower().endswith('.geojson'):
|
303
|
-
save_track_as_geojson(output_file, response)
|
304
|
-
elif output_file.lower().endswith('.gpx'):
|
305
|
-
save_track_as_gpx(output_file, response)
|
306
|
-
elif output_file.lower().endswith('.kml'):
|
307
|
-
save_track_as_kml(output_file, response)
|
308
|
-
elif output_file.lower().endswith('.little_r'):
|
309
|
-
save_track_as_little_r(output_file, response)
|
271
|
+
print('You may check again in a few hour.')
|
272
|
+
else:
|
273
|
+
save_track(output_file, response, require_ids=True)
|
310
274
|
|
311
275
|
if print_response:
|
312
276
|
if len(response) == 0:
|
@@ -1,15 +1,69 @@
|
|
1
|
-
from datetime import datetime
|
1
|
+
from datetime import datetime
|
2
|
+
import json
|
2
3
|
|
4
|
+
TRACK_SUPPORTED_FORMATS = ['.csv', '.json', '.geojson', '.gpx', '.kml', 'little_r']
|
3
5
|
|
4
|
-
def
|
6
|
+
def save_track(output_file, track_data, time_key='time', require_ids=False):
|
5
7
|
"""
|
6
|
-
|
8
|
+
Save track data to a file in the specified format.
|
9
|
+
Expects track_data to be a dictionary with cyclone/mission IDs as keys and lists of track points as values.
|
7
10
|
"""
|
11
|
+
include_id = require_ids or len(track_data) > 1
|
12
|
+
|
13
|
+
if output_file.lower().endswith('.json'):
|
14
|
+
with open(output_file, 'w', encoding='utf-8') as f:
|
15
|
+
json.dump(track_data, f, indent=4)
|
16
|
+
elif output_file.lower().endswith('.csv'):
|
17
|
+
save_track_as_csv(output_file, track_data, time_key=time_key, include_id=include_id)
|
18
|
+
elif output_file.lower().endswith('.geojson'):
|
19
|
+
save_track_as_geojson(output_file, track_data, time_key=time_key)
|
20
|
+
elif output_file.lower().endswith('.gpx'):
|
21
|
+
save_track_as_gpx(output_file, track_data, time_key=time_key)
|
22
|
+
elif output_file.lower().endswith('.kml'):
|
23
|
+
save_track_as_kml(output_file, track_data)
|
24
|
+
elif output_file.lower().endswith('.little_r'):
|
25
|
+
save_track_as_little_r(output_file, track_data, time_key=time_key)
|
26
|
+
else:
|
27
|
+
print(f"Unsupported file format. Supported formats are: {', '.join(TRACK_SUPPORTED_FORMATS)}")
|
28
|
+
return
|
29
|
+
|
30
|
+
|
31
|
+
def save_track_as_csv(filename, track_data, time_key='time', include_id=False):
|
32
|
+
"""
|
33
|
+
Convert and save track data as CSV.
|
34
|
+
"""
|
35
|
+
flattened_data = []
|
36
|
+
for name, tracks in track_data.items():
|
37
|
+
for track in tracks:
|
38
|
+
track_data = {
|
39
|
+
'id': name,
|
40
|
+
'latitude': track['latitude'],
|
41
|
+
'longitude': track['longitude'],
|
42
|
+
'time': track[time_key]
|
43
|
+
}
|
44
|
+
flattened_data.append(track_data)
|
45
|
+
|
8
46
|
with open(filename, 'w', encoding='utf-8') as f:
|
9
|
-
|
47
|
+
if include_id:
|
48
|
+
f.write('id,latitude,longitude,time\n')
|
49
|
+
else:
|
50
|
+
f.write('latitude,longitude,time\n')
|
51
|
+
|
52
|
+
for row in flattened_data:
|
53
|
+
if include_id:
|
54
|
+
f.write(f"{row['id']},{row['latitude']},{row['longitude']},{row['time']}\n")
|
55
|
+
else:
|
56
|
+
f.write(f"{row['latitude']},{row['longitude']},{row['time']}\n")
|
57
|
+
|
58
|
+
def save_track_as_little_r(filename, track_data, time_key='time'):
|
59
|
+
"""
|
60
|
+
Convert and save track data in little_R format.
|
61
|
+
"""
|
62
|
+
with open(filename, 'w', encoding='utf-8') as f:
|
63
|
+
for cyclone_id, tracks in track_data.items():
|
10
64
|
for track in tracks:
|
11
65
|
# Parse the time
|
12
|
-
dt = datetime.fromisoformat(track[
|
66
|
+
dt = datetime.fromisoformat(track[time_key].replace('Z', '+00:00'))
|
13
67
|
|
14
68
|
# Header line 1
|
15
69
|
header1 = f"{float(track['latitude']):20.5f}{float(track['longitude']):20.5f}{'HMS':40}"
|
@@ -35,21 +89,20 @@ def save_track_as_little_r(filename, cyclone_data):
|
|
35
89
|
print("Saved to", filename)
|
36
90
|
|
37
91
|
|
38
|
-
def save_track_as_kml(filename,
|
92
|
+
def save_track_as_kml(filename, track_data):
|
39
93
|
"""
|
40
|
-
Convert and save
|
94
|
+
Convert and save track data as KML, handling meridian crossing.
|
41
95
|
"""
|
42
96
|
kml = '<?xml version="1.0" encoding="UTF-8"?>\n'
|
43
97
|
kml += '<kml xmlns="http://www.opengis.net/kml/2.2">\n<Document>\n'
|
44
98
|
|
45
|
-
for
|
46
|
-
kml += f' <Placemark>\n <name>{
|
99
|
+
for id, tracks in track_data.items():
|
100
|
+
kml += f' <Placemark>\n <name>{id}</name>\n <MultiGeometry>\n'
|
47
101
|
|
48
102
|
current_segment = []
|
49
103
|
|
50
104
|
for i in range(len(tracks)):
|
51
105
|
lon = float(tracks[i]['longitude'])
|
52
|
-
lat = float(tracks[i]['latitude'])
|
53
106
|
|
54
107
|
if not current_segment:
|
55
108
|
current_segment.append(tracks[i])
|
@@ -61,7 +114,7 @@ def save_track_as_kml(filename, cyclone_data):
|
|
61
114
|
if abs(lon - prev_lon) > 180:
|
62
115
|
# Write the current segment
|
63
116
|
kml += ' <LineString>\n <coordinates>\n'
|
64
|
-
coordinates = [f' {track["longitude"]},{track["latitude"]},{0}'
|
117
|
+
coordinates = [f' {track["longitude"]},{track["latitude"]},{track.get("altitude", 0)}'
|
65
118
|
for track in current_segment]
|
66
119
|
kml += '\n'.join(coordinates)
|
67
120
|
kml += '\n </coordinates>\n </LineString>\n'
|
@@ -74,7 +127,7 @@ def save_track_as_kml(filename, cyclone_data):
|
|
74
127
|
# Write the last segment if it's not empty
|
75
128
|
if current_segment:
|
76
129
|
kml += ' <LineString>\n <coordinates>\n'
|
77
|
-
coordinates = [f' {track["longitude"]},{track["latitude"]},{0}'
|
130
|
+
coordinates = [f' {track["longitude"]},{track["latitude"]},{track.get("altitude", 0)}'
|
78
131
|
for track in current_segment]
|
79
132
|
kml += '\n'.join(coordinates)
|
80
133
|
kml += '\n </coordinates>\n </LineString>\n'
|
@@ -88,12 +141,12 @@ def save_track_as_kml(filename, cyclone_data):
|
|
88
141
|
print(f"Saved to {filename}")
|
89
142
|
|
90
143
|
|
91
|
-
def save_track_as_gpx(filename,
|
92
|
-
"""Convert and save
|
144
|
+
def save_track_as_gpx(filename, track_data, time_key='time'):
|
145
|
+
"""Convert and save track data as GPX, handling meridian crossing."""
|
93
146
|
gpx = '<?xml version="1.0" encoding="UTF-8"?>\n'
|
94
147
|
gpx += '<gpx version="1.1" creator="Windborne" xmlns="http://www.topografix.com/GPX/1/1">\n'
|
95
148
|
|
96
|
-
for cyclone_id, tracks in
|
149
|
+
for cyclone_id, tracks in track_data.items():
|
97
150
|
gpx += f' <trk>\n <name>{cyclone_id}</name>\n'
|
98
151
|
|
99
152
|
current_segment = []
|
@@ -101,7 +154,6 @@ def save_track_as_gpx(filename, cyclone_data):
|
|
101
154
|
|
102
155
|
for i in range(len(tracks)):
|
103
156
|
lon = float(tracks[i]['longitude'])
|
104
|
-
lat = float(tracks[i]['latitude'])
|
105
157
|
|
106
158
|
if not current_segment:
|
107
159
|
current_segment.append(tracks[i])
|
@@ -130,7 +182,7 @@ def save_track_as_gpx(filename, cyclone_data):
|
|
130
182
|
gpx += ' <trkseg>\n'
|
131
183
|
for point in current_segment:
|
132
184
|
gpx += f' <trkpt lat="{point["latitude"]}" lon="{point["longitude"]}">\n'
|
133
|
-
gpx += f' <time>{point[
|
185
|
+
gpx += f' <time>{point[time_key]}</time>\n'
|
134
186
|
gpx += ' </trkpt>\n'
|
135
187
|
gpx += ' </trkseg>\n'
|
136
188
|
|
@@ -143,10 +195,10 @@ def save_track_as_gpx(filename, cyclone_data):
|
|
143
195
|
print(f"Saved to {filename}")
|
144
196
|
|
145
197
|
|
146
|
-
def save_track_as_geojson(filename,
|
147
|
-
"""Convert and save
|
198
|
+
def save_track_as_geojson(filename, track_data, time_key='time'):
|
199
|
+
"""Convert and save track data as GeoJSON, handling meridian crossing."""
|
148
200
|
features = []
|
149
|
-
for
|
201
|
+
for id, tracks in track_data.items():
|
150
202
|
# Initialize lists to store line segments
|
151
203
|
line_segments = []
|
152
204
|
current_segment = []
|
@@ -188,9 +240,9 @@ def save_track_as_geojson(filename, cyclone_data):
|
|
188
240
|
feature = {
|
189
241
|
"type": "Feature",
|
190
242
|
"properties": {
|
191
|
-
"
|
192
|
-
"start_time": tracks[0][
|
193
|
-
"end_time": tracks[-1][
|
243
|
+
"id": id,
|
244
|
+
"start_time": tracks[0][time_key],
|
245
|
+
"end_time": tracks[-1][time_key]
|
194
246
|
},
|
195
247
|
"geometry": {
|
196
248
|
"type": "MultiLineString",
|
@@ -0,0 +1,13 @@
|
|
1
|
+
windborne/__init__.py,sha256=DcxmiLq4UnjoRjZd4UeuYt29FBsi9paMXGtI91Itn04,1826
|
2
|
+
windborne/api_request.py,sha256=zh1TaaZAaRfAXp2NYMja75fKeduWLfao02JRRFVpQCA,11108
|
3
|
+
windborne/cli.py,sha256=xRPRLh50iC4Uu0AYEHL-LHRxUnpxqB_Mn-easp5VSK4,35370
|
4
|
+
windborne/data_api.py,sha256=QsG1NJyOCzTrTLRaoz5Z4nGRI0HCMEJvbDyRUlY3Usg,31388
|
5
|
+
windborne/forecasts_api.py,sha256=E0X9FyUW2yz3pQZKkFBYDMeg6PhFJ1bp4z1Cg1g0uGs,13987
|
6
|
+
windborne/observation_formatting.py,sha256=c739aaun6aaYhXl5VI-SRGR-TDS355_0Bfu1t6McoiM,14993
|
7
|
+
windborne/track_formatting.py,sha256=LaLfTyjpWoOtHmReJPLViY0MKm_iPL_5I2OB_lNvGGA,10054
|
8
|
+
windborne/utils.py,sha256=H8gvZ4Lrr0UmLl25iMZs6NsZliCY_73Ved_rBIqxJg4,7240
|
9
|
+
windborne-1.1.3.dist-info/METADATA,sha256=unPmA3uPzt8obgqSmJSrsSzSLat4yH7o3uJtb-i3wy0,1235
|
10
|
+
windborne-1.1.3.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
11
|
+
windborne-1.1.3.dist-info/entry_points.txt,sha256=j_YrqdCDrCd7p5MIwQ2BYwNXEi95VNANzLRJmcXEg1U,49
|
12
|
+
windborne-1.1.3.dist-info/top_level.txt,sha256=PE9Lauriu5S5REf7JKhXprufZ_V5RiZ_TnfnrLGJrmE,10
|
13
|
+
windborne-1.1.3.dist-info/RECORD,,
|
windborne-1.1.2.dist-info/RECORD
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
windborne/__init__.py,sha256=0bPtPzBG3djZMVfyUNhapiEqCSyN8SSDsm_eCZ4kwhc,1783
|
2
|
-
windborne/api_request.py,sha256=zh1TaaZAaRfAXp2NYMja75fKeduWLfao02JRRFVpQCA,11108
|
3
|
-
windborne/cli.py,sha256=XvI9a9mO921a_LpI1ojN9Rn_x3sYq3XTkmUUT37wg4g,34835
|
4
|
-
windborne/cyclone_formatting.py,sha256=0S8S_PflRGm6ftUlyR2aGGyX6IRn0hbUTEWptu7f8Q8,7886
|
5
|
-
windborne/data_api.py,sha256=qVA0UGcnkBGYe8TFgqgFlFjZYCsS4ly1IL97tSpgKgI,29751
|
6
|
-
windborne/forecasts_api.py,sha256=QzdayDr3MaswsFibOdBoo6SCdRY86eE2E7kWqn-Jc1Y,15479
|
7
|
-
windborne/observation_formatting.py,sha256=c739aaun6aaYhXl5VI-SRGR-TDS355_0Bfu1t6McoiM,14993
|
8
|
-
windborne/utils.py,sha256=H8gvZ4Lrr0UmLl25iMZs6NsZliCY_73Ved_rBIqxJg4,7240
|
9
|
-
windborne-1.1.2.dist-info/METADATA,sha256=NR1lkRL5m-j3UoWVj_2ti14bSrIXZLtCbwS4DfUvko0,1235
|
10
|
-
windborne-1.1.2.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
11
|
-
windborne-1.1.2.dist-info/entry_points.txt,sha256=j_YrqdCDrCd7p5MIwQ2BYwNXEi95VNANzLRJmcXEg1U,49
|
12
|
-
windborne-1.1.2.dist-info/top_level.txt,sha256=PE9Lauriu5S5REf7JKhXprufZ_V5RiZ_TnfnrLGJrmE,10
|
13
|
-
windborne-1.1.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|