windborne 1.2.5__tar.gz → 1.2.7__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.
- {windborne-1.2.5 → windborne-1.2.7}/PKG-INFO +1 -1
- {windborne-1.2.5 → windborne-1.2.7}/pyproject.toml +1 -1
- {windborne-1.2.5 → windborne-1.2.7}/windborne/__init__.py +6 -2
- {windborne-1.2.5 → windborne-1.2.7}/windborne/cli.py +32 -1
- {windborne-1.2.5 → windborne-1.2.7}/windborne/forecasts_api.py +55 -0
- {windborne-1.2.5 → windborne-1.2.7}/windborne.egg-info/PKG-INFO +1 -1
- {windborne-1.2.5 → windborne-1.2.7}/README.md +0 -0
- {windborne-1.2.5 → windborne-1.2.7}/setup.cfg +0 -0
- {windborne-1.2.5 → windborne-1.2.7}/windborne/api_request.py +0 -0
- {windborne-1.2.5 → windborne-1.2.7}/windborne/data_api.py +0 -0
- {windborne-1.2.5 → windborne-1.2.7}/windborne/observation_formatting.py +0 -0
- {windborne-1.2.5 → windborne-1.2.7}/windborne/track_formatting.py +0 -0
- {windborne-1.2.5 → windborne-1.2.7}/windborne/utils.py +0 -0
- {windborne-1.2.5 → windborne-1.2.7}/windborne.egg-info/SOURCES.txt +0 -0
- {windborne-1.2.5 → windborne-1.2.7}/windborne.egg-info/dependency_links.txt +0 -0
- {windborne-1.2.5 → windborne-1.2.7}/windborne.egg-info/entry_points.txt +0 -0
- {windborne-1.2.5 → windborne-1.2.7}/windborne.egg-info/requires.txt +0 -0
- {windborne-1.2.5 → windborne-1.2.7}/windborne.egg-info/top_level.txt +0 -0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "windborne"
|
7
|
-
version = "1.2.
|
7
|
+
version = "1.2.7"
|
8
8
|
description = "A Python library for interacting with WindBorne Data and Forecasts API"
|
9
9
|
readme = {file = "README.md", content-type = "text/markdown"}
|
10
10
|
authors = [
|
@@ -22,6 +22,7 @@ from .data_api import (
|
|
22
22
|
from .forecasts_api import (
|
23
23
|
get_point_forecasts,
|
24
24
|
get_initialization_times,
|
25
|
+
get_historical_initialization_times,
|
25
26
|
get_forecast_hours,
|
26
27
|
get_generation_times,
|
27
28
|
|
@@ -41,7 +42,8 @@ from .forecasts_api import (
|
|
41
42
|
|
42
43
|
get_tropical_cyclones,
|
43
44
|
|
44
|
-
get_population_weighted_hdd
|
45
|
+
get_population_weighted_hdd,
|
46
|
+
get_population_weighted_cdd
|
45
47
|
)
|
46
48
|
|
47
49
|
# Define what should be available when users import *
|
@@ -63,6 +65,7 @@ __all__ = [
|
|
63
65
|
|
64
66
|
"get_point_forecasts",
|
65
67
|
"get_initialization_times",
|
68
|
+
"get_historical_initialization_times",
|
66
69
|
"get_forecast_hours",
|
67
70
|
"get_generation_times",
|
68
71
|
|
@@ -86,5 +89,6 @@ __all__ = [
|
|
86
89
|
"get_historical_500hpa_wind_v",
|
87
90
|
"get_tropical_cyclones",
|
88
91
|
|
89
|
-
"get_population_weighted_hdd"
|
92
|
+
"get_population_weighted_hdd",
|
93
|
+
"get_population_weighted_cdd"
|
90
94
|
]
|
@@ -19,12 +19,14 @@ from . import (
|
|
19
19
|
|
20
20
|
get_point_forecasts,
|
21
21
|
get_initialization_times,
|
22
|
+
get_historical_initialization_times,
|
22
23
|
get_forecast_hours,
|
23
24
|
get_generation_times,
|
24
25
|
get_full_gridded_forecast,
|
25
26
|
get_gridded_forecast,
|
26
27
|
get_tropical_cyclones,
|
27
|
-
get_population_weighted_hdd
|
28
|
+
get_population_weighted_hdd,
|
29
|
+
get_population_weighted_cdd
|
28
30
|
|
29
31
|
)
|
30
32
|
|
@@ -169,6 +171,8 @@ def main():
|
|
169
171
|
'dewpoint_2m',
|
170
172
|
'wind_u_10m',
|
171
173
|
'wind_v_10m',
|
174
|
+
'wind_u_100m',
|
175
|
+
'wind_v_100m',
|
172
176
|
'pressure_msl',
|
173
177
|
'500/temperature',
|
174
178
|
'500/wind_u',
|
@@ -235,11 +239,24 @@ def main():
|
|
235
239
|
hdd_parser.add_argument('-m', '--external-model', help='External model (eg gfs, ifs, hrrr, aifs)')
|
236
240
|
hdd_parser.add_argument('-o', '--output', help='Output file (supports .csv and .json formats)')
|
237
241
|
|
242
|
+
# Population Weighted CDD Command
|
243
|
+
cdd_parser = subparsers.add_parser('cdd', help='Get population weighted cooling degree days (CDD) forecasts')
|
244
|
+
cdd_parser.add_argument('initialization_time', help='Initialization time (YYYYMMDDHH, YYYY-MM-DDTHH, or YYYY-MM-DDTHH:mm:ss)')
|
245
|
+
cdd_parser.add_argument('-i', '--intracycle', action='store_true', help='Use the intracycle forecast')
|
246
|
+
cdd_parser.add_argument('-e', '--ens-member', help='Ensemble member (eg 1 or mean)')
|
247
|
+
cdd_parser.add_argument('-m', '--external-model', help='External model (eg gfs, ifs, hrrr, aifs)')
|
248
|
+
cdd_parser.add_argument('-o', '--output', help='Output file (supports .csv and .json formats)')
|
249
|
+
|
238
250
|
# Initialization Times Command
|
239
251
|
initialization_times_parser = subparsers.add_parser('init_times', help='Get available initialization times for point forecasts')
|
240
252
|
initialization_times_parser.add_argument('-i', '--intracycle', action='store_true', help='Use the intracycle forecast')
|
241
253
|
initialization_times_parser.add_argument('-e', '--ens-member', help='Ensemble member (eg 1 or mean)')
|
242
254
|
|
255
|
+
# Historical Initialization Times Command
|
256
|
+
hist_initialization_times_parser = subparsers.add_parser('hist_init_times', help='Get available historical initialization times')
|
257
|
+
hist_initialization_times_parser.add_argument('-i', '--intracycle', action='store_true', help='Use the intracycle forecast')
|
258
|
+
hist_initialization_times_parser.add_argument('-e', '--ens-member', help='Ensemble member (eg 1 or mean)')
|
259
|
+
|
243
260
|
# Forecast Hours Command
|
244
261
|
forecast_hours_parser = subparsers.add_parser('forecast_hours', help='Get available forecast hours for WeatherMesh')
|
245
262
|
forecast_hours_parser.add_argument('-i', '--intracycle', action='store_true', help='Use the intracycle forecast')
|
@@ -449,6 +466,9 @@ def main():
|
|
449
466
|
elif args.command == 'init_times':
|
450
467
|
get_initialization_times(print_response=True, ensemble_member=args.ens_member, intracycle=args.intracycle)
|
451
468
|
|
469
|
+
elif args.command == 'hist_init_times':
|
470
|
+
get_historical_initialization_times(print_response=True, ensemble_member=args.ens_member, intracycle=args.intracycle)
|
471
|
+
|
452
472
|
elif args.command == 'forecast_hours':
|
453
473
|
get_forecast_hours(print_response=True, ensemble_member=args.ens_member, intracycle=args.intracycle)
|
454
474
|
|
@@ -547,6 +567,17 @@ def main():
|
|
547
567
|
print_response=(not args.output)
|
548
568
|
)
|
549
569
|
|
570
|
+
elif args.command == 'cdd':
|
571
|
+
# Handle population weighted CDD
|
572
|
+
get_population_weighted_cdd(
|
573
|
+
initialization_time=args.initialization_time,
|
574
|
+
intracycle=args.intracycle,
|
575
|
+
ens_member=args.ens_member,
|
576
|
+
external_model=args.external_model,
|
577
|
+
output_file=args.output,
|
578
|
+
print_response=(not args.output)
|
579
|
+
)
|
580
|
+
|
550
581
|
else:
|
551
582
|
parser.print_help()
|
552
583
|
|
@@ -301,6 +301,23 @@ def get_initialization_times(print_response=False, ensemble_member=None, intracy
|
|
301
301
|
return response
|
302
302
|
|
303
303
|
|
304
|
+
def get_historical_initialization_times(print_response=False, ensemble_member=None, intracycle=False):
|
305
|
+
"""
|
306
|
+
Get historical initialization times for forecasts from our archive
|
307
|
+
These may be higher latency to fetch and cannot be used for custom point forecasting
|
308
|
+
"""
|
309
|
+
params = {
|
310
|
+
'ens_member': ensemble_member,
|
311
|
+
'intracycle': intracycle
|
312
|
+
}
|
313
|
+
response = make_api_request(f"{FORECASTS_API_BASE_URL}/historical_initialization_times.json", params=params)
|
314
|
+
|
315
|
+
if print_response:
|
316
|
+
print("Available historical initialization times:")
|
317
|
+
for time in response:
|
318
|
+
print(f" - {time}")
|
319
|
+
|
320
|
+
|
304
321
|
def get_forecast_hours(print_response=False, ensemble_member=None, intracycle=False):
|
305
322
|
"""
|
306
323
|
Get available forecast hours for WeatherMesh
|
@@ -427,3 +444,41 @@ def get_population_weighted_hdd(initialization_time, intracycle=False, ens_membe
|
|
427
444
|
print(f" {dates[i]}: {response['hdd'][region][dates[i]]}")
|
428
445
|
|
429
446
|
return response
|
447
|
+
|
448
|
+
def get_population_weighted_cdd(initialization_time, intracycle=False, ens_member=None, external_model=None, output_file=None, print_response=False):
|
449
|
+
"""
|
450
|
+
Get population weighted CDD data from the API.
|
451
|
+
"""
|
452
|
+
params = {
|
453
|
+
"initialization_time": initialization_time,
|
454
|
+
"intracycle": intracycle,
|
455
|
+
"ens_member": ens_member,
|
456
|
+
"external_model": external_model
|
457
|
+
}
|
458
|
+
response = make_api_request(f"{FORECASTS_API_BASE_URL}/cdd", params=params, as_json=True)
|
459
|
+
|
460
|
+
if output_file:
|
461
|
+
if output_file.endswith('.csv'):
|
462
|
+
import csv
|
463
|
+
|
464
|
+
# save as csv, with a row for each region, and a column for each date, sorted alphabetically by region
|
465
|
+
regions = sorted(response['cdd'].keys())
|
466
|
+
dates = response['dates']
|
467
|
+
data = [[response['cdd'][region][dates[i]] for region in regions] for i in range(len(dates))]
|
468
|
+
|
469
|
+
with open(output_file, 'w') as f:
|
470
|
+
writer = csv.writer(f)
|
471
|
+
writer.writerow(['Region'] + dates)
|
472
|
+
|
473
|
+
for region in regions:
|
474
|
+
writer.writerow([region] + [response['cdd'][region][date] for date in dates])
|
475
|
+
|
476
|
+
if print_response:
|
477
|
+
dates = response['dates']
|
478
|
+
print(response['cdd']['Alabama'])
|
479
|
+
for region in sorted(response['cdd'].keys()):
|
480
|
+
print(f"{region}:")
|
481
|
+
for i in range(len(dates)):
|
482
|
+
print(f" {dates[i]}: {response['cdd'][region][dates[i]]}")
|
483
|
+
|
484
|
+
return response
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|