geo-polygonize 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +250 -0
- package/dist/geo_polygonize.wasm +0 -0
- package/dist/geo_polygonize_simd.wasm +0 -0
- package/dist/slim/cjs/index.d.ts +3 -0
- package/dist/slim/cjs/index_slim.d.ts +3 -0
- package/dist/slim/cjs/index_slim.js +875 -0
- package/dist/slim/cjs/index_threads.d.ts +3 -0
- package/dist/slim/es/index.d.ts +3 -0
- package/dist/slim/es/index_slim.d.ts +3 -0
- package/dist/slim/es/index_slim.js +867 -0
- package/dist/slim/es/index_threads.d.ts +3 -0
- package/dist/standard/cjs/index.d.ts +3 -0
- package/dist/standard/cjs/index.js +462 -0
- package/dist/standard/cjs/index_slim.d.ts +3 -0
- package/dist/standard/cjs/index_threads.d.ts +3 -0
- package/dist/standard/es/index.d.ts +3 -0
- package/dist/standard/es/index.js +452 -0
- package/dist/standard/es/index_slim.d.ts +3 -0
- package/dist/standard/es/index_threads.d.ts +3 -0
- package/dist/threads/es/index.d.ts +3 -0
- package/dist/threads/es/index.js +652 -0
- package/dist/threads/es/index_slim.d.ts +3 -0
- package/dist/threads/es/index_threads.d.ts +3 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# Geo Polygonize
|
|
2
|
+
|
|
3
|
+
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.
|
|
4
|
+
|
|
5
|
+
[](https://deepwiki.com/graydonpleasants/geo-polygonize)
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Robust Polygonization**: Extracts polygons from unstructured linework.
|
|
10
|
+
- **Robust Noding**: Implements **Iterated Snap Rounding (ISR)** to guarantee topological correctness on dirty inputs (self-intersections, overlaps).
|
|
11
|
+
- **Hardware Acceleration**: Uses **SIMD** instructions (via `wide` crate) for critical geometric predicates like Point-in-Polygon checks.
|
|
12
|
+
- **Wasm Optimized**: Tailored for WebAssembly with `talc` allocator and Zero-Copy data support (`geoarrow`).
|
|
13
|
+
- **Performance**: Competitive with GEOS/Shapely (C++), outperforming it on random sparse inputs and scaling well on dense grids.
|
|
14
|
+
- **Geo Ecosystem**: Fully integrated with `geo-types` and `geo` crates.
|
|
15
|
+
- **GeoArrow Support**: Zero-copy data transfer via Arrow C Data Interface and Arrow IPC (Wasm).
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
### Library
|
|
20
|
+
|
|
21
|
+
```rust
|
|
22
|
+
use geo_polygonize_core::Polygonizer;
|
|
23
|
+
use geo_types::LineString;
|
|
24
|
+
|
|
25
|
+
fn main() {
|
|
26
|
+
let mut poly = Polygonizer::new();
|
|
27
|
+
|
|
28
|
+
// Enable robust noding if lines might intersect
|
|
29
|
+
poly.node_input = true;
|
|
30
|
+
// Optional: Configure snap grid (default 1e-10)
|
|
31
|
+
poly.snap_grid_size = 1e-6;
|
|
32
|
+
|
|
33
|
+
// Add lines (e.g., a square with diagonals)
|
|
34
|
+
poly.add_geometry(LineString::from(vec![
|
|
35
|
+
(0.0, 0.0), (10.0, 0.0), (10.0, 10.0), (0.0, 10.0), (0.0, 0.0)
|
|
36
|
+
]).into());
|
|
37
|
+
poly.add_geometry(LineString::from(vec![
|
|
38
|
+
(0.0, 0.0), (10.0, 10.0)
|
|
39
|
+
]).into());
|
|
40
|
+
|
|
41
|
+
let polygons = poly.polygonize().expect("Polygonization failed");
|
|
42
|
+
|
|
43
|
+
for p in polygons {
|
|
44
|
+
println!("Found polygon with area: {}", p.unsigned_area());
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### GeoArrow Integration
|
|
50
|
+
|
|
51
|
+
The library supports ingesting data directly from Arrow arrays via the `arrow_api` module and `ffi`.
|
|
52
|
+
|
|
53
|
+
```rust
|
|
54
|
+
use geo_polygonize_core::arrow_api::{polygonize_arrow, PolygonizerOptions};
|
|
55
|
+
// ... create Arrow array ...
|
|
56
|
+
// let result = polygonize_arrow(&array, &field, options);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Python
|
|
60
|
+
|
|
61
|
+
The library provides native Python bindings via PyO3, packaged as `geo-polygonize`.
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
import numpy as np
|
|
65
|
+
from geo_polygonize import polygonize
|
|
66
|
+
|
|
67
|
+
# 1. Using Shapely LineStrings or coordinate lists directly
|
|
68
|
+
lines = [
|
|
69
|
+
[(0, 0), (10, 0), (10, 10), (0, 10), (0, 0)],
|
|
70
|
+
[(0, 0), (10, 10)]
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
# return_polygons=True returns a list of shapely.geometry.Polygon objects
|
|
74
|
+
polygons = polygonize(lines=lines, return_polygons=True)
|
|
75
|
+
for p in polygons:
|
|
76
|
+
print(p.area)
|
|
77
|
+
|
|
78
|
+
# 2. Using High-Performance Flat Arrays
|
|
79
|
+
# Perfect for zero-copy integrations or massive datasets
|
|
80
|
+
coords = np.array([
|
|
81
|
+
0.0, 0.0, 10.0, 0.0, 10.0, 10.0, 0.0, 10.0, 0.0, 0.0,
|
|
82
|
+
0.0, 0.0, 10.0, 10.0
|
|
83
|
+
], dtype=np.float64)
|
|
84
|
+
|
|
85
|
+
# Start indices for each line segment.
|
|
86
|
+
# The final closing offset is computed implicitly.
|
|
87
|
+
offsets = np.array([0, 5], dtype=np.uint32)
|
|
88
|
+
|
|
89
|
+
# Returns a dictionary with 'flat_coords', 'ring_offsets', 'polygon_offsets', etc.
|
|
90
|
+
result_dict = polygonize(coords=coords, offsets=offsets)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### WebAssembly (WASM)
|
|
94
|
+
|
|
95
|
+
This library supports WebAssembly with an ergonomic dual-build configuration that automatically utilizes SIMD instructions where available.
|
|
96
|
+
|
|
97
|
+
**Installation:**
|
|
98
|
+
```bash
|
|
99
|
+
npm install geo-polygonize
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Standard Usage (Bundlers / Browser):**
|
|
103
|
+
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.
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
import init, { polygonize, polygonize_geoarrow } from "geo-polygonize";
|
|
107
|
+
|
|
108
|
+
async function run() {
|
|
109
|
+
await init();
|
|
110
|
+
|
|
111
|
+
const geojson = {
|
|
112
|
+
"type": "FeatureCollection",
|
|
113
|
+
"features": [
|
|
114
|
+
// ... your line features
|
|
115
|
+
]
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// Returns a GeoJSON FeatureCollection string
|
|
119
|
+
const result = polygonize(JSON.stringify(geojson));
|
|
120
|
+
console.log(JSON.parse(result));
|
|
121
|
+
|
|
122
|
+
// Or use Arrow IPC bytes
|
|
123
|
+
// const ipcBuffer = ...;
|
|
124
|
+
// const arrowResult = polygonize_geoarrow(ipcBuffer, false, 1e-10, false);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Slim Usage (Manual Loading):**
|
|
129
|
+
If you prefer to manage the Wasm binary yourself (e.g., to reduce bundle size or load from a CDN), import from `geo-polygonize/slim`.
|
|
130
|
+
|
|
131
|
+
```javascript
|
|
132
|
+
import { initBest, polygonize } from "geo-polygonize/slim";
|
|
133
|
+
|
|
134
|
+
async function run() {
|
|
135
|
+
// You must provide the compiled WebAssembly.Module or URL
|
|
136
|
+
// You can choose to load the SIMD or Scalar version based on your own detection or availability
|
|
137
|
+
const response = await fetch("geo_polygonize.wasm");
|
|
138
|
+
const buffer = await response.arrayBuffer();
|
|
139
|
+
const module = await WebAssembly.compile(buffer);
|
|
140
|
+
|
|
141
|
+
// Helper to initialize the best available implementation
|
|
142
|
+
// Pass the module to both arguments if you only have one version
|
|
143
|
+
await initBest(module, module);
|
|
144
|
+
|
|
145
|
+
// ... use polygonize
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Multithreaded Usage (Experimental):**
|
|
150
|
+
This library provides a multithreaded build powered by `wasm-bindgen-rayon`.
|
|
151
|
+
|
|
152
|
+
```javascript
|
|
153
|
+
import init, { initThreadPool, polygonize } from "geo-polygonize/threads";
|
|
154
|
+
|
|
155
|
+
async function run() {
|
|
156
|
+
await init();
|
|
157
|
+
|
|
158
|
+
// Initialize thread pool (e.g., with navigator.hardwareConcurrency)
|
|
159
|
+
await initThreadPool(navigator.hardwareConcurrency);
|
|
160
|
+
|
|
161
|
+
// ... use polygonize as usual
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Important:** Multithreaded WebAssembly requires `SharedArrayBuffer`, which is only available in secure contexts. You **must** serve your page with the following headers:
|
|
166
|
+
```
|
|
167
|
+
Cross-Origin-Opener-Policy: same-origin
|
|
168
|
+
Cross-Origin-Embedder-Policy: require-corp
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### CLI Example
|
|
172
|
+
|
|
173
|
+
The repository includes a CLI tool to polygonize GeoJSON files.
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# Build the example
|
|
177
|
+
cargo build -p geo-polygonize-core --example polygonize --release
|
|
178
|
+
|
|
179
|
+
# Run on input lines
|
|
180
|
+
cargo run -p geo-polygonize-core --release --example polygonize -- --input lines.geojson --output polygons.geojson --node
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Visualization
|
|
184
|
+
|
|
185
|
+
You can visualize the results using the provided Python script (requires `matplotlib` and `shapely`).
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
python3 scripts/visualize.py --input lines.geojson --output polygons.geojson --save result.png
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Examples
|
|
192
|
+
|
|
193
|
+
Below are some examples of what the polygonizer can do.
|
|
194
|
+
|
|
195
|
+
### Nested Holes and Islands
|
|
196
|
+
|
|
197
|
+
The algorithm correctly identifies nested structures (Island inside a Hole inside a Shell).
|
|
198
|
+
|
|
199
|
+

|
|
200
|
+
|
|
201
|
+
### Incomplete Grid / Dangles
|
|
202
|
+
|
|
203
|
+
The algorithm prunes dangles (dead-end lines) and extracts only closed cycles.
|
|
204
|
+
|
|
205
|
+

|
|
206
|
+
|
|
207
|
+
### Touching Polygons (Shared Edges)
|
|
208
|
+
|
|
209
|
+
Using robust noding (`--node`), it can reconstruct adjacent polygons that share boundaries, even if the input lines are not perfectly noded.
|
|
210
|
+
|
|
211
|
+

|
|
212
|
+
|
|
213
|
+
### Self-Intersecting Geometry (Bowtie)
|
|
214
|
+
|
|
215
|
+
Self-intersecting lines are split at intersection points, and valid cycles are extracted.
|
|
216
|
+
|
|
217
|
+

|
|
218
|
+
|
|
219
|
+
### Complex Geometries
|
|
220
|
+
|
|
221
|
+
The polygonizer can handle complex, curved inputs (approximated by LineStrings) such as overlapping circles and shapes with multiple holes.
|
|
222
|
+
|
|
223
|
+
**Overlapping Circles**: Note how the intersection regions are correctly identified as separate polygons.
|
|
224
|
+
|
|
225
|
+

|
|
226
|
+
|
|
227
|
+
**Curved Holes**: A complex polygon with multiple circular holes.
|
|
228
|
+
|
|
229
|
+

|
|
230
|
+
|
|
231
|
+
## Benchmarks
|
|
232
|
+
|
|
233
|
+
This library includes a "severe" comparison suite against `shapely` (GEOS).
|
|
234
|
+
|
|
235
|
+
See [BENCHMARKS.md](BENCHMARKS.md) for detailed results and instructions on how to run them.
|
|
236
|
+
|
|
237
|
+
## Architecture
|
|
238
|
+
|
|
239
|
+
This implementation moves away from the pointer-based graph structures of JTS/GEOS to a Rust-idiomatic Index Graph (Arena) approach.
|
|
240
|
+
|
|
241
|
+
See [ARCHITECTURE.md](ARCHITECTURE.md) for a deep dive into the optimization strategies.
|
|
242
|
+
|
|
243
|
+
Key optimizations include:
|
|
244
|
+
1. **Robust Noding**: Iterated Snap Rounding (ISR) using `rstar` for intersection detection and grid snapping.
|
|
245
|
+
2. **Vectorization**: SIMD-accelerated Ray Casting for efficient Hole Assignment.
|
|
246
|
+
3. **Memory Layout**: Structure of Arrays (SoA) for graph nodes and `talc` allocator for Wasm.
|
|
247
|
+
|
|
248
|
+
## License
|
|
249
|
+
|
|
250
|
+
MIT/Apache-2.0
|
|
Binary file
|
|
Binary file
|