@@ -52,102 +52,117 @@ 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
62
63
- # dilate both 'valid' and 'water'
64
- for key , val in self .BAD_BITS_MASK .items ():
65
- if self .cloud_filters .get (key ) is not None :
66
- raw_mask = (xx ["water" ] & val ) > 0
67
- raw_mask = mask_cleanup (
68
- raw_mask , mask_filters = self .cloud_filters .get (key )
69
- )
70
- valid &= ~ raw_mask
71
- wet &= ~ raw_mask
72
-
73
- xx = xx .drop_vars (["water" ])
63
+ # clear dry pixels
64
+ clear = xx ["water" ].data == 0
74
65
75
- # get valid wo pixels, both dry and wet
76
- wet = expr_eval (
66
+ # get " valid" wo pixels, both dry and wet used in veg_frequency
67
+ wet_valid = expr_eval (
77
68
"where(a|b, a, _nan)" ,
78
- {"a" : wet . data , "b" : valid . data },
69
+ {"a" : wet , "b" : valid },
79
70
name = "get_valid_pixels" ,
80
71
dtype = "float32" ,
81
72
** {"_nan" : np .nan },
82
73
)
83
74
84
- # pick all valid fc pixels
85
- xx = keep_good_only (xx , valid , nodata = NODATA )
86
- xx = to_float (xx , dtype = "float32" )
87
-
88
- # get high ue valid pixels
89
- ue = expr_eval (
90
- "where(a>=_v, 1, _nan)" ,
91
- {"a" : xx ["ue" ].data },
92
- name = "get_high_ue" ,
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" ,
93
80
dtype = "float32" ,
94
- ** {
95
- "_v" : self .ue_threshold ,
96
- "_nan" : np .nan ,
97
- },
81
+ ** {"_nan" : np .nan },
98
82
)
99
83
100
- # get low ue valid pixels
84
+ # dilate both 'valid' and 'water'
85
+ for key , val in self .BAD_BITS_MASK .items ():
86
+ if self .cloud_filters .get (key ) is not None :
87
+ raw_mask = (xx ["water" ] & val ) > 0
88
+ raw_mask = mask_cleanup (
89
+ raw_mask , mask_filters = self .cloud_filters .get (key )
90
+ )
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_lear_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
+ )
111
+ xx = xx .drop_vars (["water" ])
112
+
113
+ # Pick out the fc pixels that have an unmixing error of less than the threshold
101
114
valid = expr_eval (
102
- "where(b<_v, 1 , 0)" ,
103
- {"b" : xx ["ue" ].data },
104
- name = "get_valid_pixels " ,
115
+ "where(b<_v, a , 0)" ,
116
+ {"a" : valid , " b" : xx ["ue" ].data },
117
+ name = "get_low_ue " ,
105
118
dtype = "bool" ,
106
119
** {"_v" : self .ue_threshold },
107
120
)
108
121
xx = xx .drop_vars (["ue" ])
109
122
valid = xr .DataArray (valid , dims = xx ["pv" ].dims , coords = xx ["pv" ].coords )
110
- xx = keep_good_only (xx , valid , nodata = np .nan )
111
123
112
- xx ["wet" ] = xr .DataArray (wet , dims = xx ["pv" ].dims , coords = xx ["pv" ].coords )
113
- xx ["ue" ] = xr .DataArray (ue , dims = xx ["pv" ].dims , coords = xx ["pv" ].coords )
124
+ xx = keep_good_only (xx , valid , nodata = NODATA )
125
+ xx = to_float (xx , dtype = "float32" )
126
+
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
+ )
133
+
114
134
return xx
115
135
116
136
def fuser (self , xx ):
117
137
118
- wet = xx ["wet " ]
119
- ue = xx ["ue " ]
138
+ wet_valid = xx ["wet_valid " ]
139
+ wet_clear = xx ["wet_clear " ]
120
140
121
141
xx = _xr_fuse (
122
- xx .drop_vars (["wet " , "ue " ]),
142
+ xx .drop_vars (["wet_valid " , "wet_clear " ]),
123
143
partial (_fuse_mean_np , nodata = np .nan ),
124
- "fuse_fc " ,
144
+ "" ,
125
145
)
126
146
127
- xx ["wet " ] = _nodata_fuser (wet , nodata = np .nan )
128
- xx ["ue " ] = _nodata_fuser (ue , 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 )
129
149
130
150
return xx
131
151
132
152
def _veg_or_not (self , xx : xr .Dataset ):
133
- # pv or npv > bs: 1
153
+ # either pv or npv > bs: 1
134
154
# otherwise 0
135
155
data = expr_eval (
136
156
"where((a>b)|(c>b), 1, 0)" ,
137
- {
138
- "a" : xx ["pv" ].data ,
139
- "c" : xx ["npv" ].data ,
140
- "b" : xx ["bs" ].data ,
141
- },
157
+ {"a" : xx ["pv" ].data , "c" : xx ["npv" ].data , "b" : xx ["bs" ].data },
142
158
name = "get_veg" ,
143
159
dtype = "uint8" ,
144
160
)
145
161
146
- # mark nans only if not valid & low ue
147
- # if any high ue valid (ue is not nan): 0
162
+ # mark nans
148
163
data = expr_eval (
149
- "where(( a!=a)&(c!=c) , nodata, b)" ,
150
- {"a" : xx ["pv" ].data , "c" : xx [ "ue" ]. data , " b" : data },
164
+ "where(a!=a, nodata, b)" ,
165
+ {"a" : xx ["pv" ].data , "b" : data },
151
166
name = "get_veg" ,
152
167
dtype = "uint8" ,
153
168
** {"nodata" : int (NODATA )},
@@ -156,7 +171,7 @@ def _veg_or_not(self, xx: xr.Dataset):
156
171
# mark water freq >= 0.5 as 0
157
172
data = expr_eval (
158
173
"where(a>0, 0, b)" ,
159
- {"a" : xx ["wet " ].data , "b" : data },
174
+ {"a" : xx ["wet_valid " ].data , "b" : data },
160
175
name = "get_veg" ,
161
176
dtype = "uint8" ,
162
177
)
@@ -167,25 +182,25 @@ def _water_or_not(self, xx: xr.Dataset):
167
182
# mark water freq > 0.5 as 1
168
183
data = expr_eval (
169
184
"where(a>0.5, 1, 0)" ,
170
- {"a" : xx ["wet " ].data },
185
+ {"a" : xx ["wet_clear " ].data },
171
186
name = "get_water" ,
172
187
dtype = "uint8" ,
173
188
)
174
189
175
190
# mark nans
176
191
data = expr_eval (
177
192
"where(a!=a, nodata, b)" ,
178
- {"a" : xx ["wet " ].data , "b" : data },
193
+ {"a" : xx ["wet_clear " ].data , "b" : data },
179
194
name = "get_water" ,
180
195
dtype = "uint8" ,
181
196
** {"nodata" : int (NODATA )},
182
197
)
183
198
return data
184
199
185
- def _max_consecutive_months (self , data , nodata ):
186
- nan_mask = da .ones (data .shape [1 :], chunks = data .chunks [1 :], dtype = "bool" )
200
+ def _max_consecutive_months (self , data , nodata , normalize = False ):
187
201
tmp = da .zeros (data .shape [1 :], chunks = data .chunks [1 :], dtype = "uint8" )
188
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" )
189
204
190
205
for t in data :
191
206
# +1 if not nodata
@@ -213,23 +228,34 @@ def _max_consecutive_months(self, data, nodata):
213
228
dtype = "uint8" ,
214
229
)
215
230
216
- # mark nodata
217
- nan_mask = expr_eval (
218
- "where(a==nodata, b, False )" ,
219
- {"a" : t , "b" : nan_mask },
220
- name = "mark_nodata " ,
221
- 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 " ,
222
237
** {"nodata" : nodata },
223
238
)
224
239
225
240
# mark nodata
226
- max_count = expr_eval (
227
- "where(a, nodata, b)" ,
228
- {"a" : nan_mask , "b" : max_count },
229
- name = "mark_nodata" ,
230
- dtype = "uint8" ,
231
- ** {"nodata" : int (nodata )},
232
- )
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
+
233
259
return max_count
234
260
235
261
def reduce (self , xx : xr .Dataset ) -> xr .Dataset :
@@ -240,15 +266,15 @@ def reduce(self, xx: xr.Dataset) -> xr.Dataset:
240
266
max_count_veg = self ._max_consecutive_months (data , NODATA )
241
267
242
268
data = self ._water_or_not (xx )
243
- max_count_water = self ._max_consecutive_months (data , NODATA )
269
+ max_count_water = self ._max_consecutive_months (data , NODATA , normalize = True )
244
270
245
271
attrs = xx .attrs .copy ()
246
272
attrs ["nodata" ] = int (NODATA )
247
273
data_vars = {
248
- k : xr .DataArray (v , dims = xx ["wet " ].dims [1 :], attrs = attrs )
274
+ k : xr .DataArray (v , dims = xx ["pv " ].dims [1 :], attrs = attrs )
249
275
for k , v in zip (self .measurements , [max_count_veg , max_count_water ])
250
276
}
251
- 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 :])
252
278
return xr .Dataset (data_vars = data_vars , coords = coords , attrs = xx .attrs )
253
279
254
280
0 commit comments