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

ENH: add feature to exclude tree crowns from agricultural parcels #156

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
13 changes: 11 additions & 2 deletions cropclassification/calc_cropclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ def run_cropclass(
input_model_to_use_relativepath = conf.calc_marker_params.getpath(
"input_model_to_use_relativepath"
)
erase_layer_filename = conf.calc_marker_params.getpath("erase_layer_filename")

# Prepare input paths
if input_model_to_use_relativepath is not None:
Expand All @@ -138,6 +139,11 @@ def run_cropclass(
refe_dir = conf.paths.getpath("refe_dir")
classes_refe_path = refe_dir / classes_refe_filename

if erase_layer_filename is not None:
erase_layer_path = input_dir / erase_layer_filename
else:
erase_layer_path = None

# Check if the necessary input files exist...
for path in [classes_refe_path, input_parcel_path]:
if path is not None and not path.exists():
Expand All @@ -157,8 +163,9 @@ def run_cropclass(
# -------------------------------------------------------------

# Prepare the input data for optimal image data extraction:
# 1) apply a negative buffer on the parcel to evade mixels
# 2) remove features that became null because of buffer
# 1) exlude erase_layer from agricultural parcels
# 2) apply a negative buffer on the parcel to evade mixels
# 3) remove features that became null because of buffer
input_preprocessed_dir = conf.paths.getpath("input_preprocessed_dir")
buffer = conf.timeseries.getfloat("buffer")
input_parcel_nogeo_path = (
Expand All @@ -174,6 +181,8 @@ def run_cropclass(
input_parcel_path=input_parcel_path,
output_imagedata_parcel_input_path=imagedata_input_parcel_path,
output_parcel_nogeo_path=input_parcel_nogeo_path,
erase_layer_path=erase_layer_path,
classes_refe_path=classes_refe_path,
)

# STEP 2: Get the timeseries data needed for the classification
Expand Down
2 changes: 2 additions & 0 deletions cropclassification/general.ini
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ input_parcel_filename = MUST_OVERRIDE
input_parcel_filetype = MUST_OVERRIDE
# The lookup table (LUT) file where classes to classify to are specified
classes_refe_filename = MUST_OVERRIDE
# The filename of the erase_layer geofile
erase_layer_filename
# A groundtruth file for extra reporting, optional
input_groundtruth_filename
# The model to use if you want to reuse one instead of training one, optional
Expand Down
68 changes: 68 additions & 0 deletions cropclassification/preprocess/_timeseries_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ def prepare_input(
input_parcel_path: Path,
output_imagedata_parcel_input_path: Path,
output_parcel_nogeo_path: Optional[Path] = None,
erase_layer_path: Optional[Path] = None,
classes_refe_path: Optional[Path] = None,
force: bool = False,
) -> bool:
"""Prepare a file so it is ready for timeseries extraction of sentinel images.
Expand All @@ -35,6 +37,8 @@ def prepare_input(
input_parcel_path (Path): input file
output_imagedata_parcel_input_path (Path): prepared output file
output_parcel_nogeo_path (Path): output file with a copy of the non-geo data
erase_layer_path (Optional[Path]): erase layer path
classes_refe_path: (Optional[Path]): classes reference path
force: force creation, even if output file(s) exist already
"""
# Check if parameters are OK and init some extra params
Expand Down Expand Up @@ -102,6 +106,17 @@ def prepare_input(
)
return False

# Exclude erase_layer from agricultural parcels
if erase_layer_path is not None and classes_refe_path is not None:
output_imagedata_parcel_input_path = exclude_erase_layer(
input_parcel_path=input_parcel_path,
output_imagedata_parcel_input_path=output_imagedata_parcel_input_path,
erase_layer_path=erase_layer_path,
classes_refe_path=classes_refe_path,
)
else:
logger.info("No erase layer file found, erase_layer will not be excluded")

# Apply buffer
parceldata_buf_gdf = parceldata_gdf.copy()
# resolution = number of segments per circle
Expand Down Expand Up @@ -629,3 +644,56 @@ def get_fileinfo_timeseries_periods(path: Path) -> dict:
}

return get_fileinfo_timeseries(path)


def exclude_erase_layer(
input_parcel_path: Path,
output_imagedata_parcel_input_path: Path,
classes_refe_path: Path,
erase_layer_path: Path,
) -> Path:
"""This function excludes the erase layer from the agricultural parcels."""
# Read reference data
# Select all parcels where ignore_erase_layer is True
classes_refe_df = pdh.read_file(classes_refe_path)

# Check if ignore_erase_layer_df has columns IGNORE_ERASE_LAYER
if "IGNORE_ERASE_LAYER" not in classes_refe_df.columns:
raise Exception("IGNORE_ERASE_LAYER column not found in classes reference file")

ignore_erase_layer_df = classes_refe_df[classes_refe_df["IGNORE_ERASE_LAYER"] == 1][
"CROPCODE"
]
gewascodes = tuple(map(str, (ignore_erase_layer_df)))

logger.info("Erase layer will be excluded")

# Create temp dir to store temporary data for tracebility
temp_output_dir = output_imagedata_parcel_input_path.parent / "temp"
temp_output_dir.mkdir(parents=True, exist_ok=True)

# Select all parcels where gwscod_h is in gewascodes
sql_stmt = f"""
SELECT *
FROM "{{input_layer}}"
WHERE gwscod_h IN {gewascodes}
"""
gfo.select(
input_path=input_parcel_path,
output_path=temp_output_dir / "ignore_erase_layer.gpkg",
sql_stmt=sql_stmt,
)
# Create erase_layer_prc file by erasing all
# agricultural parcels with "ignore_erase_layer" True from the erase_layer" file
gfo.erase(
input_path=erase_layer_path,
erase_path=temp_output_dir / "ignore_erase_layer.gpkg",
output_path=temp_output_dir / "erase_layer_prc.gpkg",
)
# Erase the erase_layer_prc from the input agricultural parcels
gfo.erase(
input_path=input_parcel_path,
erase_path=temp_output_dir / "erase_layer_prc.gpkg",
output_path=output_imagedata_parcel_input_path,
)
return output_imagedata_parcel_input_path
Binary file modified markers/_inputdata/Prc_BEFL_2023_2023-07-24.gpkg
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ UID HOOFDTEELT_CTRL_COD HOOFDTEELT_CTRL_COD_ORIG HOOFDTEELT_CTRL_NAM_ORIG GESP_P
000028105F1645A400000001 91 91 Suikerbieten
000028104AD7C78000000007 60 60 Grasland MAA BG
000028104AD7CB7600000004 60 60 Grasland MAA BG
000028104C44425B00000001 60 60 Grasland MAA BG
000028104C44425B00000001 8915 8915 Bebossing
000028104C4440C700000001 60 60 Grasland MAA BG
00002810694D925500000002 60 60 Grasland MAA BG
000028104AD7C7E500000001 60 60 Grasland MAA BG
Expand Down
Binary file not shown.
Loading
Loading