@@ -52,13 +52,34 @@ def native_transform(self, xx):
52
52
5. Drop the WOfS band
53
53
"""
54
54
55
- # clear and dry pixels not mask against bit 4: terrain high slope,
55
+ # valid and dry pixels not mask against bit 4: terrain high slope,
56
56
# bit 3: terrain shadow, and
57
57
# bit 2: low solar angle
58
- valid = (xx ["water" ] & ~ ((1 << 4 ) | (1 << 3 ) | (1 << 2 ))) == 0
58
+ valid = (xx ["water" ]. data & ~ ((1 << 4 ) | (1 << 3 ) | (1 << 2 ))) == 0
59
59
60
- # clear and wet pixels not mask against bit 2: low solar angle
61
- wet = (xx ["water" ] & ~ (1 << 2 )) == 128
60
+ # clear wet pixels not mask against bit 2: low solar angle
61
+ wet = (xx ["water" ].data & ~ (1 << 2 )) == 128
62
+
63
+ # clear dry pixels
64
+ clear = xx ["water" ].data == 0
65
+
66
+ # get "valid" wo pixels, both dry and wet used in veg_frequency
67
+ wet_valid = expr_eval (
68
+ "where(a|b, a, _nan)" ,
69
+ {"a" : wet , "b" : valid },
70
+ name = "get_valid_pixels" ,
71
+ dtype = "float32" ,
72
+ ** {"_nan" : np .nan },
73
+ )
74
+
75
+ # get "clear" wo pixels, both dry and wet used in water_frequency
76
+ wet_clear = expr_eval (
77
+ "where(a|b, a, _nan)" ,
78
+ {"a" : wet , "b" : clear },
79
+ name = "get_clear_pixels" ,
80
+ dtype = "float32" ,
81
+ ** {"_nan" : np .nan },
82
+ )
62
83
63
84
# dilate both 'valid' and 'water'
64
85
for key , val in self .BAD_BITS_MASK .items ():
@@ -67,37 +88,64 @@ def native_transform(self, xx):
67
88
raw_mask = mask_cleanup (
68
89
raw_mask , mask_filters = self .cloud_filters .get (key )
69
90
)
70
- valid &= ~ raw_mask
71
- wet &= ~ raw_mask
72
-
91
+ valid = expr_eval (
92
+ "where(b>0, 0, a)" ,
93
+ {"a" : valid , "b" : raw_mask .data },
94
+ name = "get_valid_pixels" ,
95
+ dtype = "uint8" ,
96
+ )
97
+ wet_clear = expr_eval (
98
+ "where(b>0, _nan, a)" ,
99
+ {"a" : wet_clear , "b" : raw_mask .data },
100
+ name = "get_clear_pixels" ,
101
+ dtype = "float32" ,
102
+ ** {"_nan" : np .nan },
103
+ )
104
+ wet_valid = expr_eval (
105
+ "where(b>0, _nan, a)" ,
106
+ {"a" : wet_valid , "b" : raw_mask .data },
107
+ name = "get_valid_pixels" ,
108
+ dtype = "float32" ,
109
+ ** {"_nan" : np .nan },
110
+ )
73
111
xx = xx .drop_vars (["water" ])
74
112
75
- # get valid wo pixels, both dry and wet
76
- data = expr_eval (
77
- "where(a|b, a, _nan)" ,
78
- {"a" : wet .data , "b" : valid .data },
79
- name = "get_valid_pixels" ,
80
- dtype = "float32" ,
81
- ** {"_nan" : np .nan },
82
- )
83
-
84
113
# Pick out the fc pixels that have an unmixing error of less than the threshold
85
- valid &= xx ["ue" ] < self .ue_threshold
114
+ valid = expr_eval (
115
+ "where(b<_v, a, 0)" ,
116
+ {"a" : valid , "b" : xx ["ue" ].data },
117
+ name = "get_low_ue" ,
118
+ dtype = "bool" ,
119
+ ** {"_v" : self .ue_threshold },
120
+ )
86
121
xx = xx .drop_vars (["ue" ])
122
+ valid = xr .DataArray (valid , dims = xx ["pv" ].dims , coords = xx ["pv" ].coords )
123
+
87
124
xx = keep_good_only (xx , valid , nodata = NODATA )
88
125
xx = to_float (xx , dtype = "float32" )
89
126
90
- xx ["wet" ] = xr .DataArray (data , dims = wet .dims , coords = wet .coords )
127
+ xx ["wet_valid" ] = xr .DataArray (
128
+ wet_valid , dims = xx ["pv" ].dims , coords = xx ["pv" ].coords
129
+ )
130
+ xx ["wet_clear" ] = xr .DataArray (
131
+ wet_clear , dims = xx ["pv" ].dims , coords = xx ["pv" ].coords
132
+ )
91
133
92
134
return xx
93
135
94
136
def fuser (self , xx ):
95
137
96
- wet = xx ["wet" ]
138
+ wet_valid = xx ["wet_valid" ]
139
+ wet_clear = xx ["wet_clear" ]
97
140
98
- xx = _xr_fuse (xx .drop_vars (["wet" ]), partial (_fuse_mean_np , nodata = np .nan ), "" )
141
+ xx = _xr_fuse (
142
+ xx .drop_vars (["wet_valid" , "wet_clear" ]),
143
+ partial (_fuse_mean_np , nodata = np .nan ),
144
+ "" ,
145
+ )
99
146
100
- xx ["wet" ] = _nodata_fuser (wet , nodata = np .nan )
147
+ xx ["wet_valid" ] = _nodata_fuser (wet_valid , nodata = np .nan )
148
+ xx ["wet_clear" ] = _nodata_fuser (wet_clear , nodata = np .nan )
101
149
102
150
return xx
103
151
@@ -123,7 +171,7 @@ def _veg_or_not(self, xx: xr.Dataset):
123
171
# mark water freq >= 0.5 as 0
124
172
data = expr_eval (
125
173
"where(a>0, 0, b)" ,
126
- {"a" : xx ["wet " ].data , "b" : data },
174
+ {"a" : xx ["wet_valid " ].data , "b" : data },
127
175
name = "get_veg" ,
128
176
dtype = "uint8" ,
129
177
)
@@ -134,25 +182,25 @@ def _water_or_not(self, xx: xr.Dataset):
134
182
# mark water freq > 0.5 as 1
135
183
data = expr_eval (
136
184
"where(a>0.5, 1, 0)" ,
137
- {"a" : xx ["wet " ].data },
185
+ {"a" : xx ["wet_clear " ].data },
138
186
name = "get_water" ,
139
187
dtype = "uint8" ,
140
188
)
141
189
142
190
# mark nans
143
191
data = expr_eval (
144
192
"where(a!=a, nodata, b)" ,
145
- {"a" : xx ["wet " ].data , "b" : data },
193
+ {"a" : xx ["wet_clear " ].data , "b" : data },
146
194
name = "get_water" ,
147
195
dtype = "uint8" ,
148
196
** {"nodata" : int (NODATA )},
149
197
)
150
198
return data
151
199
152
- def _max_consecutive_months (self , data , nodata ):
153
- nan_mask = da .ones (data .shape [1 :], chunks = data .chunks [1 :], dtype = "bool" )
200
+ def _max_consecutive_months (self , data , nodata , normalize = False ):
154
201
tmp = da .zeros (data .shape [1 :], chunks = data .chunks [1 :], dtype = "uint8" )
155
202
max_count = da .zeros (data .shape [1 :], chunks = data .chunks [1 :], dtype = "uint8" )
203
+ total = da .zeros (data .shape [1 :], chunks = data .chunks [1 :], dtype = "uint8" )
156
204
157
205
for t in data :
158
206
# +1 if not nodata
@@ -180,23 +228,34 @@ def _max_consecutive_months(self, data, nodata):
180
228
dtype = "uint8" ,
181
229
)
182
230
183
- # mark nodata
184
- nan_mask = expr_eval (
185
- "where(a==nodata, b, False )" ,
186
- {"a" : t , "b" : nan_mask },
187
- name = "mark_nodata " ,
188
- dtype = "bool " ,
231
+ # total valid
232
+ total = expr_eval (
233
+ "where(a==nodata, b, b+1 )" ,
234
+ {"a" : t , "b" : total },
235
+ name = "get_total_valid " ,
236
+ dtype = "uint8 " ,
189
237
** {"nodata" : nodata },
190
238
)
191
239
192
240
# mark nodata
193
- max_count = expr_eval (
194
- "where(a, nodata, b)" ,
195
- {"a" : nan_mask , "b" : max_count },
196
- name = "mark_nodata" ,
197
- dtype = "uint8" ,
198
- ** {"nodata" : int (nodata )},
199
- )
241
+ if normalize :
242
+ max_count = expr_eval (
243
+ "where(a<=0, nodata, b/a*12)" ,
244
+ {"a" : total , "b" : max_count },
245
+ name = "normalize_max_count" ,
246
+ dtype = "float32" ,
247
+ ** {"nodata" : int (nodata )},
248
+ )
249
+ max_count = da .ceil (max_count ).astype ("uint8" )
250
+ else :
251
+ max_count = expr_eval (
252
+ "where(a<=0, nodata, b)" ,
253
+ {"a" : total , "b" : max_count },
254
+ name = "mark_nodata" ,
255
+ dtype = "uint8" ,
256
+ ** {"nodata" : int (nodata )},
257
+ )
258
+
200
259
return max_count
201
260
202
261
def reduce (self , xx : xr .Dataset ) -> xr .Dataset :
@@ -207,15 +266,15 @@ def reduce(self, xx: xr.Dataset) -> xr.Dataset:
207
266
max_count_veg = self ._max_consecutive_months (data , NODATA )
208
267
209
268
data = self ._water_or_not (xx )
210
- max_count_water = self ._max_consecutive_months (data , NODATA )
269
+ max_count_water = self ._max_consecutive_months (data , NODATA , normalize = True )
211
270
212
271
attrs = xx .attrs .copy ()
213
272
attrs ["nodata" ] = int (NODATA )
214
273
data_vars = {
215
- k : xr .DataArray (v , dims = xx ["wet " ].dims [1 :], attrs = attrs )
274
+ k : xr .DataArray (v , dims = xx ["pv " ].dims [1 :], attrs = attrs )
216
275
for k , v in zip (self .measurements , [max_count_veg , max_count_water ])
217
276
}
218
- coords = dict ((dim , xx .coords [dim ]) for dim in xx ["wet " ].dims [1 :])
277
+ coords = dict ((dim , xx .coords [dim ]) for dim in xx ["pv " ].dims [1 :])
219
278
return xr .Dataset (data_vars = data_vars , coords = coords , attrs = xx .attrs )
220
279
221
280
0 commit comments