smoothify 0.2.1__tar.gz → 0.2.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.
Files changed (56) hide show
  1. smoothify-0.2.3/.github/workflows/ci.yml +40 -0
  2. smoothify-0.2.3/.github/workflows/publish.yml +75 -0
  3. smoothify-0.2.3/.gitignore +186 -0
  4. smoothify-0.2.3/.pre-commit-config.yaml +21 -0
  5. smoothify-0.2.3/.python-version +1 -0
  6. smoothify-0.2.3/.vscode/settings.json +42 -0
  7. smoothify-0.2.3/CHANGELOG.md +40 -0
  8. {smoothify-0.2.1 → smoothify-0.2.3}/PKG-INFO +61 -7
  9. smoothify-0.2.1/smoothify.egg-info/PKG-INFO → smoothify-0.2.3/README.md +45 -24
  10. smoothify-0.2.3/RELEASING.md +50 -0
  11. smoothify-0.2.3/examples/Water.gpkg +0 -0
  12. smoothify-0.2.3/examples/Water_Smoothed.gpkg +0 -0
  13. smoothify-0.2.3/examples/real_world_water_example.ipynb +773 -0
  14. smoothify-0.2.3/examples/smoothify_vs_shapely_comparison.ipynb +683 -0
  15. smoothify-0.2.3/examples/usage_examples.ipynb +830 -0
  16. smoothify-0.2.3/images/example_1_polygon.png +0 -0
  17. smoothify-0.2.3/images/example_2_linestring.png +0 -0
  18. smoothify-0.2.3/images/example_3_iterations.png +0 -0
  19. smoothify-0.2.3/images/example_4_merging.png +0 -0
  20. smoothify-0.2.3/images/generate_example_images.ipynb +366 -0
  21. smoothify-0.2.3/images/generate_readme_image.ipynb +220 -0
  22. smoothify-0.2.3/images/smoothify_hero.png +0 -0
  23. smoothify-0.2.3/images/smoothify_logo.png +0 -0
  24. smoothify-0.2.3/pyproject.toml +80 -0
  25. smoothify-0.2.3/pytest.ini +16 -0
  26. {smoothify-0.2.1 → smoothify-0.2.3}/smoothify/__init__.py +7 -1
  27. smoothify-0.2.3/smoothify/_version.py +24 -0
  28. {smoothify-0.2.1 → smoothify-0.2.3}/smoothify/coordinator.py +23 -7
  29. {smoothify-0.2.1 → smoothify-0.2.3}/smoothify/geometry_ops.py +109 -17
  30. smoothify-0.2.3/smoothify/py.typed +0 -0
  31. {smoothify-0.2.1 → smoothify-0.2.3}/smoothify/smoothify_core.py +32 -12
  32. smoothify-0.2.1/README.md → smoothify-0.2.3/smoothify.egg-info/PKG-INFO +78 -6
  33. smoothify-0.2.3/smoothify.egg-info/SOURCES.txt +51 -0
  34. smoothify-0.2.3/tests/README.md +26 -0
  35. smoothify-0.2.3/tests/__init__.py +1 -0
  36. smoothify-0.2.3/tests/conftest.py +42 -0
  37. smoothify-0.2.3/tests/test_all_geometry_types.py +988 -0
  38. {smoothify-0.2.1 → smoothify-0.2.3}/tests/test_area_tolerance.py +0 -1
  39. {smoothify-0.2.1 → smoothify-0.2.3}/tests/test_auto_segment_length.py +122 -55
  40. smoothify-0.2.3/tests/test_data/naip_owm_water_bodies.geojson +121 -0
  41. {smoothify-0.2.1 → smoothify-0.2.3}/tests/test_edge_cases_coverage.py +12 -5
  42. {smoothify-0.2.1 → smoothify-0.2.3}/tests/test_geometry_types.py +38 -24
  43. smoothify-0.2.3/tests/test_invalid_polygon.py +174 -0
  44. {smoothify-0.2.1 → smoothify-0.2.3}/tests/test_real_world_data.py +5 -1
  45. {smoothify-0.2.1 → smoothify-0.2.3}/tests/test_smoothify_api.py +40 -33
  46. {smoothify-0.2.1 → smoothify-0.2.3}/tests/test_smoothify_core.py +35 -4
  47. smoothify-0.2.3/tests/visual_tests.ipynb +1231 -0
  48. smoothify-0.2.1/pyproject.toml +0 -43
  49. smoothify-0.2.1/smoothify/__version__.py +0 -1
  50. smoothify-0.2.1/smoothify.egg-info/SOURCES.txt +0 -21
  51. {smoothify-0.2.1 → smoothify-0.2.3}/LICENSE +0 -0
  52. {smoothify-0.2.1 → smoothify-0.2.3}/setup.cfg +0 -0
  53. {smoothify-0.2.1 → smoothify-0.2.3}/smoothify.egg-info/dependency_links.txt +0 -0
  54. {smoothify-0.2.1 → smoothify-0.2.3}/smoothify.egg-info/requires.txt +0 -0
  55. {smoothify-0.2.1 → smoothify-0.2.3}/smoothify.egg-info/top_level.txt +0 -0
  56. {smoothify-0.2.1 → smoothify-0.2.3}/tests/test_chaikin.py +0 -0
