Coverage for wrainfo/geometry.py: 99%
74 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-10-18 12:40 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2022-10-18 12:40 +0000
1"""Geometry module."""
3# WRaINfo, Is a software to process FURUNO weather radar data.
4#
5# Copyright (c) 2022, FernLab (GFZ Potsdam, fernlab@gfz-potsdam.de)
6#
7# This software was developed within the context of the RaINfo ("Potential use of
8# high resolution weather data in agriculture") project of FernLab funded by
9# the Impulse and Networking Fund of the Helmholtz Association.
10#
11# Licensed under the Apache License, Version 2.0 (the "License");
12# you may not use this file except in compliance with the License.
13#
14# You may obtain a copy of the License at
15# http://www.apache.org/licenses/LICENSE-2.0
16#
17# Unless required by applicable law or agreed to in writing, software
18# distributed under the License is distributed on an "AS IS" BASIS,
19# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20# See the License for the specific language governing permissions and
21# limitations under the License.
23# imports
24# -------
26import wradlib as wrl
27import numpy as np
28import os
29import xarray as xr
30from wrainfo.reader import read_config_file
33# functions to georeferencing and gridding the Furuno data
34# --------------------------------------------------------
36def get_target_grid(ds, nb_pixels):
37 """Get target grid."""
38 xgrid = np.linspace(ds.x.min(), ds.x.max(), nb_pixels, dtype=np.float32)
39 ygrid = np.linspace(ds.y.min(), ds.y.max(), nb_pixels, dtype=np.float32)
40 grid_xy_raw = np.meshgrid(xgrid, ygrid)
41 grid_xy_grid = np.dstack((grid_xy_raw[0], grid_xy_raw[1]))
42 return xgrid, ygrid, grid_xy_grid
45def get_target_coordinates(grid):
46 """Get target coordinates."""
47 grid_xy = np.stack((grid[..., 0].ravel(), grid[..., 1].ravel()), axis=-1)
48 return grid_xy
51def get_source_coordinates(ds):
52 """Get source coordinates."""
53 xy = np.stack((ds.x.values.ravel(), ds.y.values.ravel()), axis=-1)
54 return xy
57def create_dataarray(ds, data, moment):
58 """Create data array."""
59 # get moment attrributes
60 mom_attrs = ds[moment].attrs
61 # remove _Undetect since it's not in the original data
62 mom_attrs.pop("_Undetect", None)
63 # create moment DataArray
64 mom = xr.DataArray(data.astype(np.float32),
65 dims=["y", "x"],
66 attrs=mom_attrs
67 )
68 # fix encoding
69 mom.encoding = dict(zlib=True, _FillValue=0., least_significant_digit=2)
70 return mom
73def create_dataset(ds, moments, xgrid, ygrid):
74 """Create xarray dataset."""
75 # create x,y DataArrays
76 x = xr.DataArray(xgrid, dims=["x"], attrs=dict(standard_name="projection_x_coordinate",
77 long_name="x coordinate of projection",
78 units="m"))
79 y = xr.DataArray(ygrid, dims=["y"], attrs=dict(standard_name="projection_y_coordinate",
80 long_name="y coordinate of projection",
81 units="m"))
82 data_vars = dict()
83 data_vars.update(moments)
84 data_vars.update({"time": ds.reset_coords().time})
86 # create Dataset
87 ds_out = xr.Dataset(
88 data_vars=data_vars,
89 coords={"x": x, "y": y},
90 )
91 # apply CF Conventions
92 ds_out.attrs['Conventions'] = 'CF-1.5'
93 return ds_out
96# georeferencing and gridding of Furuno data
97# ------------------------------------------
99def furuno_georeferencing(ds, path):
100 """Georeference FURUNO Dataset, Grid/Project moments to raster dataset with EPSG Code.
102 Parameters
103 ----------
104 ds : xarray.Dataset
105 moments : list
106 list of selected data variables from the dataset
107 path : str
108 path to configuration file
110 Returns
111 -------
112 : xarray.Dataset
113 Georeferenced xarray.Dataset.
114 """
115 # read configuration file for epsg_code and nb_pixels
116 epsg_code = read_config_file(path=path, selection="epsg_code")
117 nb_pixels = read_config_file(path=path, selection="nb_pixels")
118 moments = read_config_file(path=path, selection="moments_in_processed_files")
120 # create projection
121 proj = wrl.georef.epsg_to_osr(epsg_code)
123 # georeference single sweep
124 ds = ds.pipe(wrl.georef.georeference_dataset, proj=proj)
126 # get source coordinates
127 src = get_source_coordinates(ds)
129 # create target grid
130 xgrid, ygrid, trg_grid = get_target_grid(ds, nb_pixels)
132 # get target coordinates
133 trg = get_target_coordinates(trg_grid)
135 # setup interpolator
136 ip_near = wrl.ipol.Nearest(src, trg)
138 # calculate moments, create DataArrays
139 moment_dict = dict()
140 for mom in moments:
141 res = ip_near(ds[mom].values.ravel()).reshape((len(ygrid), len(xgrid)))
142 moment_dict[mom] = create_dataarray(ds, res, mom)
144 # create output dataset
145 ds_out = create_dataset(ds, moment_dict, xgrid, ygrid)
147 # write crs (projection) to netcdf_file
148 ds_out = ds_out.rio.write_crs(epsg_code, grid_mapping_name="transverse_mercator")
149 ds_out.attrs["elevation_angle"] = ds.elevation.values[0]
151 # add time dimension
152 time1 = [1]
153 ds_out = ds_out.expand_dims(time=time1)
155 return ds_out
158# output as netcdf
159# ----------------
161def furuno_sweep_to_netcdf(ds,
162 path,
163 data_type=None):
164 """Create and save georeferenced NetCDF files.
166 Parameter
167 ---------
168 ds : xarray.Dataset
169 path: str
170 path to configuration file
171 data_type : str
172 level of processing (Level1, Level2, or Level3)
174 Return
175 ------
176 : NetCDF files
177 NetCDF files in output directory.
178 """
179 # read configuration file
180 radar_location_identifier = read_config_file(path=path, selection="radar_location_identifier")
181 outdir = read_config_file(path=path, selection="output_path_processed_files")
182 moments = read_config_file(path=path, selection="moments_in_processed_files")
184 # choose moments, which will save in the netcdf
185 ds = ds[moments]
186 el = ds.elevation_angle
188 # use time, elevation, and epsg code to create filename
189 t0 = ds.time.values.astype("M8[s]").astype("O")[0]
191 if data_type is not None:
192 outfilename = f"{radar_location_identifier}_{t0:%Y%m%d}_{t0:%H%M%S}UTC_elev_{el}_{data_type}.nc"
193 else:
194 outfilename = f"{radar_location_identifier}_{t0:%Y%m%d}_{t0:%H%M%S}UTC_elev_{el}.nc"
196 # extract year, month, day from filename
197 year = outfilename[10:14]
198 month = outfilename[14:16]
199 day = outfilename[16:18]
200 el = outfilename[29:37]
202 # create output directory
203 path1 = outdir + "/" + year + "/" + month + "/" + day + "/" + el + "/"
205 # absolute output path
206 outfilename = os.path.join(path1, outfilename)
208 # output path will create if not exists
209 if not os.path.exists(path1):
210 os.makedirs(path1, exist_ok=True)
212 # check whether output file exists
213 if not os.path.isfile(outfilename):
214 # output file
215 print(f"-- output to {outfilename}")
216 ds.to_netcdf(outfilename)
218 return True