smoothify 0.3.2__tar.gz → 0.3.3__tar.gz
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.
- {smoothify-0.3.2 → smoothify-0.3.3}/.github/workflows/ci.yml +5 -2
- {smoothify-0.3.2 → smoothify-0.3.3}/CHANGELOG.md +9 -0
- {smoothify-0.3.2/smoothify.egg-info → smoothify-0.3.3}/PKG-INFO +14 -15
- {smoothify-0.3.2 → smoothify-0.3.3}/README.md +13 -14
- {smoothify-0.3.2 → smoothify-0.3.3}/examples/Water_Smoothed.gpkg +0 -0
- smoothify-0.3.3/examples/merge_holes_examples.ipynb +388 -0
- smoothify-0.3.3/examples/real_world_water_example.ipynb +773 -0
- smoothify-0.3.3/examples/smoothify_vs_shapely_comparison.ipynb +683 -0
- smoothify-0.3.3/examples/usage_examples.ipynb +839 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/images/generate_pipeline_graphic.py +32 -22
- smoothify-0.3.3/images/pipeline_steps.png +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/pyproject.toml +2 -1
- {smoothify-0.3.2 → smoothify-0.3.3}/pytest.ini +2 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/smoothify/_version.py +3 -3
- {smoothify-0.3.2 → smoothify-0.3.3}/smoothify/smoothify_core.py +177 -63
- {smoothify-0.3.2 → smoothify-0.3.3/smoothify.egg-info}/PKG-INFO +14 -15
- smoothify-0.3.3/smoothify.egg-info/scm_version.json +8 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/test_smoothify_core.py +48 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/test_water_quality_sweep.py +11 -5
- smoothify-0.3.2/examples/merge_holes_examples.ipynb +0 -388
- smoothify-0.3.2/examples/real_world_water_example.ipynb +0 -773
- smoothify-0.3.2/examples/smoothify_vs_shapely_comparison.ipynb +0 -683
- smoothify-0.3.2/examples/usage_examples.ipynb +0 -837
- smoothify-0.3.2/images/pipeline_steps.png +0 -0
- smoothify-0.3.2/smoothify.egg-info/scm_version.json +0 -8
- {smoothify-0.3.2 → smoothify-0.3.3}/.github/workflows/publish.yml +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/.gitignore +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/.pre-commit-config.yaml +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/.python-version +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/.vscode/settings.json +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/LICENSE +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/RELEASING.md +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/benchmarks/bench_water.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/examples/Water.gpkg +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/fuzz/__init__.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/fuzz/generators.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/fuzz/oracle.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/fuzz/runner.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/images/example_1_polygon.png +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/images/example_2_linestring.png +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/images/example_3_iterations.png +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/images/example_4_merging.png +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/images/generate_example_images.ipynb +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/images/generate_readme_image.ipynb +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/images/smoothify_hero.png +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/images/smoothify_logo.png +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/scripts/fuzz_run.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/scripts/fuzz_visualize.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/setup.cfg +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/smoothify/__init__.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/smoothify/coordinator.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/smoothify/geometry_ops.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/smoothify/py.typed +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/smoothify.egg-info/SOURCES.txt +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/smoothify.egg-info/dependency_links.txt +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/smoothify.egg-info/requires.txt +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/smoothify.egg-info/scm_file_list.json +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/smoothify.egg-info/top_level.txt +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/README.md +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/__init__.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/conftest.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/test_all_geometry_types.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/test_area_tolerance.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/test_auto_segment_length.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/test_chaikin.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/test_congruence_dedup.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/test_convexity_artifacts.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/test_corner_rounding.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/test_data/convex_pixel_rectangle.gpkg +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/test_data/naip_owm_water_bodies.geojson +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/test_edge_cases_coverage.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/test_fuzz.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/test_geometry_types.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/test_invalid_polygon.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/test_merge_holes.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/test_real_world_data.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/test_self_intersecting_variant.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/test_smoothify_api.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/test_synthetic_blob_fuzz.py +0 -0
- {smoothify-0.3.2 → smoothify-0.3.3}/tests/visual_tests.ipynb +0 -0
|
@@ -33,8 +33,11 @@ jobs:
|
|
|
33
33
|
run: uv run mypy smoothify/
|
|
34
34
|
|
|
35
35
|
- name: Test
|
|
36
|
-
|
|
36
|
+
# -n 2 overrides the local `-n auto` default: GitHub runners have 4
|
|
37
|
+
# vCPUs and some tests spawn smoothify's own worker pool, so a smaller
|
|
38
|
+
# fan-out avoids oversubscribing the runner.
|
|
39
|
+
run: uv run pytest tests/ -n 2 -x -q
|
|
37
40
|
|
|
38
41
|
- name: Notebook smoke test
|
|
39
42
|
if: matrix.python-version == '3.11'
|
|
40
|
-
run: uv run pytest --nbmake examples/*.ipynb -q
|
|
43
|
+
run: uv run pytest --nbmake examples/*.ipynb -n 2 -q
|
|
@@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.3.3] - 2026-07-02
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- Start-point variants are now merged by a per-point median consensus instead of a geometric union. Douglas-Peucker is start-vertex dependent, so the four rotated variants disagree slightly about where a feature's vertices land; the union superimposed those disagreements, whereas the median resamples the variants to a common point count, brings them into phase via an FFT cross-correlation, and takes the coordinate-wise median — a start-invariant consensus that outvotes a single bad variant instead of averaging toward it. This is also faster than the union it replaces (~1.3x end-to-end at the default 3 iterations, ~1.4x at 5, on `examples/Water.gpkg`). The median sits at the consensus interior rather than the outer envelope, so the merged ring is marginally smaller before area preservation restores it (with `preserve_area=True`); with `preserve_area=False` the small reduction is left as-is.
|
|
11
|
+
- Faster smoothing with no meaningful change to output. Start-point variants now begin at evenly spaced arc-length positions, computed by interpolating a single new start vertex, rather than rotating to a vertex index on a fully densified ring. The old path segmentized the whole ring only so that index-based rotation would land on evenly spaced points — but every vertex it added was collinear and immediately stripped by the following Douglas-Peucker. Feeding DP just the original vertices plus one cuts its input roughly 3.4x and the simplify step ~3.5x on `examples/Water.gpkg` (total single-core CPU work ~4.4s → ~2.7s, about 1.25x faster end-to-end at the default 3 iterations). The per-point median phase-alignment and Chaikin corner cutting also replace `numpy.roll` with a slice-based cyclic shift (bit-identical, fewer allocations). Output is unchanged within tolerance (per-polygon area drift ≤ 0.01%, worst concave turn unchanged).
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- Eliminated the residual "dimple" fold that appeared where the start-point variants disagreed about a shallow feature near the simplify tolerance. Their union superimposed the disagreement as a double peak with a valley that the area-preservation buffer then sharpened into a fold slipping under the concave-turn seal threshold. The per-point median consensus (see Changed) resolves the variants to one boundary instead of overlaying them, removing the artifact (raster-noise fuzz harness: 4 → 0 residual folds; `examples/Water.gpkg`: 8 → 0).
|
|
15
|
+
|
|
7
16
|
## [0.3.2] - 2026-06-30
|
|
8
17
|
|
|
9
18
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: smoothify
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: Transform pixelated geometries from raster data into smooth natural looking features
|
|
5
5
|
Author-email: Nick Wright <nicholas.wright@dpird.wa.gov.au>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -229,22 +229,21 @@ smoothed = smoothify(
|
|
|
229
229
|
|
|
230
230
|
## How It Works
|
|
231
231
|
|
|
232
|
-
Smoothify uses an advanced multi-step smoothing pipeline:
|
|
232
|
+
Smoothify uses an advanced multi-step smoothing pipeline. The numbered steps below correspond to the panels in the figure:
|
|
233
233
|
|
|
234
234
|
<p align="left">
|
|
235
235
|
<img src="https://raw.githubusercontent.com/DPIRD-DMA/Smoothify/main/images/pipeline_steps.png" alt="Smoothify pipeline steps" width="800">
|
|
236
236
|
</p>
|
|
237
237
|
|
|
238
238
|
|
|
239
|
-
1.
|
|
240
|
-
2.
|
|
241
|
-
3.
|
|
242
|
-
4.
|
|
243
|
-
5.
|
|
244
|
-
6.
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
9. Detects and repairs any sharp folds left by features near the smoothing scale (e.g. one-pixel-wide arms), using a small morphological opening/closing bounded at `segment_length / 4`
|
|
239
|
+
1. **Pixelated input** — a polygon straight from raster-to-vector conversion, with a stair-stepped boundary
|
|
240
|
+
2. **Multiple variants** (for Polygons) that start at evenly spaced arc-length positions, each simplified to strip staircase noise, so no artifact is tied to a fixed start vertex
|
|
241
|
+
3. **Chaikin corner cutting** applied to each variant
|
|
242
|
+
4. **Per-point median merge** — a start-invariant consensus that resolves the variants' disagreements
|
|
243
|
+
5. **Final smoothing pass** on the merged result
|
|
244
|
+
6. **Restore original area** via buffering (for Polygons, when `preserve_area=True`)
|
|
245
|
+
|
|
246
|
+
Two steps are not shown in the figure: before step 2, touching holes are joined (for Polygons, when `merge_holes=True`) so they smooth as one opening; and after step 6, the result is checked for sharp concave folds left by features near the smoothing scale (e.g. one-pixel-wide arms) and repaired with a small morphological opening/closing bounded at `segment_length / 4`.
|
|
248
247
|
|
|
249
248
|
## Invalid Geometries
|
|
250
249
|
|
|
@@ -278,17 +277,17 @@ Smoothify uses [pytest](https://pytest.org/). After cloning the repository, inst
|
|
|
278
277
|
# Install dependencies (including the dev group)
|
|
279
278
|
uv sync
|
|
280
279
|
|
|
281
|
-
# Run all tests
|
|
280
|
+
# Run all tests (parallelised across CPU cores by default via pytest-xdist)
|
|
282
281
|
uv run pytest tests/
|
|
283
282
|
|
|
284
283
|
# Run with coverage
|
|
285
284
|
uv run pytest tests/ --cov=smoothify --cov-report=html
|
|
286
285
|
|
|
287
|
-
# Run a single test
|
|
288
|
-
uv run pytest tests/test_chaikin.py::TestChaikinCornerCutting::test_simple_square_polygon
|
|
286
|
+
# Run a single test (add `-n 0` to disable parallelism for clearer output)
|
|
287
|
+
uv run pytest tests/test_chaikin.py::TestChaikinCornerCutting::test_simple_square_polygon -n 0
|
|
289
288
|
```
|
|
290
289
|
|
|
291
|
-
If you prefer not to use uv, install the dev dependencies into your environment and run `pytest tests/` directly.
|
|
290
|
+
The suite runs in parallel by default (`-n auto` in `pytest.ini`); pass `-n 0` to run serially when debugging. If you prefer not to use uv, install the dev dependencies into your environment and run `pytest tests/` directly.
|
|
292
291
|
|
|
293
292
|
## Contributing
|
|
294
293
|
|
|
@@ -196,22 +196,21 @@ smoothed = smoothify(
|
|
|
196
196
|
|
|
197
197
|
## How It Works
|
|
198
198
|
|
|
199
|
-
Smoothify uses an advanced multi-step smoothing pipeline:
|
|
199
|
+
Smoothify uses an advanced multi-step smoothing pipeline. The numbered steps below correspond to the panels in the figure:
|
|
200
200
|
|
|
201
201
|
<p align="left">
|
|
202
202
|
<img src="https://raw.githubusercontent.com/DPIRD-DMA/Smoothify/main/images/pipeline_steps.png" alt="Smoothify pipeline steps" width="800">
|
|
203
203
|
</p>
|
|
204
204
|
|
|
205
205
|
|
|
206
|
-
1.
|
|
207
|
-
2.
|
|
208
|
-
3.
|
|
209
|
-
4.
|
|
210
|
-
5.
|
|
211
|
-
6.
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
9. Detects and repairs any sharp folds left by features near the smoothing scale (e.g. one-pixel-wide arms), using a small morphological opening/closing bounded at `segment_length / 4`
|
|
206
|
+
1. **Pixelated input** — a polygon straight from raster-to-vector conversion, with a stair-stepped boundary
|
|
207
|
+
2. **Multiple variants** (for Polygons) that start at evenly spaced arc-length positions, each simplified to strip staircase noise, so no artifact is tied to a fixed start vertex
|
|
208
|
+
3. **Chaikin corner cutting** applied to each variant
|
|
209
|
+
4. **Per-point median merge** — a start-invariant consensus that resolves the variants' disagreements
|
|
210
|
+
5. **Final smoothing pass** on the merged result
|
|
211
|
+
6. **Restore original area** via buffering (for Polygons, when `preserve_area=True`)
|
|
212
|
+
|
|
213
|
+
Two steps are not shown in the figure: before step 2, touching holes are joined (for Polygons, when `merge_holes=True`) so they smooth as one opening; and after step 6, the result is checked for sharp concave folds left by features near the smoothing scale (e.g. one-pixel-wide arms) and repaired with a small morphological opening/closing bounded at `segment_length / 4`.
|
|
215
214
|
|
|
216
215
|
## Invalid Geometries
|
|
217
216
|
|
|
@@ -245,17 +244,17 @@ Smoothify uses [pytest](https://pytest.org/). After cloning the repository, inst
|
|
|
245
244
|
# Install dependencies (including the dev group)
|
|
246
245
|
uv sync
|
|
247
246
|
|
|
248
|
-
# Run all tests
|
|
247
|
+
# Run all tests (parallelised across CPU cores by default via pytest-xdist)
|
|
249
248
|
uv run pytest tests/
|
|
250
249
|
|
|
251
250
|
# Run with coverage
|
|
252
251
|
uv run pytest tests/ --cov=smoothify --cov-report=html
|
|
253
252
|
|
|
254
|
-
# Run a single test
|
|
255
|
-
uv run pytest tests/test_chaikin.py::TestChaikinCornerCutting::test_simple_square_polygon
|
|
253
|
+
# Run a single test (add `-n 0` to disable parallelism for clearer output)
|
|
254
|
+
uv run pytest tests/test_chaikin.py::TestChaikinCornerCutting::test_simple_square_polygon -n 0
|
|
256
255
|
```
|
|
257
256
|
|
|
258
|
-
If you prefer not to use uv, install the dev dependencies into your environment and run `pytest tests/` directly.
|
|
257
|
+
The suite runs in parallel by default (`-n auto` in `pytest.ini`); pass `-n 0` to run serially when debugging. If you prefer not to use uv, install the dev dependencies into your environment and run `pytest tests/` directly.
|
|
259
258
|
|
|
260
259
|
## Contributing
|
|
261
260
|
|
|
Binary file
|