Skip to content

Commit be359d1

Browse files
author
Gabriel de Courval-Pare
committed
Added metadata for forecast hour step and end
1 parent 74e1423 commit be359d1

File tree

1 file changed

+149
-76
lines changed

1 file changed

+149
-76
lines changed

msc_pygeoapi/process/weather/extract_wind_data.py

Lines changed: 149 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
'type': 'string',
5555
},
5656
'minOccurs': 1,
57-
'maxOccurs': 1
57+
'maxOccurs': 1,
5858
},
5959
'model_run': {
6060
'title': 'Model run',
@@ -63,9 +63,23 @@
6363
'minOccurs': 1,
6464
'maxOccurs': 1,
6565
},
66-
'forecast_hour': {
67-
'title': 'Forecast hour',
68-
'description': 'Forecast hour in XXX format.',
66+
'forecast_start_hour': {
67+
'title': 'Forecast start hour',
68+
'description': 'Forecast start hour in XXX format.',
69+
'schema': {'type': 'string'},
70+
'minOccurs': 1,
71+
'maxOccurs': 1,
72+
},
73+
'forecast_step': {
74+
'title': 'Forecast time step',
75+
'description': 'Forecast time step, usually a multiple of 3',
76+
'schema': {'type': 'number'},
77+
'minOccurs': 1,
78+
'maxOccurs': 1,
79+
},
80+
'forecast_end_hour': {
81+
'title': 'Forecast end hour',
82+
'description': 'Forecast end hour in XXX format.',
6983
'schema': {'type': 'string'},
7084
'minOccurs': 1,
7185
'maxOccurs': 1,
@@ -77,7 +91,7 @@
7791
'type': 'number',
7892
},
7993
'minOccurs': 1,
80-
'maxOccurs': 1
94+
'maxOccurs': 1,
8195
},
8296
'lat': {
8397
'title': 'lat coordinate',
@@ -86,7 +100,7 @@
86100
'type': 'number',
87101
},
88102
'minOccurs': 1,
89-
'maxOccurs': 1
103+
'maxOccurs': 1,
90104
},
91105
},
92106
"outputs": {
@@ -99,7 +113,7 @@
99113
'inputs': {
100114
'model': 'GDWPS',
101115
'model_run': f'{(datetime.date.today() - datetime.timedelta(days=1)).strftime("%Y-%m-%d")}T12:00:00Z', # noqa
102-
'forecast_hour': '003',
116+
'forecast_start_hour': '003',
103117
'lon': -28.75,
104118
'lat': 39.25,
105119
}
@@ -150,103 +164,158 @@ def geo2xy(ds, x, y):
150164
return (x, y)
151165

152166

153-
def extract_wind_data(
154-
model,
155-
model_run,
156-
forecast_hour,
157-
lon,
158-
lat,
159-
):
160-
from msc_pygeoapi.env import GEOMET_HPFX_BASEPATH
161-
162-
data_basepath = GEOMET_HPFX_BASEPATH
163-
164-
date = datetime.datetime.strptime(model_run, DATE_FORMAT)
165-
run_hour = f"{date.hour:02}"
166-
date_formatted = date.strftime("%Y%m%d")
167-
167+
def get_single_wind_data(model, date_formatted, run_hour, forecast_hour, data_basepath, lon, lat):
168168
match(model):
169169
case "GDWPS":
170170
inter_path = f"/model_gdwps/25km/{run_hour}/"
171-
file_name_x = f"{date_formatted}T{run_hour}Z_MSC_GDWPS_UGRD_AGL-10m_LatLon0.25_PT{forecast_hour}H.grib2"
172-
file_name_y = f"{date_formatted}T{run_hour}Z_MSC_GDWPS_VGRD_AGL-10m_LatLon0.25_PT{forecast_hour}H.grib2"
171+
file_name_u = f"{date_formatted}T{run_hour}Z_MSC_GDWPS_UGRD_AGL-10m_LatLon0.25_PT{forecast_hour}H.grib2"
172+
file_name_v = f"{date_formatted}T{run_hour}Z_MSC_GDWPS_VGRD_AGL-10m_LatLon0.25_PT{forecast_hour}H.grib2"
173173

174174
case "GDPS":
175175
inter_path = f"/model_gem_global/15km/grib2/lat_lon/{run_hour}/{forecast_hour}/"
176-
file_name_x = f"CMC_glb_UGRD_TGL_10_latlon.15x.15_{date_formatted}{run_hour}_P{forecast_hour}.grib2"
177-
file_name_y = f"CMC_glb_VGRD_TGL_10_latlon.15x.15_{date_formatted}{run_hour}_P{forecast_hour}.grib2"
176+
file_name_u = f"CMC_glb_UGRD_TGL_10_latlon.15x.15_{date_formatted}{run_hour}_P{forecast_hour}.grib2"
177+
file_name_v = f"CMC_glb_VGRD_TGL_10_latlon.15x.15_{date_formatted}{run_hour}_P{forecast_hour}.grib2"
178178

179179
case "GEPS":
180180
raise NotImplementedError("GEPS raw data not yet in Geomet HPFX")
181181

182182
case "RDWPS":
183-
inter_path = f"/model_rdwps/national/2.5km/{run_hour}/"
184-
file_name_x = f"{date_formatted}T{run_hour}Z_MSC_RDWPS_UGRD_AGL-10m_RLatLon0.0225_PT{forecast_hour}H.grib2"
185-
file_name_y = f"{date_formatted}T{run_hour}Z_MSC_RDWPS_VGRD_AGL-10m_RLatLon0.0225_PT{forecast_hour}H.grib2"
183+
# RDWPS grid is rotated so we're using HRDPS instead
184+
inter_path = f"/model_hrdps/continental/2.5km/{run_hour}/{forecast_hour}/"
185+
file_name_wind = f"{date_formatted}T{run_hour}Z_MSC_HRDPS_WIND_AGL-10m_RLatLon0.0225_PT{forecast_hour}H.grib2"
186+
file_name_wdir = f"{date_formatted}T{run_hour}Z_MSC_HRDPS_WDIR_AGL-10m_RLatLon0.0225_PT{forecast_hour}H.grib2"
186187

187188
case "HRDPS":
188189
inter_path = f"/model_hrdps/continental/2.5km/{run_hour}/{forecast_hour}/"
189-
file_name_x = f"{date_formatted}T{run_hour}Z_MSC_HRDPS_UGRD_AGL-10m_RLatLon0.0225_PT{forecast_hour}H.grib2"
190-
file_name_y = f"{date_formatted}T{run_hour}Z_MSC_HRDPS_VGRD_AGL-10m_RLatLon0.0225_PT{forecast_hour}H.grib2"
190+
file_name_wind = f"{date_formatted}T{run_hour}Z_MSC_HRDPS_WIND_AGL-10m_RLatLon0.0225_PT{forecast_hour}H.grib2"
191+
file_name_wdir = f"{date_formatted}T{run_hour}Z_MSC_HRDPS_WDIR_AGL-10m_RLatLon0.0225_PT{forecast_hour}H.grib2"
191192

192193
case "REPS":
193194
inter_path = f"/ensemble/reps/10km/grib2/{run_hour}/{forecast_hour}/"
194-
file_name_x = f"{date_formatted}T{run_hour}Z_MSC_REPS_UGRD_AGL-10m_RLatLon0.09x0.09_PT{forecast_hour}H.grib2"
195-
file_name_y = f"{date_formatted}T{run_hour}Z_MSC_REPS_VGRD_AGL-10m_RLatLon0.09x0.09_PT{forecast_hour}H.grib2"
195+
file_name_u = f"{date_formatted}T{run_hour}Z_MSC_REPS_UGRD_AGL-10m_RLatLon0.09x0.09_PT{forecast_hour}H.grib2"
196+
file_name_v = f"{date_formatted}T{run_hour}Z_MSC_REPS_VGRD_AGL-10m_RLatLon0.09x0.09_PT{forecast_hour}H.grib2"
196197

197198
case _:
198199
raise ValueError(f"Unknown model: {model}")
199200

200-
output = {
201-
'type': "Feature",
202-
'geometry': {"type": "Point", "coordinates": [lon, lat]},
203-
'properties': {
204-
'wind_speed_unit': "knots",
205-
'wind_direction_unit': "°",
206-
},
207-
}
201+
if model in ("GDWPS", "GDPS", "GEPS", "REPS"):
202+
full_path_u = f"{data_basepath}{inter_path}{file_name_u}"
203+
full_path_v = f"{data_basepath}{inter_path}{file_name_v}"
204+
205+
ds_u = gdal.Open(full_path_u, gdal.GA_ReadOnly)
206+
ds_v = gdal.Open(full_path_v, gdal.GA_ReadOnly)
207+
208+
if ds_u is None:
209+
raise NameError(f"Couldn't open {full_path_u}, check if file exists")
210+
211+
if ds_v is None:
212+
raise NameError(f"Couldn't open {full_path_v}, check if file exists")
208213

209-
full_path_x = f"{data_basepath}{inter_path}{file_name_x}"
210-
full_path_y = f"{data_basepath}{inter_path}{file_name_y}"
214+
out_proj = ds_u.GetProjection()
215+
transformer = Transformer.from_crs("EPSG:4326", out_proj, always_xy=True)
216+
_x, _y = transformer.transform(lon, lat)
217+
x, y = geo2xy(ds_u, _x, _y)
211218

212-
ds_x = gdal.Open(full_path_x, gdal.GA_ReadOnly)
213-
ds_y = gdal.Open(full_path_y, gdal.GA_ReadOnly)
219+
band_u = ds_u.GetRasterBand(1)
220+
bitmap_u = band_u.GetMaskBand().ReadAsArray()
214221

215-
if ds_x is None:
216-
raise NameError(f"Couldn't open {full_path_x}, check if file exists")
222+
try:
223+
_ = bitmap_u[y, x]
224+
except IndexError:
225+
raise IndexError("ERROR: no data at requested latitude and longitude - point outside of model grid")
217226

218-
if ds_y is None:
219-
raise NameError(f"Couldn't open {full_path_y}, check if file exists")
227+
if not bitmap_u[y, x]:
228+
raise IndexError("ERROR: no data at requested latitude and longitude - data at point is masked")
220229

221-
out_proj = ds_x.GetProjection()
222-
transformer = Transformer.from_crs("EPSG:4326", out_proj, always_xy=True)
223-
_x, _y = transformer.transform(lon, lat)
224-
x, y = geo2xy(ds_x, _x, _y)
230+
band_y = ds_v.GetRasterBand(1)
225231

226-
band_x = ds_x.GetRasterBand(1)
227-
bitmap_x = band_x.GetMaskBand().ReadAsArray()
232+
data_array_u = band_u.ReadAsArray()
233+
data_array_v = band_y.ReadAsArray()
228234

229-
try:
230-
_ = bitmap_x[y, x]
231-
except IndexError:
232-
raise IndexError("ERROR: no data at requested latitude and longitude - point outside of model grid")
235+
wind_speed_u = data_array_u[y, x]
236+
wind_speed_v = data_array_v[y, x]
233237

234-
if not bitmap_x[y, x]:
235-
raise IndexError("ERROR: no data at requested latitude and longitude - data at point is masked")
238+
norm = get_norm(wind_speed_u, wind_speed_v) * MS_TO_KNOTS
239+
dir = get_dir(wind_speed_u, wind_speed_v)
236240

237-
band_y = ds_y.GetRasterBand(1)
241+
else:
242+
full_path_wind = f"{data_basepath}{inter_path}{file_name_wind}"
243+
full_path_wdir = f"{data_basepath}{inter_path}{file_name_wdir}"
238244

239-
data_array_x = band_x.ReadAsArray()
240-
data_array_y = band_y.ReadAsArray()
245+
ds_wind = gdal.Open(full_path_wind, gdal.GA_ReadOnly)
246+
ds_wdir = gdal.Open(full_path_wdir, gdal.GA_ReadOnly)
241247

242-
wind_speed_x = data_array_x[y, x]
243-
wind_speed_y = data_array_y[y, x]
248+
if ds_wind is None:
249+
raise NameError(f"Couldn't open {full_path_wind}, check if file exists")
250+
251+
if ds_wdir is None:
252+
raise NameError(f"Couldn't open {full_path_wdir}, check if file exists")
253+
254+
out_proj = ds_wind.GetProjection()
255+
transformer = Transformer.from_crs("EPSG:4326", out_proj, always_xy=True)
256+
_x, _y = transformer.transform(lon, lat)
257+
x, y = geo2xy(ds_wind, _x, _y)
258+
259+
band_wind = ds_wind.GetRasterBand(1)
260+
bitmap_wind = band_wind.GetMaskBand().ReadAsArray()
261+
262+
try:
263+
_ = bitmap_wind[y, x]
264+
except IndexError:
265+
raise IndexError("ERROR: no data at requested latitude and longitude - point outside of model grid")
266+
267+
if not bitmap_wind[y, x]:
268+
raise IndexError("ERROR: no data at requested latitude and longitude - data at point is masked")
269+
270+
band_wdir = ds_wdir.GetRasterBand(1)
271+
272+
data_array_wind = band_wind.ReadAsArray()
273+
data_array_wdir = band_wdir.ReadAsArray()
274+
275+
norm = data_array_wind[y, x] * MS_TO_KNOTS
276+
dir = data_array_wdir[y, x]
277+
278+
output = {}
279+
280+
output['wind_speed'] = norm
281+
output['wind_dir'] = dir
282+
283+
return output
284+
285+
286+
def extract_wind_data(
287+
model,
288+
model_run,
289+
forecast_start_hour,
290+
forecast_step,
291+
forecast_end_hour,
292+
lon,
293+
lat,
294+
):
295+
from msc_pygeoapi.env import GEOMET_HPFX_BASEPATH
296+
297+
data_basepath = GEOMET_HPFX_BASEPATH
298+
299+
date = datetime.datetime.strptime(model_run, DATE_FORMAT)
300+
run_hour = f"{date.hour:02}"
301+
date_formatted = date.strftime("%Y%m%d")
302+
303+
output = {
304+
'type': "Feature",
305+
'geometry': {"type": "Point", "coordinates": [lon, lat]},
306+
'properties': {
307+
'wind_speed_unit': "knots",
308+
'wind_direction_unit': "°",
309+
},
310+
}
244311

245-
norm = get_norm(wind_speed_x, wind_speed_y) * MS_TO_KNOTS
246-
dir = get_dir(wind_speed_x, wind_speed_y)
312+
n = (int(forecast_end_hour) - int(forecast_start_hour)) / forecast_step
313+
n = int(n) + 1
247314

248-
output['properties']['wind_speed'] = norm
249-
output['properties']['wind_dir'] = dir
315+
for i in range(n):
316+
forecast_hour = int(forecast_start_hour) + (i * forecast_step)
317+
forecast_hour = f'{forecast_hour}'.zfill(3)
318+
output['properties'][forecast_hour] = get_single_wind_data(model, date_formatted, run_hour, forecast_hour, data_basepath, lon, lat)
250319

251320
return output
252321

@@ -271,7 +340,7 @@ def extract_wind_data_execute():
271340
)
272341
@click.option(
273342
"-fh",
274-
"--forecast_hour",
343+
"--forecast_start_hour",
275344
help="forecast hour 3 digits format",
276345
required=True
277346
)
@@ -289,7 +358,7 @@ def extract_wind_data_cli(
289358
ctx,
290359
model,
291360
model_run,
292-
forecast_hour,
361+
forecast_start_hour,
293362
lon,
294363
lat,
295364
):
@@ -301,7 +370,7 @@ def extract_wind_data_cli(
301370
output = extract_wind_data(
302371
model,
303372
model_run,
304-
forecast_hour,
373+
forecast_start_hour,
305374
float(lon),
306375
float(lat),
307376
)
@@ -332,23 +401,27 @@ def __init__(self, provider_def):
332401
def execute(self, data, outputs=None):
333402
mimetype = "application/json"
334403

335-
required = ["model", "model_run", "forecast_hour", "lon", "lat"]
404+
required = ["model", "model_run", "forecast_start_hour", "forecast_step", "forecast_end_hour", "lon", "lat"]
336405
if not all([param in data for param in required]):
337406
msg = "Missing required parameters."
338407
LOGGER.error(msg)
339408
raise ProcessorExecuteError(msg)
340409

341410
model = data.get("model")
342411
mr = data.get("model_run")
343-
fh = data.get("forecast_hour")
412+
fsh = data.get("forecast_start_hour")
413+
fs = data.get("forecast_step")
414+
fse = data.get("forecast_end_hour")
344415
lon = float(data.get("lon"))
345416
lat = float(data.get("lat"))
346417

347418
try:
348419
output = extract_wind_data(
349420
model,
350421
mr,
351-
fh,
422+
fsh,
423+
fs,
424+
fse,
352425
lon,
353426
lat,
354427
)

0 commit comments

Comments
 (0)