windborne 1.0.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.
@@ -0,0 +1,459 @@
1
+ from .config import (FORECASTS_API_BASE_URL,
2
+ FORECASTS_GRIDDED_URL,
3
+ FORECASTS_HISTORICAL_URL,
4
+ FORECASTS_TCS_URL,
5
+ TCS_SUPPORTED_FORMATS)
6
+
7
+ from .utils import (make_api_request,
8
+ parse_time,
9
+ download_and_save_nc,
10
+ save_csv_json,
11
+ save_as_geojson,
12
+ save_as_gpx,
13
+ save_as_kml,
14
+ save_as_little_r)
15
+
16
+ # Point forecasts
17
+ def get_point_forecasts(coordinates, min_forecast_time=None, max_forecast_time=None, min_forecast_hour=None, max_forecast_hour=None, initialization_time=None, save_to_file=None):
18
+ # Sanitize coordinates by removing whitespace
19
+ coordinates = coordinates.replace(" ", "")
20
+
21
+ params = {"coordinates": coordinates}
22
+
23
+ if not coordinates:
24
+ print("To get points forecasts you must provide coordinates.")
25
+ return
26
+ if min_forecast_time:
27
+ params["min_forecast_time"] = parse_time(min_forecast_time)
28
+ if max_forecast_time:
29
+ params["max_forecast_time"] = parse_time(max_forecast_time)
30
+ if min_forecast_hour:
31
+ params["min_forecast_hour"] = int(min_forecast_hour)
32
+ if max_forecast_hour:
33
+ params["max_forecast_hour"] = int(max_forecast_hour)
34
+ if initialization_time:
35
+ initialization_time = parse_time(initialization_time,init_time_flag=True)
36
+ params["initialization_time"] = initialization_time
37
+
38
+ print("We are initiating handshake procedure with our S3 server.\n")
39
+
40
+ response = make_api_request(f"{FORECASTS_API_BASE_URL}/points", params=params)
41
+
42
+ if save_to_file:
43
+ # Save as .json
44
+ save_csv_json(save_to_file, response, csv_data_key='forecasts')
45
+
46
+ return response
47
+
48
+ # Gridded forecasts
49
+ # We return the whole response, not just the url
50
+
51
+ # 500hPa geopotential
52
+ # 850hPa geopotential
53
+ # 500hPa wind u
54
+ # 500hPa wind v
55
+ # 500hPa temperature
56
+ # 850hPa temperature
57
+ # wind_u_10m
58
+ # wind_v_10m
59
+ # pressure_msl
60
+ # temperature_2m
61
+
62
+ def get_temperature_2m(time, save_to_file=None):
63
+ params = {}
64
+
65
+ if not time:
66
+ print("To get the gridded output of global 2m temperature forecast you need to provide the time for which to get the forecast.")
67
+ return
68
+ else:
69
+ time_parsed = parse_time(time)
70
+ params["time"] = time_parsed
71
+
72
+ print("We are initiating handshake procedure with our S3 server.\n")
73
+
74
+ response = make_api_request(f"{FORECASTS_GRIDDED_URL}/temperature_2m", params=params, return_type='all')
75
+
76
+ if save_to_file:
77
+ download_and_save_nc(save_to_file, response)
78
+
79
+ return response
80
+
81
+ # not implemented yet
82
+ def get_dewpoint_2m(time, save_to_file=None):
83
+ params = {}
84
+
85
+ if not time:
86
+ print("To get the gridded output of global 2m dewpoint forecast you need to provide the time for which to get the forecast.")
87
+ return
88
+ else:
89
+ time_parsed = parse_time(time)
90
+ params["time"] = time_parsed
91
+
92
+ print("We are initiating handshake procedure with our S3 server.\n")
93
+ response = make_api_request(f"{FORECASTS_GRIDDED_URL}/dewpoint_2m", params=params, return_type='all')
94
+
95
+ if save_to_file:
96
+ download_and_save_nc(save_to_file, response)
97
+
98
+ return response
99
+
100
+ def get_wind_u_10m(time, save_to_file=None):
101
+ params = {}
102
+
103
+ if not time:
104
+ print("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.")
105
+ return
106
+ else:
107
+ time_parsed = parse_time(time)
108
+ params["time"] = time_parsed
109
+
110
+ print("We are initiating handshake procedure with our S3 server.\n")
111
+ response = make_api_request(f"{FORECASTS_GRIDDED_URL}/wind_u_10m", params=params, return_type='all')
112
+
113
+ if save_to_file:
114
+ download_and_save_nc(save_to_file, response)
115
+
116
+ return response
117
+
118
+ def get_wind_v_10m(time, save_to_file=None):
119
+ params = {}
120
+
121
+ if not time:
122
+ print("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.")
123
+ return
124
+ else:
125
+ time_parsed = parse_time(time)
126
+ params["time"] = time_parsed
127
+
128
+ print("We are initiating handshake procedure with our S3 server.\n")
129
+ response = make_api_request(f"{FORECASTS_GRIDDED_URL}/wind_v_10m", params=params, return_type='all')
130
+
131
+ if save_to_file:
132
+ download_and_save_nc(save_to_file, response)
133
+
134
+ return response
135
+
136
+ def get_500hpa_wind_u(time, save_to_file=None):
137
+ params = {}
138
+
139
+ if not time:
140
+ print("To get the gridded output of global 500hPa wind u-component of wind forecasts you need to provide the time for which to get the forecast.")
141
+ return
142
+ else:
143
+ time_parsed = parse_time(time)
144
+ params["time"] = time_parsed
145
+
146
+ print("We are initiating handshake procedure with our S3 server.\n")
147
+ response = make_api_request(f"{FORECASTS_GRIDDED_URL}/500/wind_u", params=params, return_type='all')
148
+
149
+ if save_to_file:
150
+ download_and_save_nc(save_to_file, response)
151
+
152
+ return response
153
+
154
+ def get_500hpa_wind_v(time, save_to_file=None):
155
+ params = {}
156
+
157
+ if not time:
158
+ print("To get the gridded output of global 500hPa wind v-component of wind forecasts you need to provide the time for which to get the forecast.")
159
+ return
160
+ else:
161
+ time_parsed = parse_time(time)
162
+ params["time"] = time_parsed
163
+
164
+ print("We are initiating handshake procedure with our S3 server.\n")
165
+ response = make_api_request(f"{FORECASTS_GRIDDED_URL}/500/wind_v", params=params, return_type='all')
166
+
167
+ if save_to_file:
168
+ download_and_save_nc(save_to_file, response)
169
+
170
+ return response
171
+
172
+ def get_500hpa_temperature(time, save_to_file=None):
173
+ params = {}
174
+
175
+ if not time:
176
+ print("To get the gridded output of global 500hPa temperature forecasts you need to provide the time for which to get the forecast.")
177
+ return
178
+ else:
179
+ time_parsed = parse_time(time)
180
+ params["time"] = time_parsed
181
+
182
+ print("We are initiating handshake procedure with our S3 server.\n")
183
+ response = make_api_request(f"{FORECASTS_GRIDDED_URL}/500/temperature", params=params, return_type='all')
184
+
185
+ if save_to_file:
186
+ download_and_save_nc(save_to_file, response)
187
+
188
+ return response
189
+
190
+ def get_850hpa_temperature(time, save_to_file=None):
191
+ params = {}
192
+
193
+ if not time:
194
+ print("To get the gridded output of global 850hPa temperature forecasts you need to provide the time for which to get the forecast.")
195
+ return
196
+ else:
197
+ time_parsed = parse_time(time)
198
+ params["time"] = time_parsed
199
+
200
+ print("We are initiating handshake procedure with our S3 server.\n")
201
+ response = make_api_request(f"{FORECASTS_GRIDDED_URL}/850/temperature", params=params, return_type='all')
202
+
203
+ if save_to_file:
204
+ download_and_save_nc(save_to_file, response)
205
+
206
+ return response
207
+
208
+ def get_pressure_msl(time, save_to_file=None):
209
+ params = {}
210
+
211
+ if not time:
212
+ print("To get the gridded output of global mean sea level pressure forecasts you need to provide the time for which to get the forecast.")
213
+ return
214
+ else:
215
+ time_parsed = parse_time(time)
216
+ params["time"] = time_parsed
217
+
218
+ print("We are initiating handshake procedure with our S3 server.\n")
219
+ response = make_api_request(f"{FORECASTS_GRIDDED_URL}/pressure_msl", params=params, return_type='all')
220
+
221
+ if save_to_file:
222
+ download_and_save_nc(save_to_file, response)
223
+
224
+ return response
225
+
226
+ def get_500hpa_geopotential(time, save_to_file=None):
227
+ params = {}
228
+
229
+ if not time:
230
+ print("To get the gridded output of global 500hPa geopotential forecasts you need to provide the time for which to get the forecast.")
231
+ return
232
+ else:
233
+ time_parsed = parse_time(time)
234
+ params["time"] = time_parsed
235
+
236
+ print("We are initiating handshake procedure with our S3 server.\n")
237
+ response = make_api_request(f"{FORECASTS_GRIDDED_URL}/500/geopotential", params=params, return_type='all')
238
+
239
+ if save_to_file:
240
+ download_and_save_nc(save_to_file, response)
241
+
242
+ return response
243
+
244
+ def get_850hpa_geopotential(time, save_to_file=None):
245
+ params = {}
246
+
247
+ if not time:
248
+ print("To get the gridded output of global 850hPa geopotential forecasts you need to provide the time for which to get the forecast.")
249
+ return
250
+ else:
251
+ time_parsed = parse_time(time)
252
+ params["time"] = time_parsed
253
+
254
+ print("We are initiating handshake procedure with our S3 server.\n")
255
+ response = make_api_request(f"{FORECASTS_GRIDDED_URL}/850/geopotential", params=params, return_type='all')
256
+
257
+ if save_to_file:
258
+ download_and_save_nc(save_to_file, response)
259
+
260
+ return response
261
+
262
+ # Historical forecasts
263
+ # We return the whole response, not just the url
264
+
265
+ def get_historical_temperature_2m(initialization_time, forecast_hour, save_to_file=None):
266
+ params = {}
267
+
268
+ if not initialization_time or not forecast_hour:
269
+ print("To get the historical output of global temperature forecasts you need to provide:\n"
270
+ "- the initialization time of the forecast\n"
271
+ "- how many hours after the run time the forecast is valid at.\n")
272
+ return
273
+ else:
274
+ time_parsed = parse_time(initialization_time, init_time_flag=True)
275
+ params["initialization_time"] = time_parsed
276
+ params["forecast_hour"] = forecast_hour
277
+
278
+ print("We are initiating handshake procedure with our S3 server.\n")
279
+
280
+ response = make_api_request(f"{FORECASTS_HISTORICAL_URL}/temperature_2m", params=params, return_type='all')
281
+
282
+ if save_to_file:
283
+ download_and_save_nc(save_to_file, response)
284
+
285
+ return response
286
+
287
+ def get_historical_500hpa_geopotential(initialization_time, forecast_hour, save_to_file=None):
288
+ params = {}
289
+
290
+ if not initialization_time or not forecast_hour:
291
+ print("To get the historical output of global 500hPa geopotential forecasts you need to provide:\n"
292
+ "- the initialization time of the forecast\n"
293
+ "- how many hours after the run time the forecast is valid at.\n")
294
+ return
295
+ else:
296
+ time_parsed = parse_time(initialization_time,init_time_flag=True)
297
+ params["initialization_time"] = time_parsed
298
+ params["forecast_hour"] = forecast_hour
299
+
300
+ print("We are initiating handshake procedure with our S3 server.\n")
301
+
302
+ response = make_api_request(f"{FORECASTS_HISTORICAL_URL}/500/geopotential", params=params, return_type='all')
303
+
304
+ if save_to_file:
305
+ download_and_save_nc(save_to_file, response)
306
+
307
+ return response
308
+
309
+ def get_historical_500hpa_wind_u(initialization_time, forecast_hour, save_to_file=None):
310
+ params = {}
311
+
312
+ if not initialization_time or not forecast_hour:
313
+ print("To get the historical output of global 500hPa wind u forecasts you need to provide:\n"
314
+ "- the initialization time of the forecast\n"
315
+ "- how many hours after the run time the forecast is valid at.\n")
316
+ return
317
+ else:
318
+ time_parsed = parse_time(initialization_time,init_time_flag=True)
319
+ params["initialization_time"] = time_parsed
320
+ params["forecast_hour"] = forecast_hour
321
+
322
+ print("We are initiating handshake procedure with our S3 server.\n")
323
+
324
+ response = make_api_request(f"{FORECASTS_HISTORICAL_URL}/500/wind_u", params=params, return_type='all')
325
+
326
+ if save_to_file:
327
+ download_and_save_nc(save_to_file, response)
328
+
329
+ return response
330
+
331
+ def get_historical_500hpa_wind_v(initialization_time, forecast_hour, save_to_file=None):
332
+ params = {}
333
+
334
+ if not initialization_time or not forecast_hour:
335
+ print("To get the historical output of global 500hPa wind v forecasts you need to provide:\n"
336
+ "- the initialization time of the forecast\n"
337
+ "- how many hours after the run time the forecast is valid at.\n")
338
+ return
339
+ else:
340
+ time_parsed = parse_time(initialization_time, init_time_flag=True)
341
+ params["initialization_time"] = time_parsed
342
+ params["forecast_hour"] = forecast_hour
343
+
344
+ print("We are initiating handshake procedure with our S3 server.\n")
345
+
346
+ response = make_api_request(f"{FORECASTS_HISTORICAL_URL}/500/wind_v", params=params, return_type='all')
347
+
348
+ if save_to_file:
349
+ download_and_save_nc(save_to_file, response)
350
+
351
+ return response
352
+
353
+ # Other
354
+ # TCs
355
+ def get_tropical_cyclones(initialization_time=None, basin=None, save_to_file=None):
356
+ """
357
+ Get tropical cyclone data from the API.
358
+
359
+ Args:
360
+ initialization_time (str): Date in either ISO 8601 format (YYYY-MM-DDTHH:00:00)
361
+ or compact format (YYYYMMDDHH)
362
+ where HH must be 00, 06, 12, or 18
363
+ save_to_file (str, optional): Path to save the response data
364
+ Supported formats: .json, .csv, .gpx, .geojson, .kml, .little_r
365
+
366
+ Returns:
367
+ dict: API response data or None if there's an error
368
+ """
369
+ params = {}
370
+
371
+ if initialization_time:
372
+ initialization_time_parsed = parse_time(initialization_time, init_time_flag=True)
373
+ params["initialization_time"] = initialization_time_parsed
374
+ else:
375
+ # Madee this for our displaying message when no active tcs found
376
+ initialization_time = 'latest'
377
+
378
+ if basin:
379
+ if basin not in ['NA', 'EP', 'WP', 'NI', 'SI', 'AU', 'SP']:
380
+ print("Basin should be one of the following:")
381
+ print("NA - North Atlantic")
382
+ print("EP - Eastern Pacific")
383
+ print("WP - Western Pacific")
384
+ print("NI - North Indian")
385
+ print("SI - South West Indian")
386
+ print("AU - Australian Region")
387
+ print("SP - South Pacific")
388
+ exit(44)
389
+ params["basin"] = basin
390
+
391
+ # Response here is a .json
392
+ response = make_api_request(FORECASTS_TCS_URL, params=params)
393
+
394
+ if save_to_file:
395
+ if '.' not in save_to_file:
396
+ print("You have to provide a filetype for your output file.")
397
+ print_tc_supported_formats()
398
+ exit (4)
399
+ elif not save_to_file.lower().endswith(TCS_SUPPORTED_FORMATS):
400
+ print("Unsupported file format.")
401
+ print_tc_supported_formats()
402
+ exit(44)
403
+ elif response == {}:
404
+ # This should be prior to any check of specific .filetype format check and post filetype valid check
405
+ # make_api_request covers 403, 404, 502, HTTP, Connections Errors
406
+ # If we pass all of these and we get an empty dictionary ==> there are no active TCs
407
+ print("There are no active tropical cyclones for your request\n")
408
+ print("We didn't save any file on your machine.")
409
+ # It's pointless to save an empty file
410
+ # save_response_to_file() will throw error on saving {}
411
+ elif response is None:
412
+ print("-------------------------------------------------------")
413
+ print("You are too quick!\nThe tropical cyclone data for initialization time are not uploaded yet.")
414
+ print('You may check again in a few hours again.')
415
+ elif save_to_file.lower().endswith('.csv'):
416
+ # Flatten for CSV
417
+ flattened_data = []
418
+ for cyclone_id, tracks in response.items():
419
+ for track in tracks:
420
+ track_data = {
421
+ 'cyclone_id': cyclone_id,
422
+ 'latitude': track['latitude'],
423
+ 'longitude': track['longitude'],
424
+ 'time': track['time']
425
+ }
426
+ flattened_data.append(track_data)
427
+ save_csv_json(save_to_file, {'prediction': flattened_data}, csv_data_key='prediction')
428
+ elif save_to_file.lower().endswith('.json'):
429
+ # Direct save for JSON
430
+ save_csv_json(save_to_file, response)
431
+ elif save_to_file.lower().endswith('.geojson'):
432
+ save_as_geojson(save_to_file, response)
433
+ elif save_to_file.lower().endswith('.gpx'):
434
+ save_as_gpx(save_to_file, response)
435
+ elif save_to_file.lower().endswith('.kml'):
436
+ save_as_kml(save_to_file, response)
437
+ elif save_to_file.lower().endswith('.little_r'):
438
+ save_as_little_r(save_to_file, response)
439
+
440
+ return response
441
+
442
+ def get_initialization_times():
443
+ """
444
+ Get available initialization times for pointy.
445
+ Returns:
446
+ dict: API response data or None if there's an error
447
+ """
448
+
449
+ # Response here is a .json
450
+ response = make_api_request(f"{FORECASTS_API_BASE_URL}/initialization_times.json")
451
+
452
+ return response
453
+
454
+ # Tropical cyclones
455
+ def print_tc_supported_formats():
456
+ """Print supported file formats for saving tcs data."""
457
+ print("Supported formats:")
458
+ for fmt in TCS_SUPPORTED_FORMATS:
459
+ print(f" - {fmt}")