Skip to content

Commit d54f9f8

Browse files
Merge pull request #49 from GeoscienceAustralia/duplicate-entry-bugfix-ANAE
Duplicate entry bugfix
2 parents 3ae1623 + 8a44576 commit d54f9f8

11 files changed

Lines changed: 146 additions & 4 deletions

dea_conflux/stack.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import collections
1111
import concurrent.futures
1212
import datetime
13+
from datetime import timedelta
1314
import enum
1415
import logging
1516
import multiprocessing
@@ -189,11 +190,17 @@ def remove_timeseries_with_duplicated(df: pd.DataFrame) -> pd.DataFrame:
189190
df = df.assign(DAY=[e.split("T")[0] for e in df.index])
190191
else:
191192
df = df.assign(DAY=[e.split("T")[0] for e in df["date"]])
192-
193193
df = df.sort_values(["DAY", "pc_missing"], ascending=True)
194194
# The pc_missing the less the better, so we only keep the first one
195195
df = df.drop_duplicates("DAY", keep="first")
196196

197+
# Remove entries within 60s, deals with edge cases where duplicates wrap across midnight UTC
198+
199+
if "date" in df.columns and len(df) > 1:
200+
df['TIMEDIFF'] = pd.to_datetime(df['date'].shift(-1)) - pd.to_datetime(df['date'])
201+
df = df[~(df['TIMEDIFF'] < timedelta(seconds=60))]
202+
df = df.drop(columns=["TIMEDIFF"])
203+
197204
# Remember to remove the temp column day in result_df
198205
return df.drop(columns=["DAY"])
199206

examples/wit_ls9.conflux.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import numpy as np
2+
import xarray as xr
3+
4+
product_name = "wit_ls9"
5+
version = "0.0.1"
6+
resampling = {
7+
"water": "nearest",
8+
"bs": "nearest",
9+
"pv": "nearest",
10+
"npv": "nearest",
11+
"fmask": "nearest",
12+
"*": "bilinear",
13+
}
14+
output_crs = "EPSG:3577"
15+
resolution = (-30, 30)
16+
17+
# load Water Observations, Landsat 9 and Fractional Cover data
18+
input_products = {
19+
"ga_ls_wo_3": ["water"],
20+
"ga_ls9c_ard_3": [
21+
"nbart_blue",
22+
"nbart_green",
23+
"nbart_red",
24+
"nbart_nir",
25+
"nbart_swir_1",
26+
"nbart_swir_2",
27+
],
28+
"ga_ls_fc_3": ["bs", "pv", "npv"],
29+
}
30+
31+
32+
def _tcw(ds: xr.Dataset) -> xr.DataArray:
33+
# Tasseled Cap Wetness, Crist 1985
34+
ds = ds # don't normalise!
35+
return (
36+
0.0315 * ds.nbart_blue
37+
+ 0.2021 * ds.nbart_green
38+
+ 0.3102 * ds.nbart_red
39+
+ 0.1594 * ds.nbart_nir
40+
+ -0.6806 * ds.nbart_swir_1
41+
+ -0.6109 * ds.nbart_swir_2
42+
)
43+
44+
45+
def transform(inputs: xr.Dataset) -> xr.Dataset:
46+
# organize the datas structure
47+
# to apply WIT Notebook processing
48+
# approach
49+
50+
ard_ds = xr.merge([inputs[e] for e in input_products["ga_ls9c_ard_3"]])
51+
wo_ds = xr.merge([inputs[e] for e in input_products["ga_ls_wo_3"]])
52+
fc_ds = xr.merge([inputs[e] for e in input_products["ga_ls_fc_3"]])
53+
54+
tcw = _tcw(ard_ds)
55+
56+
# divide FC values by 100 to keep them in [0, 1]
57+
bs = fc_ds.bs / 100
58+
pv = fc_ds.pv / 100
59+
npv = fc_ds.npv / 100
60+
61+
# generate the WIT raster bands
62+
# create an empty dataset called 'output_rast' and populate with values from input datasets
63+
rast_names = ["pv", "npv", "bs", "wet", "water"]
64+
output_rast = {n: xr.zeros_like(ard_ds) for n in rast_names}
65+
66+
output_rast["bs"] = bs
67+
output_rast["pv"] = pv
68+
output_rast["npv"] = npv
69+
70+
# Mask noncontiguous data, low solar incidence angle, cloud, and water out of the wet category
71+
# by disabling those flags
72+
mask = (wo_ds.water & 0b01100011) == 0
73+
# not apply poly_raster cause we will do it before summarise
74+
75+
open_water = wo_ds.water & (1 << 7) > 0
76+
77+
# Thresholding
78+
# set wet pixels where not masked and above threshold of -350
79+
wet = tcw.where(mask) > -350
80+
81+
# TCW
82+
output_rast["wet"] = wet.astype(float)
83+
for name in rast_names[:3]:
84+
output_rast[name].values[wet.values] = 0
85+
86+
# WO
87+
output_rast["water"] = open_water.astype(float)
88+
89+
for name in rast_names[0:4]:
90+
output_rast[name].values[open_water.values] = 0
91+
92+
# save this mask then can do 90% check in summarise
93+
output_rast["mask"] = (mask).astype(int)
94+
95+
# masking
96+
ds_wit = xr.Dataset(output_rast).where(mask)
97+
98+
return ds_wit
99+
100+
101+
def summarise(inputs: xr.Dataset) -> xr.Dataset:
102+
103+
# calculate percentage missing
104+
pc_missing = 1 - (np.nansum(inputs.mask.values) / len(inputs.mask.values))
105+
# inputs = inputs.where(pc_missing < 0.1)
106+
107+
output = {} # band -> value
108+
output["water"] = inputs.water.mean()
109+
output["wet"] = inputs.wet.mean()
110+
output["bs"] = inputs.bs.mean()
111+
output["pv"] = inputs.pv.mean()
112+
output["npv"] = inputs.npv.mean()
113+
output["pc_missing"] = pc_missing
114+
115+
return xr.Dataset(output)

0 commit comments

Comments
 (0)