Skip to content

Commit

Permalink
Merge 6f5ca2b into 6caeb19
Browse files Browse the repository at this point in the history
  • Loading branch information
rhugonnet committed May 7, 2024
2 parents 6caeb19 + 6f5ca2b commit 2805aa8
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 359 deletions.
11 changes: 0 additions & 11 deletions doc/source/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,17 +244,6 @@ To build and pass your coregistration pipeline to {func}`~xdem.DEM.coregister_3d
xdem.coreg.BiasCorr
```

**Classes for any 1-, 2- and N-D biases:**

```{eval-rst}
.. autosummary::
:toctree: gen_modules/
xdem.coreg.BiasCorr1D
xdem.coreg.BiasCorr2D
xdem.coreg.BiasCorrND
```

**Convenience classes for specific corrections:**

```{eval-rst}
Expand Down
13 changes: 0 additions & 13 deletions doc/source/biascorr.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,16 +158,3 @@ terbias.fit(ref_dem, tba_dem, inlier_mask=inlier_mask)
# Apply the transformation
corrected_dem = terbias.apply(tba_dem)
```

## Generic 1-D, 2-D and N-D classes

All bias-corrections methods are inherited from generic classes that perform corrections in 1-, 2- or N-D. Having these
separate helps the user navigating the dimensionality of the functions, optimizer, binning or variables used.

{class}`xdem.coreg.BiasCorr1D`
{class}`xdem.coreg.BiasCorr2D`
{class}`xdem.coreg.BiasCorrND`

- **Performs:** Correct biases with any function and optimizer, or any binning, in 1-, 2- or N-D.
- **Supports weights** Yes.
- **Recommended for:** Anything.
20 changes: 10 additions & 10 deletions tests/test_coreg/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -568,8 +568,8 @@ def test_pipeline_combinations__nobiasvar(self, coreg1: Coreg, coreg2: Coreg) ->
@pytest.mark.parametrize(
"coreg2",
[
coreg.BiasCorr1D(bias_var_names=["slope"], fit_or_bin="bin"),
coreg.BiasCorr2D(bias_var_names=["slope", "aspect"], fit_or_bin="bin"),
coreg.BiasCorr(bias_var_names=["slope"], fit_or_bin="bin"),
coreg.BiasCorr(bias_var_names=["slope", "aspect"], fit_or_bin="bin"),
],
) # type: ignore
def test_pipeline_combinations__biasvar(self, coreg1: Coreg, coreg2: Coreg) -> None:
Expand All @@ -588,24 +588,24 @@ def test_pipeline_combinations__biasvar(self, coreg1: Coreg, coreg2: Coreg) -> N
def test_pipeline__errors(self) -> None:
"""Test pipeline raises proper errors."""

pipeline = coreg.CoregPipeline([coreg.NuthKaab(), coreg.BiasCorr1D()])
pipeline = coreg.CoregPipeline([coreg.NuthKaab(), coreg.BiasCorr()])
with pytest.raises(
ValueError,
match=re.escape(
"No `bias_vars` passed to .fit() for bias correction step "
"<class 'xdem.coreg.biascorr.BiasCorr1D'> of the pipeline."
"<class 'xdem.coreg.biascorr.BiasCorr'> of the pipeline."
),
):
pipeline.fit(**self.fit_params)

pipeline2 = coreg.CoregPipeline([coreg.NuthKaab(), coreg.BiasCorr1D(), coreg.BiasCorr1D()])
pipeline2 = coreg.CoregPipeline([coreg.NuthKaab(), coreg.BiasCorr(), coreg.BiasCorr()])
with pytest.raises(
ValueError,
match=re.escape(
"No `bias_vars` passed to .fit() for bias correction step <class 'xdem.coreg.biascorr.BiasCorr1D'> "
"No `bias_vars` passed to .fit() for bias correction step <class 'xdem.coreg.biascorr.BiasCorr'> "
"of the pipeline. As you are using several bias correction steps requiring"
" `bias_vars`, don't forget to explicitly define their `bias_var_names` "
"during instantiation, e.g. BiasCorr1D(bias_var_names=['slope'])."
"during instantiation, e.g. BiasCorr(bias_var_names=['slope'])."
),
):
pipeline2.fit(**self.fit_params)
Expand All @@ -615,17 +615,17 @@ def test_pipeline__errors(self) -> None:
match=re.escape(
"When using several bias correction steps requiring `bias_vars` in a pipeline,"
"the `bias_var_names` need to be explicitly defined at each step's "
"instantiation, e.g. BiasCorr1D(bias_var_names=['slope'])."
"instantiation, e.g. BiasCorr(bias_var_names=['slope'])."
),
):
pipeline2.fit(**self.fit_params, bias_vars={"slope": xdem.terrain.slope(self.ref)})

