Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace print statements and verbose flag with logging and log levels #610

Merged
merged 4 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions doc/source/logging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
file_format: mystnb
jupytext:
formats: md:myst
text_representation:
extension: .md
format_name: myst
kernelspec:
display_name: xdem-env
language: python
name: xdem
---
# Logging configuration
To configure logging for xDEM, you can utilize Python's built-in `logging` module. Begin by setting up the logging
configuration at the start. This involves specifying the logging level, format, and handlers. For example :
```python
import logging

# Configuration
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
handlers=[
logging.FileHandler('app.log'), # Log messages will be saved to this file
logging.StreamHandler() # Log messages will also be printed to the console
])
```
This configuration will log messages with a severity level of `INFO` and above, including timestamps, logger names, and
log levels in the output. You can change the logging level to `DEBUG`, `WARNING`, `ERROR` or `CRITICAL` as needed.
47 changes: 47 additions & 0 deletions examples/basic/logging_configuration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""
Configuring Logging in xDEM
===============================

This example demonstrates how to configure logging in xDEM by using the Nuth and Kääb
(`2011 <https:https://doi.org/10.5194/tc-5-271-2011>`_) coregistration method.
Logging can be customized to various levels, from `DEBUG` for detailed diagnostic output, to `INFO` for general
updates, `WARNING` for potential issues, and `ERROR` or `CRITICAL` for serious problems.

