Extracting Polygons and Generating Random Points from a Raster File based on RGB Value of Land Class: A Step-by-Step Guide for Single Crop Category


In this tutorial, I’ll guide you through the process of extracting polygons from a raster file using a specific land class defined by its RGB values. We will also generate random points within those polygons. This guide is perfect for those working with land cover classifications and interested in visualizing specific land use categories—like the Single Crop class—on a map.

What We’ll Cover:

  • Extracting polygons from raster data based on specific RGB values.
  • Generating random points within the extracted polygons.
  • Visualizing the shapefiles and creating a feature image for your project.

Tools and Libraries:

We will use several Python libraries for this task:

  • rasterio: To read and process the raster file.
  • shapely: To handle the geometrical aspects of the shapes.
  • fiona: To write shapefiles.
  • geopandas: To work with geospatial data and generate random points.
  • matplotlib: For visualization of the shapefiles.

Let's jump right in!


1. Understanding the Data

Land cover classification maps usually encode different land types using specific RGB values. In our example, the Single Crop land cover class is represented by the RGB value (254, 239, 187).

We will use this RGB value to extract the Single Crop polygons from the raster file and then generate random points inside the polygons.


2. Setting Up the Environment

Before we start coding, install the required libraries. You can install them using pip:

pip install rasterio shapely geopandas fiona matplotlib

Once installed, you’re ready to start working with the code.


3. Extracting Polygons from the Raster File

Step 1: Loading the Raster File

The first step is to load your raster file (.tif) and locate areas matching the RGB value for Single Crop.

import numpy as np
import rasterio
from rasterio.features import shapes
from shapely.geometry import shape, mapping
import fiona
from fiona.crs import from_epsg
import os

class SingleCropShapefileGenerator:
    def __init__(self, file_path, target_rgb=(254, 239, 187)):
        self.file_path = file_path
        self.target_rgb = target_rgb

    def classify(self):
        # Open the TIFF file
        with rasterio.open(self.file_path) as src:
            # Read the bands as arrays (assuming it's RGB)
            r = src.read(1)  # Red band
            g = src.read(2)  # Green band
            b = src.read(3)  # Blue band
            
            # Stack the bands to form an RGB array
            rgb = np.stack([r, g, b], axis=-1)
            
            # Create a boolean mask where the RGB values match the target
            mask = np.all(rgb == self.target_rgb, axis=-1)
            
            return mask, src.transform, src.crs

    def generate_shapefile(self, output_shp):
        mask, transform, crs = self.classify()
        
        # Convert the mask into shapes (polygons)
        shapes_gen = shapes(mask.astype(np.uint8), transform=transform)

        # Define schema for shapefile (geometry type: Polygon)
        schema = {
            'geometry': 'Polygon',
            'properties': {'category': 'str'}
        }
        
        # Write the shapes (polygons) to a shapefile
        with fiona.open(output_shp, 'w', driver='ESRI Shapefile', crs=crs.to_dict(), schema=schema) as shp:
            for geom, value in shapes_gen:
                if value:  # Only take shapes where the mask is True
                    shp.write({
                        'geometry': mapping(shape(geom)),
                        'properties': {'category': 'Single Crop'}
                    })

# Usage
file_path = '/path/to/your/raster/BDLC2015.tif'  # Path to your TIFF file
output_shp = '/path/to/save/single_crop.shp'  # Path to save the shapefile
output_dir = '/path/to/save/directory/'
if not os.path.exists(output_dir):
    os.makedirs(output_dir)

output_shp = os.path.join(output_dir, 'single_crop.shp')
single_crop_generator = SingleCropShapefileGenerator(file_path)
single_crop_generator.generate_shapefile(output_shp)

4. Generating Random Points within the Extracted Polygons

Now that we have the Single Crop polygons, let's generate random points inside these polygons.

Step 2: Generate Random Points

import geopandas as gpd
from shapely.geometry import Point
import random

def generate_random_points(shapefile_path, num_points=10, output_csv=None):
    # Load the shapefile
    gdf = gpd.read_file(shapefile_path)
    
    # Create an empty list to hold random points
    points = []
    
    # Generate random points inside the polygons
    for _ in range(num_points):
        while True:
            # Choose a random polygon from the GeoDataFrame
            polygon = gdf.sample(1).geometry.values[0]
            
            # Generate a random point inside the bounding box of the polygon
            minx, miny, maxx, maxy = polygon.bounds
            point = Point(random.uniform(minx, maxx), random.uniform(miny, maxy))
            
            # Check if the point is within the polygon
            if polygon.contains(point):
                points.append((point.x, point.y))
                break
    
    # Save the points to a CSV file
    if output_csv:
        np.savetxt(output_csv, points, delimiter=',', header="Longitude,Latitude", comments='')
        print(f"Random points saved to {output_csv}")
    
    return points

# Usage
shapefile_path = '/path/to/save/single_crop.shp'
output_csv = '/path/to/save/single_crop_random_points.csv'
generate_random_points(shapefile_path, num_points=20, output_csv=output_csv)

5. Visualizing the Shapefile

To create a feature image for your blog post, we’ll plot the shapefile in Python using GeoPandas and Matplotlib.

import geopandas as gpd
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable

def plot_shapefile_enhanced(shapefile_path, title="Single Crop Land Cover", output_image_path=None):
    # Load the shapefile into a GeoDataFrame
    gdf = gpd.read_file(shapefile_path)

    # Create the figure and axis
    fig, ax = plt.subplots(1, 1, figsize=(12, 10))

    # Choose a colorful colormap
    cmap = 'Set2'
    
    # Plot the shapefile with polygons filled with vibrant colors
    gdf.boundary.plot(ax=ax, linewidth=1, color='black')
    gdf.plot(ax=ax, column='category', cmap=cmap, legend=True, edgecolor='black')

    # Customize the legend
    leg = ax.get_legend()
    leg.set_bbox_to_anchor((1, 0.6))  # Position the legend outside the plot
    leg.set_title("Land Cover Types", prop={'size': 12})

    # Set the title and axis labels with more prominent fonts
    ax.set_title(title, fontsize=18, pad=20, color='darkblue')
    ax.set_xlabel("Longitude", fontsize=14)
    ax.set_ylabel("Latitude", fontsize=14)

    # Save or display the image
    if output_image_path:
        plt.savefig(output_image_path, dpi=300, bbox_inches='tight', transparent=True)
        print(f"Image saved to {output_image_path}")
    else:
        plt.show()

# Usage
shapefile_path = '/path/to/save/single_crop.shp'
output_image_path = '/path/to/save/single_crop_visualization_enhanced.png'
plot_shapefile_enhanced(shapefile_path, title="Single Crop Land Cover Visualization", output_image_path=output_image_path)

Conclusion

By following these steps, you can easily extract specific land cover classes from a raster file using RGB values and visualize them as polygons. Additionally, you can generate random points inside those polygons, which can be useful for sampling and analysis tasks.

Feel free to experiment with other land cover categories by adjusting the RGB values. If you have any questions or suggestions, leave a comment below!


0% Positive Review (0 Comments)