@@ -0,0 +1,40 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ check:
9
+ runs-on: ubuntu-latest
10
+ strategy:
11
+ fail-fast: false
12
+ matrix:
13
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
14
+ steps:
15
+ - uses: actions/checkout@v6
16
+ with:
17
+ fetch-depth: 0 # full history so setuptools-scm can read tags
18
+
19
+ - uses: actions/setup-python@v6
20
+ with:
21
+ python-version: ${{ matrix.python-version }}
22
+
23
+ - uses: astral-sh/setup-uv@v8.0.0
24
+ with:
25
+ cache-suffix: ${{ matrix.python-version }}
26
+
27
+ - run: uv sync --all-extras --dev
28
+
29
+ - name: Lint
30
+ run: uv run ruff check .
31
+
32
+ - name: Type check
33
+ run: uv run mypy smoothify/
34
+
35
+ - name: Test
36
+ run: uv run pytest tests/ -x -q
37
+
38
+ - name: Notebook smoke test
39
+ if: matrix.python-version == '3.11'
40
+ run: uv run pytest --nbmake examples/*.ipynb -q
@@ -0,0 +1,75 @@
1
+ name: Publish to PyPI
2
+
3
+ # Triggered by pushing a tag that looks like a version, e.g. `v1.0.0`.
4
+ # The version comes from the git tag itself (via setuptools-scm) — no file to bump.
5
+ # Publishing uses PyPI trusted publishing (OIDC) — no API tokens, no secrets.
6
+ # To enable: at https://pypi.org/manage/project/smoothify/settings/publishing/
7
+ # add a publisher with:
8
+ # Owner DPIRD-DMA
9
+ # Repository name Smoothify
10
+ # Workflow name publish.yml
11
+ # Environment pypi
12
+
13
+ on:
14
+ push:
15
+ tags:
16
+ - "v[0-9]+.[0-9]+.[0-9]+*"
17
+
18
+ jobs:
19
+ build:
20
+ name: Build distribution
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - uses: actions/checkout@v6
24
+ with:
25
+ fetch-depth: 0 # full history so setuptools-scm can read tags
26
+
27
+ - uses: actions/setup-python@v6
28
+ with:
29
+ python-version: "3.11"
30
+
31
+ - uses: astral-sh/setup-uv@v8.0.0
32
+
33
+ - name: Build sdist + wheel
34
+ run: uv build
35
+
36
+ - uses: actions/upload-artifact@v4
37
+ with:
38
+ name: python-package-distributions
39
+ path: dist/
40
+
41
+ publish-to-pypi:
42
+ name: Publish to PyPI
43
+ needs: build
44
+ runs-on: ubuntu-latest
45
+ environment:
46
+ name: pypi
47
+ url: https://pypi.org/p/smoothify
48
+ permissions:
49
+ id-token: write # required for trusted publishing
50
+
51
+ steps:
52
+ - uses: actions/download-artifact@v4
53
+ with:
54
+ name: python-package-distributions
55
+ path: dist/
56
+
57
+ - uses: pypa/gh-action-pypi-publish@release/v1
58
+
59
+ github-release:
60
+ name: Create GitHub Release
61
+ needs: publish-to-pypi
62
+ runs-on: ubuntu-latest
63
+ permissions:
64
+ contents: write # required to create releases
65
+
66
+ steps:
67
+ - uses: actions/download-artifact@v4
68
+ with:
69
+ name: python-package-distributions
70
+ path: dist/
71
+
72
+ - uses: softprops/action-gh-release@v2
73
+ with:
74
+ files: dist/*
75
+ generate_release_notes: true
@@ -0,0 +1,186 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ #uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ #poetry.lock
109
+
110
+ # pdm
111
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
112
+ #pdm.lock
113
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
114
+ # in version control.
115
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
116
+ .pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
121
+ __pypackages__/
122
+
123
+ # Celery stuff
124
+ celerybeat-schedule
125
+ celerybeat.pid
126
+
127
+ # SageMath parsed files
128
+ *.sage.py
129
+
130
+ # Environments
131
+ .env
132
+ .venv
133
+ env/
134
+ venv/
135
+ ENV/
136
+ env.bak/
137
+ venv.bak/
138
+
139
+ # Spyder project settings
140
+ .spyderproject
141
+ .spyproject
142
+
143
+ # Rope project settings
144
+ .ropeproject
145
+
146
+ # mkdocs documentation
147
+ /site
148
+
149
+ # mypy
150
+ .mypy_cache/
151
+ .dmypy.json
152
+ dmypy.json
153
+
154
+ # Pyre type checker
155
+ .pyre/
156
+
157
+ # pytype static type analyzer
158
+ .pytype/
159
+
160
+ # Cython debug symbols
161
+ cython_debug/
162
+
163
+ # PyCharm
164
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
165
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
166
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
167
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
168
+ #.idea/
169
+
170
+ # Ruff stuff:
171
+ .ruff_cache/
172
+
173
+ # PyPI configuration file
174
+ .pypirc
175
+ private test data/*
176
+ *.gpkg-shm
177
+ *.gpkg-wal
178
+ *.DS_Store
179
+ test data/output/*
180
+ examples/Water_Smoothed.gpkg
181
+
182
+ # setuptools-scm generated version file
183
+ smoothify/_version.py
184
+
185
+ # uv lockfile (not tracked — library, resolve fresh)
186
+ uv.lock
@@ -0,0 +1,21 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.15.8
4
+ hooks:
5
+ - id: ruff-check
6
+ - id: ruff-format
7
+ - repo: local
8
+ hooks:
9
+ - id: mypy
10
+ name: mypy
11
+ entry: uv run mypy smoothify/
12
+ language: system
13
+ pass_filenames: false
14
+ always_run: true
15
+ - id: pytest
16
+ name: pytest
17
+ entry: uv run pytest tests/ -x -q -m "not slow"
18
+ language: system
19
+ pass_filenames: false
20
+ always_run: true
21
+ stages: [pre-push]
@@ -0,0 +1 @@
1
+ 3.13
@@ -0,0 +1,42 @@
1
+ {
2
+ "cSpell.words": [
3
+ "brentq",
4
+ "capstyle",
5
+ "chaikin",
6
+ "darkgreen",
7
+ "edgecolor",
8
+ "facecolor",
9
+ "figsize",
10
+ "fontsize",
11
+ "fontweight",
12
+ "geodataframe",
13
+ "geopandas",
14
+ "gpkg",
15
+ "ipykernel",
16
+ "joblib",
17
+ "lightgreen",
18
+ "linalg",
19
+ "linearing",
20
+ "linestrings",
21
+ "matplotlib",
22
+ "multipolygons",
23
+ "ndarray",
24
+ "numpy",
25
+ "polygonized",
26
+ "pytest",
27
+ "scipy",
28
+ "segmentize",
29
+ "setuptools",
30
+ "shapley",
31
+ "smoothify",
32
+ "soomthify",
33
+ "suptitle",
34
+ "xlabel",
35
+ "xlim",
36
+ "xticks",
37
+ "xtol",
38
+ "ylabel",
39
+ "ylim",
40
+ "yticks"
41
+ ],
42
+ }
@@ -0,0 +1,40 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [0.2.3] - 2026-06-02
8
+
9
+ ### Added
10
+ - The package now ships inline type hints and a `py.typed` marker, so type checkers (mypy, Pyright/Pylance) pick up `smoothify()`'s signatures and overloads in downstream code.
11
+
12
+ ### Changed
13
+ - Invalid geometries (e.g. self-intersecting polygons) are now returned unchanged with a warning instead of crashing with a cryptic error or silently collapsing to an empty geometry. Behaviour is consistent across single geometries, lists/collections, and GeoDataFrames (and regardless of `merge_collection`). Repair them with shapely's `make_valid()` first if you want them smoothed.
14
+
15
+ ## [0.2.2] - 2026-03-24
16
+
17
+ ### Fixed
18
+ - Fixed LineString smoothing: self-intersecting lines are no longer run through the polygon-only `make_valid`/`unary_union` step, which could split them into a MultiLineString at crossing points
19
+ - Preserve coordinate dimensionality during Chaikin corner cutting instead of forcing 2D output, so smoothed geometries keep their original dimensions
20
+
21
+ ## [0.2.1] - 2026-02-25
22
+
23
+ ### Fixed
24
+ - Fixed `TopologyException` crash on thin/elongated polygons by validating smoothed variants before union
25
+ - Fixed crash when hole subtraction splits a polygon into a MultiPolygon (e.g. tiny holes relative to segment length)
26
+
27
+ ## [0.2.0] - 2026-02-25
28
+
29
+ ### Fixed
30
+ - `smooth_iterations=0` now returns the original input unchanged instead of running the geometry through segmentize/simplify pipeline without smoothing
31
+
32
+ ## [0.1.0] - 2025-11-25
33
+
34
+ ### Added
35
+ - Initial public release
36
+ - Core smoothing functionality using Chaikin's corner-cutting algorithm
37
+ - Support for all Shapely geometry types (Polygon, LineString, MultiPolygon, etc.)
38
+ - Automatic segment length detection
39
+ - Parallel processing support
40
+ - Area preservation for polygons
@@ -1,11 +1,26 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: smoothify
3
- Version: 0.2.1
3
+ Version: 0.2.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
7
7
  Project-URL: Homepage, https://github.com/DPIRD-DMA/Smoothify
8
+ Project-URL: Repository, https://github.com/DPIRD-DMA/Smoothify
9
+ Project-URL: Issues, https://github.com/DPIRD-DMA/Smoothify/issues
10
+ Project-URL: Changelog, https://github.com/DPIRD-DMA/Smoothify/blob/main/CHANGELOG.md
8
11
  Keywords: geometry,smoothing,smooth,GIS,raster,vector,chaikin,shapely,geopandas
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Operating System :: OS Independent
22
+ Classifier: Topic :: Scientific/Engineering :: GIS
23
+ Classifier: Topic :: Scientific/Engineering :: Image Processing
9
24
  Requires-Python: >=3.10
10
25
  Description-Content-Type: text/markdown
11
26
  License-File: LICENSE
@@ -16,15 +31,18 @@ Requires-Dist: scipy>=1.11.0
16
31
  Requires-Dist: shapely>=2.0.2
17
32
  Dynamic: license-file
18
33
 
19
- <p align="left">
34
+ <p align="center">
20
35
  <img src="https://raw.githubusercontent.com/DPIRD-DMA/Smoothify/main/images/smoothify_logo.png" alt="Smoothify Text" width="600">
21
36
  </p>
22
37
 
23
- [![Python Version](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/downloads/)
24
- [![License](https://img.shields.io/badge/license-MIT-green)](https://github.com/DPIRD-DMA/Smoothify/blob/main/LICENSE)
25
- [![PyPI version](https://img.shields.io/pypi/v/smoothify.svg)](https://pypi.org/project/smoothify/)
26
- [![Conda version](https://img.shields.io/conda/vn/conda-forge/smoothify.svg)](https://anaconda.org/conda-forge/smoothify)
27
- [![Tutorials](https://img.shields.io/badge/Tutorials-Learn-brightgreen)](https://github.com/DPIRD-DMA/Smoothify/tree/main/examples)
38
+ <p align="center">
39
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.10%2B-blue" alt="Python Version"></a>
40
+ <a href="https://github.com/DPIRD-DMA/Smoothify/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License"></a>
41
+ <a href="https://pypi.org/project/smoothify/"><img src="https://img.shields.io/pypi/v/smoothify.svg" alt="PyPI version"></a>
42
+ <a href="https://pepy.tech/projects/smoothify"><img src="https://static.pepy.tech/badge/smoothify" alt="PyPI downloads"></a>
43
+ <a href="https://anaconda.org/conda-forge/smoothify"><img src="https://img.shields.io/conda/vn/conda-forge/smoothify.svg" alt="Conda version"></a>
44
+ <a href="https://github.com/DPIRD-DMA/Smoothify/tree/main/examples"><img src="https://img.shields.io/badge/Tutorials-Learn-brightgreen" alt="Tutorials"></a>
45
+ </p>
28
46
 
29
47
  📋 [View Changelog](https://github.com/DPIRD-DMA/Smoothify/blob/main/CHANGELOG.md)
30
48
 
@@ -220,6 +238,22 @@ Smoothify uses an advanced multi-step smoothing pipeline:
220
238
  6. Applies final smoothing pass
221
239
  7. Optionally restores original area via buffering (for Polygons)
222
240
 
241
+ ## Invalid Geometries
242
+
243
+ Smoothify does not repair invalid input. If it encounters an invalid geometry (e.g. a self-intersecting polygon), it returns that geometry **unchanged** and emits a warning, instead of crashing or silently producing an empty geometry. This is consistent whether you pass a single geometry, a list/collection, or a GeoDataFrame.
244
+
245
+ If you want invalid geometries smoothed, repair them first with shapely's `make_valid()`:
246
+
247
+ ```python
248
+ # GeoDataFrame
249
+ gdf.geometry = gdf.geometry.make_valid()
250
+ smoothed_gdf = smoothify(gdf, segment_length=10.0)
251
+
252
+ # Single geometry
253
+ from shapely import make_valid
254
+ smoothed = smoothify(make_valid(polygon), segment_length=1.0)
255
+ ```
256
+
223
257
  ## Performance Considerations
224
258
 
225
259
  - **Parallel Processing**: For large GeoDataFrames or collections, use `num_cores` = 0 to enable parallel processing
@@ -227,6 +261,26 @@ Smoothify uses an advanced multi-step smoothing pipeline:
227
261
  - **Memory Usage**: Scales with geometry complexity. The algorithm creates multiple variants during smoothing
228
262
  - **Optimal segment_length**: Should match the original raster cell size (pixel size) or be slightly larger for best results
229
263
 
264
+ ## Running the Tests
265
+
266
+ Smoothify uses [pytest](https://pytest.org/). After cloning the repository, install the development dependencies and run the suite with [uv](https://docs.astral.sh/uv/):
267
+
268
+ ```bash
269
+ # Install dependencies (including the dev group)
270
+ uv sync
271
+
272
+ # Run all tests
273
+ uv run pytest tests/
274
+
275
+ # Run with coverage
276
+ uv run pytest tests/ --cov=smoothify --cov-report=html
277
+
278
+ # Run a single test
279
+ uv run pytest tests/test_chaikin.py::TestChaikinCornerCutting::test_simple_square_polygon
280
+ ```
281
+
282
+ If you prefer not to use uv, install the dev dependencies into your environment and run `pytest tests/` directly.
283
+
230
284
  ## Contributing
231
285
 
232
286
  Contributions are welcome! Please feel free to submit a Pull Request.
@@ -1,30 +1,15 @@
1
- Metadata-Version: 2.4
2
- Name: smoothify
3
- Version: 0.2.1
4
- Summary: Transform pixelated geometries from raster data into smooth natural looking features
5
- Author-email: Nick Wright <nicholas.wright@dpird.wa.gov.au>
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/DPIRD-DMA/Smoothify
8
- Keywords: geometry,smoothing,smooth,GIS,raster,vector,chaikin,shapely,geopandas
9
- Requires-Python: >=3.10
10
- Description-Content-Type: text/markdown
11
- License-File: LICENSE
12
- Requires-Dist: geopandas>=1.0.0
13
- Requires-Dist: joblib>=1.4.0
14
- Requires-Dist: numpy>=1.27.0
15
- Requires-Dist: scipy>=1.11.0
16
- Requires-Dist: shapely>=2.0.2
17
- Dynamic: license-file
18
-
19
- <p align="left">
1
+ <p align="center">
20
2
  <img src="https://raw.githubusercontent.com/DPIRD-DMA/Smoothify/main/images/smoothify_logo.png" alt="Smoothify Text" width="600">
21
3
  </p>
22
4
 
23
- [![Python Version](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/downloads/)
24
- [![License](https://img.shields.io/badge/license-MIT-green)](https://github.com/DPIRD-DMA/Smoothify/blob/main/LICENSE)
25
- [![PyPI version](https://img.shields.io/pypi/v/smoothify.svg)](https://pypi.org/project/smoothify/)
26
- [![Conda version](https://img.shields.io/conda/vn/conda-forge/smoothify.svg)](https://anaconda.org/conda-forge/smoothify)
27
- [![Tutorials](https://img.shields.io/badge/Tutorials-Learn-brightgreen)](https://github.com/DPIRD-DMA/Smoothify/tree/main/examples)
5
+ <p align="center">
6
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.10%2B-blue" alt="Python Version"></a>
7
+ <a href="https://github.com/DPIRD-DMA/Smoothify/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License"></a>
8
+ <a href="https://pypi.org/project/smoothify/"><img src="https://img.shields.io/pypi/v/smoothify.svg" alt="PyPI version"></a>
9
+ <a href="https://pepy.tech/projects/smoothify"><img src="https://static.pepy.tech/badge/smoothify" alt="PyPI downloads"></a>
10
+ <a href="https://anaconda.org/conda-forge/smoothify"><img src="https://img.shields.io/conda/vn/conda-forge/smoothify.svg" alt="Conda version"></a>
11
+ <a href="https://github.com/DPIRD-DMA/Smoothify/tree/main/examples"><img src="https://img.shields.io/badge/Tutorials-Learn-brightgreen" alt="Tutorials"></a>
12
+ </p>
28
13
 
29
14
  📋 [View Changelog](https://github.com/DPIRD-DMA/Smoothify/blob/main/CHANGELOG.md)
30
15
 
@@ -220,6 +205,22 @@ Smoothify uses an advanced multi-step smoothing pipeline:
220
205
  6. Applies final smoothing pass
221
206
  7. Optionally restores original area via buffering (for Polygons)
222
207
 
208
+ ## Invalid Geometries
209
+
210
+ Smoothify does not repair invalid input. If it encounters an invalid geometry (e.g. a self-intersecting polygon), it returns that geometry **unchanged** and emits a warning, instead of crashing or silently producing an empty geometry. This is consistent whether you pass a single geometry, a list/collection, or a GeoDataFrame.
211
+
212
+ If you want invalid geometries smoothed, repair them first with shapely's `make_valid()`:
213
+
214
+ ```python
215
+ # GeoDataFrame
216
+ gdf.geometry = gdf.geometry.make_valid()
217
+ smoothed_gdf = smoothify(gdf, segment_length=10.0)
218
+
219
+ # Single geometry
220
+ from shapely import make_valid
221
+ smoothed = smoothify(make_valid(polygon), segment_length=1.0)
222
+ ```
223
+
223
224
  ## Performance Considerations
224
225
 
225
226
  - **Parallel Processing**: For large GeoDataFrames or collections, use `num_cores` = 0 to enable parallel processing
@@ -227,6 +228,26 @@ Smoothify uses an advanced multi-step smoothing pipeline:
227
228
  - **Memory Usage**: Scales with geometry complexity. The algorithm creates multiple variants during smoothing
228
229
  - **Optimal segment_length**: Should match the original raster cell size (pixel size) or be slightly larger for best results
229
230
 
231
+ ## Running the Tests
232
+
233
+ Smoothify uses [pytest](https://pytest.org/). After cloning the repository, install the development dependencies and run the suite with [uv](https://docs.astral.sh/uv/):
234
+
235
+ ```bash
236
+ # Install dependencies (including the dev group)
237
+ uv sync
238
+
239
+ # Run all tests
240
+ uv run pytest tests/
241
+
242
+ # Run with coverage
243
+ uv run pytest tests/ --cov=smoothify --cov-report=html
244
+
245
+ # Run a single test
246
+ uv run pytest tests/test_chaikin.py::TestChaikinCornerCutting::test_simple_square_polygon
247
+ ```
248
+
249
+ If you prefer not to use uv, install the dev dependencies into your environment and run `pytest tests/` directly.
250
+
230
251
  ## Contributing
231
252
 
232
253
  Contributions are welcome! Please feel free to submit a Pull Request.
@@ -0,0 +1,50 @@
1
+ # Releasing
2
+
3
+ Smoothify is published to PyPI by tag-push. The version comes from the git tag
4
+ itself (via `setuptools-scm`) — there is **no version file to bump**.
5
+
6
+ ## Versioning
7
+
8
+ Follow [SemVer](https://semver.org/):
9
+
10
+ - **Major** (`v1.0.0` → `v2.0.0`): incompatible API change — removed args,
11
+ changed signatures, behavioural changes that break existing callers.
12
+ - **Minor** (`v1.0.0` → `v1.1.0`): backwards-compatible feature added.
13
+ - **Patch** (`v1.0.0` → `v1.0.1`): backwards-compatible bug fix.
14
+
15
+ Any commit that lands on `main` between releases shows up at install time as
16
+ a development version like `0.2.4.dev3+g1234abc`.
17
+
18
+ ## Release checklist
19
+
20
+ 1. **Sync `main`** locally:
21
+ ```bash
22
+ git checkout main
23
+ git pull
24
+ ```
25
+
26
+ 2. **Update [`CHANGELOG.md`](CHANGELOG.md)**: move the `## [Unreleased]` entries to
27
+ `## [<version>] - <YYYY-MM-DD>` and add a fresh empty `[Unreleased]` block at the top.
28
+ Commit on `main`:
29
+ ```bash
30
+ git commit -am "Prepare <version> changelog"
31
+ git push
32
+ ```
33
+
34
+ 3. **Tag and push**:
35
+ ```bash
36
+ git tag v<version> # e.g. v0.3.0
37
+ git push origin v<version>
38
+ ```
39
+ The tag must match `v<major>.<minor>.<patch>` (pre-release suffixes like
40
+ `v0.3.0rc1` also match the publish workflow's filter).
41
+
42
+ 4. **Approve the deploy**: the [`Publish to PyPI`](.github/workflows/publish.yml)
43
+ workflow runs `uv build` and uploads to PyPI via OIDC trusted publishing.
44
+ The `pypi` GitHub environment is gated by a required reviewer — go to
45
+ **Actions → Publish to PyPI → Review deployments → Approve** to release.
46
+
47
+ 5. **Verify**:
48
+ - PyPI page: <https://pypi.org/project/smoothify/>
49
+ - GitHub Releases: <https://github.com/DPIRD-DMA/Smoothify/releases>
50
+ (auto-generated release notes + wheel/sdist attached).
Binary file