We will demonstrate how to set up logging and show logs while running a typical xDEM function.
"""

import logging

import xdem

# Step 1: Configure logging
logging.basicConfig(
level=logging.INFO, # Change this level to DEBUG or WARNING to see different outputs.
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
handlers=[
logging.FileHandler("../xdem_example.log"), # Save logs to a file
logging.StreamHandler(), # Print logs to the console
],
)

# Step 2: Load example DEMs (Digital Elevation Models) to work with
reference_dem = xdem.DEM(xdem.examples.get_path("longyearbyen_ref_dem"))
dem_to_be_aligned = xdem.DEM(xdem.examples.get_path("longyearbyen_tba_dem"))

# Step 3: Perform Nuth & Kaab coregistration (for alignment of DEMs)
coreg = xdem.coreg.NuthKaab()

# Step 4: Demonstrate logging at various levels
logging.info("Starting Nuth & Kaab coregistration...")
coreg.fit(reference_dem, dem_to_be_aligned)
logging.debug("Coregistration successful. Applying transformation...")

# Apply the coregistration
aligned_dem = coreg.apply(dem_to_be_aligned)

# Output some results
logging.info(f"Coregistration completed. Aligned DEM shape: {aligned_dem.shape}")

# Displaying final logs
logging.debug("Aligned DEM has been computed and saved.")
12 changes: 12 additions & 0 deletions examples/basic/temp.tif.aux.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<PAMDataset>
<PAMRasterBand band="1">
<Metadata>
<MDI key="STATISTICS_APPROXIMATE">YES</MDI>
<MDI key="STATISTICS_MAXIMUM">24.196350097656</MDI>
<MDI key="STATISTICS_MEAN">-1.1318853018028</MDI>
<MDI key="STATISTICS_MINIMUM">-45.626251220703</MDI>
<MDI key="STATISTICS_STDDEV">5.4521095015072</MDI>
<MDI key="STATISTICS_VALID_PERCENT">100</MDI>
</Metadata>
</PAMRasterBand>
</PAMDataset>
35 changes: 9 additions & 26 deletions tests/test_coreg/test_affine.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,32 +96,17 @@ class TestAffineCoreg:
# Check all point-raster possibilities supported
# Use the reference DEM for both, it will be artificially misaligned during tests
# Raster-Raster
fit_args_rst_rst = dict(
reference_elev=ref,
to_be_aligned_elev=tba,
inlier_mask=inlier_mask,
verbose=True,
)
fit_args_rst_rst = dict(reference_elev=ref, to_be_aligned_elev=tba, inlier_mask=inlier_mask)

# Convert DEMs to points with a bit of subsampling for speed-up
ref_pts = ref.to_pointcloud(data_column_name="z", subsample=50000, random_state=42).ds
tba_pts = ref.to_pointcloud(data_column_name="z", subsample=50000, random_state=42).ds

# Raster-Point
fit_args_rst_pts = dict(
reference_elev=ref,
to_be_aligned_elev=tba_pts,
inlier_mask=inlier_mask,
verbose=True,
)
fit_args_rst_pts = dict(reference_elev=ref, to_be_aligned_elev=tba_pts, inlier_mask=inlier_mask)

# Point-Raster
fit_args_pts_rst = dict(
reference_elev=ref_pts,
to_be_aligned_elev=tba,
inlier_mask=inlier_mask,
verbose=True,
)
fit_args_pts_rst = dict(reference_elev=ref_pts, to_be_aligned_elev=tba, inlier_mask=inlier_mask)

all_fit_args = [fit_args_rst_rst, fit_args_rst_pts, fit_args_pts_rst]

Expand Down Expand Up @@ -289,7 +274,7 @@ def test_coreg_translations__synthetic(self, fit_args, shifts, coreg_method) ->
],
) # type: ignore
def test_coreg_translations__example(
self, coreg_method__shift: tuple[type[AffineCoreg], tuple[float, float, float]], verbose: bool = False
self, coreg_method__shift: tuple[type[AffineCoreg], tuple[float, float, float]]
) -> None:
"""
Test that the translation co-registration outputs are always exactly the same on the real example data.
Expand All @@ -303,7 +288,7 @@ def test_coreg_translations__example(
coreg_method, expected_shifts = coreg_method__shift

c = coreg_method(subsample=50000)
c.fit(ref, tba, inlier_mask=inlier_mask, verbose=verbose, random_state=42)
c.fit(ref, tba, inlier_mask=inlier_mask, random_state=42)

# Check the output translations match the exact values
shifts = [c.meta["outputs"]["affine"][k] for k in ["shift_x", "shift_y", "shift_z"]] # type: ignore
Expand Down Expand Up @@ -367,7 +352,7 @@ def test_coreg_vertical_translation__synthetic(self, fit_args, vshift) -> None:

@pytest.mark.parametrize("coreg_method__vshift", [(coreg.VerticalShift, -2.305015)]) # type: ignore
def test_coreg_vertical_translation__example(
self, coreg_method__vshift: tuple[type[AffineCoreg], tuple[float, float, float]], verbose: bool = False
self, coreg_method__vshift: tuple[type[AffineCoreg], tuple[float, float, float]]
) -> None:
"""
Test that the vertical translation co-registration output is always exactly the same on the real example data.
Expand All @@ -382,7 +367,7 @@ def test_coreg_vertical_translation__example(

# Run co-registration
c = coreg_method(subsample=50000)
c.fit(ref, tba, inlier_mask=inlier_mask, verbose=verbose, random_state=42)
c.fit(ref, tba, inlier_mask=inlier_mask, random_state=42)

# Check the output translations match the exact values
vshift = c.meta["outputs"]["affine"]["shift_z"]
Expand Down Expand Up @@ -479,9 +464,7 @@ def test_coreg_rigid__synthetic(self, fit_args, shifts_rotations, coreg_method)
[(coreg.ICP, (8.738332, 1.584255, -1.943957, 0.0069004, -0.00703, -0.0119733))],
) # type: ignore
def test_coreg_rigid__example(
self,
coreg_method__shifts_rotations: tuple[type[AffineCoreg], tuple[float, float, float]],
verbose: bool = False,
self, coreg_method__shifts_rotations: tuple[type[AffineCoreg], tuple[float, float, float]]
) -> None:
"""
Test that the rigid co-registration outputs is always exactly the same on the real example data.
Expand All @@ -496,7 +479,7 @@ def test_coreg_rigid__example(

# Run co-registration
c = coreg_method(subsample=50000)
c.fit(ref, tba, inlier_mask=inlier_mask, verbose=verbose, random_state=42)
c.fit(ref, tba, inlier_mask=inlier_mask, random_state=42)

# Check the output translations match the exact values
fit_matrix = c.meta["outputs"]["affine"]["matrix"]
Expand Down
11 changes: 2 additions & 9 deletions tests/test_coreg/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,7 @@ class TestCoregClass:
ref, tba, outlines = load_examples() # Load example reference, to-be-aligned and mask.
inlier_mask = ~outlines.create_mask(ref)

fit_params = dict(
reference_elev=ref,
to_be_aligned_elev=tba,
inlier_mask=inlier_mask,
verbose=False,
)
fit_params = dict(reference_elev=ref, to_be_aligned_elev=tba, inlier_mask=inlier_mask)
# Create some 3D coordinates with Z coordinates being 0 to try the apply functions.
points_arr = np.array([[1, 2, 3, 4], [1, 2, 3, 4], [0, 0, 0, 0]], dtype="float64").T
points = gpd.GeoDataFrame(
Expand Down Expand Up @@ -131,7 +126,7 @@ def recursive_typeddict_items(typed_dict: Mapping[str, Any]) -> Iterable[str]:

# Check that info() contains the mapped string for an example
c = coreg.Coreg(meta={"subsample": 10000})
assert dict_key_to_str["subsample"] in c.info(verbose=False)
assert dict_key_to_str["subsample"] in c.info()

@pytest.mark.parametrize("coreg_class", [coreg.VerticalShift, coreg.ICP, coreg.NuthKaab]) # type: ignore
def test_copy(self, coreg_class: Callable[[], Coreg]) -> None:
Expand Down Expand Up @@ -629,7 +624,6 @@ class TestCoregPipeline:
inlier_mask=inlier_mask,
transform=ref.transform,
crs=ref.crs,
verbose=True,
)
# Create some 3D coordinates with Z coordinates being 0 to try the apply functions.
points_arr = np.array([[1, 2, 3, 4], [1, 2, 3, 4], [0, 0, 0, 0]], dtype="float64").T
Expand Down Expand Up @@ -860,7 +854,6 @@ class TestBlockwiseCoreg:
inlier_mask=inlier_mask,
transform=ref.transform,
crs=ref.crs,
verbose=False,
)
# Create some 3D coordinates with Z coordinates being 0 to try the apply functions.
points_arr = np.array([[1, 2, 3, 4], [1, 2, 3, 4], [0, 0, 0, 0]], dtype="float64").T
Expand Down
21 changes: 3 additions & 18 deletions tests/test_coreg/test_biascorr.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,32 +45,17 @@ class TestBiasCorr:

# Check all possibilities supported by biascorr:
# Raster-Raster
fit_args_rst_rst = dict(
reference_elev=ref,
to_be_aligned_elev=tba,
inlier_mask=inlier_mask,
verbose=True,
)
fit_args_rst_rst = dict(reference_elev=ref, to_be_aligned_elev=tba, inlier_mask=inlier_mask)

# Convert DEMs to points with a bit of subsampling for speed-up
tba_pts = tba.to_pointcloud(data_column_name="z", subsample=50000, random_state=42).ds
ref_pts = ref.to_pointcloud(data_column_name="z", subsample=50000, random_state=42).ds

# Raster-Point
fit_args_rst_pts = dict(
reference_elev=ref,
to_be_aligned_elev=tba_pts,
inlier_mask=inlier_mask,
verbose=True,
)
fit_args_rst_pts = dict(reference_elev=ref, to_be_aligned_elev=tba_pts, inlier_mask=inlier_mask)

# Point-Raster
fit_args_pts_rst = dict(
reference_elev=ref_pts,
to_be_aligned_elev=tba,
inlier_mask=inlier_mask,
verbose=True,
)
fit_args_pts_rst = dict(reference_elev=ref_pts, to_be_aligned_elev=tba, inlier_mask=inlier_mask)

all_fit_args = [fit_args_rst_rst, fit_args_rst_pts, fit_args_pts_rst]

Expand Down
2 changes: 1 addition & 1 deletion tests/test_demcollection.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def test_init(self) -> None:
if "NaNs found in dDEM" not in str(exception):
raise exception

# print(cumulative_dh)
# logging.info(cumulative_dh)

# raise NotImplementedError

Expand Down
3 changes: 2 additions & 1 deletion tests/test_doc.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Functions to test the documentation."""
import logging
import os
import platform
import shutil
Expand Down Expand Up @@ -34,7 +35,7 @@ def run_code(filename: str) -> None:
exec(infile.read().replace("plt.show()", "plt.close()"))
except Exception as exception:
if isinstance(exception, DeprecationWarning):
print(exception)
logging.warning(exception)
else:
raise RuntimeError(f"Failed on {filename}") from exception

Expand Down
8 changes: 4 additions & 4 deletions tests/test_spatialstats.py
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ def test_sample_multirange_variogram_default(self) -> None:
# Check that all type of coordinate inputs work
# Only the array and the ground sampling distance
xdem.spatialstats.sample_empirical_variogram(
values=self.diff.data, gsd=self.diff.res[0], subsample=10, random_state=42, verbose=True
values=self.diff.data, gsd=self.diff.res[0], subsample=10, random_state=42
)

# Test multiple runs
Expand Down Expand Up @@ -583,7 +583,7 @@ def test_sample_empirical_variogram_speed(self) -> None:
# Shape
shape = values.shape

keyword_arguments = {"subsample": subsample, "extent": extent, "shape": shape, "verbose": False}
keyword_arguments = {"subsample": subsample, "extent": extent, "shape": shape}
runs, samples, ratio_subsample = xdem.spatialstats._choose_cdist_equidistant_sampling_parameters(
**keyword_arguments
)
Expand Down Expand Up @@ -720,7 +720,7 @@ def test_choose_cdist_equidistant_sampling_parameters(self, subsample: int, shap
pdist_pairwise_combinations = subsample**2 / 2

# Run the function
keyword_arguments = {"subsample": subsample, "extent": extent, "shape": shape, "verbose": False}
keyword_arguments = {"subsample": subsample, "extent": extent, "shape": shape}
runs, samples, ratio_subsample = xdem.spatialstats._choose_cdist_equidistant_sampling_parameters(
**keyword_arguments
)
Expand All @@ -745,7 +745,7 @@ def test_choose_cdist_equidistant_sampling_parameters(self, subsample: int, shap
def test_errors_subsample_parameter(self) -> None:
"""Tests that an error is raised when the subsample argument is too little"""

keyword_arguments = {"subsample": 3, "extent": (0, 1, 0, 1), "shape": (10, 10), "verbose": False}
keyword_arguments = {"subsample": 3, "extent": (0, 1, 0, 1), "shape": (10, 10)}

with pytest.raises(ValueError, match="The number of subsamples needs to be at least 10."):
xdem.spatialstats._choose_cdist_equidistant_sampling_parameters(**keyword_arguments)
Expand Down
Loading