grdwindinversion 0.3.3__py3-none-any.whl → 0.3.6__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.
- grdwindinversion/__init__.py +1 -1
- grdwindinversion/gradientFeatures.py +204 -138
- grdwindinversion/inversion.py +692 -427
- grdwindinversion/load_config.py +10 -7
- grdwindinversion/main.py +71 -28
- grdwindinversion/utils.py +47 -16
- grdwindinversion/utils_memory.py +20 -22
- {grdwindinversion-0.3.3.dist-info → grdwindinversion-0.3.6.dist-info}/METADATA +3 -3
- grdwindinversion-0.3.6.dist-info/RECORD +22 -0
- {grdwindinversion-0.3.3.dist-info → grdwindinversion-0.3.6.dist-info}/WHEEL +1 -1
- grdwindinversion-0.3.3.dist-info/RECORD +0 -22
- {grdwindinversion-0.3.3.dist-info → grdwindinversion-0.3.6.dist-info}/AUTHORS.rst +0 -0
- {grdwindinversion-0.3.3.dist-info → grdwindinversion-0.3.6.dist-info}/LICENSE +0 -0
- {grdwindinversion-0.3.3.dist-info → grdwindinversion-0.3.6.dist-info}/entry_points.txt +0 -0
- {grdwindinversion-0.3.3.dist-info → grdwindinversion-0.3.6.dist-info}/top_level.txt +0 -0
grdwindinversion/inversion.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
# To place here in the code to not have errors with cv2.
|
|
2
|
+
# if placed in main => error ..
|
|
1
3
|
import tempfile
|
|
2
4
|
import traceback
|
|
3
|
-
|
|
4
5
|
import xsar
|
|
5
6
|
import xsarsea
|
|
6
7
|
from xsarsea import windspeed
|
|
@@ -9,17 +10,28 @@ import xarray as xr
|
|
|
9
10
|
import numpy as np
|
|
10
11
|
import sys
|
|
11
12
|
import datetime
|
|
12
|
-
import os
|
|
13
13
|
import yaml
|
|
14
14
|
from scipy.ndimage import binary_dilation
|
|
15
|
-
|
|
16
15
|
import re
|
|
17
16
|
import string
|
|
18
|
-
import
|
|
19
|
-
from grdwindinversion.utils import check_incidence_range, get_pol_ratio_name, timing
|
|
17
|
+
from grdwindinversion.utils import check_incidence_range, get_pol_ratio_name, timing, convert_polarization_name
|
|
20
18
|
from grdwindinversion.load_config import getConf
|
|
21
|
-
# optional debug messages
|
|
22
19
|
import logging
|
|
20
|
+
import os
|
|
21
|
+
|
|
22
|
+
os.environ["OMP_NUM_THREADS"] = "1"
|
|
23
|
+
os.environ["OPENBLAS_NUM_THREADS"] = "1"
|
|
24
|
+
os.environ["MKL_NUM_THREADS"] = "1"
|
|
25
|
+
os.environ["VECLIB_MAXIMUM_THREADS"] = "1"
|
|
26
|
+
os.environ["NUMEXPR_NUM_THREADS"] = "1"
|
|
27
|
+
try:
|
|
28
|
+
import cv2
|
|
29
|
+
except:
|
|
30
|
+
import cv2
|
|
31
|
+
cv2.setNumThreads(1)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# optional debug messages
|
|
23
35
|
logger = logging.getLogger('grdwindinversion.inversion')
|
|
24
36
|
logger.addHandler(logging.NullHandler())
|
|
25
37
|
|
|
@@ -38,25 +50,28 @@ def getSensorMetaDataset(filename):
|
|
|
38
50
|
tuple
|
|
39
51
|
sensor name, sensor long name, meta function, dataset function
|
|
40
52
|
"""
|
|
41
|
-
if
|
|
53
|
+
if "S1A" in filename:
|
|
42
54
|
return "S1A", "SENTINEL-1 A", xsar.Sentinel1Meta, xsar.Sentinel1Dataset
|
|
43
|
-
elif
|
|
55
|
+
elif "S1B" in filename:
|
|
44
56
|
return "S1B", "SENTINEL-1 B", xsar.Sentinel1Meta, xsar.Sentinel1Dataset
|
|
45
|
-
elif
|
|
57
|
+
elif "RS2" in filename:
|
|
46
58
|
return "RS2", "RADARSAT-2", xsar.RadarSat2Meta, xsar.RadarSat2Dataset
|
|
47
|
-
elif
|
|
59
|
+
elif "RCM1" in filename:
|
|
48
60
|
return "RCM", "RADARSAT Constellation 1", xsar.RcmMeta, xsar.RcmDataset
|
|
49
|
-
elif
|
|
61
|
+
elif "RCM2" in filename:
|
|
50
62
|
return "RCM", "RADARSAT Constellation 2", xsar.RcmMeta, xsar.RcmDataset
|
|
51
|
-
elif
|
|
63
|
+
elif "RCM3" in filename:
|
|
52
64
|
return "RCM", "RADARSAT Constellation 3", xsar.RcmMeta, xsar.RcmDataset
|
|
53
65
|
|
|
54
66
|
else:
|
|
55
67
|
raise ValueError(
|
|
56
|
-
"must be S1A|S1B|RS2|RCM1|RCM2|RCM3, got filename %s" % filename
|
|
68
|
+
"must be S1A|S1B|RS2|RCM1|RCM2|RCM3, got filename %s" % filename
|
|
69
|
+
)
|
|
57
70
|
|
|
58
71
|
|
|
59
|
-
def getOutputName(
|
|
72
|
+
def getOutputName(
|
|
73
|
+
input_file, outdir, sensor, meta_start_date, meta_stop_date, subdir=True
|
|
74
|
+
):
|
|
60
75
|
"""
|
|
61
76
|
Create output filename for L2-GRD product
|
|
62
77
|
|
|
@@ -81,41 +96,71 @@ def getOutputName(input_file, outdir, sensor, meta_start_date, meta_stop_date, s
|
|
|
81
96
|
basename = os.path.basename(input_file)
|
|
82
97
|
basename_match = basename
|
|
83
98
|
|
|
84
|
-
if sensor ==
|
|
99
|
+
if sensor == "S1A" or sensor == "S1B":
|
|
85
100
|
regex = re.compile(
|
|
86
|
-
"(...)_(..)_(...)(.)_(.)(.)(..)_(........T......)_(........T......)_(......)_(......)_(....).SAFE"
|
|
101
|
+
"(...)_(..)_(...)(.)_(.)(.)(..)_(........T......)_(........T......)_(......)_(......)_(....).SAFE"
|
|
102
|
+
)
|
|
87
103
|
template = string.Template(
|
|
88
|
-
"${MISSIONID}_${
|
|
104
|
+
"${MISSIONID}_${SWATH}_${PRODUCT}${RESOLUTION}_${LEVEL}${CLASS}${POLARIZATION}_${STARTDATE}_${STOPDATE}_${ORBIT}_${TAKEID}_${PRODID}.SAFE"
|
|
105
|
+
)
|
|
106
|
+
# S1A_IW_GRDH_1SDV_20210909T130650_20210909T130715_039605_04AE83_C34F
|
|
89
107
|
match = regex.match(basename_match)
|
|
90
108
|
if not match:
|
|
91
109
|
raise AttributeError(
|
|
92
|
-
f"S1 file {basename_match} does not match the expected pattern"
|
|
110
|
+
f"S1 file {basename_match} does not match the expected pattern"
|
|
111
|
+
)
|
|
93
112
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
113
|
+
(
|
|
114
|
+
MISSIONID,
|
|
115
|
+
SWATH,
|
|
116
|
+
PRODUCT,
|
|
117
|
+
RESOLUTION,
|
|
118
|
+
LEVEL,
|
|
119
|
+
CLASS,
|
|
120
|
+
POLARIZATION,
|
|
121
|
+
STARTDATE,
|
|
122
|
+
STOPDATE,
|
|
123
|
+
ORBIT,
|
|
124
|
+
TAKEID,
|
|
125
|
+
PRODID,
|
|
126
|
+
) = match.groups()
|
|
127
|
+
# last two terms of polarization are removed
|
|
128
|
+
new_format = f"{MISSIONID.lower()}-{SWATH.lower()}-owi-{POLARIZATION.lower()}-{STARTDATE.lower()}-{STOPDATE.lower()}-{ORBIT}-{TAKEID}.nc"
|
|
129
|
+
elif sensor == "RS2":
|
|
97
130
|
regex = re.compile(
|
|
98
|
-
"(RS2)_OK([0-9]+)_PK([0-9]+)_DK([0-9]+)_(....)_(........)_(......)_(.._?.?.?)_(S.F)"
|
|
131
|
+
"(RS2)_OK([0-9]+)_PK([0-9]+)_DK([0-9]+)_(....)_(........)_(......)_(.._?.?.?)_(S.F)"
|
|
132
|
+
)
|
|
133
|
+
# RS2_OK141302_PK1242223_DK1208537_SCWA_20220904_093402_VV_VH_SGF
|
|
99
134
|
template = string.Template(
|
|
100
|
-
"${MISSIONID}_OK${DATA1}_PK${DATA2}_DK${DATA3}_${
|
|
135
|
+
"${MISSIONID}_OK${DATA1}_PK${DATA2}_DK${DATA3}_${SWATH}_${DATE}_${TIME}_${POLARIZATION}_${LAST}"
|
|
136
|
+
)
|
|
101
137
|
match = regex.match(basename_match)
|
|
102
138
|
if not match:
|
|
103
139
|
raise AttributeError(
|
|
104
|
-
f"RC2 file {basename_match} does not match the expected pattern"
|
|
140
|
+
f"RC2 file {basename_match} does not match the expected pattern"
|
|
141
|
+
)
|
|
105
142
|
|
|
106
|
-
MISSIONID, DATA1, DATA2, DATA3,
|
|
107
|
-
|
|
108
|
-
|
|
143
|
+
MISSIONID, DATA1, DATA2, DATA3, SWATH, DATE, TIME, POLARIZATION, LAST = (
|
|
144
|
+
match.groups()
|
|
145
|
+
)
|
|
146
|
+
new_format = f"{MISSIONID.lower()}-{SWATH.lower()}-owi-{convert_polarization_name(POLARIZATION)}-{meta_start_date.lower()}-{meta_stop_date.lower()}-_____-_____.nc"
|
|
147
|
+
elif sensor == "RCM":
|
|
109
148
|
|
|
110
149
|
regex = re.compile(
|
|
111
|
-
r"(RCM[0-9])_OK([0-9]+)_PK([0-9]+)_([0-9]+)_([A-Z0-9]+)_(\d{8})_(\d{6})_([A-Z]{2}(?:_[A-Z]{2})?)_([A-Z]+)$"
|
|
150
|
+
r"(RCM[0-9])_OK([0-9]+)_PK([0-9]+)_([0-9]+)_([A-Z0-9]+)_(\d{8})_(\d{6})_([A-Z]{2}(?:_[A-Z]{2})?)_([A-Z]+)$"
|
|
151
|
+
)
|
|
152
|
+
# RCM1_OK2767220_PK2769320_1_SCLND_20230930_214014_VV_VH_GRD
|
|
153
|
+
|
|
112
154
|
match = regex.match(basename_match)
|
|
113
155
|
if not match:
|
|
114
156
|
raise AttributeError(
|
|
115
|
-
f"RCM file {basename_match} does not match the expected pattern"
|
|
157
|
+
f"RCM file {basename_match} does not match the expected pattern"
|
|
158
|
+
)
|
|
116
159
|
|
|
117
|
-
MISSIONID, DATA1, DATA2, DATA3,
|
|
118
|
-
|
|
160
|
+
MISSIONID, DATA1, DATA2, DATA3, SWATH, DATE, TIME, POLARIZATION, PRODUCT = (
|
|
161
|
+
match.groups()
|
|
162
|
+
)
|
|
163
|
+
new_format = f"{MISSIONID.lower()}-{SWATH.lower()}-owi-{convert_polarization_name(POLARIZATION)}-{meta_start_date.lower()}-{meta_stop_date.lower()}-_____-_____.nc"
|
|
119
164
|
|
|
120
165
|
else:
|
|
121
166
|
raise ValueError(
|
|
@@ -128,7 +173,7 @@ def getOutputName(input_file, outdir, sensor, meta_start_date, meta_stop_date, s
|
|
|
128
173
|
return out_file
|
|
129
174
|
|
|
130
175
|
|
|
131
|
-
def getAncillary(meta, ancillary_name=
|
|
176
|
+
def getAncillary(meta, ancillary_name="ecmwf"):
|
|
132
177
|
"""
|
|
133
178
|
Map ancillary wind from ECMWF or ERA5.
|
|
134
179
|
This function is used to check if the model files are available and to map the model to the SAR data.
|
|
@@ -143,28 +188,34 @@ def getAncillary(meta, ancillary_name='ecmwf'):
|
|
|
143
188
|
map model to SAR data
|
|
144
189
|
"""
|
|
145
190
|
|
|
146
|
-
if ancillary_name ==
|
|
191
|
+
if ancillary_name == "ecmwf":
|
|
147
192
|
|
|
148
|
-
logging.debug(
|
|
149
|
-
ec01 = getConf()[
|
|
150
|
-
ec0125 = getConf()[
|
|
151
|
-
logging.debug(
|
|
152
|
-
meta.set_raster(
|
|
153
|
-
meta.set_raster(
|
|
193
|
+
logging.debug("conf: %s", getConf())
|
|
194
|
+
ec01 = getConf()["ecmwf_0100_1h"]
|
|
195
|
+
ec0125 = getConf()["ecmwf_0125_1h"]
|
|
196
|
+
logging.debug("ec01 : %s", ec01)
|
|
197
|
+
meta.set_raster("ecmwf_0100_1h", ec01)
|
|
198
|
+
meta.set_raster("ecmwf_0125_1h", ec0125)
|
|
154
199
|
|
|
155
200
|
map_model = None
|
|
156
201
|
# only keep best ecmwf (FIXME: it's hacky, and xsar should provide a better method to handle this)
|
|
157
|
-
for ecmwf_name in [
|
|
202
|
+
for ecmwf_name in ["ecmwf_0125_1h", "ecmwf_0100_1h"]:
|
|
158
203
|
ecmwf_infos = meta.rasters.loc[ecmwf_name]
|
|
159
204
|
try:
|
|
160
|
-
ecmwf_file = ecmwf_infos[
|
|
161
|
-
|
|
162
|
-
|
|
205
|
+
ecmwf_file = ecmwf_infos["get_function"](
|
|
206
|
+
ecmwf_infos["resource"],
|
|
207
|
+
date=datetime.datetime.strptime(
|
|
208
|
+
meta.start_date, "%Y-%m-%d %H:%M:%S.%f"
|
|
209
|
+
),
|
|
210
|
+
)[1]
|
|
163
211
|
# temporary for RCM issue https://github.com/umr-lops/xarray-safe-rcm/issues/34
|
|
164
212
|
except Exception as e:
|
|
165
|
-
ecmwf_file = ecmwf_infos[
|
|
166
|
-
|
|
167
|
-
|
|
213
|
+
ecmwf_file = ecmwf_infos["get_function"](
|
|
214
|
+
ecmwf_infos["resource"],
|
|
215
|
+
date=datetime.datetime.strptime(
|
|
216
|
+
meta.start_date, "%Y-%m-%d %H:%M:%S"
|
|
217
|
+
),
|
|
218
|
+
)[1]
|
|
168
219
|
if not os.path.isfile(ecmwf_file):
|
|
169
220
|
# temporary
|
|
170
221
|
# if repro does not exist we look at not repro folder (only one will exist after)
|
|
@@ -191,41 +242,58 @@ def getAncillary(meta, ancillary_name='ecmwf'):
|
|
|
191
242
|
"""
|
|
192
243
|
meta.rasters = meta.rasters.drop([ecmwf_name])
|
|
193
244
|
else:
|
|
194
|
-
map_model = {
|
|
195
|
-
|
|
245
|
+
map_model = {
|
|
246
|
+
"%s_%s" % (ecmwf_name, uv): "model_%s" % uv for uv in ["U10", "V10"]
|
|
247
|
+
}
|
|
196
248
|
|
|
197
249
|
return map_model
|
|
198
250
|
|
|
199
|
-
elif ancillary_name ==
|
|
251
|
+
elif ancillary_name == "era5":
|
|
200
252
|
era5_name = "era5_0250_1h"
|
|
201
|
-
logging.debug(
|
|
253
|
+
logging.debug("conf: %s", getConf())
|
|
202
254
|
era0250 = getConf()[era5_name]
|
|
203
|
-
logging.debug(
|
|
255
|
+
logging.debug("%s : %s", (era5_name, era0250))
|
|
204
256
|
meta.set_raster(era5_name, era0250)
|
|
205
257
|
|
|
206
258
|
era5_infos = meta.rasters.loc[era5_name]
|
|
207
259
|
try:
|
|
208
|
-
era5_file = era5_infos[
|
|
209
|
-
|
|
210
|
-
|
|
260
|
+
era5_file = era5_infos["get_function"](
|
|
261
|
+
era5_infos["resource"],
|
|
262
|
+
date=datetime.datetime.strptime(
|
|
263
|
+
meta.start_date, "%Y-%m-%d %H:%M:%S.%f"
|
|
264
|
+
),
|
|
265
|
+
)[1]
|
|
211
266
|
except Exception as e:
|
|
212
|
-
era5_file = era5_infos[
|
|
213
|
-
|
|
214
|
-
|
|
267
|
+
era5_file = era5_infos["get_function"](
|
|
268
|
+
era5_infos["resource"],
|
|
269
|
+
date=datetime.datetime.strptime(
|
|
270
|
+
meta.start_date, "%Y-%m-%d %H:%M:%S"),
|
|
271
|
+
)[1]
|
|
215
272
|
if not os.path.isfile(era5_file):
|
|
216
273
|
raise ValueError(f"era5 file {era5_file} not found")
|
|
217
274
|
|
|
218
|
-
map_model = {
|
|
219
|
-
|
|
275
|
+
map_model = {
|
|
276
|
+
"%s_%s" % (era5_name, uv): "model_%s" % uv for uv in ["U10", "V10"]
|
|
277
|
+
}
|
|
220
278
|
return map_model
|
|
221
279
|
|
|
222
280
|
else:
|
|
223
|
-
raise ValueError(
|
|
224
|
-
|
|
281
|
+
raise ValueError(
|
|
282
|
+
"ancillary_name must be ecmwf/era5, got %s" % ancillary_name)
|
|
225
283
|
|
|
226
284
|
|
|
227
285
|
@timing(logger=logger.debug)
|
|
228
|
-
def inverse(
|
|
286
|
+
def inverse(
|
|
287
|
+
dual_pol,
|
|
288
|
+
inc,
|
|
289
|
+
sigma0,
|
|
290
|
+
sigma0_dual,
|
|
291
|
+
ancillary_wind,
|
|
292
|
+
dsig_cr,
|
|
293
|
+
model_co,
|
|
294
|
+
model_cross,
|
|
295
|
+
**kwargs,
|
|
296
|
+
):
|
|
229
297
|
"""
|
|
230
298
|
Invert sigma0 to retrieve wind using model (lut or gmf).
|
|
231
299
|
|
|
@@ -263,14 +331,19 @@ def inverse(dual_pol, inc, sigma0, sigma0_dual, ancillary_wind, dsig_cr, model_c
|
|
|
263
331
|
"""
|
|
264
332
|
logging.debug("inversion")
|
|
265
333
|
|
|
266
|
-
list_mods =
|
|
267
|
-
|
|
334
|
+
list_mods = (
|
|
335
|
+
windspeed.available_models().index.tolist()
|
|
336
|
+
+ windspeed.available_models().alias.tolist()
|
|
337
|
+
+ [None]
|
|
338
|
+
)
|
|
268
339
|
if model_co not in list_mods:
|
|
269
340
|
raise ValueError(
|
|
270
|
-
f"model_co {model_co} not in windspeed.available_models() : not going further"
|
|
341
|
+
f"model_co {model_co} not in windspeed.available_models() : not going further"
|
|
342
|
+
)
|
|
271
343
|
if model_cross not in list_mods:
|
|
272
344
|
raise ValueError(
|
|
273
|
-
f"model_cross {model_cross} not in windspeed.available_models() : not going further"
|
|
345
|
+
f"model_cross {model_cross} not in windspeed.available_models() : not going further"
|
|
346
|
+
)
|
|
274
347
|
|
|
275
348
|
winds = windspeed.invert_from_model(
|
|
276
349
|
inc,
|
|
@@ -279,7 +352,8 @@ def inverse(dual_pol, inc, sigma0, sigma0_dual, ancillary_wind, dsig_cr, model_c
|
|
|
279
352
|
ancillary_wind=ancillary_wind,
|
|
280
353
|
dsig_cr=dsig_cr,
|
|
281
354
|
model=(model_co, model_cross),
|
|
282
|
-
**kwargs
|
|
355
|
+
**kwargs,
|
|
356
|
+
)
|
|
283
357
|
|
|
284
358
|
if dual_pol:
|
|
285
359
|
wind_co, wind_dual = winds
|
|
@@ -289,7 +363,8 @@ def inverse(dual_pol, inc, sigma0, sigma0_dual, ancillary_wind, dsig_cr, model_c
|
|
|
289
363
|
sigma0_dual.values,
|
|
290
364
|
dsig_cr=dsig_cr.values,
|
|
291
365
|
model=model_cross,
|
|
292
|
-
**kwargs
|
|
366
|
+
**kwargs,
|
|
367
|
+
)
|
|
293
368
|
|
|
294
369
|
return wind_co, wind_dual, wind_cross
|
|
295
370
|
else:
|
|
@@ -321,38 +396,42 @@ def makeL2asOwi(xr_dataset, config):
|
|
|
321
396
|
--------
|
|
322
397
|
"""
|
|
323
398
|
|
|
324
|
-
xr_dataset = xr_dataset.rename(
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
399
|
+
xr_dataset = xr_dataset.rename(
|
|
400
|
+
{
|
|
401
|
+
"longitude": "owiLon",
|
|
402
|
+
"latitude": "owiLat",
|
|
403
|
+
"incidence": "owiIncidenceAngle",
|
|
404
|
+
"elevation": "owiElevationAngle",
|
|
405
|
+
"ground_heading": "owiHeading",
|
|
406
|
+
"land_mask": "owiLandFlag",
|
|
407
|
+
"mask": "owiMask",
|
|
408
|
+
"windspeed_co": "owiWindSpeed_co",
|
|
409
|
+
"winddir_co": "owiWindDirection_co",
|
|
410
|
+
"ancillary_wind_speed": "owiAncillaryWindSpeed",
|
|
411
|
+
"ancillary_wind_direction": "owiAncillaryWindDirection",
|
|
412
|
+
"sigma0_detrend": "owiNrcs_detrend",
|
|
413
|
+
}
|
|
414
|
+
)
|
|
338
415
|
|
|
339
416
|
if "offboresight" in xr_dataset:
|
|
340
417
|
xr_dataset = xr_dataset.rename(
|
|
341
418
|
{"offboresight": "owiOffboresightAngle"})
|
|
342
419
|
|
|
343
420
|
if config["add_nrcs_model"]:
|
|
344
|
-
xr_dataset = xr_dataset.rename(
|
|
345
|
-
{"ancillary_nrcs": "owiAncillaryNrcs"})
|
|
421
|
+
xr_dataset = xr_dataset.rename({"ancillary_nrcs": "owiAncillaryNrcs"})
|
|
346
422
|
xr_dataset.owiAncillaryNrcs.attrs["units"] = "m^2 / m^2"
|
|
347
|
-
xr_dataset.owiAncillaryNrcs.attrs[
|
|
348
|
-
|
|
423
|
+
xr_dataset.owiAncillaryNrcs.attrs["long_name"] = (
|
|
424
|
+
f"Ancillary Normalized Radar Cross Section - simulated from {config['l2_params']['copol_gmf']} & ancillary wind"
|
|
425
|
+
)
|
|
349
426
|
|
|
350
427
|
if config["l2_params"]["dual_pol"]:
|
|
351
428
|
xr_dataset = xr_dataset.rename(
|
|
352
|
-
{"ancillary_nrcs_cross": "owiAncillaryNrcs_cross"}
|
|
429
|
+
{"ancillary_nrcs_cross": "owiAncillaryNrcs_cross"}
|
|
430
|
+
)
|
|
353
431
|
xr_dataset.owiAncillaryNrcs_cross.attrs["units"] = "m^2 / m^2"
|
|
354
|
-
xr_dataset.owiAncillaryNrcs_cross.attrs[
|
|
355
|
-
|
|
432
|
+
xr_dataset.owiAncillaryNrcs_cross.attrs["long_name"] = (
|
|
433
|
+
f"Ancillary Normalized Radar Cross Section - simulated from {config['l2_params']['crosspol_gmf']} & ancillary wind"
|
|
434
|
+
)
|
|
356
435
|
|
|
357
436
|
xr_dataset.owiLon.attrs["units"] = "degrees_east"
|
|
358
437
|
xr_dataset.owiLon.attrs["long_name"] = "Longitude at wind cell center"
|
|
@@ -363,146 +442,196 @@ def makeL2asOwi(xr_dataset, config):
|
|
|
363
442
|
xr_dataset.owiLat.attrs["standard_name"] = "latitude"
|
|
364
443
|
|
|
365
444
|
xr_dataset.owiIncidenceAngle.attrs["units"] = "degrees"
|
|
366
|
-
xr_dataset.owiIncidenceAngle.attrs["long_name"] =
|
|
445
|
+
xr_dataset.owiIncidenceAngle.attrs["long_name"] = (
|
|
446
|
+
"Incidence angle at wind cell center"
|
|
447
|
+
)
|
|
367
448
|
xr_dataset.owiIncidenceAngle.attrs["standard_name"] = "incidence"
|
|
368
449
|
|
|
369
450
|
xr_dataset.owiElevationAngle.attrs["units"] = "degrees"
|
|
370
|
-
xr_dataset.owiElevationAngle.attrs["long_name"] =
|
|
451
|
+
xr_dataset.owiElevationAngle.attrs["long_name"] = (
|
|
452
|
+
"Elevation angle at wind cell center"
|
|
453
|
+
)
|
|
371
454
|
xr_dataset.owiElevationAngle.attrs["standard_name"] = "elevation"
|
|
372
455
|
|
|
373
|
-
xr_dataset[
|
|
374
|
-
pol=config["l2_params"]["copol"]
|
|
456
|
+
xr_dataset["owiNrcs"] = xr_dataset["sigma0_ocean"].sel(
|
|
457
|
+
pol=config["l2_params"]["copol"]
|
|
458
|
+
)
|
|
375
459
|
xr_dataset.owiNrcs.attrs = xr_dataset.sigma0_ocean.attrs
|
|
376
|
-
xr_dataset.owiNrcs.attrs[
|
|
377
|
-
xr_dataset.owiNrcs.attrs[
|
|
378
|
-
xr_dataset.owiNrcs.attrs[
|
|
460
|
+
xr_dataset.owiNrcs.attrs["units"] = "m^2 / m^2"
|
|
461
|
+
xr_dataset.owiNrcs.attrs["long_name"] = "Normalized Radar Cross Section"
|
|
462
|
+
xr_dataset.owiNrcs.attrs["definition"] = "owiNrcs_no_noise_correction - owiNesz"
|
|
379
463
|
|
|
380
|
-
xr_dataset[
|
|
381
|
-
pol=config["l2_params"]["copol"]
|
|
464
|
+
xr_dataset["owiMask_Nrcs"] = xr_dataset["sigma0_mask"].sel(
|
|
465
|
+
pol=config["l2_params"]["copol"]
|
|
466
|
+
)
|
|
382
467
|
xr_dataset.owiMask_Nrcs.attrs = xr_dataset.sigma0_mask.attrs
|
|
383
468
|
|
|
384
469
|
# NESZ & DSIG
|
|
385
470
|
xr_dataset = xr_dataset.assign(
|
|
386
|
-
owiNesz=(
|
|
387
|
-
|
|
388
|
-
|
|
471
|
+
owiNesz=(
|
|
472
|
+
["line", "sample"],
|
|
473
|
+
xr_dataset.nesz.sel(pol=config["l2_params"]["copol"]).values,
|
|
474
|
+
)
|
|
475
|
+
)
|
|
476
|
+
xr_dataset.owiNesz.attrs["units"] = "m^2 / m^2"
|
|
477
|
+
xr_dataset.owiNesz.attrs["long_name"] = "Noise Equivalent SigmaNaught"
|
|
389
478
|
|
|
390
|
-
xr_dataset[
|
|
391
|
-
pol=config["l2_params"]["copol"]
|
|
479
|
+
xr_dataset["owiNrcs_no_noise_correction"] = xr_dataset["sigma0_ocean_raw"].sel(
|
|
480
|
+
pol=config["l2_params"]["copol"]
|
|
481
|
+
)
|
|
392
482
|
xr_dataset.owiNrcs_no_noise_correction.attrs = xr_dataset.sigma0_ocean_raw.attrs
|
|
393
|
-
xr_dataset.owiNrcs_no_noise_correction.attrs[
|
|
394
|
-
xr_dataset.owiNrcs_no_noise_correction.attrs[
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
483
|
+
xr_dataset.owiNrcs_no_noise_correction.attrs["units"] = "m^2 / m^2"
|
|
484
|
+
xr_dataset.owiNrcs_no_noise_correction.attrs["long_name"] = (
|
|
485
|
+
"Normalized Radar Cross Section ; no noise correction applied"
|
|
486
|
+
)
|
|
487
|
+
xr_dataset.owiNrcs_no_noise_correction.attrs["comment"] = (
|
|
488
|
+
"owiNrcs_no_noise_correction ; no recalibration"
|
|
489
|
+
)
|
|
398
490
|
|
|
399
|
-
if
|
|
400
|
-
xr_dataset = xr_dataset.rename(
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
491
|
+
if "swath_number" in xr_dataset:
|
|
492
|
+
xr_dataset = xr_dataset.rename(
|
|
493
|
+
{
|
|
494
|
+
"swath_number": "owiSwathNumber",
|
|
495
|
+
"swath_number_flag": "owiSwathNumberFlag",
|
|
496
|
+
}
|
|
497
|
+
)
|
|
404
498
|
|
|
405
499
|
xr_dataset["owiSwathNumber"].attrs["standart_name"] = "swath number"
|
|
406
500
|
|
|
407
501
|
# sigma0_raw__corrected cross
|
|
408
502
|
if "sigma0_raw__corrected" in xr_dataset:
|
|
409
|
-
xr_dataset[
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
xr_dataset.owiNrcs_no_noise_correction_recalibrated.attrs
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
xr_dataset.owiNrcs_no_noise_correction_recalibrated.attrs[
|
|
416
|
-
|
|
503
|
+
xr_dataset["owiNrcs_no_noise_correction_recalibrated"] = xr_dataset[
|
|
504
|
+
"sigma0_raw__corrected"
|
|
505
|
+
].sel(pol=config["l2_params"]["copol"])
|
|
506
|
+
xr_dataset.owiNrcs_no_noise_correction_recalibrated.attrs = (
|
|
507
|
+
xr_dataset.sigma0_raw__corrected.attrs
|
|
508
|
+
)
|
|
509
|
+
xr_dataset.owiNrcs_no_noise_correction_recalibrated.attrs["units"] = "m^2 / m^2"
|
|
510
|
+
xr_dataset.owiNrcs_no_noise_correction_recalibrated.attrs["long_name"] = (
|
|
511
|
+
"Normalized Radar Cross Section, no noise correction applied"
|
|
512
|
+
)
|
|
513
|
+
xr_dataset.owiNrcs_no_noise_correction_recalibrated.attrs["comment"] = (
|
|
514
|
+
"owiNrcs_no_noise_correction ; recalibrated with kersten method"
|
|
515
|
+
)
|
|
417
516
|
|
|
418
|
-
xr_dataset.owiNrcs.attrs[
|
|
517
|
+
xr_dataset.owiNrcs.attrs["definition"] = (
|
|
518
|
+
"owiNrcs_no_noise_correction_recalibrated - owiNesz"
|
|
519
|
+
)
|
|
419
520
|
|
|
420
521
|
if config["l2_params"]["dual_pol"]:
|
|
421
|
-
|
|
422
522
|
xr_dataset = xr_dataset.rename({
|
|
423
523
|
'dsig_cross': 'owiDsig_cross',
|
|
424
524
|
'winddir_cross': 'owiWindDirection_cross',
|
|
425
525
|
'winddir_dual': 'owiWindDirection',
|
|
426
526
|
'windspeed_cross': 'owiWindSpeed_cross',
|
|
427
527
|
'windspeed_dual': 'owiWindSpeed',
|
|
428
|
-
'sigma0_detrend_cross': 'owiNrcs_detrend_cross'
|
|
528
|
+
'sigma0_detrend_cross': 'owiNrcs_detrend_cross',
|
|
529
|
+
'nesz_cross_flattened': 'owiNesz_cross_flattened'
|
|
429
530
|
})
|
|
430
531
|
|
|
431
|
-
if config["apply_flattening"]:
|
|
432
|
-
xr_dataset = xr_dataset.rename({
|
|
433
|
-
'nesz_cross_flattened': 'owiNesz_cross_flattened',
|
|
434
|
-
})
|
|
435
|
-
|
|
436
532
|
# nrcs cross
|
|
437
|
-
xr_dataset[
|
|
438
|
-
pol=config["l2_params"]["crosspol"]
|
|
533
|
+
xr_dataset["owiNrcs_cross"] = xr_dataset["sigma0_ocean"].sel(
|
|
534
|
+
pol=config["l2_params"]["crosspol"]
|
|
535
|
+
)
|
|
439
536
|
|
|
440
|
-
xr_dataset.owiNrcs_cross.attrs[
|
|
441
|
-
xr_dataset.owiNrcs_cross.attrs[
|
|
442
|
-
xr_dataset.owiNrcs_cross.attrs[
|
|
537
|
+
xr_dataset.owiNrcs_cross.attrs["units"] = "m^2 / m^2"
|
|
538
|
+
xr_dataset.owiNrcs_cross.attrs["long_name"] = "Normalized Radar Cross Section"
|
|
539
|
+
xr_dataset.owiNrcs_cross.attrs["definition"] = (
|
|
540
|
+
"owiNrcs_cross_no_noise_correction - owiNesz_cross"
|
|
541
|
+
)
|
|
443
542
|
|
|
444
|
-
xr_dataset[
|
|
445
|
-
pol=config["l2_params"]["crosspol"]
|
|
543
|
+
xr_dataset["owiMask_Nrcs_cross"] = xr_dataset["sigma0_mask"].sel(
|
|
544
|
+
pol=config["l2_params"]["crosspol"]
|
|
545
|
+
)
|
|
446
546
|
xr_dataset.owiMask_Nrcs_cross.attrs = xr_dataset.sigma0_mask.attrs
|
|
447
547
|
|
|
448
548
|
# nesz cross
|
|
449
|
-
xr_dataset = xr_dataset.assign(
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
549
|
+
xr_dataset = xr_dataset.assign(
|
|
550
|
+
owiNesz_cross=(
|
|
551
|
+
["line", "sample"],
|
|
552
|
+
xr_dataset.nesz.sel(
|
|
553
|
+
pol=config["l2_params"]["crosspol"]).values,
|
|
554
|
+
)
|
|
555
|
+
) # no flattening
|
|
556
|
+
xr_dataset.owiNesz_cross.attrs["units"] = "m^2 / m^2"
|
|
557
|
+
xr_dataset.owiNesz_cross.attrs["long_name"] = "Noise Equivalent SigmaNaught"
|
|
558
|
+
|
|
559
|
+
xr_dataset.owiNesz_cross_flattened.attrs["units"] = "m^2 / m^2"
|
|
560
|
+
xr_dataset.owiNesz_cross_flattened.attrs["long_name"] = "Noise Equivalent SigmaNaught"
|
|
453
561
|
|
|
454
|
-
xr_dataset[
|
|
455
|
-
|
|
562
|
+
xr_dataset["owiNrcs_cross_no_noise_correction"] = xr_dataset[
|
|
563
|
+
"sigma0_ocean_raw"
|
|
564
|
+
].sel(pol=config["l2_params"]["crosspol"])
|
|
456
565
|
|
|
457
|
-
xr_dataset.owiNrcs_cross_no_noise_correction.attrs[
|
|
458
|
-
xr_dataset.owiNrcs_cross_no_noise_correction.attrs[
|
|
459
|
-
|
|
566
|
+
xr_dataset.owiNrcs_cross_no_noise_correction.attrs["units"] = "m^2 / m^2"
|
|
567
|
+
xr_dataset.owiNrcs_cross_no_noise_correction.attrs["long_name"] = (
|
|
568
|
+
"Normalized Radar Cross Section, no noise correction applied"
|
|
569
|
+
)
|
|
460
570
|
|
|
461
571
|
# sigma0_raw__corrected cross
|
|
462
572
|
if "sigma0_raw__corrected" in xr_dataset:
|
|
463
|
-
xr_dataset[
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
xr_dataset.owiNrcs_cross_no_noise_correction_recalibrated.attrs
|
|
573
|
+
xr_dataset["owiNrcs_cross_no_noise_correction_recalibrated"] = xr_dataset[
|
|
574
|
+
"sigma0_raw__corrected"
|
|
575
|
+
].sel(pol=config["l2_params"]["crosspol"])
|
|
576
|
+
xr_dataset.owiNrcs_cross_no_noise_correction_recalibrated.attrs = (
|
|
577
|
+
xr_dataset.sigma0_raw__corrected.attrs
|
|
578
|
+
)
|
|
579
|
+
xr_dataset.owiNrcs_cross_no_noise_correction_recalibrated.attrs["units"] = (
|
|
580
|
+
"m^2 / m^2"
|
|
581
|
+
)
|
|
467
582
|
xr_dataset.owiNrcs_cross_no_noise_correction_recalibrated.attrs[
|
|
468
|
-
|
|
583
|
+
"long_name"
|
|
584
|
+
] = "Normalized Radar Cross Section ; no noise correction applied"
|
|
469
585
|
xr_dataset.owiNrcs_cross_no_noise_correction_recalibrated.attrs[
|
|
470
|
-
|
|
586
|
+
"comment"
|
|
587
|
+
] = "owiNrcs_cross_no_noise_correction ; recalibrated with kersten method"
|
|
471
588
|
|
|
472
|
-
xr_dataset.owiNrcs_cross.attrs[
|
|
589
|
+
xr_dataset.owiNrcs_cross.attrs["definition"] = (
|
|
590
|
+
"owiNrcs_cross_no_noise_correction_recalibrated - owiNesz_cross"
|
|
591
|
+
)
|
|
473
592
|
|
|
474
593
|
if config["add_gradientsfeatures"]:
|
|
475
|
-
xr_dataset = xr_dataset.rename({
|
|
476
|
-
'heterogeneity_mask': 'owiWindFilter'
|
|
477
|
-
})
|
|
594
|
+
xr_dataset = xr_dataset.rename({"heterogeneity_mask": "owiWindFilter"})
|
|
478
595
|
else:
|
|
479
|
-
xr_dataset[
|
|
480
|
-
xr_dataset[
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
xr_dataset[
|
|
485
|
-
|
|
596
|
+
xr_dataset["owiWindFilter"] = xr.full_like(xr_dataset.owiNrcs, 0)
|
|
597
|
+
xr_dataset["owiWindFilter"].attrs[
|
|
598
|
+
"long_name"
|
|
599
|
+
] = "Quality flag taking into account the local heterogeneity"
|
|
600
|
+
xr_dataset["owiWindFilter"].attrs["valid_range"] = np.array([0, 3])
|
|
601
|
+
xr_dataset["owiWindFilter"].attrs["flag_values"] = np.array([
|
|
602
|
+
0, 1, 2, 3])
|
|
603
|
+
xr_dataset["owiWindFilter"].attrs[
|
|
604
|
+
"flag_meanings"
|
|
605
|
+
] = "homogeneous_NRCS, heterogeneous_from_co-polarization_NRCS, heterogeneous_from_cross-polarization_NRCS, heterogeneous_from_dual-polarization_NRCS"
|
|
486
606
|
|
|
487
607
|
# other variables
|
|
488
608
|
|
|
489
|
-
xr_dataset[
|
|
490
|
-
xr_dataset[
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
xr_dataset[
|
|
494
|
-
|
|
495
|
-
xr_dataset[
|
|
496
|
-
xr_dataset[
|
|
609
|
+
xr_dataset["owiWindQuality"] = xr.full_like(xr_dataset.owiNrcs, 0)
|
|
610
|
+
xr_dataset["owiWindQuality"].attrs[
|
|
611
|
+
"long_name"
|
|
612
|
+
] = "Quality flag taking into account the consistency_between_wind_inverted_and_NRCS_and_Doppler_measured"
|
|
613
|
+
xr_dataset["owiWindQuality"].attrs["valid_range"] = np.array([0, 3])
|
|
614
|
+
xr_dataset["owiWindQuality"].attrs["flag_values"] = np.array([0, 1, 2, 3])
|
|
615
|
+
xr_dataset["owiWindQuality"].attrs["flag_meanings"] = "good medium low poor"
|
|
616
|
+
xr_dataset["owiWindQuality"].attrs["comment"] = "NOT COMPUTED YET"
|
|
497
617
|
|
|
498
618
|
xr_dataset = xr_dataset.rename(
|
|
499
619
|
{"line": "owiAzSize", "sample": "owiRaSize"})
|
|
500
620
|
|
|
501
621
|
xr_dataset = xr_dataset.drop_vars(
|
|
502
|
-
[
|
|
503
|
-
|
|
622
|
+
[
|
|
623
|
+
"sigma0_ocean",
|
|
624
|
+
"sigma0",
|
|
625
|
+
"sigma0_ocean_raw",
|
|
626
|
+
"sigma0_raw",
|
|
627
|
+
"ancillary_wind",
|
|
628
|
+
"nesz",
|
|
629
|
+
"spatial_ref",
|
|
630
|
+
]
|
|
631
|
+
)
|
|
632
|
+
if "sigma0_raw__corrected" in xr_dataset:
|
|
504
633
|
xr_dataset = xr_dataset.drop_vars(["sigma0_raw__corrected"])
|
|
505
|
-
xr_dataset = xr_dataset.drop_dims([
|
|
634
|
+
xr_dataset = xr_dataset.drop_dims(["pol"])
|
|
506
635
|
|
|
507
636
|
table_fillValue = {
|
|
508
637
|
"owiWindQuality": -1,
|
|
@@ -521,17 +650,24 @@ def makeL2asOwi(xr_dataset, config):
|
|
|
521
650
|
for var in list(set(xr_dataset.coords.keys()) | set(xr_dataset.keys())):
|
|
522
651
|
encoding[var] = {}
|
|
523
652
|
try:
|
|
524
|
-
encoding[var].update({
|
|
653
|
+
encoding[var].update({"_FillValue": table_fillValue[var]})
|
|
525
654
|
except:
|
|
526
|
-
if
|
|
527
|
-
encoding[var].update({
|
|
655
|
+
if var in ["owiWindSpeed_co", "owiWindSpeed_cross", "owiWindSpeed"]:
|
|
656
|
+
encoding[var].update({"_FillValue": -9999.0})
|
|
528
657
|
else:
|
|
529
|
-
encoding[var].update({
|
|
658
|
+
encoding[var].update({"_FillValue": None})
|
|
530
659
|
|
|
531
660
|
return xr_dataset, encoding
|
|
532
661
|
|
|
533
662
|
|
|
534
|
-
def preprocess(
|
|
663
|
+
def preprocess(
|
|
664
|
+
filename,
|
|
665
|
+
outdir,
|
|
666
|
+
config_path,
|
|
667
|
+
overwrite=False,
|
|
668
|
+
add_gradientsfeatures=False,
|
|
669
|
+
resolution="1000m",
|
|
670
|
+
):
|
|
535
671
|
"""
|
|
536
672
|
Main function to generate L2 product.
|
|
537
673
|
|
|
@@ -558,11 +694,8 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_gradientsfeat
|
|
|
558
694
|
filename)
|
|
559
695
|
|
|
560
696
|
if os.path.exists(config_path):
|
|
561
|
-
with open(config_path,
|
|
562
|
-
config_base = yaml.load(
|
|
563
|
-
file,
|
|
564
|
-
Loader=yaml.FullLoader
|
|
565
|
-
)
|
|
697
|
+
with open(config_path, "r") as file:
|
|
698
|
+
config_base = yaml.load(file, Loader=yaml.FullLoader)
|
|
566
699
|
try:
|
|
567
700
|
# check if sensor is in the config
|
|
568
701
|
config = config_base[sensor]
|
|
@@ -570,15 +703,20 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_gradientsfeat
|
|
|
570
703
|
raise KeyError("sensor %s not in this config" % sensor)
|
|
571
704
|
else:
|
|
572
705
|
raise FileNotFoundError(
|
|
573
|
-
|
|
706
|
+
"config_path do not exists, got %s " % config_path)
|
|
574
707
|
|
|
575
708
|
recalibration = config["recalibration"]
|
|
576
709
|
meta = fct_meta(filename)
|
|
577
710
|
|
|
578
711
|
# si une des deux n'est pas VV VH HH HV on ne fait rien
|
|
579
|
-
if not all([pol in ["VV", "VH", "HH", "HV"] for pol in meta.pols.split(
|
|
712
|
+
if not all([pol in ["VV", "VH", "HH", "HV"] for pol in meta.pols.split(" ")]):
|
|
580
713
|
raise ValueError(f"Polarisation non gérée : meta.pols = {meta.pols}")
|
|
581
714
|
|
|
715
|
+
overwrite_cfg = config_base.get("overwrite", None)
|
|
716
|
+
# If overwrite is specified in config, it will overwrite the value given as argument (which defaults to False)
|
|
717
|
+
if overwrite_cfg is not None:
|
|
718
|
+
overwrite = overwrite_cfg
|
|
719
|
+
|
|
582
720
|
no_subdir_cfg = config_base.get("no_subdir", False)
|
|
583
721
|
config["no_subdir"] = no_subdir_cfg
|
|
584
722
|
|
|
@@ -587,39 +725,53 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_gradientsfeat
|
|
|
587
725
|
else:
|
|
588
726
|
winddir_convention = "meteorological"
|
|
589
727
|
logging.warning(
|
|
590
|
-
f'Using meteorological convention because "winddir_convention" was not found in config.'
|
|
728
|
+
f'Using meteorological convention because "winddir_convention" was not found in config.'
|
|
729
|
+
)
|
|
591
730
|
config["winddir_convention"] = winddir_convention
|
|
592
731
|
|
|
593
732
|
if "add_gradientsfeatures" in config_base:
|
|
594
733
|
add_gradientsfeatures = config_base["add_gradientsfeatures"]
|
|
595
734
|
else:
|
|
596
735
|
add_gradientsfeatures = False
|
|
597
|
-
logging.warning(
|
|
598
|
-
f'Not computing gradients by default')
|
|
736
|
+
logging.warning(f"Not computing gradients by default")
|
|
599
737
|
config["add_gradientsfeatures"] = add_gradientsfeatures
|
|
600
738
|
|
|
601
739
|
if "add_nrcs_model" in config_base:
|
|
602
740
|
add_nrcs_model = config_base["add_nrcs_model"]
|
|
603
741
|
add_nrcs_model = False
|
|
604
742
|
logging.warning(
|
|
605
|
-
f
|
|
606
|
-
)
|
|
743
|
+
f"Force add_nrcs_model to be false, before fixing an issue")
|
|
607
744
|
else:
|
|
608
745
|
add_nrcs_model = False
|
|
609
|
-
logging.warning(
|
|
610
|
-
f'Not computing nrcs from model by default')
|
|
746
|
+
logging.warning(f"Not computing nrcs from model by default")
|
|
611
747
|
config["add_nrcs_model"] = add_nrcs_model
|
|
612
748
|
|
|
613
749
|
# creating a dictionnary of parameters
|
|
614
750
|
config["l2_params"] = {}
|
|
615
751
|
|
|
616
|
-
meta_start_date =
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
752
|
+
meta_start_date = (
|
|
753
|
+
meta.start_date.split(".")[0]
|
|
754
|
+
.replace("-", "")
|
|
755
|
+
.replace(":", "")
|
|
756
|
+
.replace(" ", "t")
|
|
757
|
+
.replace("Z", "")
|
|
758
|
+
)
|
|
759
|
+
meta_stop_date = (
|
|
760
|
+
meta.stop_date.split(".")[0]
|
|
761
|
+
.replace("-", "")
|
|
762
|
+
.replace(":", "")
|
|
763
|
+
.replace(" ", "t")
|
|
764
|
+
.replace("Z", "")
|
|
765
|
+
)
|
|
620
766
|
|
|
621
|
-
out_file = getOutputName(
|
|
622
|
-
|
|
767
|
+
out_file = getOutputName(
|
|
768
|
+
filename,
|
|
769
|
+
outdir,
|
|
770
|
+
sensor,
|
|
771
|
+
meta_start_date,
|
|
772
|
+
meta_stop_date,
|
|
773
|
+
subdir=not no_subdir_cfg,
|
|
774
|
+
)
|
|
623
775
|
|
|
624
776
|
if os.path.exists(out_file) and overwrite is False:
|
|
625
777
|
raise FileExistsError("outfile %s already exists" % out_file)
|
|
@@ -628,37 +780,48 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_gradientsfeat
|
|
|
628
780
|
map_model = getAncillary(meta, ancillary_name)
|
|
629
781
|
if map_model is None:
|
|
630
782
|
raise Exception(
|
|
631
|
-
f"the weather model is not set `map_model` is None -> you probably don't have access to {ancillary_name} archive"
|
|
783
|
+
f"the weather model is not set `map_model` is None -> you probably don't have access to {ancillary_name} archive"
|
|
784
|
+
)
|
|
632
785
|
|
|
633
786
|
try:
|
|
634
|
-
if (
|
|
787
|
+
if (recalibration) & ("SENTINEL" in sensor_longname):
|
|
635
788
|
logging.info(
|
|
636
|
-
f
|
|
789
|
+
f"recalibration is {recalibration} : Kersten formula is applied"
|
|
790
|
+
)
|
|
637
791
|
xsar_dataset = fct_dataset(
|
|
638
|
-
meta, resolution=resolution, recalibration=recalibration
|
|
639
|
-
|
|
640
|
-
xr_dataset =
|
|
641
|
-
|
|
792
|
+
meta, resolution=resolution, recalibration=recalibration
|
|
793
|
+
)
|
|
794
|
+
xr_dataset = xsar_dataset.datatree["measurement"].to_dataset()
|
|
795
|
+
xr_dataset = xr_dataset.merge(
|
|
796
|
+
xsar_dataset.datatree["recalibration"].to_dataset()[
|
|
797
|
+
["swath_number", "swath_number_flag", "sigma0_raw__corrected"]
|
|
798
|
+
]
|
|
799
|
+
)
|
|
642
800
|
|
|
643
801
|
else:
|
|
644
802
|
logging.info(
|
|
645
|
-
f
|
|
646
|
-
|
|
803
|
+
f"recalibration is {recalibration} : Kersten formula is not applied"
|
|
804
|
+
)
|
|
805
|
+
if "SENTINEL" in sensor_longname:
|
|
647
806
|
xsar_dataset = fct_dataset(
|
|
648
|
-
meta, resolution=resolution, recalibration=recalibration
|
|
649
|
-
|
|
650
|
-
xr_dataset =
|
|
651
|
-
|
|
807
|
+
meta, resolution=resolution, recalibration=recalibration
|
|
808
|
+
)
|
|
809
|
+
xr_dataset = xsar_dataset.datatree["measurement"].to_dataset()
|
|
810
|
+
xr_dataset = xr_dataset.merge(
|
|
811
|
+
xsar_dataset.datatree["recalibration"].to_dataset()[
|
|
812
|
+
["swath_number", "swath_number_flag"]
|
|
813
|
+
]
|
|
814
|
+
)
|
|
652
815
|
|
|
653
816
|
else:
|
|
654
817
|
xsar_dataset = fct_dataset(meta, resolution=resolution)
|
|
655
|
-
xr_dataset = xsar_dataset.datatree[
|
|
818
|
+
xr_dataset = xsar_dataset.datatree["measurement"].to_dataset()
|
|
656
819
|
|
|
657
820
|
xr_dataset = xr_dataset.rename(map_model)
|
|
658
821
|
xr_dataset.attrs = xsar_dataset.dataset.attrs
|
|
659
822
|
|
|
660
823
|
except Exception as e:
|
|
661
|
-
logging.info(
|
|
824
|
+
logging.info("%s", traceback.format_exc())
|
|
662
825
|
logging.error(e)
|
|
663
826
|
sys.exit(-1)
|
|
664
827
|
|
|
@@ -676,22 +839,24 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_gradientsfeat
|
|
|
676
839
|
else:
|
|
677
840
|
dual_pol = False
|
|
678
841
|
|
|
679
|
-
if
|
|
680
|
-
copol =
|
|
681
|
-
crosspol =
|
|
682
|
-
copol_gmf =
|
|
683
|
-
crosspol_gmf =
|
|
842
|
+
if "VV" in xr_dataset.pol.values:
|
|
843
|
+
copol = "VV"
|
|
844
|
+
crosspol = "VH"
|
|
845
|
+
copol_gmf = "VV"
|
|
846
|
+
crosspol_gmf = "VH"
|
|
684
847
|
else:
|
|
685
|
-
logging.warning(
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
848
|
+
logging.warning(
|
|
849
|
+
"for now this processor does not support entirely HH+HV acquisitions\n "
|
|
850
|
+
"it wont crash but it will use HH+VH GMF for wind inversion -> wrong hypothesis\n "
|
|
851
|
+
"!! dual WIND SPEED IS NOT USABLE !! But co WIND SPEED IS USABLE !!"
|
|
852
|
+
)
|
|
853
|
+
copol = "HH"
|
|
854
|
+
crosspol = "HV"
|
|
855
|
+
copol_gmf = "HH"
|
|
856
|
+
crosspol_gmf = "VH"
|
|
692
857
|
|
|
693
|
-
model_co = config["GMF_"+copol_gmf+"_NAME"]
|
|
694
|
-
model_cross = config["GMF_"+crosspol_gmf+"_NAME"]
|
|
858
|
+
model_co = config["GMF_" + copol_gmf + "_NAME"]
|
|
859
|
+
model_cross = config["GMF_" + crosspol_gmf + "_NAME"]
|
|
695
860
|
|
|
696
861
|
# register paramaters in config
|
|
697
862
|
config["l2_params"]["dual_pol"] = dual_pol
|
|
@@ -710,16 +875,37 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_gradientsfeat
|
|
|
710
875
|
if len(nc_luts) > 0:
|
|
711
876
|
windspeed.register_nc_luts(getConf()["nc_luts_path"])
|
|
712
877
|
|
|
713
|
-
if
|
|
878
|
+
if model_co == "gmf_cmod7":
|
|
714
879
|
windspeed.register_cmod7(getConf()["lut_cmod7_path"])
|
|
715
880
|
# Step 2 - clean and prepare dataset
|
|
716
881
|
|
|
717
882
|
# variables to not keep in the L2
|
|
718
|
-
black_list = [
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
883
|
+
black_list = [
|
|
884
|
+
"digital_number",
|
|
885
|
+
"gamma0_raw",
|
|
886
|
+
"negz",
|
|
887
|
+
"azimuth_time",
|
|
888
|
+
"slant_range_time",
|
|
889
|
+
"velocity",
|
|
890
|
+
"range_ground_spacing",
|
|
891
|
+
"gamma0",
|
|
892
|
+
"time",
|
|
893
|
+
"nd_co",
|
|
894
|
+
"nd_cr",
|
|
895
|
+
"gamma0_lut",
|
|
896
|
+
"sigma0_lut",
|
|
897
|
+
"noise_lut_range",
|
|
898
|
+
"lineSpacing",
|
|
899
|
+
"sampleSpacing",
|
|
900
|
+
"noise_lut",
|
|
901
|
+
"noise_lut_azi",
|
|
902
|
+
"nebz",
|
|
903
|
+
"beta0_raw",
|
|
904
|
+
"lines_flipped",
|
|
905
|
+
"samples_flipped",
|
|
906
|
+
"altitude",
|
|
907
|
+
"beta0",
|
|
908
|
+
]
|
|
723
909
|
variables = list(set(xr_dataset) - set(black_list))
|
|
724
910
|
xr_dataset = xr_dataset[variables]
|
|
725
911
|
|
|
@@ -746,122 +932,164 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_gradientsfeat
|
|
|
746
932
|
# TOREMOVE
|
|
747
933
|
if "offboresight" in xr_dataset:
|
|
748
934
|
xr_dataset.offboresight.attrs["units"] = "degrees"
|
|
749
|
-
xr_dataset.offboresight.attrs["long_name"] =
|
|
935
|
+
xr_dataset.offboresight.attrs["long_name"] = (
|
|
936
|
+
"Offboresight angle at wind cell center"
|
|
937
|
+
)
|
|
750
938
|
xr_dataset.elevation.attrs["standard_name"] = "offboresight"
|
|
751
939
|
|
|
752
940
|
# masks (no ice / no_valid)
|
|
753
|
-
xr_dataset.land_mask.values = binary_dilation(
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
xr_dataset.land_mask.attrs[
|
|
941
|
+
xr_dataset.land_mask.values = binary_dilation(
|
|
942
|
+
xr_dataset["land_mask"].values.astype("uint8"),
|
|
943
|
+
structure=np.ones((3, 3), np.uint8),
|
|
944
|
+
iterations=3,
|
|
945
|
+
)
|
|
946
|
+
xr_dataset.land_mask.attrs["long_name"] = "Mask of data"
|
|
947
|
+
xr_dataset.land_mask.attrs["valid_range"] = np.array([0, 1])
|
|
948
|
+
xr_dataset.land_mask.attrs["flag_values"] = np.array([0, 1])
|
|
949
|
+
xr_dataset.land_mask.attrs["flag_meanings"] = "valid no_valid"
|
|
759
950
|
|
|
760
951
|
logging.debug("mask is a copy of land_mask")
|
|
761
952
|
|
|
762
|
-
xr_dataset[
|
|
953
|
+
xr_dataset["mask"] = xr.DataArray(xr_dataset.land_mask)
|
|
763
954
|
xr_dataset.mask.attrs = {}
|
|
764
|
-
xr_dataset.mask.attrs[
|
|
765
|
-
xr_dataset.mask.attrs[
|
|
766
|
-
xr_dataset.mask.attrs[
|
|
767
|
-
xr_dataset.mask.attrs[
|
|
955
|
+
xr_dataset.mask.attrs["long_name"] = "Mask of data"
|
|
956
|
+
xr_dataset.mask.attrs["valid_range"] = np.array([0, 3])
|
|
957
|
+
xr_dataset.mask.attrs["flag_values"] = np.array([0, 1, 2, 3])
|
|
958
|
+
xr_dataset.mask.attrs["flag_meanings"] = "valid land ice no_valid"
|
|
768
959
|
|
|
769
960
|
# ancillary
|
|
770
|
-
xr_dataset[
|
|
771
|
-
90. - np.rad2deg(np.arctan2(xr_dataset.model_V10,
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
xr_dataset[
|
|
778
|
-
xr_dataset[
|
|
779
|
-
|
|
780
|
-
xr_dataset[
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
xr_dataset[
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
xr_dataset[
|
|
792
|
-
|
|
793
|
-
xr_dataset[
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
961
|
+
xr_dataset["ancillary_wind_direction"] = (
|
|
962
|
+
90.0 - np.rad2deg(np.arctan2(xr_dataset.model_V10,
|
|
963
|
+
xr_dataset.model_U10)) + 180
|
|
964
|
+
) % 360
|
|
965
|
+
|
|
966
|
+
xr_dataset["ancillary_wind_direction"] = xr.where(
|
|
967
|
+
xr_dataset["mask"], np.nan, xr_dataset["ancillary_wind_direction"]
|
|
968
|
+
).transpose(*xr_dataset["ancillary_wind_direction"].dims)
|
|
969
|
+
xr_dataset["ancillary_wind_direction"].attrs = {}
|
|
970
|
+
xr_dataset["ancillary_wind_direction"].attrs["units"] = "degrees_north"
|
|
971
|
+
xr_dataset["ancillary_wind_direction"].attrs[
|
|
972
|
+
"long_name"
|
|
973
|
+
] = f"{ancillary_name} wind direction in meteorological convention (clockwise, from), ex: 0°=from north, 90°=from east"
|
|
974
|
+
xr_dataset["ancillary_wind_direction"].attrs["standart_name"] = "wind_direction"
|
|
975
|
+
|
|
976
|
+
xr_dataset["ancillary_wind_speed"] = np.sqrt(
|
|
977
|
+
xr_dataset["model_U10"] ** 2 + xr_dataset["model_V10"] ** 2
|
|
978
|
+
)
|
|
979
|
+
xr_dataset["ancillary_wind_speed"] = xr.where(
|
|
980
|
+
xr_dataset["mask"], np.nan, xr_dataset["ancillary_wind_speed"]
|
|
981
|
+
).transpose(*xr_dataset["ancillary_wind_speed"].dims)
|
|
982
|
+
xr_dataset["ancillary_wind_speed"].attrs = {}
|
|
983
|
+
xr_dataset["ancillary_wind_speed"].attrs["units"] = "m s^-1"
|
|
984
|
+
xr_dataset["ancillary_wind_speed"].attrs[
|
|
985
|
+
"long_name"
|
|
986
|
+
] = f"{ancillary_name} wind speed"
|
|
987
|
+
xr_dataset["ancillary_wind_speed"].attrs["standart_name"] = "wind_speed"
|
|
988
|
+
|
|
989
|
+
xr_dataset["ancillary_wind"] = xr.where(
|
|
990
|
+
xr_dataset["mask"],
|
|
991
|
+
np.nan,
|
|
992
|
+
(
|
|
993
|
+
xr_dataset.ancillary_wind_speed
|
|
994
|
+
* np.exp(
|
|
995
|
+
1j
|
|
996
|
+
* xsarsea.dir_meteo_to_sample(
|
|
997
|
+
xr_dataset.ancillary_wind_direction, xr_dataset.ground_heading
|
|
998
|
+
)
|
|
999
|
+
)
|
|
1000
|
+
),
|
|
1001
|
+
).transpose(*xr_dataset["ancillary_wind_speed"].dims)
|
|
1002
|
+
|
|
1003
|
+
xr_dataset.attrs["ancillary_source"] = (
|
|
1004
|
+
xr_dataset["model_U10"].attrs["history"].split("decoded: ")[1].strip()
|
|
1005
|
+
)
|
|
1006
|
+
xr_dataset = xr_dataset.drop_vars(["model_U10", "model_V10"])
|
|
800
1007
|
|
|
801
1008
|
# nrcs processing
|
|
802
|
-
xr_dataset[
|
|
803
|
-
|
|
804
|
-
|
|
1009
|
+
xr_dataset["sigma0_ocean"] = xr.where(
|
|
1010
|
+
xr_dataset["mask"], np.nan, xr_dataset["sigma0"]
|
|
1011
|
+
).transpose(*xr_dataset["sigma0"].dims)
|
|
1012
|
+
xr_dataset["sigma0_ocean"].attrs = xr_dataset["sigma0"].attrs
|
|
805
1013
|
# we forced it to 1e-15
|
|
806
|
-
xr_dataset[
|
|
1014
|
+
xr_dataset["sigma0_ocean"].attrs[
|
|
1015
|
+
"comment"
|
|
1016
|
+
] = "clipped, no values <=0 ; 1e-15 instread"
|
|
807
1017
|
|
|
808
1018
|
# rajout d'un mask pour les valeurs <=0:
|
|
809
|
-
xr_dataset[
|
|
810
|
-
xr_dataset[
|
|
811
|
-
xr_dataset
|
|
812
|
-
xr_dataset.sigma0_mask.attrs[
|
|
813
|
-
xr_dataset.sigma0_mask.attrs[
|
|
814
|
-
xr_dataset[
|
|
815
|
-
|
|
1019
|
+
xr_dataset["sigma0_mask"] = xr.where(
|
|
1020
|
+
xr_dataset["sigma0_ocean"] <= 0, 1, 0
|
|
1021
|
+
).transpose(*xr_dataset["sigma0"].dims)
|
|
1022
|
+
xr_dataset.sigma0_mask.attrs["valid_range"] = np.array([0, 1])
|
|
1023
|
+
xr_dataset.sigma0_mask.attrs["flag_values"] = np.array([0, 1])
|
|
1024
|
+
xr_dataset.sigma0_mask.attrs["flag_meanings"] = "valid no_valid"
|
|
1025
|
+
xr_dataset["sigma0_ocean"] = xr.where(
|
|
1026
|
+
xr_dataset["sigma0_ocean"] <= 0, 1e-15, xr_dataset["sigma0_ocean"]
|
|
1027
|
+
)
|
|
816
1028
|
|
|
817
|
-
xr_dataset[
|
|
818
|
-
|
|
1029
|
+
xr_dataset["sigma0_ocean_raw"] = xr.where(
|
|
1030
|
+
xr_dataset["mask"], np.nan, xr_dataset["sigma0_raw"]
|
|
1031
|
+
).transpose(*xr_dataset["sigma0_raw"].dims)
|
|
819
1032
|
|
|
820
|
-
xr_dataset[
|
|
1033
|
+
xr_dataset["sigma0_ocean_raw"].attrs = xr_dataset["sigma0_raw"].attrs
|
|
821
1034
|
|
|
822
|
-
xr_dataset[
|
|
823
|
-
xr_dataset.sigma0.sel(pol=copol), xr_dataset.incidence, model=model_co
|
|
1035
|
+
xr_dataset["sigma0_detrend"] = xsarsea.sigma0_detrend(
|
|
1036
|
+
xr_dataset.sigma0.sel(pol=copol), xr_dataset.incidence, model=model_co
|
|
1037
|
+
)
|
|
824
1038
|
|
|
825
1039
|
# processing
|
|
826
1040
|
if dual_pol:
|
|
827
1041
|
|
|
828
1042
|
xr_dataset['sigma0_detrend_cross'] = xsarsea.sigma0_detrend(
|
|
829
1043
|
xr_dataset.sigma0.sel(pol=crosspol), xr_dataset.incidence, model=model_cross)
|
|
1044
|
+
|
|
1045
|
+
xr_dataset = xr_dataset.assign(nesz_cross_flattened=(
|
|
1046
|
+
['line', 'sample'], windspeed.nesz_flattening(xr_dataset.nesz.sel(pol=crosspol), xr_dataset.incidence).data))
|
|
1047
|
+
|
|
1048
|
+
xr_dataset['nesz_cross_flattened'].attrs[
|
|
1049
|
+
"comment"] = 'nesz has been flattened using windspeed.nesz_flattening'
|
|
1050
|
+
|
|
830
1051
|
if config["apply_flattening"]:
|
|
831
|
-
xr_dataset = xr_dataset.assign(nesz_cross_flattened=(
|
|
832
|
-
['line', 'sample'], windspeed.nesz_flattening(xr_dataset.nesz.sel(pol=crosspol), xr_dataset.incidence).data))
|
|
833
|
-
xr_dataset['nesz_cross_flattened'].attrs[
|
|
834
|
-
"comment"] = 'nesz has been flattened using windspeed.nesz_flattening'
|
|
835
1052
|
# dsig
|
|
836
|
-
xr_dataset["dsig_cross"] = windspeed.get_dsig(
|
|
837
|
-
|
|
1053
|
+
xr_dataset["dsig_cross"] = windspeed.get_dsig(
|
|
1054
|
+
config["dsig_" + crosspol_gmf + "_NAME"],
|
|
1055
|
+
xr_dataset.incidence,
|
|
1056
|
+
xr_dataset["sigma0_ocean"].sel(pol=crosspol),
|
|
1057
|
+
xr_dataset.nesz_cross_flattened,
|
|
1058
|
+
)
|
|
1059
|
+
|
|
1060
|
+
xr_dataset.dsig_cross.attrs["formula_used"] = config[
|
|
1061
|
+
"dsig_" + crosspol_gmf + "_NAME"
|
|
1062
|
+
]
|
|
1063
|
+
|
|
838
1064
|
else:
|
|
839
1065
|
# dsig
|
|
840
|
-
xr_dataset["dsig_cross"] = windspeed.get_dsig(
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
xr_dataset.attrs["
|
|
858
|
-
os.path.dirname(xsar_dataset.datatree['recalibration'].attrs['path_aux_cal_old'])))
|
|
1066
|
+
xr_dataset["dsig_cross"] = windspeed.get_dsig(
|
|
1067
|
+
config["dsig_" + crosspol_gmf + "_NAME"],
|
|
1068
|
+
xr_dataset.incidence,
|
|
1069
|
+
xr_dataset["sigma0_ocean"].sel(pol=crosspol),
|
|
1070
|
+
xr_dataset.nesz.sel(pol=crosspol),
|
|
1071
|
+
)
|
|
1072
|
+
|
|
1073
|
+
xr_dataset.dsig_cross.attrs["comment"] = (
|
|
1074
|
+
"variable used to ponderate copol and crosspol"
|
|
1075
|
+
)
|
|
1076
|
+
|
|
1077
|
+
xr_dataset.dsig_cross.attrs["apply_flattening"] = str(
|
|
1078
|
+
config["apply_flattening"]
|
|
1079
|
+
)
|
|
1080
|
+
|
|
1081
|
+
if (recalibration) & ("SENTINEL" in sensor_longname):
|
|
1082
|
+
xr_dataset.attrs["aux_cal_recal"] = xsar_dataset.datatree["recalibration"].attrs["aux_cal_new"]
|
|
1083
|
+
xr_dataset.attrs["aux_pp1_recal"] = xsar_dataset.datatree["recalibration"].attrs["aux_pp1_new"]
|
|
859
1084
|
|
|
860
1085
|
if add_nrcs_model:
|
|
861
1086
|
# add timing
|
|
862
1087
|
phi = np.abs(
|
|
863
|
-
np.rad2deg(
|
|
864
|
-
|
|
1088
|
+
np.rad2deg(
|
|
1089
|
+
xsarsea.dir_meteo_to_sample(
|
|
1090
|
+
xr_dataset["ancillary_wind_direction"], xr_dataset["ground_heading"]
|
|
1091
|
+
)
|
|
1092
|
+
)
|
|
865
1093
|
)
|
|
866
1094
|
|
|
867
1095
|
varnames = ["ancillary_nrcs"]
|
|
@@ -883,9 +1111,7 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_gradientsfeat
|
|
|
883
1111
|
incidence=incidence, wspd=wspd, phi=phi, method="nearest"
|
|
884
1112
|
)
|
|
885
1113
|
else:
|
|
886
|
-
return lut.sel(
|
|
887
|
-
incidence=incidence, wspd=wspd, method="nearest"
|
|
888
|
-
)
|
|
1114
|
+
return lut.sel(incidence=incidence, wspd=wspd, method="nearest")
|
|
889
1115
|
|
|
890
1116
|
xr_dataset[varnames[idx]] = xr.apply_ufunc(
|
|
891
1117
|
lut_selection,
|
|
@@ -937,38 +1163,46 @@ def process_gradients(xr_dataset, config):
|
|
|
937
1163
|
dual_pol = config["l2_params"]["dual_pol"]
|
|
938
1164
|
|
|
939
1165
|
# Load the 100m dataset
|
|
940
|
-
xsar_dataset_100 = fct_dataset(
|
|
941
|
-
meta, resolution='100m')
|
|
1166
|
+
xsar_dataset_100 = fct_dataset(meta, resolution="100m")
|
|
942
1167
|
|
|
943
|
-
xr_dataset_100 = xsar_dataset_100.datatree[
|
|
1168
|
+
xr_dataset_100 = xsar_dataset_100.datatree["measurement"].to_dataset()
|
|
944
1169
|
xr_dataset_100 = xr_dataset_100.rename(map_model)
|
|
945
1170
|
# load dataset
|
|
946
1171
|
xr_dataset_100 = xr_dataset_100.load()
|
|
947
1172
|
|
|
948
1173
|
# adding sigma0 detrend
|
|
949
|
-
xr_dataset_100[
|
|
950
|
-
xr_dataset_100.sigma0.sel(pol=copol), xr_dataset_100.incidence, model=model_co
|
|
1174
|
+
xr_dataset_100["sigma0_detrend"] = xsarsea.sigma0_detrend(
|
|
1175
|
+
xr_dataset_100.sigma0.sel(pol=copol), xr_dataset_100.incidence, model=model_co
|
|
1176
|
+
)
|
|
951
1177
|
|
|
952
1178
|
if dual_pol:
|
|
953
|
-
xr_dataset_100[
|
|
954
|
-
xr_dataset_100.sigma0.sel(pol=crosspol),
|
|
1179
|
+
xr_dataset_100["sigma0_detrend_cross"] = xsarsea.sigma0_detrend(
|
|
1180
|
+
xr_dataset_100.sigma0.sel(pol=crosspol),
|
|
1181
|
+
xr_dataset_100.incidence,
|
|
1182
|
+
model=model_cross,
|
|
1183
|
+
)
|
|
955
1184
|
|
|
956
1185
|
sigma0_detrend_combined = xr.concat(
|
|
957
|
-
[xr_dataset_100[
|
|
958
|
-
xr_dataset_100[
|
|
959
|
-
dim=
|
|
1186
|
+
[xr_dataset_100["sigma0_detrend"],
|
|
1187
|
+
xr_dataset_100["sigma0_detrend_cross"]],
|
|
1188
|
+
dim="pol",
|
|
960
1189
|
)
|
|
961
|
-
sigma0_detrend_combined[
|
|
1190
|
+
sigma0_detrend_combined["pol"] = [copol, crosspol]
|
|
962
1191
|
|
|
963
|
-
xr_dataset_100[
|
|
1192
|
+
xr_dataset_100["sigma0_detrend"] = sigma0_detrend_combined
|
|
964
1193
|
|
|
965
|
-
xr_dataset_100.land_mask.values = binary_dilation(
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
1194
|
+
xr_dataset_100.land_mask.values = binary_dilation(
|
|
1195
|
+
xr_dataset_100["land_mask"].values.astype("uint8"),
|
|
1196
|
+
structure=np.ones((3, 3), np.uint8),
|
|
1197
|
+
iterations=3,
|
|
1198
|
+
)
|
|
1199
|
+
xr_dataset_100["sigma0_detrend"] = xr.where(
|
|
1200
|
+
xr_dataset_100["land_mask"], np.nan, xr_dataset_100["sigma0"]
|
|
1201
|
+
).transpose(*xr_dataset_100["sigma0"].dims)
|
|
969
1202
|
|
|
970
|
-
xr_dataset_100[
|
|
971
|
-
xr_dataset_100.model_U10 + 1j * xr_dataset_100.model_V10
|
|
1203
|
+
xr_dataset_100["ancillary_wind"] = (
|
|
1204
|
+
xr_dataset_100.model_U10 + 1j * xr_dataset_100.model_V10
|
|
1205
|
+
) * np.exp(1j * np.deg2rad(xr_dataset_100.ground_heading))
|
|
972
1206
|
|
|
973
1207
|
downscales_factors = [1, 2, 4, 8]
|
|
974
1208
|
# 4 and 8 must be in downscales_factors
|
|
@@ -979,7 +1213,7 @@ def process_gradients(xr_dataset, config):
|
|
|
979
1213
|
xr_dataset_100=xr_dataset_100,
|
|
980
1214
|
windows_sizes=[1600, 3200],
|
|
981
1215
|
downscales_factors=downscales_factors,
|
|
982
|
-
window_step=1
|
|
1216
|
+
window_step=1,
|
|
983
1217
|
)
|
|
984
1218
|
|
|
985
1219
|
# Compute heterogeneity mask and variables
|
|
@@ -988,24 +1222,29 @@ def process_gradients(xr_dataset, config):
|
|
|
988
1222
|
|
|
989
1223
|
# Add streaks dataset
|
|
990
1224
|
streaks_indiv = gradientFeatures.streaks_individual()
|
|
991
|
-
if
|
|
992
|
-
xr_dataset_streaks = xr.Dataset(
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1225
|
+
if "longitude" in streaks_indiv:
|
|
1226
|
+
xr_dataset_streaks = xr.Dataset(
|
|
1227
|
+
{
|
|
1228
|
+
"longitude": streaks_indiv.longitude,
|
|
1229
|
+
"latitude": streaks_indiv.latitude,
|
|
1230
|
+
"dir_smooth": streaks_indiv.angle,
|
|
1231
|
+
"dir_mean_smooth": gradientFeatures.streaks_mean_smooth().angle,
|
|
1232
|
+
"dir_smooth_mean": gradientFeatures.streaks_smooth_mean().angle,
|
|
1233
|
+
}
|
|
1234
|
+
)
|
|
999
1235
|
else:
|
|
1000
1236
|
logger.warn(
|
|
1001
|
-
"'longitude' not found in streaks_indiv : there is probably an error"
|
|
1237
|
+
"'longitude' not found in streaks_indiv : there is probably an error"
|
|
1238
|
+
)
|
|
1002
1239
|
xr_dataset_streaks = None
|
|
1003
1240
|
|
|
1004
1241
|
return xr_dataset, xr_dataset_streaks
|
|
1005
1242
|
|
|
1006
1243
|
|
|
1007
1244
|
@timing(logger=logger.info)
|
|
1008
|
-
def makeL2(
|
|
1245
|
+
def makeL2(
|
|
1246
|
+
filename, outdir, config_path, overwrite=False, generateCSV=True, resolution="1000m"
|
|
1247
|
+
):
|
|
1009
1248
|
"""
|
|
1010
1249
|
Main function to generate L2 product.
|
|
1011
1250
|
|
|
@@ -1033,11 +1272,11 @@ def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, res
|
|
|
1033
1272
|
"""
|
|
1034
1273
|
|
|
1035
1274
|
xr_dataset, out_file, config = preprocess(
|
|
1036
|
-
filename, outdir, config_path, overwrite, resolution
|
|
1275
|
+
filename, outdir, config_path, overwrite, resolution
|
|
1276
|
+
)
|
|
1037
1277
|
|
|
1038
1278
|
if config["add_gradientsfeatures"]:
|
|
1039
|
-
xr_dataset, xr_dataset_streaks = process_gradients(
|
|
1040
|
-
xr_dataset, config)
|
|
1279
|
+
xr_dataset, xr_dataset_streaks = process_gradients(xr_dataset, config)
|
|
1041
1280
|
else:
|
|
1042
1281
|
xr_dataset_streaks = None
|
|
1043
1282
|
|
|
@@ -1052,8 +1291,8 @@ def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, res
|
|
|
1052
1291
|
sensor_longname = config["sensor_longname"]
|
|
1053
1292
|
|
|
1054
1293
|
if dual_pol:
|
|
1055
|
-
sigma0_ocean_cross = xr_dataset[
|
|
1056
|
-
dsig_cross = xr_dataset[
|
|
1294
|
+
sigma0_ocean_cross = xr_dataset["sigma0_ocean"].sel(pol=crosspol)
|
|
1295
|
+
dsig_cross = xr_dataset["dsig_cross"]
|
|
1057
1296
|
else:
|
|
1058
1297
|
sigma0_ocean_cross = None
|
|
1059
1298
|
dsig_cross = 0.1 # default value set in xsarsea
|
|
@@ -1071,97 +1310,119 @@ def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, res
|
|
|
1071
1310
|
logging.info("Checking incidence range within LUTS incidence range")
|
|
1072
1311
|
# warning if incidence is out of lut incidence range
|
|
1073
1312
|
inc_check_co, inc_check_cross = check_incidence_range(
|
|
1074
|
-
xr_dataset[
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1313
|
+
xr_dataset["incidence"], [model_co, model_cross], **kwargs
|
|
1314
|
+
)
|
|
1315
|
+
|
|
1316
|
+
wind_co, wind_dual, windspeed_cr = inverse(
|
|
1317
|
+
dual_pol,
|
|
1318
|
+
inc=xr_dataset["incidence"],
|
|
1319
|
+
sigma0=xr_dataset["sigma0_ocean"].sel(pol=copol),
|
|
1320
|
+
sigma0_dual=sigma0_ocean_cross,
|
|
1321
|
+
ancillary_wind=xr_dataset["ancillary_wind"],
|
|
1322
|
+
dsig_cr=dsig_cross,
|
|
1323
|
+
model_co=model_co,
|
|
1324
|
+
model_cross=model_cross,
|
|
1325
|
+
**kwargs,
|
|
1326
|
+
)
|
|
1086
1327
|
# windspeed_co
|
|
1087
|
-
xr_dataset[
|
|
1328
|
+
xr_dataset["windspeed_co"] = np.abs(wind_co)
|
|
1088
1329
|
xr_dataset["windspeed_co"].attrs["units"] = "m.s⁻1"
|
|
1089
|
-
xr_dataset["windspeed_co"].attrs["long_name"] =
|
|
1090
|
-
model_co, copol)
|
|
1330
|
+
xr_dataset["windspeed_co"].attrs["long_name"] = (
|
|
1331
|
+
"Wind speed inverted from model %s (%s)" % (model_co, copol)
|
|
1332
|
+
)
|
|
1091
1333
|
xr_dataset["windspeed_co"].attrs["standart_name"] = "wind_speed"
|
|
1092
1334
|
xr_dataset["windspeed_co"].attrs["model"] = wind_co.attrs["model"]
|
|
1093
|
-
del xr_dataset["windspeed_co"].attrs[
|
|
1335
|
+
del xr_dataset["windspeed_co"].attrs["comment"]
|
|
1094
1336
|
|
|
1095
1337
|
# winddir_co
|
|
1096
|
-
xr_dataset[
|
|
1097
|
-
wind_co,
|
|
1098
|
-
|
|
1338
|
+
xr_dataset["winddir_co"] = transform_winddir(
|
|
1339
|
+
wind_co,
|
|
1340
|
+
xr_dataset.ground_heading,
|
|
1341
|
+
winddir_convention=config["winddir_convention"],
|
|
1342
|
+
)
|
|
1343
|
+
xr_dataset["winddir_co"].attrs["model"] = "%s (%s)" % (model_co, copol)
|
|
1099
1344
|
|
|
1100
1345
|
# windspeed_dual / windspeed_cr / /winddir_dual / winddir_cr
|
|
1101
1346
|
if dual_pol and wind_dual is not None:
|
|
1102
|
-
xr_dataset[
|
|
1347
|
+
xr_dataset["windspeed_dual"] = np.abs(wind_dual)
|
|
1103
1348
|
xr_dataset["windspeed_dual"].attrs["units"] = "m.s⁻1"
|
|
1104
|
-
xr_dataset["windspeed_dual"].attrs["long_name"] =
|
|
1105
|
-
|
|
1349
|
+
xr_dataset["windspeed_dual"].attrs["long_name"] = (
|
|
1350
|
+
"Wind speed inverted from model %s (%s) & %s (%s)"
|
|
1351
|
+
% (model_co, copol, model_cross, crosspol)
|
|
1352
|
+
)
|
|
1106
1353
|
xr_dataset["windspeed_dual"].attrs["standart_name"] = "wind_speed"
|
|
1107
1354
|
xr_dataset["windspeed_dual"].attrs["model"] = wind_dual.attrs["model"]
|
|
1108
|
-
del xr_dataset["windspeed_dual"].attrs[
|
|
1355
|
+
del xr_dataset["windspeed_dual"].attrs["comment"]
|
|
1109
1356
|
|
|
1110
|
-
xr_dataset[
|
|
1111
|
-
wind_dual,
|
|
1112
|
-
|
|
1357
|
+
xr_dataset["winddir_dual"] = transform_winddir(
|
|
1358
|
+
wind_dual,
|
|
1359
|
+
xr_dataset.ground_heading,
|
|
1360
|
+
winddir_convention=config["winddir_convention"],
|
|
1361
|
+
)
|
|
1362
|
+
xr_dataset["winddir_dual"].attrs[
|
|
1363
|
+
"model"
|
|
1364
|
+
] = "winddir_dual is a copy of copol wind direction"
|
|
1113
1365
|
|
|
1114
1366
|
xr_dataset = xr_dataset.assign(
|
|
1115
|
-
windspeed_cross=([
|
|
1367
|
+
windspeed_cross=(["line", "sample"], windspeed_cr.data)
|
|
1368
|
+
)
|
|
1116
1369
|
xr_dataset["windspeed_cross"].attrs["units"] = "m.s⁻1"
|
|
1117
|
-
xr_dataset["windspeed_cross"].attrs["long_name"] =
|
|
1118
|
-
model_cross, crosspol)
|
|
1370
|
+
xr_dataset["windspeed_cross"].attrs["long_name"] = (
|
|
1371
|
+
"Wind Speed inverted from model %s (%s)" % (model_cross, crosspol)
|
|
1372
|
+
)
|
|
1119
1373
|
xr_dataset["windspeed_cross"].attrs["standart_name"] = "wind_speed"
|
|
1120
1374
|
xr_dataset["windspeed_cross"].attrs["model"] = "%s" % (model_cross)
|
|
1121
1375
|
|
|
1122
|
-
xr_dataset[
|
|
1123
|
-
xr_dataset[
|
|
1124
|
-
xr_dataset["winddir_cross"].attrs[
|
|
1376
|
+
xr_dataset["winddir_cross"] = xr_dataset["winddir_dual"].copy()
|
|
1377
|
+
xr_dataset["winddir_cross"].attrs = xr_dataset["winddir_dual"].attrs
|
|
1378
|
+
xr_dataset["winddir_cross"].attrs[
|
|
1379
|
+
"model"
|
|
1380
|
+
] = "winddir_cross is a copy of copol wind direction"
|
|
1125
1381
|
|
|
1126
1382
|
if config["winddir_convention"] == "oceanographic":
|
|
1127
|
-
attrs = xr_dataset[
|
|
1128
|
-
xr_dataset[
|
|
1129
|
-
xr_dataset[
|
|
1130
|
-
|
|
1131
|
-
xr_dataset[
|
|
1132
|
-
|
|
1383
|
+
attrs = xr_dataset["ancillary_wind_direction"].attrs
|
|
1384
|
+
xr_dataset["ancillary_wind_direction"] = xsarsea.dir_meteo_to_oceano(
|
|
1385
|
+
xr_dataset["ancillary_wind_direction"]
|
|
1386
|
+
)
|
|
1387
|
+
xr_dataset["ancillary_wind_direction"].attrs = attrs
|
|
1388
|
+
xr_dataset["ancillary_wind_direction"].attrs[
|
|
1389
|
+
"long_name"
|
|
1390
|
+
] = f"{ancillary_name} wind direction in oceanographic convention (clockwise, to), ex: 0°=to north, 90°=to east"
|
|
1133
1391
|
|
|
1134
|
-
xr_dataset, encoding = makeL2asOwi(
|
|
1135
|
-
xr_dataset, config)
|
|
1392
|
+
xr_dataset, encoding = makeL2asOwi(xr_dataset, config)
|
|
1136
1393
|
|
|
1137
1394
|
xr_dataset = xr_dataset.compute()
|
|
1138
1395
|
# add attributes
|
|
1139
1396
|
firstMeasurementTime = None
|
|
1140
1397
|
lastMeasurementTime = None
|
|
1141
1398
|
try:
|
|
1142
|
-
firstMeasurementTime = datetime.datetime.strptime(
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
lastMeasurementTime = datetime.datetime.strptime(
|
|
1146
|
-
|
|
1147
|
-
|
|
1399
|
+
firstMeasurementTime = datetime.datetime.strptime(
|
|
1400
|
+
xr_dataset.attrs["start_date"], "%Y-%m-%d %H:%M:%S.%f"
|
|
1401
|
+
).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
1402
|
+
lastMeasurementTime = datetime.datetime.strptime(
|
|
1403
|
+
xr_dataset.attrs["stop_date"], "%Y-%m-%d %H:%M:%S.%f"
|
|
1404
|
+
).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
1148
1405
|
except:
|
|
1149
|
-
firstMeasurementTime = datetime.datetime.strptime(
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
lastMeasurementTime = datetime.datetime.strptime(
|
|
1153
|
-
|
|
1154
|
-
|
|
1406
|
+
firstMeasurementTime = datetime.datetime.strptime(
|
|
1407
|
+
xr_dataset.attrs["start_date"], "%Y-%m-%d %H:%M:%S"
|
|
1408
|
+
).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
1409
|
+
lastMeasurementTime = datetime.datetime.strptime(
|
|
1410
|
+
xr_dataset.attrs["stop_date"], "%Y-%m-%d %H:%M:%S"
|
|
1411
|
+
).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
1155
1412
|
|
|
1156
1413
|
attrs = {
|
|
1157
1414
|
"TITLE": "Sentinel-1 OWI Component",
|
|
1158
1415
|
"productOwner": "IFREMER",
|
|
1159
|
-
"sourceProduct": (
|
|
1160
|
-
|
|
1416
|
+
"sourceProduct": (
|
|
1417
|
+
xr_dataset.attrs["safe"]
|
|
1418
|
+
if "safe" in xr_dataset.attrs
|
|
1419
|
+
else os.path.basename(xr_dataset.attrs["product_path"])
|
|
1420
|
+
),
|
|
1421
|
+
"sourceProduct_fullpath": xr_dataset.attrs.pop("name"),
|
|
1161
1422
|
"missionName": sensor_longname,
|
|
1162
|
-
"missionPhase":
|
|
1163
|
-
"polarisation": xr_dataset.attrs[
|
|
1164
|
-
"acquisitionStation":
|
|
1423
|
+
"missionPhase": "Operational",
|
|
1424
|
+
"polarisation": xr_dataset.attrs["pols"],
|
|
1425
|
+
"acquisitionStation": "",
|
|
1165
1426
|
"xsar_version": xsar.__version__,
|
|
1166
1427
|
"xsarsea_version": xsarsea.__version__,
|
|
1167
1428
|
"pythonVersion": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
|
|
@@ -1174,25 +1435,27 @@ def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, res
|
|
|
1174
1435
|
"bathySource": "/",
|
|
1175
1436
|
"oswAlgorithmName": "grdwindinversion",
|
|
1176
1437
|
"owiAlgorithmVersion": grdwindinversion.__version__,
|
|
1177
|
-
"gmf": config[
|
|
1438
|
+
"gmf": config["GMF_" + copol_gmf + "_NAME"]
|
|
1439
|
+
+ ", "
|
|
1440
|
+
+ config["GMF_" + crosspol_gmf + "_NAME"],
|
|
1178
1441
|
"iceSource": "/",
|
|
1179
1442
|
"owiNoiseCorrection": "True",
|
|
1180
|
-
"inversionTabGMF": config[
|
|
1181
|
-
|
|
1443
|
+
"inversionTabGMF": config["GMF_" + copol_gmf + "_NAME"]
|
|
1444
|
+
+ ", "
|
|
1445
|
+
+ config["GMF_" + crosspol_gmf + "_NAME"],
|
|
1182
1446
|
"wnf_3km_average": "False",
|
|
1183
1447
|
"owiWindSpeedSrc": "owiWindSpeed",
|
|
1184
1448
|
"owiWindDirectionSrc": "/",
|
|
1185
|
-
"ancillary_source": xr_dataset.attrs[
|
|
1449
|
+
"ancillary_source": xr_dataset.attrs["ancillary_source"],
|
|
1186
1450
|
"winddir_convention": config["winddir_convention"],
|
|
1187
1451
|
"incidence_within_lut_copol_incidence_range": str(inc_check_co),
|
|
1188
1452
|
"incidence_within_lut_crosspol_incidence_range": str(inc_check_cross),
|
|
1189
|
-
"swath": xr_dataset.attrs[
|
|
1190
|
-
"footprint": xr_dataset.attrs[
|
|
1191
|
-
"coverage": xr_dataset.attrs[
|
|
1192
|
-
|
|
1453
|
+
"swath": xr_dataset.attrs["swath"],
|
|
1454
|
+
"footprint": xr_dataset.attrs["footprint"],
|
|
1455
|
+
"coverage": xr_dataset.attrs["coverage"],
|
|
1193
1456
|
}
|
|
1194
1457
|
|
|
1195
|
-
for recalib_attrs in ["
|
|
1458
|
+
for recalib_attrs in ["aux_pp1_recal", "aux_pp1", "aux_cal_recal", "aux_cal"]:
|
|
1196
1459
|
if recalib_attrs in xr_dataset.attrs:
|
|
1197
1460
|
attrs[recalib_attrs] = xr_dataset.attrs[recalib_attrs]
|
|
1198
1461
|
|
|
@@ -1200,18 +1463,18 @@ def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, res
|
|
|
1200
1463
|
if arg in xr_dataset.attrs:
|
|
1201
1464
|
attrs["passDirection"] = xr_dataset.attrs[arg]
|
|
1202
1465
|
|
|
1203
|
-
_S1_added_attrs = ["
|
|
1466
|
+
_S1_added_attrs = ["ipf_version", "platform_heading"]
|
|
1204
1467
|
_RCM_added_attrs = ["productId"]
|
|
1205
1468
|
|
|
1206
1469
|
for sup_attr in _S1_added_attrs + _RCM_added_attrs:
|
|
1207
1470
|
if sup_attr in xr_dataset.attrs:
|
|
1208
1471
|
attrs[sup_attr] = xr_dataset.attrs[sup_attr]
|
|
1209
1472
|
|
|
1210
|
-
attrs[
|
|
1473
|
+
attrs["footprint"] = str(attrs["footprint"])
|
|
1211
1474
|
|
|
1212
1475
|
# add in kwargs in attrs
|
|
1213
1476
|
for key in kwargs:
|
|
1214
|
-
attrs["lut_params_"+key] = "/" if kwargs[key] is None else kwargs[key]
|
|
1477
|
+
attrs["lut_params_" + key] = "/" if kwargs[key] is None else kwargs[key]
|
|
1215
1478
|
|
|
1216
1479
|
xr_dataset.attrs = attrs
|
|
1217
1480
|
|
|
@@ -1250,7 +1513,7 @@ def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, res
|
|
|
1250
1513
|
return out_file, xr_dataset
|
|
1251
1514
|
|
|
1252
1515
|
|
|
1253
|
-
def transform_winddir(wind_cpx, ground_heading, winddir_convention=
|
|
1516
|
+
def transform_winddir(wind_cpx, ground_heading, winddir_convention="meteorological"):
|
|
1254
1517
|
"""
|
|
1255
1518
|
|
|
1256
1519
|
Parameters
|
|
@@ -1271,7 +1534,8 @@ def transform_winddir(wind_cpx, ground_heading, winddir_convention='meteorologic
|
|
|
1271
1534
|
"""
|
|
1272
1535
|
# to meteo winddir_convention
|
|
1273
1536
|
dataArray = xsarsea.dir_sample_to_meteo(
|
|
1274
|
-
np.angle(wind_cpx, deg=True), ground_heading
|
|
1537
|
+
np.angle(wind_cpx, deg=True), ground_heading
|
|
1538
|
+
)
|
|
1275
1539
|
long_name = "Wind direction in meteorological convention (clockwise, from), ex: 0°=from north, 90°=from east"
|
|
1276
1540
|
|
|
1277
1541
|
if winddir_convention == "meteorological":
|
|
@@ -1284,7 +1548,8 @@ def transform_winddir(wind_cpx, ground_heading, winddir_convention='meteorologic
|
|
|
1284
1548
|
else:
|
|
1285
1549
|
# warning
|
|
1286
1550
|
logging.warning(
|
|
1287
|
-
f"wind direction convention {winddir_convention} is not supported, using meteorological",
|
|
1551
|
+
f"wind direction convention {winddir_convention} is not supported, using meteorological",
|
|
1552
|
+
)
|
|
1288
1553
|
long_name = "Wind direction in meteorological convention (clockwise, from), ex: 0°=from north, 90°=from east"
|
|
1289
1554
|
|
|
1290
1555
|
dataArray = xsarsea.dir_to_360(dataArray)
|