Skip to content

Commit 8ef7618

Browse files
Supports transforming coordinates for GSSHA output
1 parent cc56a38 commit 8ef7618

3 files changed

Lines changed: 65 additions & 9 deletions

File tree

src/xarray_data_accessor/data_converters/to_gssha.py

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import warnings
22
import logging
3+
import pyproj
34
import pandas as pd
45
import numpy as np
56
import xarray as xr
@@ -118,8 +119,10 @@ def _write_ascii_file(
118119

119120
@ staticmethod
120121
def _write_precip_coords(
121-
easting: pd.Series,
122-
northing: pd.Series,
122+
easting: np.ndarray,
123+
northing: np.ndarray,
124+
input_epsg: Optional[int] = None,
125+
output_epsg: Optional[int] = None,
123126
) -> str:
124127
"""Writes the coordinate lines of a precipitation ASCII file.
125128
@@ -136,8 +139,17 @@ def _write_precip_coords(
136139
COORD 204555.0 4751268.0 "Center of precipitation pixel #1"
137140
COORD 205642.0 4750491.0 "Center of precipitation pixel #2"
138141
"""
142+
# reproject if necessary
143+
if output_epsg:
144+
easting, northing = utility_functions._convert_xy_coordinates(
145+
x=easting,
146+
y=northing,
147+
input_epsg=input_epsg,
148+
output_epsg=output_epsg,
149+
)
150+
139151
# zip the coordinates
140-
coordinates = zip(easting.to_list(), northing.to_list())
152+
coordinates = zip(easting.tolist(), northing.tolist())
141153

142154
# get the number of "gages"
143155
num_gages = len(easting)
@@ -238,6 +250,7 @@ def make_gssha_precipitation_input(
238250
precipitation_variable: str,
239251
precipitation_type: Optional[PrecipitationType] = None,
240252
event_intervals: Optional[List[EventIntervals]] = None,
253+
output_epsg: Optional[int] = None,
241254
file_dir: Optional[Union[str, Path]] = None,
242255
file_name: Optional[str] = None,
243256
file_suffix: Optional[str] = None,
@@ -296,8 +309,10 @@ def make_gssha_precipitation_input(
296309
)
297310
ts1: datetime = data_df['time'].unique()[0]
298311
coordinates_header: str = cls._write_precip_coords(
299-
easting=data_df.loc[data_df.time == ts1, x_dim],
300-
northing=data_df.loc[data_df.time == ts1, y_dim],
312+
easting=data_df.loc[data_df.time == ts1, x_dim].to_numpy(),
313+
northing=data_df.loc[data_df.time == ts1, y_dim].to_numpy(),
314+
input_epsg=xarray_dataset.attrs.get('EPSG', None),
315+
output_epsg=output_epsg,
301316
)
302317

303318
# get events
@@ -353,6 +368,7 @@ def make_gssha_grass_ascii(
353368
hmet_variable: Optional[str] = None,
354369
start_time: Optional[TimeInput] = None,
355370
end_time: Optional[TimeInput] = None,
371+
output_epsg: Optional[int] = None,
356372
file_dir: Optional[Union[str, Path]] = None,
357373
file_name: Optional[str] = None,
358374
file_suffix: Optional[str] = None,
@@ -402,11 +418,20 @@ def make_gssha_grass_ascii(
402418
x_dim = xarray_dataset.attrs['x_dim']
403419
y_dim = xarray_dataset.attrs['y_dim']
404420

421+
# get easting and northing coordinate arrays (reproject if necessary)
422+
easting, northing = utility_functions._convert_xy_coordinates(
423+
x=xarray_dataset[x_dim].values,
424+
y=xarray_dataset[y_dim].values,
425+
input_epsg=xarray_dataset.attrs.get('EPSG', None),
426+
output_epsg=output_epsg,
427+
)
428+
429+
# make bounding box for header
405430
bbox = BoundingBoxDict(
406-
north=np.max(xarray_dataset[y_dim].values),
407-
south=np.min(xarray_dataset[y_dim].values),
408-
east=np.max(xarray_dataset[x_dim].values),
409-
west=np.min(xarray_dataset[x_dim].values),
431+
north=np.max(northing),
432+
south=np.min(northing),
433+
east=np.max(easting),
434+
west=np.min(easting),
410435
)
411436

412437
for direction in ['north', 'south', 'east', 'west']:
@@ -482,6 +507,8 @@ def make_gssha_hmet_wes(
482507
file_suffix: The file suffix to use.
483508
how: The method to use to aggregate the data at each time step.
484509
Options include: 'mean', 'median', 'min', 'max', 'sum'.
510+
xy_coords: The x and y coordinate names to use for aggregation.
511+
Note that this must be in the same units as the xarray dataset.
485512
486513
Returns:
487514
The path of the output WES ASCII file.

src/xarray_data_accessor/utility_functions.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,12 +221,40 @@ def _coords_in_bbox(
221221
return False
222222

223223

224+
def _convert_xy_coordinates(
225+
x: np.ndarray,
226+
y: np.ndarray,
227+
input_epsg: Optional[int] = None,
228+
output_epsg: Optional[int] = None,
229+
) -> Tuple[np.ndarray, np.ndarray]:
230+
"""Transforms coordinates from one EPSG to another."""
231+
232+
if not input_epsg and output_epsg:
233+
raise ValueError(
234+
'An input EPSG must be provided if an output EPSG is provided! '
235+
'This is pulled from xarray_dataset.attrs["EPSG"]. '
236+
'Please set this attribute to a valid integer EPSG code.',
237+
)
238+
if output_epsg and output_epsg != input_epsg:
239+
# Create a pyproj Transformer for the coordinate conversion
240+
transformer = pyproj.Transformer.from_crs(
241+
input_epsg,
242+
output_epsg,
243+
always_xy=True,
244+
)
245+
246+
# Perform the coordinate conversion
247+
x, y = transformer.transform(x, y)
248+
return x, y
249+
250+
224251
def _convert_bbox(
225252
bbox: BoundingBoxDict,
226253
known_epsg: int,
227254
) -> BoundingBoxDict:
228255
"""Converts bbox coordinates to a different EPSG."""
229256

257+
# TODO: consider deprecating this method
230258
# EPSG:4326 bbox list
231259
bbox_list_in = [
232260
bbox['west'],

testing/test_5_gssha.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ def test_precipitation_input(test_dataset) -> None:
4343
out_path = xda.DataConversionFunctions.make_gssha_precipitation_input(
4444
test_dataset,
4545
precipitation_variable='2m_temperature',
46+
output_epsg=26915,
4647
)
4748

4849
assert out_path.exists()

0 commit comments

Comments
 (0)