Source code for lusos.geometry.ops

from typing import NamedTuple

import geopandas as gpd
import mapbox_earcut as earcut
import numpy as np
import shapely
import xarray as xr
import xugrid as xu


[docs] class PolygonGridArea(NamedTuple): """ Sparse matrix-like structure for the result of `polygon_area_in_grid` containing the indices of the grid cells that overlap with polygons, the number of polygons that overlap with those grid cells, the index ids of the polygons and the corresponding area in the grid cell. Parameters ---------- cell_idx : np.ndarray Indices of the grid cells that overlap with polygons. nitems : np.ndarray Number of polygons that overlap with the grid cell. polygon : np.ndarray Index id of the polygon that overlaps with the grid cell. area : np.ndarray Area of the polygon in the grid cell. """ cell_idx: np.ndarray nitems: np.ndarray polygon: np.ndarray area: np.ndarray
class Triangles(NamedTuple): triangles: np.ndarray index: np.ndarray coords: np.ndarray
[docs] def triangulate(polygons: gpd.GeoDataFrame) -> Triangles: triangles = [] index = [] coords = [] increment = 0 for ii, polygon in enumerate(polygons["geometry"]): vertices = shapely.get_coordinates(polygon) n_vertices = len(vertices) interior_verts = shapely.get_num_coordinates(polygon.interiors) if interior_verts.any(): end_of_exterior = n_vertices - np.sum(interior_verts) vert_indices = np.append( end_of_exterior, np.cumsum(interior_verts) + end_of_exterior ) else: vert_indices = [n_vertices] connectivity = earcut.triangulate_float64(vertices, vert_indices) triangles.append(connectivity + increment) coords.append(vertices) index.append(np.full(len(connectivity) // 3, ii)) increment += len(vertices) triangles = np.concatenate(triangles).reshape((-1, 3)) index = np.concatenate(index) coords = np.concatenate(coords) return Triangles(triangles, index, coords)
[docs] def polygon_area_in_grid( polygons: gpd.GeoDataFrame, grid: xr.DataArray ) -> PolygonGridArea: tri = triangulate(polygons) ugrid = xu.Ugrid2d(*tri.coords.T, -1, tri.triangles) regridder = xu.OverlapRegridder(source=ugrid, target=grid) ds = regridder.to_dataset() cell_idx_pointer = ds["__regrid_indptr"].to_numpy() polygon_idx = ds["__regrid_indices"].to_numpy() area = ds["__regrid_data"].to_numpy() nonzero_per_row = np.diff(cell_idx_pointer) cell_idx = np.flatnonzero(nonzero_per_row) nitems = nonzero_per_row[cell_idx] return PolygonGridArea(cell_idx, nitems, tri.index[polygon_idx], area)
[docs] def triangles_to_geodataframe(triangles: Triangles) -> gpd.GeoDataFrame: return gpd.GeoDataFrame( geometry=shapely.polygons(triangles.coords[triangles.triangles]), crs=28992 )