pipeline3 = coreg.CoregPipeline([coreg.NuthKaab(), coreg.BiasCorr1D(bias_var_names=["slope"])])
pipeline3 = coreg.CoregPipeline([coreg.NuthKaab(), coreg.BiasCorr(bias_var_names=["slope"])])
with pytest.raises(
ValueError,
match=re.escape(
"Not all keys of `bias_vars` in .fit() match the `bias_var_names` defined during "
"instantiation of the bias correction step <class 'xdem.coreg.biascorr.BiasCorr1D'>: ['slope']."
"instantiation of the bias correction step <class 'xdem.coreg.biascorr.BiasCorr'>: ['slope']."
),
):
pipeline3.fit(**self.fit_params, bias_vars={"ncc": xdem.terrain.slope(self.ref)})
Expand Down
123 changes: 42 additions & 81 deletions tests/test_coreg/test_biascorr.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,48 @@ def test_biascorr__errors(self) -> None:
):
biascorr.BiasCorr(fit_or_bin="bin", bin_apply_method=1) # type: ignore

# When wrong number of parameters are passed

# Copy fit parameters
fit_args = self.fit_args_rst_rst.copy()
with pytest.raises(
ValueError,
match=re.escape("A number of 1 variable(s) has to be provided through the argument 'bias_vars', " "got 2."),
):
bias_vars_dict = {"elevation": self.ref, "slope": xdem.terrain.slope(self.ref)}
bcorr1d = biascorr.BiasCorr(bias_var_names=["elevation"])
bcorr1d.fit(**fit_args, bias_vars=bias_vars_dict)

with pytest.raises(
ValueError,
match=re.escape("A number of 2 variable(s) has to be provided through the argument " "'bias_vars', got 1."),
):
bias_vars_dict = {"elevation": self.ref}
bcorr2d = biascorr.BiasCorr(bias_var_names=["elevation", "slope"])
bcorr2d.fit(**fit_args, bias_vars=bias_vars_dict)

# When variables don't match
with pytest.raises(
ValueError,
match=re.escape(
"The keys of `bias_vars` do not match the `bias_var_names` defined during " "instantiation: ['ncc']."
),
):
bcorr1d2 = biascorr.BiasCorr(bias_var_names=["ncc"])
bias_vars_dict = {"elevation": self.ref}
bcorr1d2.fit(**fit_args, bias_vars=bias_vars_dict)

with pytest.raises(
ValueError,
match=re.escape(
"The keys of `bias_vars` do not match the `bias_var_names` defined during "
"instantiation: ['elevation', 'ncc']."
),
):
bcorr2d2 = biascorr.BiasCorr(bias_var_names=["elevation", "ncc"])
bias_vars_dict = {"elevation": self.ref, "slope": xdem.terrain.slope(self.ref)}
bcorr2d2.fit(**fit_args, bias_vars=bias_vars_dict)

