Skip to content

Commit

Permalink
Issue #16 Enable black github actions workflow and fixup all code
Browse files Browse the repository at this point in the history
  • Loading branch information
soxofaan committed Jan 22, 2024
1 parent d893c9c commit 4975b74
Show file tree
Hide file tree
Showing 23 changed files with 264 additions and 338 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
python -m pip install isort black ruff
- name: isort
run: python -m isort . --check --diff
# - name: black
# run: python -m black --check --diff .
- name: black
run: python -m black --check --diff .
- name: ruff
run: ruff check .
5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ addopts = [
profile = "black"


[tool.black]
line-length = 99


[tool.ruff]
# line-length = 100

Expand All @@ -76,4 +80,3 @@ ignore = [
"E501", # Ignore "line-too-long" issues, let black handle that.
]


4 changes: 1 addition & 3 deletions src/openeo_gfmap/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,7 @@ def vito_connection(capfd: Optional = None) -> openeo.Connection:

def cdse_connection(capfd: Optional = None) -> openeo.Connection:
"""Performs a connection to the CDSE backend using oidc authentication."""
connection = openeo.connect(
"https://openeo.dataspace.copernicus.eu/openeo/1.2"
)
connection = openeo.connect("https://openeo.dataspace.copernicus.eu/openeo/1.2")
if capfd is not None:
with capfd.disabled():
# Temporarily disable output capturing, to make sure that OIDC device
Expand Down
4 changes: 1 addition & 3 deletions src/openeo_gfmap/extractions/s2.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,7 @@ def s2_l2a_fetch_default(
), "CRS not defined within GeoJSON collection."
spatial_extent = dict(spatial_extent)

cube = connection.load_collection(
collection_name, spatial_extent, temporal_extent, bands
)
cube = connection.load_collection(collection_name, spatial_extent, temporal_extent, bands)

# Apply if the collection is a GeoJSON Feature collection
if isinstance(spatial_extent, GeoJSON):
Expand Down
6 changes: 4 additions & 2 deletions src/openeo_gfmap/fetching/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from .s2 import build_sentinel2_l2a_extractor

__all__ = [
"build_sentinel2_l2a_extractor", "CollectionFetcher", "FetchType",
"build_sentinel1_grd_extractor"
"build_sentinel2_l2a_extractor",
"CollectionFetcher",
"FetchType",
"build_sentinel1_grd_extractor",
]
3 changes: 1 addition & 2 deletions src/openeo_gfmap/fetching/commons.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,7 @@ def load_collection(
pre_mask = params.get("pre_mask", None)
if pre_mask is not None:
assert isinstance(pre_mask, openeo.DataCube), (
f"The 'pre_mask' parameter must be an openeo datacube, "
f"got {pre_mask}."
f"The 'pre_mask' parameter must be an openeo datacube, " f"got {pre_mask}."
)
cube = cube.mask(pre_mask.resample_cube_spatial(cube))

Expand Down
34 changes: 10 additions & 24 deletions src/openeo_gfmap/fetching/s1.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,15 @@ def s1_grd_fetch_default(
return s1_grd_fetch_default


def get_s1_grd_default_processor(
collection_name: str, fetch_type: FetchType
) -> Callable:
def get_s1_grd_default_processor(collection_name: str, fetch_type: FetchType) -> Callable:
"""Builds the preprocessing function from the collection name as it is stored
in the target backend.
"""

def s1_grd_default_processor(cube: openeo.DataCube, **params):
"""Default collection preprocessing method.
This method performs:
* Compute the backscatter of all the S1 products. By default, the
"sigma0-ellipsoid" method is used with "COPERNICUS_30" DEM, but those
can be changed by specifying "coefficient" and "elevation_model" in
Expand All @@ -113,9 +111,7 @@ def s1_grd_default_processor(cube: openeo.DataCube, **params):
)

cube = resample_reproject(
cube,
params.get("target_resolution", 10.0),
params.get("target_crs", None)
cube, params.get("target_resolution", 10.0), params.get("target_crs", None)
)

cube = rename_bands(cube, BASE_SENTINEL1_GRD_MAPPING)
Expand All @@ -127,35 +123,25 @@ def s1_grd_default_processor(cube: openeo.DataCube, **params):

SENTINEL1_GRD_BACKEND_MAP = {
Backend.TERRASCOPE: {
"default": partial(
get_s1_grd_default_fetcher, collection_name="SENTINEL1_GRD"
),
"preprocessor": partial(
get_s1_grd_default_processor, collection_name="SENTINEL1_GRD"
)
"default": partial(get_s1_grd_default_fetcher, collection_name="SENTINEL1_GRD"),
"preprocessor": partial(get_s1_grd_default_processor, collection_name="SENTINEL1_GRD"),
},
Backend.CDSE: {
"default": partial(
get_s1_grd_default_fetcher, collection_name="SENTINEL1_GRD"
),
"preprocessor": partial(
get_s1_grd_default_processor, collection_name="SENTINEL1_GRD"
)
}
"default": partial(get_s1_grd_default_fetcher, collection_name="SENTINEL1_GRD"),
"preprocessor": partial(get_s1_grd_default_processor, collection_name="SENTINEL1_GRD"),
},
}


def build_sentinel1_grd_extractor(
backend_context: BackendContext, bands: list, fetch_type: FetchType, **params
) -> CollectionFetcher:
""" Creates a S1 GRD collection extractor for the given backend."""
"""Creates a S1 GRD collection extractor for the given backend."""
backend_functions = SENTINEL1_GRD_BACKEND_MAP.get(backend_context.backend)

fetcher, preprocessor = (
backend_functions["default"](fetch_type=fetch_type),
backend_functions["preprocessor"](fetch_type=fetch_type),
)

return CollectionFetcher(
backend_context, bands, fetcher, preprocessor, **params
)
return CollectionFetcher(backend_context, bands, fetcher, preprocessor, **params)
16 changes: 4 additions & 12 deletions src/openeo_gfmap/fetching/s2.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,7 @@ def s2_l2a_fetch_default(
return s2_l2a_fetch_default


def get_s2_l2a_element84_fetcher(
collection_name: str, fetch_type: FetchType
) -> Callable:
def get_s2_l2a_element84_fetcher(collection_name: str, fetch_type: FetchType) -> Callable:
"""Fetches the collections from the Sentinel-2 Cloud-Optimized GeoTIFFs
bucket provided by Amazon and managed by Element84.
"""
Expand Down Expand Up @@ -157,9 +155,7 @@ def s2_l2a_element84_fetcher(
return s2_l2a_element84_fetcher


def get_s2_l2a_default_processor(
collection_name: str, fetch_type: FetchType
) -> Callable:
def get_s2_l2a_default_processor(collection_name: str, fetch_type: FetchType) -> Callable:
"""Builds the preprocessing function from the collection name as it stored
in the target backend.
"""
Expand Down Expand Up @@ -188,15 +184,11 @@ def s2_l2a_default_processor(cube: openeo.DataCube, **params):
SENTINEL2_L2A_BACKEND_MAP = {
Backend.TERRASCOPE: {
"fetch": partial(get_s2_l2a_default_fetcher, collection_name="SENTINEL2_L2A"),
"preprocessor": partial(
get_s2_l2a_default_processor, collection_name="SENTINEL2_L2A"
),
"preprocessor": partial(get_s2_l2a_default_processor, collection_name="SENTINEL2_L2A"),
},
Backend.CDSE: {
"fetch": partial(get_s2_l2a_default_fetcher, collection_name="SENTINEL2_L2A"),
"preprocessor": partial(
get_s2_l2a_default_processor, collection_name="SENTINEL2_L2A"
),
"preprocessor": partial(get_s2_l2a_default_processor, collection_name="SENTINEL2_L2A"),
},
}

Expand Down
2 changes: 1 addition & 1 deletion src/openeo_gfmap/preprocessing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@
"get_bap_score",
"get_bap_mask",
"bap_masking",
]
]
73 changes: 36 additions & 37 deletions src/openeo_gfmap/preprocessing/cloudmasking.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@
SCL_HARMONIZED_NAME: str = "S2-SCL"
BAPSCORE_HARMONIZED_NAME: str = "S2-BAPSCORE"


def mask_scl_dilation(cube: openeo.DataCube, **params: dict) -> openeo.DataCube:
"""Creates a mask from the SCL, dilates it and applies the mask to the optical
bands of the datacube. The other bands such as DEM, SAR and METEO will not
be affected by the mask.
"""
# Asserts if the SCL layer exists
assert SCL_HARMONIZED_NAME in cube.metadata.band_names, (
f"The SCL band ({SCL_HARMONIZED_NAME}) is not present in the datacube."
)
assert (
SCL_HARMONIZED_NAME in cube.metadata.band_names
), f"The SCL band ({SCL_HARMONIZED_NAME}) is not present in the datacube."

kernel1_size = params.get("kernel1_size", 17)
kernel2_size = params.get("kernel2_size", 3)
Expand All @@ -28,15 +29,11 @@ def mask_scl_dilation(cube: openeo.DataCube, **params: dict) -> openeo.DataCube:

# Only applies the filtering to the optical part of the cube
optical_cube = cube.filter_bands(
bands=list(
filter(lambda band: band.startswith("S2"), cube.metadata.band_names)
)
bands=list(filter(lambda band: band.startswith("S2"), cube.metadata.band_names))
)

nonoptical_cube = cube.filter_bands(
bands=list(
filter(lambda band: not band.startswith("S2"), cube.metadata.band_names)
)
bands=list(filter(lambda band: not band.startswith("S2"), cube.metadata.band_names))
)

optical_cube = optical_cube.process(
Expand All @@ -47,14 +44,15 @@ def mask_scl_dilation(cube: openeo.DataCube, **params: dict) -> openeo.DataCube:
kernel2_size=kernel2_size,
mask1_values=[2, 4, 5, 6, 7],
mask2_values=[3, 8, 9, 10, 11],
erosion_kernel_size=erosion_kernel_size
erosion_kernel_size=erosion_kernel_size,
)

if len(nonoptical_cube.metadata.band_names) == 0:
return optical_cube

return optical_cube.merge_cubes(nonoptical_cube)


def get_bap_score(cube: openeo.DataCube, **params: dict) -> openeo.DataCube:
"""Calculates the Best Available Pixel (BAP) score for the given datacube,
computed from the SCL layer.
Expand All @@ -70,7 +68,7 @@ def get_bap_score(cube: openeo.DataCube, **params: dict) -> openeo.DataCube:
* Coverage Score: Per date, the percentage of all pixels that are classified
as a cloud over the entire spatial extent is calculated. The Coverage
Score is then equal to 1 - the cloud percentage.
* Date Score: In order to favor pixels that are observed in the middle of a
* Date Score: In order to favor pixels that are observed in the middle of a
month, a date score is calculated, which follows a Gaussian shape. I.e.
the largest scores are given for days in the middle of the month, the
lowest scores are given for days at the beginning and end of the month.
Expand Down Expand Up @@ -113,23 +111,30 @@ def get_bap_score(cube: openeo.DataCube, **params: dict) -> openeo.DataCube:
kernel2_size=kernel2_size,
mask1_values=[2, 4, 5, 6, 7],
mask2_values=[3, 8, 9, 10, 11],
erosion_kernel_size=erosion_kernel_size
erosion_kernel_size=erosion_kernel_size,
)

# Replace NaN to 0 to avoid issues in the UDF
scl_cube = scl_cube.apply(lambda x: if_(is_nan(x), 0, x))

score = scl_cube.apply_neighborhood(
process=openeo.UDF.from_file(str(udf_path)),
size=[{'dimension': 'x', 'unit': 'px', 'value': 256}, {'dimension': 'y', 'unit': 'px', 'value': 256}],
overlap=[{'dimension': 'x', 'unit': 'px', 'value': 16}, {'dimension': 'y', 'unit': 'px', 'value': 16}],
size=[
{"dimension": "x", "unit": "px", "value": 256},
{"dimension": "y", "unit": "px", "value": 256},
],
overlap=[
{"dimension": "x", "unit": "px", "value": 16},
{"dimension": "y", "unit": "px", "value": 16},
],
)

score = score.rename_labels('bands', [BAPSCORE_HARMONIZED_NAME])
score = score.rename_labels("bands", [BAPSCORE_HARMONIZED_NAME])

# Merge the score to the scl cube
return score


def get_bap_mask(cube: openeo.DataCube, period: Union[str, list], **params: dict):
"""Computes the bap score and masks the optical bands of the datacube using
the best scores for each pixel on a given time period. This method both
Expand All @@ -155,13 +160,14 @@ def get_bap_mask(cube: openeo.DataCube, period: Union[str, list], **params: dict
The datacube with the BAP mask applied.
"""
# Checks if the S2-SCL band is present in the datacube
assert SCL_HARMONIZED_NAME in cube.metadata.band_names, (
f"The {SCL_HARMONIZED_NAME} band is not present in the datacube."
)
assert (
SCL_HARMONIZED_NAME in cube.metadata.band_names
), f"The {SCL_HARMONIZED_NAME} band is not present in the datacube."

bap_score = get_bap_score(cube, **params)

if isinstance(period, str):

def max_score_selection(score):
max_score = score.max()
return score.array_apply(lambda x: x != max_score)
Expand All @@ -171,27 +177,26 @@ def max_score_selection(score):
size=[
{"dimension": "x", "unit": "px", "value": 1},
{"dimension": "y", "unit": "px", "value": 1},
{"dimension": "t", "value": period}
{"dimension": "t", "value": period},
],
overlap=[]
overlap=[],
)
elif isinstance(period, list):
udf_path = Path(__file__).parent / "udf_rank.py"
rank_mask = bap_score.apply_neighborhood(
process=openeo.UDF.from_file(
str(udf_path),
context={"intervals": period}
),
process=openeo.UDF.from_file(str(udf_path), context={"intervals": period}),
size=[
{'dimension': 'x', 'unit': 'px', 'value': 256},
{'dimension': 'y', 'unit': 'px', 'value': 256}
{"dimension": "x", "unit": "px", "value": 256},
{"dimension": "y", "unit": "px", "value": 256},
],
overlap=[],
)
else:
raise ValueError(f"'period' must be a string or a list of dates (in YYYY-mm-dd format), got {period}.")
raise ValueError(
f"'period' must be a string or a list of dates (in YYYY-mm-dd format), got {period}."
)

return rank_mask.rename_labels('bands', ['S2-BAPMASK'])
return rank_mask.rename_labels("bands", ["S2-BAPMASK"])


def bap_masking(cube: openeo.DataCube, period: Union[str, list], **params: dict):
Expand All @@ -213,22 +218,16 @@ def bap_masking(cube: openeo.DataCube, period: Union[str, list], **params: dict)
The datacube with the BAP mask applied.
"""
optical_cube = cube.filter_bands(
bands=list(
filter(lambda band: band.startswith("S2"), cube.metadata.band_names)
)
bands=list(filter(lambda band: band.startswith("S2"), cube.metadata.band_names))
)

nonoptical_cube = cube.filter_bands(
bands=list(
filter(lambda band: not band.startswith("S2"), cube.metadata.band_names)
)
bands=list(filter(lambda band: not band.startswith("S2"), cube.metadata.band_names))
)

rank_mask = get_bap_mask(optical_cube, period, **params)

optical_cube = optical_cube.mask(
rank_mask.resample_cube_spatial(cube)
)
optical_cube = optical_cube.mask(rank_mask.resample_cube_spatial(cube))

# Do not merge if bands are empty!
if len(nonoptical_cube.metadata.band_names) == 0:
Expand Down
1 change: 1 addition & 0 deletions src/openeo_gfmap/preprocessing/compositing.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def median_compositing(cube: openeo.DataCube, period: Union[str, list]) -> opene
elif isinstance(period, list):
return cube.aggregate_temporal(intervals=period, reducer="median", dimension="t")


def mean_compositing(cube: openeo.DataCube, period: str) -> openeo.DataCube:
"""Perfrom mean compositing on the given datacube."""
if isinstance(period, str):
Expand Down
8 changes: 4 additions & 4 deletions src/openeo_gfmap/preprocessing/interpolation.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import openeo


def linear_interpolation(cube: openeo.DataCube,) -> openeo.DataCube:
def linear_interpolation(
cube: openeo.DataCube,
) -> openeo.DataCube:
"""Perform linear interpolation on the given datacube."""
return cube.apply_dimension(
dimension="t", process="array_interpolate_linear"
)
return cube.apply_dimension(dimension="t", process="array_interpolate_linear")
Loading

0 comments on commit 4975b74

Please sign in to comment.