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

Add validation rules for input rasters #111

Merged
merged 29 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
c359d87
Change output file format enum values
soaressgabriel Feb 15, 2024
dfcfb6d
Refactor raster data rules enum
soaressgabriel Feb 15, 2024
9c3d99b
Add raster map validation handlers
soaressgabriel Feb 15, 2024
5f4aed2
Merge branch 'main' into feat/109-add-validation-rules-for-input-rasters
soaressgabriel Feb 19, 2024
d4ad9fc
Update RasterMapValidator and handler methods
soaressgabriel Feb 20, 2024
f76e5b0
Update input file formats docs
soaressgabriel Feb 20, 2024
7bbdea5
Update fileformats and userguide docs
soaressgabriel Feb 20, 2024
9bf1565
Remove VS project files
soaressgabriel Feb 20, 2024
9682571
Update userguide and fileformats doc pages
soaressgabriel Feb 21, 2024
32eabb1
Update output file format constants
soaressgabriel Feb 22, 2024
50059d8
Add unit tests for AllOnesValidatorHandler
soaressgabriel Feb 22, 2024
680a9e9
Add unit tests for AllZeroesValidatorHandler
soaressgabriel Feb 22, 2024
d20313e
Add unit tests for NoDataValidatorHandler
soaressgabriel Feb 22, 2024
36f1b1b
Add unit tests for ValueRangeValidatorHandler
soaressgabriel Feb 22, 2024
e947e1d
Fix `ModelConfiguration` unit tests
soaressgabriel Feb 22, 2024
9944ef7
Add unit tests for RasterMap class
soaressgabriel Feb 22, 2024
efc0bb0
Add unit tests for RasterBand class
soaressgabriel Feb 22, 2024
81f7234
Add Dependabot configuration for GitHub Actions
soaressgabriel Feb 22, 2024
fad1ddf
Add unit test workflow using Micromamba
soaressgabriel Feb 22, 2024
98a574b
Update build-test-micromamba.yml
soaressgabriel Feb 22, 2024
2648a46
Update environment files
soaressgabriel Feb 22, 2024
2ff053d
Add shell command to run pytest with bash
soaressgabriel Feb 22, 2024
e728ba7
Remove unused test_pcraster_map.py file
soaressgabriel Feb 22, 2024
3831e52
Refactor output directory validation and error handling
soaressgabriel Feb 22, 2024
ef3ea0e
Refactor unit tests for model configuration and raster map classes
soaressgabriel Feb 22, 2024
5948dca
Fix mocker.patch syntax error in test_raster_map.py
soaressgabriel Feb 22, 2024
7e76f47
Fix `TestOutputDataDirectory` tests
soaressgabriel Feb 22, 2024
a4e8ea0
Fix unit test for string representation of `OutputDataDirectory`
soaressgabriel Feb 22, 2024
5639e2a
Merge branch 'main' into feat/109-add-validation-rules-for-input-rasters
soaressgabriel Feb 22, 2024
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
4 changes: 2 additions & 2 deletions rubem/_dynamic_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def __stepReport(self):
continue

# Export TIFF raster series
if self.config.output_variables.file_format is OutputFileFormat.GeoTIFF:
if self.config.output_variables.file_format is OutputFileFormat.GEOTIFF:
reportTIFFSeries(
self,
self.ref,
Expand All @@ -101,7 +101,7 @@ def __stepReport(self):
)

# Export PCRaster map format raster series
if self.config.output_variables.file_format is OutputFileFormat.PCRaster:
if self.config.output_variables.file_format is OutputFileFormat.PCRASTER:
self.report(self.outputVarsDict.get(outputVar), outputVar)

# Check if we have to export the time series of the selected
Expand Down
51 changes: 41 additions & 10 deletions rubem/configuration/input_raster_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
import os
from typing import Union

from rubem.configuration.pcraster_map import PCRasterMap
from rubem.configuration.raster_map import RasterMap
from rubem.configuration.data_ranges_settings import DataRangesSettings
from rubem.validation.raster_data import RasterMapValidator
from rubem.validation.raster_data_rules import RasterDataRules


