Getting Started#
This notebook introduces the basic concepts of pyGuidos and shows how to:
Import pyGuidos and check the version
Load and inspect input GeoTIFF files
Understand the pixel value conventions
Visualise the input maps
We will use two example GeoTIFFs derived from the Corine Land Cover 2018 (CLC 2018) dataset at 100m resolution, covering the island of Corsica, France:
CLC2018_corsica_FNF.tif— a binary Forest/Non-Forest mapCLC2018_corsica_LandMos.tif— a three-class land cover map (Agriculture, Natural, Developed)
And a vector file derived from the GISCO Communes database:
GISCO_adm_corsica.geojson— administrative subdivisions of Corsica (4 regions)
🛠️ Environment Initialization#
This cell initializes the pyguidos workspace.
If you are running from a git clone, it uses a local
pyguidos/work/folder.If installed via pip, it uses your home directory or a custom-configured path.
1. Import Libraries#
[ ]:
# Install pyguidos if running in Google Colab
try:
import pyguidos as pg
except ImportError:
!pip install -q pyguidos
import pyguidos as pg
from pyguidos import utils
print(f"pyGuidos version: {pg.__version__}")
print(f"Example Data: {pg.DATA_DIR}")
[ ]:
from pathlib import Path
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import rasterio
from rasterio.plot import show
import pyogrio
import geopandas as gp
import pandas as pd
2. Define Data Paths#
The example data is bundled with pyGuidos in the data/ folder.
[ ]:
# Input files
fnf_tiff = pg.DATA_DIR / "CLC2018_corsica_FNF.tif"
lm_tiff = pg.DATA_DIR / "CLC2018_corsica_LandMos.tif"
vector_file = pg.DATA_DIR / "GISCO_adm_corsica.gpkg"
# Verify files exist
for f in [fnf_tiff, lm_tiff, vector_file]:
status = "✓" if f.exists() else "✗ NOT FOUND"
print(f"{status} {f.name}")
3: Set Output Directory#
If you want to save your results in a specific folder, change CUSTOM_OUTPUT below. If left as None, the system will use the default output folder inside the main project folder.
[ ]:
# --- ACTION REQUIRED: SET YOUR PREFERRED FOLDER ---
# Example: CUSTOM_OUTPUT = "C:/Users/Name/Documents/Project"
CUSTOM_OUTPUT = ''
# --------------------------------------------------
[ ]:
CONFIG_PATH = pg.PROJECT_ROOT / ".notebook_config"
# Update config if user provided a new path
if CUSTOM_OUTPUT:
OUT_DIR = Path(CUSTOM_OUTPUT).resolve()
CONFIG_PATH.write_text(str(OUT_DIR), encoding="utf-8")
print(f"Workspace updated to: {OUT_DIR}")
# Load from config or use default fallback
elif CONFIG_PATH.exists():
OUT_DIR = Path(CONFIG_PATH.read_text(encoding="utf-8").strip())
print(f"Loaded existing workspace: {OUT_DIR}")
else:
OUT_DIR = pg.PROJECT_ROOT / "output"
print(f"Using default workspace: {OUT_DIR}")
OUT_DIR.mkdir(parents=True, exist_ok=True)
4. Inspect Raster Metadata#
pyGuidos provides utils.get_raster_info() to extract all relevant metadata from a GeoTIFF in a single call.
[ ]:
# Forest/Non-Forest map metadata
info_fnf = utils.get_raster_info(fnf_tiff)
print("=" * 50)
print("Forest/Non-Forest map")
print("=" * 50)
print(f" File : {fnf_tiff.name}")
print(f" Size : {info_fnf['rows']} rows x {info_fnf['cols']} cols")
print(f" Bands : {info_fnf['bands']}")
print(f" Dtype : {info_fnf['dtype']}")
print(f" Resolution: {info_fnf['resX']} x {info_fnf['resY']} m")
print(f" EPSG : {info_fnf['epsg']}")
[ ]:
# Land Mosaic map metadata
info_lm = utils.get_raster_info(lm_tiff)
print("=" * 50)
print("Land Mosaic map")
print("=" * 50)
print(f" File : {lm_tiff.name}")
print(f" Size : {info_lm['rows']} rows x {info_lm['cols']} cols")
print(f" Bands : {info_lm['bands']}")
print(f" Dtype : {info_lm['dtype']}")
print(f" Resolution: {info_lm['resX']} x {info_lm['resY']} m")
print(f" EPSG : {info_lm['epsg']}")
5. Pixel Value Frequencies#
pyGuidos uses specific pixel value conventions for all input maps. Use utils.get_pxl_freq() to verify your input and understand the class distribution.
[ ]:
# Read the arrays
with rasterio.open(fnf_tiff) as src:
fnf_data = src.read(1)
with rasterio.open(lm_tiff) as src:
lm_data = src.read(1)
# Compute pixel frequencies
fnf_freq = utils.get_pxl_freq(fnf_data)
lm_freq = utils.get_pxl_freq(lm_data)
tot_fnf = info_fnf['rows'] * info_fnf['cols']
tot_lm = info_lm['rows'] * info_lm['cols']
print("Forest/Non-Forest map — pixel value convention:")
print(f" Value 0 — NoData : {fnf_freq[0]:>8} px ({fnf_freq[0]/tot_fnf*100:.2f}%)")
print(f" Value 1 — Background : {fnf_freq[1]:>8} px ({fnf_freq[1]/tot_fnf*100:.2f}%)")
print(f" Value 2 — Foreground : {fnf_freq[2]:>8} px ({fnf_freq[2]/tot_fnf*100:.2f}%)")
print()
print("Land Mosaic map — pixel value convention:")
print(f" Value 0 — NoData : {lm_freq[0]:>8} px ({lm_freq[0]/tot_lm*100:.2f}%)")
print(f" Value 1 — Agriculture : {lm_freq[1]:>8} px ({lm_freq[1]/tot_lm*100:.2f}%)")
print(f" Value 2 — Natural : {lm_freq[2]:>8} px ({lm_freq[2]/tot_lm*100:.2f}%)")
print(f" Value 3 — Developed : {lm_freq[3]:>8} px ({lm_freq[3]/tot_lm*100:.2f}%)")
6. Visualise the Input Maps#
6.1 Forest/Non-Forest Map#
The binary Forest/Non-Forest map uses the standard pyGuidos convention:
Value 0 — NoData (shown as white)
Value 1 — Background / Non-Forest (shown as light grey)
Value 2 — Foreground / Forest (shown as green)
[ ]:
# Color map for Forest/Non-Forest
from matplotlib.colors import ListedColormap, BoundaryNorm
fnf_colors = ['white', 'lightgrey', 'darkgreen']
fnf_cmap = ListedColormap(fnf_colors)
fnf_norm = BoundaryNorm([0, 1, 2, 3], fnf_cmap.N)
fig, ax = plt.subplots(figsize=(8, 8))
img = ax.imshow(fnf_data, cmap=fnf_cmap, norm=fnf_norm, interpolation='none')
ax.set_title('Forest/Non-Forest Map — Corsica\nCLC 2018, 100m resolution', fontsize=14, pad=15)
ax.axis('off')
# Legend
legend_patches_fnf = [
mpatches.Patch(facecolor='white', edgecolor='black', label='NoData (0)'),
mpatches.Patch(color='lightgrey', label='Non-Forest / Background (1)'),
mpatches.Patch(color='darkgreen', label='Forest / Foreground (2)'),
]
ax.legend(handles=legend_patches_fnf, loc='upper left', fontsize=10, framealpha=0.9)
plt.tight_layout()
plt.show()
6.2 Land Mosaic Map#
The three-class Land Mosaic map uses:
Value 0 — NoData (shown as white)
Value 1 — Agriculture (shown as gold)
Value 2 — Natural vegetation (shown as green)
Value 3 — Developed / Urban (shown as red)
Note: The class labels Agriculture, Natural and Developed follow the GuidosToolbox convention. In this example they correspond to CLC agricultural, natural vegetation and artificial surface classes respectively.
[ ]:
# Color map for Land Mosaic
lm_colors = ['white', 'gold', 'darkgreen', 'firebrick']
lm_cmap = ListedColormap(lm_colors)
lm_norm = BoundaryNorm([0, 1, 2, 3, 4], lm_cmap.N)
fig, ax = plt.subplots(figsize=(8, 8))
img = ax.imshow(lm_data, cmap=lm_cmap, norm=lm_norm, interpolation='none')
ax.set_title('Land Mosaic Input Map — Corsica\nCLC 2018, 100m resolution', fontsize=14, pad=15)
ax.axis('off')
# Legend
legend_patches_lm = [
mpatches.Patch(facecolor='white', edgecolor='black', label='NoData (0)'),
mpatches.Patch(color='gold', label='Agriculture (1)'),
mpatches.Patch(color='darkgreen', label='Natural (2)'),
mpatches.Patch(color='firebrick', label='Developed (3)'),
]
ax.legend(handles=legend_patches_lm, loc='upper left', fontsize=10, framealpha=0.9)
plt.tight_layout()
plt.show()
6.3 Administrative Subdivisions#
The vector file contains 5 administrative subdivisions of Corsica from the GISCO Communes database, re-aggregated to a higher administrative level.
[ ]:
# Get the field names (schema)
info = pyogrio.read_info(vector_file)
fields = info['fields']
# Print Header
print(f"{' '.join(f'{f:<15}' for f in fields)}")
print("-" * (15 * len(fields)))
# Read the data
# Setting use_arrow=True is often faster if your environment supports it
df = pyogrio.read_dataframe(vector_file)
# Print Rows
for _, row in df.iterrows():
print(f"{' '.join(f'{str(row[f]):<15}' for f in fields)}")
[ ]:
colors = ['#4e79a7', '#f28e2b', '#59a14f', '#e15759', '#b07aa1']
# Read the file
gdf = pyogrio.read_dataframe(vector_file)
# Assign the colors to a new column so 'plot' can use them
# This replicates your (i % len(colors)) logic across the whole dataframe
gdf['plot_color'] = [colors[i % len(colors)] for i in range(len(gdf))]
fig, ax = plt.subplots(figsize=(8, 8))
# Plot using the explicit color column
# We name the column used for the legend, but pass the color array for the fill
first_col = gdf.columns[0]
gdf.plot(
ax=ax,
color=gdf['plot_color'], # Use our pre-calculated colors
alpha=0.5,
edgecolor='black',
linewidth=0.8
)
# Manual Legend (since 'color' and 'legend=True' don't play nice together)
# This recreates the logic from your original script
import matplotlib.patches as mpatches
legend_handles = []
for i, name in enumerate(gdf[first_col].unique()[:len(colors)]):
patch = mpatches.Patch(color=colors[i], alpha=0.5, label=name)
legend_handles.append(patch)
ax.legend(handles=legend_handles, loc='upper left', fontsize=11, framealpha=0.9)
# Styling
ax.set_title('Administrative Subdivisions — Corsica\nGISCO Communes database', fontsize=14, pad=15)
ax.set_aspect('equal')
ax.axis('off')
plt.tight_layout()
plt.show()
6.4 Combined View#
A side-by-side overview of all three input datasets.
[ ]:
fig, axes = plt.subplots(1, 3, figsize=(18, 7))
# 1. Forest/Non-Forest (Raster remains the same)
axes[0].imshow(fnf_data, cmap=fnf_cmap, norm=fnf_norm, interpolation='none')
axes[0].set_title('Forest/Non-Forest Map', fontsize=12)
axes[0].axis('off')
legend_patches_fnf = [
mpatches.Patch(facecolor='white', edgecolor='black', label='NoData'),
mpatches.Patch(color='lightgrey', label='Non-Forest'),
mpatches.Patch(color='darkgreen', label='Forest'),
]
axes[0].legend(handles=legend_patches_fnf, loc='upper left', fontsize=9)
# 2. Land Mosaic (Raster remains the same)
axes[1].imshow(lm_data, cmap=lm_cmap, norm=lm_norm, interpolation='none')
axes[1].set_title('Land Mosaic Input Map', fontsize=12)
axes[1].axis('off')
legend_patches_lm = [
mpatches.Patch(facecolor='white', edgecolor='black', label='NoData'),
mpatches.Patch(color='gold', label='Agriculture'),
mpatches.Patch(color='darkgreen', label='Natural'),
mpatches.Patch(color='firebrick', label='Developed'),
]
axes[1].legend(handles=legend_patches_lm, loc='upper left', fontsize=9)
# 3. Administrative Subdivisions (The Pyogrio/GeoPandas way)
gdf = pyogrio.read_dataframe(vector_file)
colors = ['#4e79a7', '#f28e2b', '#59a14f', '#e15759', '#b07aa1']
# Assign colors to rows
gdf['assigned_color'] = [colors[i % len(colors)] for i in range(len(gdf))]
# Plot the vector data
first_col = gdf.columns[0]
gdf.plot(
ax=axes[2],
color=gdf['assigned_color'],
alpha=0.5,
edgecolor='black',
linewidth=0.8
)
# Recreate the deduplicated legend
unique_names = gdf[first_col].unique()
legend_handles_vec = [
mpatches.Patch(color=colors[i % len(colors)], alpha=0.5, label=name)
for i, name in enumerate(unique_names[:10]) # Limit to 10 if list is long
]
axes[2].legend(handles=legend_handles_vec, loc='upper left', fontsize=9)
axes[2].set_title('Administrative Subdivisions', fontsize=12)
axes[2].set_aspect('equal')
axes[2].axis('off')
fig.suptitle('pyGuidos Example Data — Corsica, CLC 2018 (100m)', fontsize=14, y=1.02)
plt.tight_layout()
plt.show()
7. Summary#
In this notebook we have:
Verified the pyGuidos installation and version
Inspected the metadata of two input GeoTIFFs using
utils.get_raster_info()Verified pixel value distributions using
utils.get_pxl_freq()Visualised the two input maps and the administrative vector file
The input data is ready for analysis. In the next notebooks we will apply the pyGuidos analysis tools:
Notebook 2 — Landscape Mosaic Analysis
Notebook 3 — Fragmentation Change Analysis
Notebook 4 — Regional Analysis using administrative subdivisions
[ ]: