Geo Polygonize
A native Rust port of the JTS/GEOS polygonization algorithm. This crate allows you to reconstruct valid polygons from a set of lines, including handling of complex topologies like holes, nested shells, and disconnected components.
Features
- Robust Polygonization: Extracts polygons from unstructured linework.
- Robust Noding: Implements Iterated Snap Rounding (ISR) to guarantee topological correctness on dirty inputs (self-intersections, overlaps).
- Hardware Acceleration: Uses SIMD instructions (via
widecrate) for critical geometric predicates like Point-in-Polygon checks. - Wasm Optimized: Tailored for WebAssembly with
talcallocator and binary GeoArrow support. - Performance: Competitive with GEOS/Shapely (C++), outperforming it on random sparse inputs and scaling well on dense grids.
- Geo Ecosystem: Fully integrated with
geo-typesandgeocrates. - GeoArrow Support: Arrow C Data Interface and Arrow IPC integration with GeoArrow metadata.
Engineering Roadmap
For an ambitious, prioritized plan covering performance, security, API consistency, and maintainability, see docs/roadmap.md.
Usage
Library
use Polygonizer;
use LineString;
Choosing node_input and snap_grid_size
Polygonization quality is heavily influenced by input noding strategy.
node_input = false(default): Fastest path. Use this when your input linework is already noded (all intersections are explicit vertices).node_input = true: Enables Iterated Snap Rounding (ISR). Use this for real-world datasets that may contain slight misalignments, overlaps, or self-intersections.snap_grid_sizecontrols how aggressively coordinates are snapped during robust noding:- Start with
1e-10for high-precision projected data. - Increase to
1e-8or1e-6when near-duplicate vertices prevent clean topology. - Avoid very large values unless your coordinate units are coarse; oversnapping can collapse narrow features.
- Start with
Practical workflow:
- Run with
node_input = falsefirst on trusted data. - If you observe missing polygons, sliver artifacts, or unresolved intersections, enable
node_input. - Tune
snap_grid_sizeupward incrementally until topology stabilizes.
Output semantics
The polygonizer intentionally returns only valid polygonal areas that can be formed from closed cycles:
- Dangles are removed: dead-end edges do not appear in output polygons.
- Cut edges are excluded: edges that are connected but cannot bound a face are ignored.
- Holes and nested shells are preserved when enough boundary information is present.
This behavior matches classical JTS/GEOS polygonization semantics and is useful for cleaning linework before area analysis.
GeoArrow Integration
The library supports ingesting data directly from Arrow arrays via the arrow_api module and ffi.
use ;
// ... create Arrow array ...
// let result = polygonize_arrow(&array, &field, options);
Python
The Python package is published as geo-polygonize-py and imported as geo_polygonize.
# 1. Using Shapely LineStrings or coordinate lists directly
=
# return_polygons=True returns a list of shapely.geometry.Polygon objects
=
# 2. Using High-Performance Flat Arrays
# Flat buffers avoid Python object-per-coordinate overhead
=
# Start indices for each line segment.
# The final closing offset is computed implicitly.
=
# Returns a stable dictionary with 'polygons', diagnostics, and provenance.
=
# Native-extension probes are cheap and safe for optional integrations.
, =
CFB/autograder integrations should use the versioned production profile rather
than assembling caller-side knobs or using legacy polygonize(..., node=True, snap=0.5) calls:
=
The default return shape is a stable dictionary with polygons as
SimplePolygon values. Use return_polygons=True only when you want Shapely
Polygon objects.
SnapStrategy::Grid keeps topology and output coordinates on the configured
precision grid. The CFB profile uses GeosCompat: the grid establishes robust
topology, then output nodes regain deterministic source coordinates to better
match Shapely snap plus full-precision noding. It is not set_precision
emulation, and exact parity is not guaranteed for many-to-one snaps.
For Shapely parity checks, compare report-mode outputs with the built-in mismatch helper:
=
=
=
=
=
=
For a minimal Shapely smoke comparison, use area signatures:
=
=
=
WebAssembly (WASM)
This library supports WebAssembly with an ergonomic dual-build configuration that automatically utilizes SIMD instructions where available.
Installation:
Standard Usage (Quick Demos): The default entry point automatically handles feature detection (SIMD) and lazy-loading of the Wasm binary. The Wasm is inlined as a Base64 Data URI, so no extra bundler configuration is needed. For app builds, prefer the slim entry point below so your bundler keeps the Wasm assets out of the JavaScript chunk.
import init from "geo-polygonize";
Slim Usage (Apps / Manual Loading):
For Vite and other app bundlers, import from geo-polygonize/slim and pass explicit Wasm asset URLs.
import from "geo-polygonize/slim";
import scalarUrl from "geo-polygonize/geo_polygonize.wasm?url";
import simdUrl from "geo-polygonize/geo_polygonize_simd.wasm?url";
Multithreaded Usage (Experimental):
This library provides a multithreaded build powered by wasm-bindgen-rayon.
import init from "geo-polygonize/threads";
Important: Multithreaded WebAssembly requires SharedArrayBuffer, which is only available in secure contexts. You must serve your page with the following headers:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
CLI Example
The repository includes a CLI tool to polygonize GeoJSON files.
# Build the example
# Run on input lines
Visualization
You can visualize the results using the provided Python script (requires matplotlib and shapely).
Examples
Below are some examples of what the polygonizer can do.
Nested Holes and Islands
The algorithm correctly identifies nested structures (Island inside a Hole inside a Shell).

Incomplete Grid / Dangles
The algorithm prunes dangles (dead-end lines) and extracts only closed cycles.

Touching Polygons (Shared Edges)
Using robust noding (--node), it can reconstruct adjacent polygons that share boundaries, even if the input lines are not perfectly noded.

Self-Intersecting Geometry (Bowtie)
Self-intersecting lines are split at intersection points, and valid cycles are extracted.

Complex Geometries
The polygonizer can handle complex, curved inputs (approximated by LineStrings) such as overlapping circles and shapes with multiple holes.
Overlapping Circles: Note how the intersection regions are correctly identified as separate polygons.

Curved Holes: A complex polygon with multiple circular holes.

Benchmarks
This library includes a "severe" comparison suite against shapely (GEOS).
See BENCHMARKS.md for detailed results and instructions on how to run them.
Architecture
This implementation moves away from the pointer-based graph structures of JTS/GEOS to a Rust-idiomatic Index Graph (Arena) approach.
See ARCHITECTURE.md for a deep dive into the optimization strategies.
Key optimizations include:
- Robust Noding: Iterated Snap Rounding (ISR) using
rstarfor intersection detection and grid snapping. - Vectorization: SIMD-accelerated Ray Casting for efficient Hole Assignment.
- Memory Layout: Structure of Arrays (SoA) for graph nodes and
talcallocator for Wasm.
License
MIT/Apache-2.0