Closed ferreirav closed 7 months ago
Hello Vitor,
thank you for using pylandstats. CORE metrics are not currently implemented but I can definetly add them for the release v3.0.0. I could have a first implementation ready by the beginning of next week, would that work for you?
Best, Martí
Hi Marti,
Yes that would be great!! In the meanwhile I have used some bespoke function that interact with pylandstats.
def remove_edges(binary_arr):
"""
Remove edge cells from a binary array.
https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.convolve.html
This function uses a convolution with a 4-neighbor kernel to identify and remove
isolated cells (i.e., cells that don't have any neighbors).
Parameters
----------
binary_arr : numpy.ndarray
A 2D binary array with 1s representing the target cells/habitat type and 0s representing background.
Returns
-------
numpy.ndarray
A binary array with edges removed removed.
Notes
-----
An isolated cell is identified when the convolution of the cell and its neighbors
results in a value of 5 (itself and four 0s as its neighbors).
"""
# Define the 4-neighbor kernel
kernel = [[0, 1, 0],
[1, 1, 1],
[0, 1, 0]]
# Convolve the binary array with the kernel
convolved = ndimage.convolve(binary_arr, kernel, mode='constant', cval=0)
# Return a new binary array: 1 if the convolution result is 5, 0 otherwise
return (convolved == 5).astype(int)
def remove_edges_labeled(landscape_arr):
"""
Remove the edges from the patches from a labeled landscape array.
https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.label.html
This function generates a binary mask from the array with habitat type labels,
removes the edges using the `remove_edges` function and then returns the patch labels
to the landscape array.
Parameters
----------
landscape_arr : numpy.ndarray
A 2D array where each unique integer (greater than 0) represents a labeled patch
in the landscape. 0s represent the background or no-data values.
Returns
-------
numpy.ndarray
A labeled array with edge cells removed.
"""
# Generate a binary mask of the landscape array
binary_mask = (landscape_arr[0] > 0).astype(int)
# Remove edge cells from the binary mask
cleaned_mask = remove_edges(binary_mask)
# Return the patch labels to the new core patch array
new_core_patch_array = landscape_arr[0] * cleaned_mask
# Return a labels features array
core_patches_array, _ = ndimage.label(new_core_patch_array)
return core_patches_array
def calculate_cpland(landscape_path):
"""
Calculate the CORE proportion of the landscape (CPLAND) for each class after removing
the edges. The Core Area metric is calculated after removing the edges of each patch.
A cell is defined as core if it has no neighbor with the different value than itself.
Parameters
----------
landscape_path : str
Path to the landscape GeoTiff file to be analyzed.
Returns
-------
pandas.DataFrame
A DataFrame with class values and their CPLAND proportions.
"""
# Load the landscape
dyn_landcover = pls.Landscape(landscape_path)
# Calculate the area for every class value
class_values = dyn_landcover.classes
patch_arrays = [dyn_landcover.class_label(class_val) for class_val in class_values]
core_patch_array = [remove_edges_labeled(arr) for arr in patch_arrays]
# Calculate patch areas and sum them up
areas = [dyn_landcover.compute_patch_areas(arr) for arr in core_patch_array]
summed_areas = [np.sum(arr) for arr in areas]
# Calculate the proportion of landscape for each class
coreland_proportions = [((area / dyn_landcover.landscape_area) * 100) for area in summed_areas]
# Create a DataFrame with the results
results_df = pd.DataFrame({
'class_val': class_values,
'Core PLAND': coreland_proportions}).set_index('class_val')
return results_df
I don't know if these will be helpful but at this end, these are working properly! Not tested though! However, I could not integrate these within the pylandstats module as that step was a bit more challenging!
Any chance you could also add the option of a 8-neighbor kernel to the function remove_edges()
?
Thanks for prompt reply and help! If need any help from this end feel free to touch by. :)
Hello @ferreirav,
I drafted a PR with the CORE metrics at: https://github.com/martibosch/pylandstats/pull/45. There are currently some issues with the build tools.
Regarding the code: rather than a convolution, I use a binary erosion to remove the edges (see the compute_core_label_arr
method. The rest of the code is organized to be as DRY and object-oriented as possible, since I have taken the opportunity to add further CORE metrics. I have checked and the computed values are the same as FRAGSTATS. By the way, to get the same values, removing the edges must be done with the Von Neumann/4-neighbour kernel.
I hope this works for your needs. If so, I will close it once the PR is merged. I plan to include this in v3.0.0rc2.
Thank you again for using pylandstats and sharing your code ideas. Best, Martí
Hi Martin,Much appreciated all your work around this!Yes, it will be extremely helpful.Kind regards,Vitor FerreiraOn 29 Sep 2023, at 08:21, Martí Bosch @.***> wrote: Hello @ferreirav, I drafted a PR with the CORE metrics at: #45. There are currently some issues with the build tools. Regarding the code: rather than a convolution, I use a binary erosion to remove the edges (see the compute_core_label_arr method. The rest of the code is organized to be as DRY and object-oriented as possible, since I have taken the opportunity to add further CORE metrics. I have checked and the computed values are the same as FRAGSTATS. By the way, to get the same values, removing the edges must be done with the Von Neumann/4-neighbour kernel. I hope this works for your needs. If so, I will close it once the PR is merged. I plan to include this in v3.0.0rc2. Thank you again for using pylandstats and sharing your code ideas. Best, Martí
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: @.***>
Calculation of Core Proportion of Landscape
Hi @martibosch,
Thanks for such great work with package and exhaustive dcumentation.
I am trying to calculate proportion_of_landscape, although I would like to remove cells that are labeled with the Moore/Queen rule (neighborhood rule), so I could remove the influence of edge effects and then calculate a CORE value for each class type? Is this implemented somehow in pylandstats? What could be the best approach to it?
Thanks for the help and suggestions, Vitor