windborne 1.1.4__py3-none-any.whl → 1.2.0__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 +6 -2
- windborne/api_request.py +4 -1
- windborne/cli.py +124 -257
- windborne/data_api.py +19 -1
- windborne/forecasts_api.py +86 -62
- {windborne-1.1.4.dist-info → windborne-1.2.0.dist-info}/METADATA +2 -2
- windborne-1.2.0.dist-info/RECORD +13 -0
- {windborne-1.1.4.dist-info → windborne-1.2.0.dist-info}/WHEEL +1 -1
- windborne-1.1.4.dist-info/RECORD +0 -13
- {windborne-1.1.4.dist-info → windborne-1.2.0.dist-info}/entry_points.txt +0 -0
- {windborne-1.1.4.dist-info → windborne-1.2.0.dist-info}/top_level.txt +0 -0
windborne/__init__.py
CHANGED
@@ -22,10 +22,12 @@ from .data_api import (
|
|
22
22
|
from .forecasts_api import (
|
23
23
|
get_point_forecasts,
|
24
24
|
get_initialization_times,
|
25
|
+
get_forecast_hours,
|
25
26
|
|
27
|
+
get_gridded_forecast,
|
26
28
|
get_full_gridded_forecast,
|
27
29
|
get_temperature_2m,
|
28
|
-
|
30
|
+
get_dewpoint_2m,
|
29
31
|
get_wind_u_10m, get_wind_v_10m,
|
30
32
|
get_pressure_msl,
|
31
33
|
get_500hpa_wind_u, get_500hpa_wind_v,
|
@@ -58,10 +60,12 @@ __all__ = [
|
|
58
60
|
|
59
61
|
"get_point_forecasts",
|
60
62
|
"get_initialization_times",
|
63
|
+
"get_forecast_hours",
|
61
64
|
|
65
|
+
"get_gridded_forecast",
|
62
66
|
"get_full_gridded_forecast",
|
63
67
|
"get_temperature_2m",
|
64
|
-
|
68
|
+
"get_dewpoint_2m",
|
65
69
|
"get_wind_u_10m",
|
66
70
|
"get_wind_v_10m",
|
67
71
|
"get_500hpa_wind_u",
|
windborne/api_request.py
CHANGED
@@ -199,6 +199,9 @@ def make_api_request(url, params=None, as_json=True, retry_counter=0):
|
|
199
199
|
mission_id = url.split('/missions/')[1].split('/')[0]
|
200
200
|
print(f"Mission ID provided: {mission_id}")
|
201
201
|
print(f"No mission found with id: {mission_id}")
|
202
|
+
print("-------------------------------------------------------")
|
203
|
+
print("Response text:")
|
204
|
+
print(http_err.response.text)
|
202
205
|
return None
|
203
206
|
elif http_err.response.status_code == 502:
|
204
207
|
print(f"Temporary connection failure; sleeping for {2**retry_counter}s before retrying")
|
@@ -223,4 +226,4 @@ def make_api_request(url, params=None, as_json=True, retry_counter=0):
|
|
223
226
|
time.sleep(2**retry_counter)
|
224
227
|
return make_api_request(url, params, as_json, retry_counter + 1)
|
225
228
|
except requests.exceptions.RequestException as req_err:
|
226
|
-
print(f"An error occurred\n\n{req_err}")
|
229
|
+
print(f"An error occurred\n\n{req_err}")
|
windborne/cli.py
CHANGED
@@ -19,18 +19,9 @@ from . import (
|
|
19
19
|
|
20
20
|
get_point_forecasts,
|
21
21
|
get_initialization_times,
|
22
|
+
get_forecast_hours,
|
22
23
|
get_full_gridded_forecast,
|
23
|
-
|
24
|
-
# get_dewpoint_2m,
|
25
|
-
get_wind_u_10m, get_wind_v_10m,
|
26
|
-
get_500hpa_wind_u, get_500hpa_wind_v,
|
27
|
-
get_500hpa_temperature, get_850hpa_temperature,
|
28
|
-
get_pressure_msl,
|
29
|
-
get_500hpa_geopotential, get_850hpa_geopotential,
|
30
|
-
|
31
|
-
get_historical_temperature_2m,
|
32
|
-
get_historical_500hpa_geopotential,
|
33
|
-
get_historical_500hpa_wind_u, get_historical_500hpa_wind_v,
|
24
|
+
get_gridded_forecast,
|
34
25
|
get_tropical_cyclones
|
35
26
|
|
36
27
|
)
|
@@ -156,70 +147,73 @@ def main():
|
|
156
147
|
|
157
148
|
# GRIDDED FORECASTS
|
158
149
|
####################################################################################################################
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
gridded_temperature_2m_parser = subparsers.add_parser('grid_temp_2m', help='Get gridded output of global 2m temperature forecasts')
|
164
|
-
gridded_temperature_2m_parser.add_argument('args', nargs='*', help='time output_file')
|
165
|
-
|
166
|
-
# Gridded 2m dewpoint Command
|
167
|
-
gridded_dewpoint_2m_parser = subparsers.add_parser('grid_dewpoint_2m', help='Get gridded output of global dewpoint forecasts')
|
168
|
-
gridded_dewpoint_2m_parser.add_argument('args', nargs='*', help='time output_file')
|
169
|
-
|
170
|
-
# Gridded wind-u 10m Command
|
171
|
-
gridded_wind_u_10m_parser = subparsers.add_parser('grid_wind_u_10m', help='Get gridded output of global 10m u-component of wind forecasts')
|
172
|
-
gridded_wind_u_10m_parser.add_argument('args', nargs='*', help='time output_file')
|
173
|
-
|
174
|
-
# Gridded wind-v 10m Command
|
175
|
-
gridded_wind_v_10m_parser = subparsers.add_parser('grid_wind_v_10m', help='Get gridded output of global 10m v-component of wind forecasts')
|
176
|
-
gridded_wind_v_10m_parser.add_argument('args', nargs='*', help='time output_file')
|
177
|
-
|
178
|
-
# Gridded 500hPa wind-u Command
|
179
|
-
gridded_500hpa_wind_u_parser = subparsers.add_parser('grid_500hpa_wind_u', help='Get gridded output of global 500hPa wind v-component of wind forecasts')
|
180
|
-
gridded_500hpa_wind_u_parser.add_argument('args', nargs='*', help='time output_file')
|
181
|
-
|
182
|
-
# Gridded 500hPa wind-v Command
|
183
|
-
gridded_500hpa_wind_v_parser = subparsers.add_parser('grid_500hpa_wind_v', help='Get gridded output of global 500hPa wind u-component of wind forecasts')
|
184
|
-
gridded_500hpa_wind_v_parser.add_argument('args', nargs='*', help='time output_file')
|
185
|
-
|
186
|
-
# Gridded 500hPa temperature Command
|
187
|
-
gridded_500hpa_temperature_parser = subparsers.add_parser('grid_500hpa_temperature', help='Get gridded output of global 500hPa temperature forecasts')
|
188
|
-
gridded_500hpa_temperature_parser.add_argument('args', nargs='*', help='time output_file')
|
189
|
-
|
190
|
-
# Gridded 850hPa temperature Command
|
191
|
-
gridded_850hpa_temperature_parser = subparsers.add_parser('grid_850hpa_temperature', help='Get gridded output of global 850hPa temperature forecasts')
|
192
|
-
gridded_850hpa_temperature_parser.add_argument('args', nargs='*', help='time output_file')
|
193
|
-
|
194
|
-
# Gridded mean sea level pressure Command
|
195
|
-
gridded_pressure_msl_parser = subparsers.add_parser('grid_pressure_msl', help='Get gridded output of global mean sea level pressure forecasts')
|
196
|
-
gridded_pressure_msl_parser.add_argument('args', nargs='*', help='time output_file')
|
197
|
-
|
198
|
-
# Gridded 500hPa geopotential Command
|
199
|
-
gridded_500hpa_geopotential_parser = subparsers.add_parser('grid_500hpa_geopotential', help='Get gridded output of global 500hPa geopotential forecasts')
|
200
|
-
gridded_500hpa_geopotential_parser.add_argument('args', nargs='*', help='time output_file')
|
150
|
+
gridded_parser = subparsers.add_parser('gridded', help='Get gridded forecast for a variable')
|
151
|
+
gridded_parser.add_argument('args', nargs='*', help='variable time output_file')
|
152
|
+
gridded_parser.add_argument('-i', '--intracycle', action='store_true', help='Use the intracycle forecast')
|
153
|
+
gridded_parser.add_argument('-e', '--ens-member', help='Ensemble member (eg 1 or mean)')
|
201
154
|
|
202
|
-
|
203
|
-
|
204
|
-
|
155
|
+
hist_gridded_parser = subparsers.add_parser('hist_gridded', help='Get historical gridded forecast for a variable')
|
156
|
+
hist_gridded_parser.add_argument('args', nargs='*', help='variable initialization_time forecast_hour output_file')
|
157
|
+
hist_gridded_parser.add_argument('-i', '--intracycle', action='store_true', help='Use the intracycle forecast')
|
158
|
+
hist_gridded_parser.add_argument('-e', '--ens-member', help='Ensemble member (eg 1 or mean)')
|
205
159
|
|
206
|
-
|
207
|
-
|
208
|
-
# Historical 500hpa geopotential Command
|
209
|
-
historical_temperature_2m_parser = subparsers.add_parser('hist_temp_2m', help='Get historical output of global temperature forecasts')
|
210
|
-
historical_temperature_2m_parser.add_argument('args', nargs='*', help='initialization_time forecast_hour output_file')
|
211
|
-
|
212
|
-
# Historical 500hpa geopotential Command
|
213
|
-
historical_500hpa_geopotential_parser = subparsers.add_parser('hist_500hpa_geopotential', help='Get historical output of global 500hPa geopotential forecasts')
|
214
|
-
historical_500hpa_geopotential_parser.add_argument('args', nargs='*', help='initialization_time forecast_hour output_file')
|
215
|
-
|
216
|
-
# Historical 500hpa wind u Command
|
217
|
-
historical_500hpa_wind_u_parser = subparsers.add_parser('hist_500hpa_wind_u', help='Get historical output of global 500hPa wind u forecasts')
|
218
|
-
historical_500hpa_wind_u_parser.add_argument('args', nargs='*', help='initialization_time forecast_hour output_file')
|
160
|
+
full_gridded_parser = subparsers.add_parser('grid_full', help='Get full gridded forecast')
|
161
|
+
full_gridded_parser.add_argument('args', nargs='*', help='time output_file')
|
219
162
|
|
220
|
-
#
|
221
|
-
|
222
|
-
|
163
|
+
# Define variables for gridded forecasts - the command name will be derived from the variable name
|
164
|
+
# except for special cases like temperature_2m -> temp_2m
|
165
|
+
gridded_variables = [
|
166
|
+
'temperature_2m',
|
167
|
+
'dewpoint_2m',
|
168
|
+
'wind_u_10m',
|
169
|
+
'wind_v_10m',
|
170
|
+
'pressure_msl',
|
171
|
+
'500/temperature',
|
172
|
+
'500/wind_u',
|
173
|
+
'500/wind_v',
|
174
|
+
'500/geopotential',
|
175
|
+
'850/temperature',
|
176
|
+
'850/geopotential'
|
177
|
+
]
|
178
|
+
|
179
|
+
gridded_human_names = {
|
180
|
+
'temperature_2m': '2m temperature',
|
181
|
+
'dewpoint_2m': '2m dewpoint',
|
182
|
+
'wind_u_10m': '10m u-component of wind',
|
183
|
+
'wind_v_10m': '10m v-component of wind',
|
184
|
+
}
|
185
|
+
|
186
|
+
gridded_forecast_mapping = {}
|
187
|
+
for var in gridded_variables:
|
188
|
+
cmd_name = var.replace('/', 'hpa_')
|
189
|
+
if var == 'temperature_2m':
|
190
|
+
cmd_name = 'temp_2m'
|
191
|
+
|
192
|
+
human_name = gridded_human_names.get(var, var)
|
193
|
+
if '/' in var:
|
194
|
+
level, real_var = var.split('/')
|
195
|
+
human_name = f"{level}hPa {real_var}"
|
196
|
+
|
197
|
+
gridded_forecast_mapping[cmd_name] = {
|
198
|
+
'variable': var,
|
199
|
+
'human_name': human_name
|
200
|
+
}
|
201
|
+
|
202
|
+
# Dynamically create parsers for gridded forecasts
|
203
|
+
for cmd_name, config in gridded_forecast_mapping.items():
|
204
|
+
grid_help = f"Get gridded output of global {config['human_name']} forecasts"
|
205
|
+
grid_parser = subparsers.add_parser(f'grid_{cmd_name}', help=grid_help)
|
206
|
+
grid_parser.add_argument('args', nargs='*', help='time output_file')
|
207
|
+
grid_parser.add_argument('-i', '--intracycle', action='store_true', help='Use the intracycle forecast')
|
208
|
+
grid_parser.add_argument('-e', '--ens-member', help='Ensemble member (eg 1 or mean)')
|
209
|
+
grid_parser.set_defaults(variable=config['variable'])
|
210
|
+
|
211
|
+
hist_help = f"Get historical output of global {config['human_name']} forecasts"
|
212
|
+
hist_parser = subparsers.add_parser(f'hist_{cmd_name}', help=hist_help)
|
213
|
+
hist_parser.add_argument('args', nargs='*', help='initialization_time forecast_hour output_file')
|
214
|
+
hist_parser.add_argument('-i', '--intracycle', action='store_true', help='Use the intracycle forecast')
|
215
|
+
hist_parser.add_argument('-e', '--ens-member', help='Ensemble member (eg 1 or mean)')
|
216
|
+
hist_parser.set_defaults(variable=config['variable'])
|
223
217
|
|
224
218
|
# OTHER
|
225
219
|
# TCS
|
@@ -233,7 +227,13 @@ def main():
|
|
233
227
|
|
234
228
|
# Initialization Times Command
|
235
229
|
initialization_times_parser = subparsers.add_parser('init_times', help='Get available initialization times for point forecasts')
|
230
|
+
initialization_times_parser.add_argument('-i', '--intracycle', action='store_true', help='Use the intracycle forecast')
|
231
|
+
initialization_times_parser.add_argument('-e', '--ens-member', help='Ensemble member (eg 1 or mean)')
|
236
232
|
|
233
|
+
# Forecast Hours Command
|
234
|
+
forecast_hours_parser = subparsers.add_parser('forecast_hours', help='Get available forecast hours for WeatherMesh')
|
235
|
+
forecast_hours_parser.add_argument('-i', '--intracycle', action='store_true', help='Use the intracycle forecast')
|
236
|
+
forecast_hours_parser.add_argument('-e', '--ens-member', help='Ensemble member (eg 1 or mean)')
|
237
237
|
|
238
238
|
args = parser.parse_args()
|
239
239
|
|
@@ -432,201 +432,68 @@ def main():
|
|
432
432
|
)
|
433
433
|
|
434
434
|
elif args.command == 'init_times':
|
435
|
-
get_initialization_times(print_response=True)
|
436
|
-
|
437
|
-
if args.command == 'grid_full':
|
438
|
-
# Parse get_full_gridded_forecast arguments
|
439
|
-
if len(args.args) in [0,1]:
|
440
|
-
print("To get the full gridded forecast you need to provide the time for which to get the forecast and an output file.")
|
441
|
-
print("\nUsage: windborne get_full_gridded_forecast time output_file")
|
442
|
-
elif len(args.args) == 2:
|
443
|
-
get_full_gridded_forecast(time=args.args[0], output_file=args.args[1])
|
444
|
-
else:
|
445
|
-
print("Too many arguments")
|
446
|
-
print("\nUsage: windborne get_full_gridded_forecast time output_file")
|
435
|
+
get_initialization_times(print_response=True, ensemble_member=args.ens_member, intracycle=args.intracycle)
|
447
436
|
|
448
|
-
elif args.command == '
|
449
|
-
|
450
|
-
if len(args.args) in [0,1]:
|
451
|
-
print("To get the gridded output of global 2m temperature forecast you need to provide the time for which to get the forecast and an output file.")
|
452
|
-
print("\nUsage: windborne grid_temp_2m time output_file")
|
453
|
-
elif len(args.args) == 2:
|
454
|
-
get_temperature_2m(time=args.args[0], output_file=args.args[1])
|
455
|
-
else:
|
456
|
-
print("Too many arguments")
|
457
|
-
print("\nUsage: windborne grid_temp_2m time output_file")
|
458
|
-
|
459
|
-
# elif args.command == 'grid_dewpoint_2m':
|
460
|
-
# # Parse grid_dewpoint_2m arguments
|
461
|
-
# if len(args.args) in [0,1]:
|
462
|
-
# print(f"To get the gridded output of global 2m dew point forecast you need to provide the time for which to get the forecast and an output file.")
|
463
|
-
# print("\nUsage: windborne grid_dewpoint_2m time output_file")
|
464
|
-
# elif len(args.args) == 2:
|
465
|
-
# get_dewpoint_2m(time=args.args[0], output_file=args.args[1])
|
466
|
-
# else:
|
467
|
-
# print("Too many arguments")
|
468
|
-
# print("\nUsage: windborne grid_dewpoint_2m time output_file")
|
469
|
-
|
470
|
-
elif args.command == 'grid_wind_u_10m':
|
471
|
-
# Parse grid_wind_u_10m arguments
|
472
|
-
if len(args.args) in [0,1]:
|
473
|
-
print(f"To get the gridded output of global 10m u-component of wind forecasts you need to provide the time for which to get the forecast and an output file.")
|
474
|
-
print("\nUsage: windborne grid_wind_u_10m time output_file")
|
475
|
-
elif len(args.args) == 2:
|
476
|
-
get_wind_u_10m(time=args.args[0], output_file=args.args[1])
|
477
|
-
else:
|
478
|
-
print("Too many arguments")
|
479
|
-
print("\nUsage: windborne grid_wind_u_10m time output_file")
|
480
|
-
|
481
|
-
elif args.command == 'grid_wind_v_10m':
|
482
|
-
# Parse grid_wind_v_10m arguments
|
483
|
-
if len(args.args) in [0,1]:
|
484
|
-
print(f"To get the gridded output of global 10m v-component of wind forecasts you need to provide the time for which to get the forecast and an output file.")
|
485
|
-
print("\nUsage: windborne grid_wind_v_10m time output_file")
|
486
|
-
elif len(args.args) == 2:
|
487
|
-
get_wind_v_10m(time=args.args[0], output_file=args.args[1])
|
488
|
-
else:
|
489
|
-
print("Too many arguments")
|
490
|
-
print("\nUsage: windborne grid_wind_v_10m time output_file")
|
491
|
-
|
492
|
-
elif args.command == 'grid_500hpa_wind_u':
|
493
|
-
# Parse grid_500hpa_wind_u arguments
|
494
|
-
if len(args.args) in [0,1]:
|
495
|
-
print(f"To get the gridded output of global 500hPa u-component of wind forecasts you need to provide the time for which to get the forecast and an output file.")
|
496
|
-
print("\nUsage: windborne grid_500hpa_wind_u time output_file")
|
497
|
-
elif len(args.args) == 2:
|
498
|
-
get_500hpa_wind_u(time=args.args[0], output_file=args.args[1])
|
499
|
-
else:
|
500
|
-
print("Too many arguments")
|
501
|
-
print("\nUsage: windborne grid_500hpa_wind_u time output_file")
|
437
|
+
elif args.command == 'forecast_hours':
|
438
|
+
get_forecast_hours(print_response=True, ensemble_member=args.ens_member, intracycle=args.intracycle)
|
502
439
|
|
503
|
-
elif args.command == '
|
504
|
-
# Parse grid_500hpa_wind_v arguments
|
505
|
-
if len(args.args) in [0,1]:
|
506
|
-
print(f"To get the gridded output of global 500hPa v-component of wind forecasts you need to provide the time for which to get the forecast and an output file.")
|
507
|
-
print("\nUsage: windborne grid_500hpa_wind_v time output_file")
|
508
|
-
elif len(args.args) == 2:
|
509
|
-
get_500hpa_wind_v(time=args.args[0], output_file=args.args[1])
|
510
|
-
else:
|
511
|
-
print("Too many arguments")
|
512
|
-
print("\nUsage: windborne grid_500hpa_wind_v time output_file")
|
513
|
-
|
514
|
-
elif args.command == 'grid_500hpa_temperature':
|
515
|
-
# Parse grid_500hpa_temperature arguments
|
516
|
-
if len(args.args) in [0,1]:
|
517
|
-
print(f"To get the gridded output of global 500hPa temperature forecasts you need to provide the time for which to get the forecast and an output file.")
|
518
|
-
print("\nUsage: windborne grid_500hpa_temperature time output_file")
|
519
|
-
return
|
520
|
-
elif len(args.args) == 2:
|
521
|
-
get_500hpa_temperature(time=args.args[0], output_file=args.args[1])
|
522
|
-
else:
|
523
|
-
print("Too many arguments")
|
524
|
-
print("\nUsage: windborne grid_500hpa_temperature time output_file")
|
525
|
-
|
526
|
-
elif args.command == 'grid_850hpa_temperature':
|
527
|
-
# Parse grid_850hpa_temperature arguments
|
528
|
-
if len(args.args) in [0,1]:
|
529
|
-
print(f"To get the gridded output of global 850hPa temperature forecasts you need to provide the time for which to get the forecast and an output file.")
|
530
|
-
print("\nUsage: windborne grid_850hpa_temperature time output_file")
|
531
|
-
return
|
532
|
-
elif len(args.args) == 2:
|
533
|
-
get_850hpa_temperature(time=args.args[0], output_file=args.args[1])
|
534
|
-
else:
|
535
|
-
print("Too many arguments")
|
536
|
-
print("\nUsage: windborne grid_850hpa_temperature time output_file")
|
537
|
-
|
538
|
-
elif args.command == 'grid_pressure_msl':
|
539
|
-
# Parse grid_pressure_msl arguments
|
540
|
-
if len(args.args) in [0,1]:
|
541
|
-
print(f"To get the gridded output of global mean sea level pressure forecasts you need to provide the time for which to get the forecast and an output file.")
|
542
|
-
print("\nUsage: windborne grid_pressure_msl time output_file")
|
543
|
-
elif len(args.args) == 2:
|
544
|
-
get_pressure_msl(time=args.args[0], output_file=args.args[1])
|
545
|
-
else:
|
546
|
-
print("Too many arguments")
|
547
|
-
print("\nUsage: windborne grid_pressure_msl time output_file")
|
548
|
-
|
549
|
-
elif args.command == 'grid_500hpa_geopotential':
|
550
|
-
# Parse grid_500hpa_geopotential arguments
|
551
|
-
if len(args.args) in [0,1]:
|
552
|
-
print(f"To get the gridded output of global 500hPa geopotential forecasts you need to provide the time for which to get the forecast and an output file.")
|
553
|
-
print("\nUsage: windborne grid_500hpa_geopotential time output_file")
|
554
|
-
return
|
555
|
-
elif len(args.args) == 2:
|
556
|
-
get_500hpa_geopotential(time=args.args[0], output_file=args.args[1])
|
557
|
-
else:
|
558
|
-
print("Too many arguments")
|
559
|
-
print("\nUsage: windborne grid_500hpa_geopotential time output_file")
|
560
|
-
|
561
|
-
elif args.command == 'grid_850hpa_geopotential':
|
562
|
-
# Parse grid_850hpa_geopotential arguments
|
563
|
-
if len(args.args) in [0,1]:
|
564
|
-
print(f"To get the gridded output of global 850hPa geopotential forecasts you need to provide the time for which to get the forecast and an output file.")
|
565
|
-
print("\nUsage: windborne grid_850hpa_geopotential time output_file")
|
566
|
-
return
|
567
|
-
elif len(args.args) == 2:
|
568
|
-
get_850hpa_geopotential(time=args.args[0], output_file=args.args[1])
|
569
|
-
else:
|
570
|
-
print("Too many arguments")
|
571
|
-
print("\nUsage: windborne grid_850hpa_geopotential time output_file")
|
572
|
-
|
573
|
-
# HISTORICAL
|
574
|
-
|
575
|
-
elif args.command == 'hist_temp_2m':
|
576
|
-
# Parse historical temperature arguments
|
577
|
-
if len(args.args) in [0,1,2]:
|
578
|
-
print("To get the historical output of global temperature forecasts you need to provide\n"
|
579
|
-
" - initialization time of the forecast\n"
|
580
|
-
" - How many hours after the run time the forecast is valid at\n"
|
581
|
-
" - An ouput file to save the data")
|
582
|
-
print("\nUsage: windborne hist_temp_2m initialization_time forecast_hour output_file")
|
583
|
-
return
|
584
|
-
elif len(args.args) == 3:
|
585
|
-
get_historical_temperature_2m(initialization_time=args.args[0], forecast_hour=args.args[1], output_file=args.args[2])
|
586
|
-
else:
|
587
|
-
print("Too many arguments")
|
588
|
-
print("\nUsage: windborne hist_temp_2m initialization_time forecast_hour output_file")
|
589
|
-
|
590
|
-
elif args.command == 'hist_500hpa_geopotential':
|
591
|
-
# Parse historical 500 hpa geopotential arguments
|
440
|
+
elif args.command == 'gridded':
|
592
441
|
if len(args.args) in [0,1,2]:
|
593
|
-
print("To get the
|
594
|
-
|
595
|
-
" - How many hours after the run time the forecast is valid at\n"
|
596
|
-
" - An ouput file to save the data")
|
597
|
-
print("\nUsage: windborne hist_500hpa_geopotential initialization_time forecast_hour output_file")
|
598
|
-
return
|
442
|
+
print(f"To get the gridded forecast for a variable you need to provide the variable, time, and an output file.")
|
443
|
+
print(f"\nUsage: windborne gridded variable time output_file")
|
599
444
|
elif len(args.args) == 3:
|
600
|
-
|
445
|
+
get_gridded_forecast(variable=args.args[0], time=args.args[1], output_file=args.args[2], ensemble_member=args.ens_member, intracycle=args.intracycle)
|
601
446
|
else:
|
602
447
|
print("Too many arguments")
|
603
|
-
print("\nUsage: windborne hist_500hpa_geopotential initialization_time forecast_hour output_file")
|
604
448
|
|
605
|
-
elif args.command == '
|
606
|
-
if len(args.args) in [0,1
|
607
|
-
print("To get the
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
print("\nUsage: windborne hist_500hpa_wind_u initialization_time forecast_hour output_file")
|
612
|
-
elif len(args.args) == 3:
|
613
|
-
get_historical_500hpa_wind_u(initialization_time=args.args[0], forecast_hour=args.args[1], output_file=args.args[2])
|
449
|
+
elif args.command == 'grid_full':
|
450
|
+
if len(args.args) in [0,1]:
|
451
|
+
print("To get the full gridded forecast you need to provide the time for which to get the forecast and an output file.")
|
452
|
+
print("\nUsage: windborne grid_full time output_file")
|
453
|
+
elif len(args.args) == 2:
|
454
|
+
get_full_gridded_forecast(time=args.args[0], output_file=args.args[1])
|
614
455
|
else:
|
615
456
|
print("Too many arguments")
|
616
|
-
print("\nUsage: windborne
|
617
|
-
|
618
|
-
elif args.command == '
|
619
|
-
if len(args.args) in [0,1,2]:
|
620
|
-
print("To get the historical
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
print("\nUsage: windborne hist_500hpa_wind_u initialization_time forecast_hour output_file")
|
625
|
-
elif len(args.args) == 3:
|
626
|
-
get_historical_500hpa_wind_v(initialization_time=args.args[0], forecast_hour=args.args[1], output_file=args.args[2])
|
457
|
+
print("\nUsage: windborne grid_full time output_file")
|
458
|
+
|
459
|
+
elif args.command == 'hist_gridded':
|
460
|
+
if len(args.args) in [0,1,2,3]:
|
461
|
+
print(f"To get the historical gridded forecast for a variable you need to provide the variable, initialization time, forecast hour, and an output file.")
|
462
|
+
print(f"\nUsage: windborne hist_gridded variable initialization_time forecast_hour output_file")
|
463
|
+
elif len(args.args) == 4:
|
464
|
+
get_gridded_forecast(variable=args.args[0], initialization_time=args.args[1], forecast_hour=args.args[2], output_file=args.args[3], ensemble_member=args.ens_member, intracycle=args.intracycle)
|
627
465
|
else:
|
628
466
|
print("Too many arguments")
|
629
|
-
print("\nUsage: windborne
|
467
|
+
print(f"\nUsage: windborne hist_gridded variable initialization_time forecast_hour output_file")
|
468
|
+
|
469
|
+
# Handle all gridded forecast commands
|
470
|
+
elif args.command.startswith('grid_'):
|
471
|
+
cmd_name = args.command[5:] # Remove 'grid_' prefix
|
472
|
+
if cmd_name in gridded_forecast_mapping:
|
473
|
+
if len(args.args) in [0,1]:
|
474
|
+
print(f"To get {gridded_forecast_mapping[cmd_name]['human_name']} you need to provide the time for which to get the forecast and an output file.")
|
475
|
+
print(f"\nUsage: windborne {args.command} time output_file")
|
476
|
+
elif len(args.args) == 2:
|
477
|
+
get_gridded_forecast(variable=args.variable, time=args.args[0], output_file=args.args[1], ensemble_member=args.ens_member, intracycle=args.intracycle)
|
478
|
+
else:
|
479
|
+
print("Too many arguments")
|
480
|
+
print(f"\nUsage: windborne {args.command} time output_file")
|
481
|
+
|
482
|
+
# Handle all historical forecast commands
|
483
|
+
elif args.command.startswith('hist_'):
|
484
|
+
cmd_name = args.command[5:] # Remove 'hist_' prefix
|
485
|
+
if cmd_name in gridded_forecast_mapping:
|
486
|
+
if len(args.args) in [0,1,2]:
|
487
|
+
print(f"To get {gridded_forecast_mapping[cmd_name]['human_name']} you need to provide\n"
|
488
|
+
" - initialization time of the forecast\n"
|
489
|
+
" - How many hours after the initialization time the forecast is valid at\n"
|
490
|
+
" - An output file to save the data")
|
491
|
+
print(f"\nUsage: windborne {args.command} initialization_time forecast_hour output_file")
|
492
|
+
elif len(args.args) == 3:
|
493
|
+
get_gridded_forecast(variable=args.variable, initialization_time=args.args[0], forecast_hour=args.args[1], output_file=args.args[2], ensemble_member=args.ens_member, intracycle=args.intracycle)
|
494
|
+
else:
|
495
|
+
print("Too many arguments")
|
496
|
+
print(f"\nUsage: windborne {args.command} initialization_time forecast_hour output_file")
|
630
497
|
|
631
498
|
elif args.command == 'tropical_cyclones':
|
632
499
|
# Parse cyclones arguments
|
@@ -655,4 +522,4 @@ def main():
|
|
655
522
|
parser.print_help()
|
656
523
|
|
657
524
|
if __name__ == '__main__':
|
658
|
-
main()
|
525
|
+
main()
|
windborne/data_api.py
CHANGED
@@ -548,10 +548,28 @@ def get_flying_missions(output_file=None, print_results=False):
|
|
548
548
|
Returns:
|
549
549
|
dict: The API response containing list of flying missions.
|
550
550
|
"""
|
551
|
+
page_size = 64
|
552
|
+
|
553
|
+
# Initial query to get total flying
|
554
|
+
query_params = {
|
555
|
+
'page': 0,
|
556
|
+
'page_size': page_size
|
557
|
+
}
|
551
558
|
|
552
559
|
url = f"{DATA_API_BASE_URL}/missions.json"
|
553
|
-
flying_missions_response = make_api_request(url)
|
560
|
+
flying_missions_response = make_api_request(url, params=query_params)
|
561
|
+
|
554
562
|
flying_missions = flying_missions_response.get("missions", [])
|
563
|
+
num_fetched_missions = len(flying_missions)
|
564
|
+
|
565
|
+
while num_fetched_missions == page_size:
|
566
|
+
query_params['page'] += 1
|
567
|
+
|
568
|
+
new_missions = make_api_request(url, params=query_params).get('missions', [])
|
569
|
+
num_fetched_missions = len(new_missions)
|
570
|
+
|
571
|
+
flying_missions += new_missions
|
572
|
+
|
555
573
|
for mission in flying_missions:
|
556
574
|
if mission.get('number'):
|
557
575
|
mission['name'] = f"W-{mission['number']}"
|
windborne/forecasts_api.py
CHANGED
@@ -107,28 +107,45 @@ def get_point_forecasts(coordinates, min_forecast_time=None, max_forecast_time=N
|
|
107
107
|
return response
|
108
108
|
|
109
109
|
|
110
|
-
def get_gridded_forecast(time,
|
110
|
+
def get_gridded_forecast(variable, time=None, initialization_time=None, forecast_hour=None, output_file=None, silent=False, intracycle=False, ensemble_member=None):
|
111
111
|
"""
|
112
112
|
Get gridded forecast data from the API.
|
113
113
|
Note that this is primarily meant to be used internally by the other functions in this module.
|
114
114
|
|
115
115
|
Args:
|
116
|
-
time (str): Date in either ISO 8601 format (YYYY-MM-DDTHH:00:00)
|
117
|
-
or compact format (YYYYMMDDHH)
|
118
|
-
|
116
|
+
time (str, optional): Date in either ISO 8601 format (YYYY-MM-DDTHH:00:00)
|
117
|
+
or compact format (YYYYMMDDHH). May be used instead of initialization_time and forecast_hour.
|
118
|
+
initialization_time (str, optional): Date in either ISO 8601 format (YYYY-MM-DDTHH:00:00)
|
119
|
+
or compact format (YYYYMMDDHH). May be used in conjunction with forecast_hour instead of time.
|
120
|
+
forecast_hour (int, optional): The forecast hour to get the forecast for. May be used in conjunction with initialization_time instead of time.
|
119
121
|
variable (str): The variable you want the forecast for
|
120
122
|
output_file (str, optional): Path to save the response data
|
121
123
|
Supported formats: .nc
|
122
124
|
"""
|
123
125
|
|
124
|
-
|
126
|
+
# backwards compatibility for time and variable order swap
|
127
|
+
if time in ['temperature_2m', 'dewpoint_2m', 'wind_u_10m', 'wind_v_10m', '500/wind_u', '500/wind_v', '500/temperature', '850/temperature', 'pressure_msl', '500/geopotential', '850/geopotential', 'FULL']:
|
128
|
+
variable, time = time, variable
|
125
129
|
|
126
|
-
|
127
|
-
|
130
|
+
# require either time or initialization_time and forecast_hour
|
131
|
+
if not time and not initialization_time and not forecast_hour:
|
132
|
+
print("Error: you must provide either time or initialization_time and forecast_hour.")
|
128
133
|
return
|
129
|
-
|
130
|
-
|
131
|
-
|
134
|
+
elif time and (initialization_time or forecast_hour):
|
135
|
+
print("Warning: time, initialization_time, forecast_hour all provided; using initialization_time and forecast_hour.")
|
136
|
+
|
137
|
+
params = {}
|
138
|
+
if initialization_time and forecast_hour:
|
139
|
+
params["initialization_time"] = parse_time(initialization_time, init_time_flag=True)
|
140
|
+
params["forecast_hour"] = forecast_hour
|
141
|
+
elif time:
|
142
|
+
params["time"] = parse_time(time)
|
143
|
+
|
144
|
+
if intracycle:
|
145
|
+
params["intracycle"] = intracycle
|
146
|
+
|
147
|
+
if ensemble_member:
|
148
|
+
params["ens_member"] = ensemble_member
|
132
149
|
|
133
150
|
response = make_api_request(f"{FORECASTS_GRIDDED_URL}/{variable}", params=params, as_json=False)
|
134
151
|
|
@@ -136,84 +153,59 @@ def get_gridded_forecast(time, variable, output_file=None):
|
|
136
153
|
return None
|
137
154
|
|
138
155
|
if output_file:
|
139
|
-
|
156
|
+
if not silent:
|
157
|
+
print(f"Output URL found; downloading to {output_file}...")
|
140
158
|
download_and_save_output(output_file, response)
|
141
159
|
|
142
160
|
return response
|
143
161
|
|
144
162
|
def get_full_gridded_forecast(time, output_file=None):
|
145
|
-
return get_gridded_forecast(
|
163
|
+
return get_gridded_forecast(variable="FULL", time=time, output_file=output_file)
|
146
164
|
|
147
165
|
def get_temperature_2m(time, output_file=None):
|
148
|
-
return get_gridded_forecast(
|
166
|
+
return get_gridded_forecast(variable="temperature_2m", time=time, output_file=output_file)
|
149
167
|
|
150
|
-
|
151
|
-
|
152
|
-
# return get_gridded_forecast(time, "dewpoint_2m", output_file)
|
168
|
+
def get_dewpoint_2m(time, output_file=None):
|
169
|
+
return get_gridded_forecast(variable="dewpoint_2m", time=time, output_file=output_file)
|
153
170
|
|
154
171
|
def get_wind_u_10m(time, output_file=None):
|
155
|
-
return get_gridded_forecast(
|
172
|
+
return get_gridded_forecast(variable="wind_u_10m", time=time, output_file=output_file)
|
156
173
|
|
157
174
|
def get_wind_v_10m(time, output_file=None):
|
158
|
-
return get_gridded_forecast(
|
175
|
+
return get_gridded_forecast(variable="wind_v_10m", time=time, output_file=output_file)
|
159
176
|
|
160
177
|
def get_500hpa_wind_u(time, output_file=None):
|
161
|
-
return get_gridded_forecast(
|
178
|
+
return get_gridded_forecast(variable="500/wind_u", time=time, output_file=output_file)
|
162
179
|
|
163
180
|
def get_500hpa_wind_v(time, output_file=None):
|
164
|
-
return get_gridded_forecast(
|
181
|
+
return get_gridded_forecast(variable="500/wind_v", time=time, output_file=output_file)
|
165
182
|
|
166
183
|
def get_500hpa_temperature(time, output_file=None):
|
167
|
-
return get_gridded_forecast(
|
184
|
+
return get_gridded_forecast(variable="500/temperature", time=time, output_file=output_file)
|
168
185
|
|
169
186
|
def get_850hpa_temperature(time, output_file=None):
|
170
|
-
return get_gridded_forecast(
|
187
|
+
return get_gridded_forecast(variable="850/temperature", time=time, output_file=output_file)
|
171
188
|
|
172
189
|
def get_pressure_msl(time, output_file=None):
|
173
|
-
return get_gridded_forecast(
|
190
|
+
return get_gridded_forecast(variable="pressure_msl", time=time, output_file=output_file)
|
174
191
|
|
175
192
|
def get_500hpa_geopotential(time, output_file=None):
|
176
|
-
return get_gridded_forecast(
|
193
|
+
return get_gridded_forecast(variable="500/geopotential", time=time, output_file=output_file)
|
177
194
|
|
178
195
|
def get_850hpa_geopotential(time, output_file=None):
|
179
|
-
return get_gridded_forecast(
|
180
|
-
|
181
|
-
|
182
|
-
def get_historical_output(initialization_time, forecast_hour, variable, output_file=None):
|
183
|
-
params = {}
|
184
|
-
|
185
|
-
if not initialization_time or not forecast_hour:
|
186
|
-
print("To get the historical output of global temperature forecasts you need to provide:\n"
|
187
|
-
"- the initialization time of the forecast\n"
|
188
|
-
"- how many hours after the run time the forecast is valid at.\n")
|
189
|
-
return
|
190
|
-
else:
|
191
|
-
params["initialization_time"] = parse_time(initialization_time, init_time_flag=True)
|
192
|
-
params["forecast_hour"] = forecast_hour
|
193
|
-
|
194
|
-
response = make_api_request(f"{FORECASTS_HISTORICAL_URL}/{variable}", params=params, as_json=False)
|
195
|
-
|
196
|
-
if response is None:
|
197
|
-
return None
|
198
|
-
|
199
|
-
if output_file:
|
200
|
-
print(f"Output URL found; downloading to {output_file}...")
|
201
|
-
download_and_save_output(output_file, response)
|
202
|
-
|
203
|
-
return response
|
204
|
-
|
196
|
+
return get_gridded_forecast(variable="850/geopotential", time=time, output_file=output_file)
|
205
197
|
|
206
198
|
def get_historical_temperature_2m(initialization_time, forecast_hour, output_file=None):
|
207
|
-
return
|
199
|
+
return get_gridded_forecast(variable="temperature_2m", initialization_time=initialization_time, forecast_hour=forecast_hour, output_file=output_file)
|
208
200
|
|
209
201
|
def get_historical_500hpa_geopotential(initialization_time, forecast_hour, output_file=None):
|
210
|
-
return
|
202
|
+
return get_gridded_forecast(variable="500/geopotential", initialization_time=initialization_time, forecast_hour=forecast_hour, output_file=output_file)
|
211
203
|
|
212
204
|
def get_historical_500hpa_wind_u(initialization_time, forecast_hour, output_file=None):
|
213
|
-
return
|
205
|
+
return get_gridded_forecast(variable="500/wind_u", initialization_time=initialization_time, forecast_hour=forecast_hour, output_file=output_file)
|
214
206
|
|
215
207
|
def get_historical_500hpa_wind_v(initialization_time, forecast_hour, output_file=None):
|
216
|
-
return
|
208
|
+
return get_gridded_forecast(variable="500/wind_v", initialization_time=initialization_time, forecast_hour=forecast_hour, output_file=output_file)
|
217
209
|
|
218
210
|
|
219
211
|
def get_tropical_cyclones(initialization_time=None, basin=None, output_file=None, print_response=False):
|
@@ -287,14 +279,18 @@ def get_tropical_cyclones(initialization_time=None, basin=None, output_file=None
|
|
287
279
|
return response
|
288
280
|
|
289
281
|
|
290
|
-
def get_initialization_times(print_response=False):
|
282
|
+
def get_initialization_times(print_response=False, ensemble_member=None, intracycle=False):
|
291
283
|
"""
|
292
284
|
Get available WeatherMesh initialization times (also known as cycle times).
|
293
285
|
|
294
|
-
Returns dict with keys "latest" and "available"
|
286
|
+
Returns dict with keys "latest" and "available"
|
295
287
|
"""
|
296
288
|
|
297
|
-
|
289
|
+
params = {
|
290
|
+
'ens_member': ensemble_member,
|
291
|
+
'intracycle': intracycle
|
292
|
+
}
|
293
|
+
response = make_api_request(f"{FORECASTS_API_BASE_URL}/initialization_times.json", params=params)
|
298
294
|
|
299
295
|
if print_response:
|
300
296
|
print("Latest initialization time:", response['latest'])
|
@@ -305,6 +301,29 @@ def get_initialization_times(print_response=False):
|
|
305
301
|
return response
|
306
302
|
|
307
303
|
|
304
|
+
def get_forecast_hours(print_response=False, ensemble_member=None, intracycle=False):
|
305
|
+
"""
|
306
|
+
Get available forecast hours for WeatherMesh
|
307
|
+
This may include initialization times that are not included in the initialization times API that represent outputs
|
308
|
+
that are still being generated.
|
309
|
+
|
310
|
+
Returns dict with keys of initialization times and values of available forecast hours
|
311
|
+
"""
|
312
|
+
|
313
|
+
params = {
|
314
|
+
'ens_member': ensemble_member,
|
315
|
+
'intracycle': intracycle
|
316
|
+
}
|
317
|
+
response = make_api_request(f"{FORECASTS_API_BASE_URL}/forecast_hours.json", params=params)
|
318
|
+
|
319
|
+
if print_response:
|
320
|
+
print("Available forecast hours:")
|
321
|
+
for time, hours in response.items():
|
322
|
+
print(f" - {time}: {', '.join([str(hour) for hour in hours])}")
|
323
|
+
|
324
|
+
return response
|
325
|
+
|
326
|
+
|
308
327
|
# Tropical cyclones
|
309
328
|
def print_tc_supported_formats():
|
310
329
|
"""Print supported file formats for saving tcs data."""
|
@@ -313,7 +332,7 @@ def print_tc_supported_formats():
|
|
313
332
|
print(f" - {fmt}")
|
314
333
|
|
315
334
|
|
316
|
-
def download_and_save_output(output_file, response):
|
335
|
+
def download_and_save_output(output_file, response, silent=False):
|
317
336
|
"""
|
318
337
|
Downloads a forecast output from a presigned S3 url contained in a response and saves it as a .nc file.
|
319
338
|
|
@@ -333,12 +352,17 @@ def download_and_save_output(output_file, response):
|
|
333
352
|
# Save the content directly to file
|
334
353
|
with open(output_file, 'wb') as f:
|
335
354
|
f.write(response.content)
|
336
|
-
|
355
|
+
|
356
|
+
if not silent:
|
357
|
+
print(f"Data Successfully saved to {output_file}")
|
358
|
+
|
337
359
|
return True
|
338
360
|
|
339
361
|
except requests.exceptions.RequestException as e:
|
340
|
-
|
362
|
+
if not silent:
|
363
|
+
print(f"Error downloading the file: {e}")
|
341
364
|
return False
|
342
365
|
except Exception as e:
|
343
|
-
|
344
|
-
|
366
|
+
if not silent:
|
367
|
+
print(f"Error processing the file: {e}")
|
368
|
+
return False
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: windborne
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.2.0
|
4
4
|
Summary: A Python library for interacting with WindBorne Data and Forecasts API
|
5
5
|
Author-email: WindBorne Systems <data@windbornesystems.com>
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
@@ -0,0 +1,13 @@
|
|
1
|
+
windborne/__init__.py,sha256=nRvcreJarSKLwx40adsZSbYm-pc3htE2HZJvgY0MQUA,2044
|
2
|
+
windborne/api_request.py,sha256=wh8-LANzB62eJt699Zk3YHv4pIzwa1DIhiTJdWRp-To,11264
|
3
|
+
windborne/cli.py,sha256=Y0oLQLqHAhGX1xCM4vpREpGAMhECM4SK87h2DkUjalU,28643
|
4
|
+
windborne/data_api.py,sha256=sYZkcQog8RuLErfdLLa4gC8fRkAoPGNKKQJ8i6o-LTQ,33826
|
5
|
+
windborne/forecasts_api.py,sha256=WZW-bXVTTIbBFQhDc7hwwMA6FCfHaX7V4Caq8W52MLA,16193
|
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.2.0.dist-info/METADATA,sha256=NptGBKui6B2Lpk5INJ_7UmWOfVxlcZXJZxblPsCqzpw,1235
|
10
|
+
windborne-1.2.0.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
11
|
+
windborne-1.2.0.dist-info/entry_points.txt,sha256=j_YrqdCDrCd7p5MIwQ2BYwNXEi95VNANzLRJmcXEg1U,49
|
12
|
+
windborne-1.2.0.dist-info/top_level.txt,sha256=PE9Lauriu5S5REf7JKhXprufZ_V5RiZ_TnfnrLGJrmE,10
|
13
|
+
windborne-1.2.0.dist-info/RECORD,,
|
windborne-1.1.4.dist-info/RECORD
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
windborne/__init__.py,sha256=jPqxHTqOEcAnPU-7VCJionGToE2tqY7Q3SuSdQrdl9Q,1944
|
2
|
-
windborne/api_request.py,sha256=zh1TaaZAaRfAXp2NYMja75fKeduWLfao02JRRFVpQCA,11108
|
3
|
-
windborne/cli.py,sha256=fJ4cLcBpc7YasfMu3pNrqCX9idGCWA-2j2necNpDfck,36696
|
4
|
-
windborne/data_api.py,sha256=f2TdMpSxQLiWKcUJxbG_9SqAp4DU-sZ3rBvqTcBHbuk,33343
|
5
|
-
windborne/forecasts_api.py,sha256=Jea1hGd2CQHbyRXnJXLamF1GdgBsoqX7oGyg8S6YoRo,14102
|
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.4.dist-info/METADATA,sha256=PqFm1t3RoKqj0K1UtUtJYMfIwl1gauKIAinS1Elyir4,1235
|
10
|
-
windborne-1.1.4.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
11
|
-
windborne-1.1.4.dist-info/entry_points.txt,sha256=j_YrqdCDrCd7p5MIwQ2BYwNXEi95VNANzLRJmcXEg1U,49
|
12
|
-
windborne-1.1.4.dist-info/top_level.txt,sha256=PE9Lauriu5S5REf7JKhXprufZ_V5RiZ_TnfnrLGJrmE,10
|
13
|
-
windborne-1.1.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|