@pytest.mark.parametrize("fit_args", all_fit_args) # type: ignore
@pytest.mark.parametrize(
"fit_func", ("norder_polynomial", "nfreq_sumsin", lambda x, a, b: x[0] * a + b)
Expand Down Expand Up @@ -354,87 +396,6 @@ def test_biascorr__bin_and_fit_2d(self, fit_args, fit_func, fit_optimizer, bin_s
# Apply the correction
bcorr.apply(elev=self.tba, bias_vars=bias_vars_dict)

@pytest.mark.parametrize("fit_args", [fit_args_rst_pts, fit_args_rst_rst]) # type: ignore
def test_biascorr1d(self, fit_args) -> None:
"""
Test the subclass BiasCorr1D, which defines default parameters for 1D.
The rest is already tested in test_biascorr.
"""

# Try default "fit" parameters instantiation
bcorr1d = biascorr.BiasCorr1D()

assert bcorr1d._meta["fit_func"] == biascorr.fit_workflows["norder_polynomial"]["func"]
assert bcorr1d._meta["fit_optimizer"] == biascorr.fit_workflows["norder_polynomial"]["optimizer"]
assert bcorr1d._needs_vars is True

# Try default "bin" parameter instantiation
bcorr1d = biascorr.BiasCorr1D(fit_or_bin="bin")

assert bcorr1d._meta["bin_sizes"] == 10
assert bcorr1d._meta["bin_statistic"] == np.nanmedian
assert bcorr1d._meta["bin_apply_method"] == "linear"

elev_fit_args = fit_args.copy()
# Raise error when wrong number of parameters are passed
with pytest.raises(
ValueError, match="A single variable has to be provided through the argument 'bias_vars', " "got 2."
):
bias_vars_dict = {"elevation": self.ref, "slope": xdem.terrain.slope(self.ref)}
bcorr1d.fit(**elev_fit_args, bias_vars=bias_vars_dict)

# Raise error when variables don't match
with pytest.raises(
ValueError,
match=re.escape(
"The keys of `bias_vars` do not match the `bias_var_names` defined during " "instantiation: ['ncc']."
),
):
bcorr1d2 = biascorr.BiasCorr1D(bias_var_names=["ncc"])
bias_vars_dict = {"elevation": self.ref}
bcorr1d2.fit(**elev_fit_args, bias_vars=bias_vars_dict)

@pytest.mark.parametrize("fit_args", all_fit_args) # type: ignore
def test_biascorr2d(self, fit_args) -> None:
"""
Test the subclass BiasCorr2D, which defines default parameters for 2D.
The rest is already tested in test_biascorr.
"""

# Try default "fit" parameters instantiation
bcorr2d = biascorr.BiasCorr2D()

assert bcorr2d._meta["fit_func"] == polynomial_2d
assert bcorr2d._meta["fit_optimizer"] == scipy.optimize.curve_fit
assert bcorr2d._needs_vars is True

# Try default "bin" parameter instantiation
bcorr2d = biascorr.BiasCorr2D(fit_or_bin="bin")

assert bcorr2d._meta["bin_sizes"] == 10
assert bcorr2d._meta["bin_statistic"] == np.nanmedian
assert bcorr2d._meta["bin_apply_method"] == "linear"

elev_fit_args = fit_args.copy()
# Raise error when wrong number of parameters are passed
with pytest.raises(
ValueError, match="Exactly two variables have to be provided through the argument " "'bias_vars', got 1."
):
bias_vars_dict = {"elevation": self.ref}
bcorr2d.fit(**elev_fit_args, bias_vars=bias_vars_dict)

# Raise error when variables don't match
with pytest.raises(
ValueError,
match=re.escape(
"The keys of `bias_vars` do not match the `bias_var_names` defined during "
"instantiation: ['elevation', 'ncc']."
),
):
bcorr2d2 = biascorr.BiasCorr2D(bias_var_names=["elevation", "ncc"])
bias_vars_dict = {"elevation": self.ref, "slope": xdem.terrain.slope(self.ref)}
bcorr2d2.fit(**elev_fit_args, bias_vars=bias_vars_dict)

def test_directionalbias(self) -> None:
"""Test the subclass DirectionalBias."""

Expand Down
10 changes: 1 addition & 9 deletions xdem/coreg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,5 @@
apply_matrix,
invert_matrix,
)
from xdem.coreg.biascorr import ( # noqa
BiasCorr,
BiasCorr1D,
BiasCorr2D,
BiasCorrND,
Deramp,
DirectionalBias,
TerrainBias,
)
from xdem.coreg.biascorr import BiasCorr, Deramp, DirectionalBias, TerrainBias # noqa
from xdem.coreg.workflows import dem_coregistration # noqa
1 change: 1 addition & 0 deletions xdem/coreg/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,7 @@ class CoregDict(TypedDict, total=False):
bin_statistic: Callable[[NDArrayf], np.floating[Any]]
bin_apply_method: Literal["linear"] | Literal["per_bin"]
bias_var_names: list[str]
nd: int | None

# 2/ Outputs
fit_params: NDArrayf
Expand Down

0 comments on commit 2805aa8

Please sign in to comment.