class InputRasterFiles:
Expand Down Expand Up @@ -67,17 +69,46 @@ def __init__(

def __validate_files(self) -> None:
files = [
(self.dem, self.__ranges.rasters["dem"]),
(self.demtif, self.__ranges.rasters["dem"]),
(self.clone, self.__ranges.rasters["clone"]),
(self.ndvi_max, self.__ranges.rasters["ndvi"]),
(self.ndvi_min, self.__ranges.rasters["ndvi"]),
(self.soil, self.__ranges.rasters["soil"]),
(self.sample_locations, self.__ranges.rasters["sample_locations"]),
(
self.dem,
self.__ranges.rasters["dem"],
RasterDataRules.FORBID_NO_DATA
| RasterDataRules.FORBID_ALL_ZEROES
| RasterDataRules.FORBID_ALL_ONES,
),
(
self.demtif,
self.__ranges.rasters["dem"],
RasterDataRules.FORBID_NO_DATA
| RasterDataRules.FORBID_ALL_ZEROES
| RasterDataRules.FORBID_ALL_ONES,
),
(self.clone, self.__ranges.rasters["clone"], RasterDataRules.FORBID_ALL_ZEROES),
(self.ndvi_max, self.__ranges.rasters["ndvi"], RasterDataRules.FORBID_NO_DATA),
(self.ndvi_min, self.__ranges.rasters["ndvi"], RasterDataRules.FORBID_NO_DATA),
(
self.soil,
self.__ranges.rasters["soil"],
RasterDataRules.FORBID_NO_DATA | RasterDataRules.FORBID_ALL_ZEROES,
),
(
self.sample_locations,
self.__ranges.rasters["sample_locations"],
RasterDataRules.FORBID_ALL_ZEROES | RasterDataRules.FORBID_ALL_ONES,
),
]

for file, valid_range in files:
_ = PCRasterMap(file, valid_range)
for file, valid_range, rules in files:
raster = RasterMap(file, valid_range, rules)
self.logger.debug(str(raster).replace("\n", ", "))

validator = RasterMapValidator()
if not validator.validate(raster):
self.logger.error(
"Raster file '%s' contains invalid data. This may lead to unexpected results.",
file,
)
raise ValueError(f"Raster file '{file}' contains invalid data.")

def __str__(self) -> str:
return (
Expand Down
48 changes: 38 additions & 10 deletions rubem/configuration/input_raster_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
from typing import Union
import re

from rubem.configuration.pcraster_map import PCRasterMap
from rubem.configuration.raster_map import RasterMap
from rubem.configuration.data_ranges_settings import DataRangesSettings
from rubem.validation.raster_data import RasterMapValidator
from rubem.validation.raster_data_rules import RasterDataRules

RASTER_SERIES_FILENAME_MAX_CHARS = 8
RASTER_SERIES_FILENAME_EXTENSION_NUM_DIGITS = 3
Expand Down Expand Up @@ -95,23 +97,40 @@ def __init__(

def __validate_directories(self) -> None:
directories = [
(self.__etp_dir_path, self.__etp_filename_prefix, self.__ranges.rasters["etp"]),
(
self.__etp_dir_path,
self.__etp_filename_prefix,
self.__ranges.rasters["etp"],
RasterDataRules.FORBID_NO_DATA,
),
(
self.__precipitation_dir_path,
self.__precipitation_filename_prefix,
self.__ranges.rasters["precipitation"],
RasterDataRules.FORBID_NO_DATA,
),
(
self.__ndvi_dir_path,
self.__ndvi_filename_prefix,
self.__ranges.rasters["ndvi"],
RasterDataRules.FORBID_NO_DATA,
),
(
self.__kp_dir_path,
self.__kp_filename_prefix,
self.__ranges.rasters["kp"],
RasterDataRules.FORBID_NO_DATA,
),
(self.__ndvi_dir_path, self.__ndvi_filename_prefix, self.__ranges.rasters["ndvi"]),
(self.__kp_dir_path, self.__kp_filename_prefix, self.__ranges.rasters["kp"]),
(
self.__landuse_dir_path,
self.__landuse_filename_prefix,
self.__ranges.rasters["landuse"],
RasterDataRules.FORBID_NO_DATA | RasterDataRules.FORBID_ALL_ZEROES,
),
]

total_num_files = []
for directory, prefix, valid_range in directories:
for directory, prefix, valid_range, rules in directories:
if not os.path.isdir(directory):
raise NotADirectoryError(f"Invalid input data directory: {directory}")

Expand All @@ -120,7 +139,7 @@ def __validate_directories(self) -> None:

self.__validate_raster_series_filenames_prefixes(prefix)
total_num_files.append(
self.__validate_files_with_prefix(directory, prefix, valid_range)
self.__validate_files_with_prefix(directory, prefix, valid_range, rules)
)

common_total_num_files = set(total_num_files)
Expand All @@ -130,7 +149,7 @@ def __validate_directories(self) -> None:
"This may lead to unexpected results."
)

def __validate_files_with_prefix(self, directory, prefix, valid_range) -> int:
def __validate_files_with_prefix(self, directory, prefix, valid_range, rules) -> int:
num_digits = RASTER_SERIES_FILENAME_MAX_CHARS - len(prefix)
regex_pattern = rf"^{prefix}[0-9]{{{num_digits}}}\.[0-9]{{{RASTER_SERIES_FILENAME_EXTENSION_NUM_DIGITS}}}$"
compiled_pattern = re.compile(regex_pattern, re.IGNORECASE)
Expand All @@ -139,7 +158,7 @@ def __validate_files_with_prefix(self, directory, prefix, valid_range) -> int:
with os.scandir(directory) as it:
for entry in it:
if entry.is_file() and compiled_pattern.match(entry.name):
self.__validate_raster_file(entry.path, valid_range)
self.__validate_raster_file(entry.path, valid_range, rules)
counter += 1

if counter == 0:
Expand All @@ -159,8 +178,17 @@ def __validate_files_with_prefix(self, directory, prefix, valid_range) -> int:

return counter

def __validate_raster_file(self, file, valid_range) -> None:
_ = PCRasterMap(file, valid_range)
def __validate_raster_file(self, file, valid_range, rules) -> None:
raster = RasterMap(file, valid_range, rules)
self.logger.debug(str(raster).replace("\n", ", "))

validator = RasterMapValidator()
if not validator.validate(raster):
self.logger.error(
"Raster file '%s' contains invalid data. This may lead to unexpected results.",
file,
)
raise ValueError(f"Raster file '{file}' contains invalid data.")

def __validate_raster_series_filenames_prefixes(self, prefix):
num_digits = RASTER_SERIES_FILENAME_MAX_CHARS - len(prefix)
Expand Down
4 changes: 2 additions & 2 deletions rubem/configuration/model_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@ def __init__(
rnf=bool(self.__get_setting("GENERATE_FILE", "rnf")),
tss=bool(self.__get_setting("GENERATE_FILE", "tss")),
output_format=(
OutputFileFormat.PCRaster
OutputFileFormat.PCRASTER
if bool(self.__get_setting("RASTER_FILE_FORMAT", "map_raster_series"))
else OutputFileFormat.GeoTIFF
else OutputFileFormat.GEOTIFF
),
)
self.raster_series = InputRasterSeries(
Expand Down
6 changes: 3 additions & 3 deletions rubem/configuration/output_format.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from enum import Enum
from enum import Enum, auto


class OutputFileFormat(Enum):
"""
Enum class representing the output file format options.
"""

PCRaster = 1
GeoTIFF = 2
PCRASTER = auto()
GEOTIFF = auto()
4 changes: 2 additions & 2 deletions rubem/configuration/output_variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class OutputVariables:
:param tss: Enable or disable Create time output time series (TSS). Defaults to `False`.
:type tss: bool, optional

:param output_format: The output file format. Defaults to `OutputFileFormat.PCRaster`.
:param output_format: The output file format. Defaults to ``OutputFileFormat.PCRASTER``.
:type output_format: OutputFileFormat, optional
"""

Expand All @@ -49,7 +49,7 @@ def __init__(
smc: bool = False,
rnf: bool = False,
tss: bool = False,
output_format: OutputFileFormat = OutputFileFormat.PCRaster,
output_format: OutputFileFormat = OutputFileFormat.PCRASTER,
) -> None:
self.logger = logging.getLogger(__name__)
self.itp = itp
Expand Down
Loading