smoothify 0.1.1__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 DPIRD-DMA
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,182 @@
1
+ Metadata-Version: 2.4
2
+ Name: smoothify
3
+ Version: 0.1.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">
20
+ <img src="https://raw.githubusercontent.com/DPIRD-DMA/Smoothify/main/images/smoothify_logo.png" alt="Smoothify Text" width="600">
21
+ </p>
22
+
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
+ [![Tutorials](https://img.shields.io/badge/Tutorials-Learn-brightgreen)](https://github.com/DPIRD-DMA/Smoothify/tree/main/examples)
27
+
28
+ 📋 [View Changelog](https://github.com/DPIRD-DMA/Smoothify/blob/main/CHANGELOG.md)
29
+
30
+ A Python package for smoothing and refining geometries derived from raster data classifications. Smoothify transforms jagged polygons and lines resulting from raster-to-vector conversion into smooth, visually appealing features using an optimized implementation of Chaikin's corner-cutting algorithm.
31
+
32
+ ## Problem
33
+
34
+ Polygons and lines derived from classified raster data (e.g., ML model predictions, spectral indices, or remote sensing classifications) often have unnatural "stair-stepped" or "pixelated" edges that:
35
+
36
+ - Are visually unappealing in maps and GIS applications
37
+ - Can be difficult to work with in downstream vector processing
38
+ - Don't represent the real-world features they're meant to depict
39
+
40
+ ## Solution
41
+
42
+ Smoothify applies an optimized implementation of Chaikin's corner-cutting algorithm along with other geometric processing to create smooth, natural-looking features while:
43
+
44
+ - Preserving the general shape and area of polygons
45
+ - Supporting all shapley geometry types
46
+ - Handling shapes with interior holes
47
+ - Efficiently processing large datasets with multiprocessing
48
+
49
+ <p align="left">
50
+ <img src="https://raw.githubusercontent.com/DPIRD-DMA/Smoothify/main/images/smoothify_hero.png" alt="Smoothify Hero Image" width="600">
51
+ </p>
52
+
53
+ ## Installation
54
+
55
+ ```bash
56
+ uv add smoothify
57
+ ```
58
+ or
59
+ ```bash
60
+ pip install smoothify
61
+ ```
62
+
63
+ ## Quick Start
64
+
65
+ ```python
66
+ import geopandas as gpd
67
+ from smoothify import smoothify
68
+
69
+ # Load your polygonized raster data
70
+ polygon_gdf = gpd.read_file("path/to/your/polygons.gpkg")
71
+
72
+ # Apply smoothing (segment_length auto-detected from geometry)
73
+ smoothed_gdf = smoothify(
74
+ geom=polygon_gdf,
75
+ smooth_iterations=3, # More iterations = smoother result
76
+ num_cores=4 # Use parallel processing for large datasets
77
+ )
78
+
79
+ # Or specify segment_length explicitly (generally recommended)
80
+ smoothed_gdf = smoothify(
81
+ geom=polygon_gdf,
82
+ segment_length=10.0, # Use the original raster resolution
83
+ smooth_iterations=3,
84
+ num_cores=4
85
+ )
86
+
87
+ # Save the result
88
+ smoothed_gdf.to_file("smoothed_polygons.gpkg")
89
+ ```
90
+
91
+ ## General Usage
92
+
93
+ The `smoothify()` function accepts three types of input:
94
+
95
+ ### 1. GeoDataFrame
96
+ ```python
97
+ import geopandas as gpd
98
+ from smoothify import smoothify
99
+
100
+ gdf = gpd.read_file("polygons.gpkg")
101
+ smoothed_gdf = smoothify(
102
+ geom=gdf,
103
+ segment_length=10.0,
104
+ smooth_iterations=3,
105
+ num_cores=4
106
+ )
107
+ ```
108
+
109
+ ### 2. Single Geometry
110
+ ```python
111
+ from shapely.geometry import Polygon
112
+ from smoothify import smoothify
113
+
114
+ polygon = Polygon([(0, 0), (10, 0), (10, 10), (0, 10)])
115
+ smoothed_polygon = smoothify(
116
+ geom=polygon,
117
+ smooth_iterations=3
118
+ )
119
+ ```
120
+
121
+ ### 3. List of Geometries or GeometryCollection
122
+ ```python
123
+ from shapely.geometry import Polygon, LineString
124
+ from smoothify import smoothify
125
+
126
+ geometries = [
127
+ Polygon([(0, 0), (10, 0), (10, 10), (0, 10)]),
128
+ LineString([(0, 0), (5, 5), (10, 0)])
129
+ ]
130
+ smoothed = smoothify(
131
+ geom=geometries,
132
+ segment_length=1.0,
133
+ smooth_iterations=3
134
+ )
135
+ ```
136
+
137
+ ## Parameters
138
+
139
+ | Parameter | Type | Default | Description |
140
+ |-----------|------|---------|-------------|
141
+ | `geom` | GeoDataFrame, BaseGeometry, or list[BaseGeometry] | Required | The geometry/geometries to smooth |
142
+ | `segment_length` | float | None | Resolution of the original raster data in map units. If None (default), automatically detects by finding the minimum segment length (from a data sample). Recommended to specify explicitly when known |
143
+ | `smooth_iterations` | int | 3 | Number of Chaikin corner-cutting iterations (typically 3-5). Higher values = smoother output with more vertices |
144
+ | `num_cores` | int | 0 | Number of CPU cores for parallel processing (0 = all available cores, 1 = serial) |
145
+ | `merge_collection` | bool | True | Whether to merge/dissolve adjacent geometries in collections before smoothing |
146
+ | `merge_multipolygons` | bool | True | Whether to merge adjacent polygons within MultiPolygons before smoothing |
147
+ | `preserve_area` | bool | True | Whether to restore original area after smoothing via buffering (applies to Polygons only) |
148
+ | `area_tolerance` | float | 0.01 | Percentage of original area allowed as error (e.g., 0.01 = 0.01% error = 99.99% preservation). Only affects Polygons when preserve_area=True |
149
+
150
+ ## How It Works
151
+
152
+ Smoothify uses an advanced multi-step smoothing pipeline:
153
+
154
+
155
+ 1. Adds intermediate vertices along line segments (segmentize)
156
+ 2. Generates multiple rotated variants (for Polygons) to avoid artifacts
157
+ 3. Simplifies each variant to remove noise
158
+ 4. Applies Chaikin corner cutting to smooth
159
+ 5. Merges all variants via union to eliminate start-point artifacts
160
+ 6. Applies final smoothing pass
161
+ 7. Optionally restores original area via buffering (for Polygons)
162
+
163
+ ## Performance Considerations
164
+
165
+ - **Parallel Processing**: For large GeoDataFrames or collections, use `num_cores` = 0 to enable parallel processing
166
+ - **Smoothing Iterations**: Values of 3-5 typically provide good results. Higher values create smoother output but increase processing time and vertex count
167
+ - **Memory Usage**: Scales with geometry complexity. The algorithm creates multiple variants during smoothing
168
+ - **Optimal segment_length**: Should match the original raster cell size (pixel size) or be slightly larger for best results
169
+
170
+ ## Contributing
171
+
172
+ Contributions are welcome! Please feel free to submit a Pull Request.
173
+
174
+ 1. Fork the repository
175
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
176
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
177
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
178
+ 5. Open a Pull Request
179
+
180
+ ## License
181
+
182
+ This project is licensed under the MIT License - see the [LICENSE](https://github.com/DPIRD-DMA/Smoothify/blob/main/LICENSE) file for details.
@@ -0,0 +1,164 @@
1
+ <p align="left">
2
+ <img src="https://raw.githubusercontent.com/DPIRD-DMA/Smoothify/main/images/smoothify_logo.png" alt="Smoothify Text" width="600">
3
+ </p>
4
+
5
+ [![Python Version](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/downloads/)
6
+ [![License](https://img.shields.io/badge/license-MIT-green)](https://github.com/DPIRD-DMA/Smoothify/blob/main/LICENSE)
7
+ [![PyPI version](https://img.shields.io/pypi/v/smoothify.svg)](https://pypi.org/project/smoothify/)
8
+ [![Tutorials](https://img.shields.io/badge/Tutorials-Learn-brightgreen)](https://github.com/DPIRD-DMA/Smoothify/tree/main/examples)
9
+
10
+ 📋 [View Changelog](https://github.com/DPIRD-DMA/Smoothify/blob/main/CHANGELOG.md)
11
+
12
+ A Python package for smoothing and refining geometries derived from raster data classifications. Smoothify transforms jagged polygons and lines resulting from raster-to-vector conversion into smooth, visually appealing features using an optimized implementation of Chaikin's corner-cutting algorithm.
13
+
14
+ ## Problem
15
+
16
+ Polygons and lines derived from classified raster data (e.g., ML model predictions, spectral indices, or remote sensing classifications) often have unnatural "stair-stepped" or "pixelated" edges that:
17
+
18
+ - Are visually unappealing in maps and GIS applications
19
+ - Can be difficult to work with in downstream vector processing
20
+ - Don't represent the real-world features they're meant to depict
21
+
22
+ ## Solution
23
+
24
+ Smoothify applies an optimized implementation of Chaikin's corner-cutting algorithm along with other geometric processing to create smooth, natural-looking features while:
25
+
26
+ - Preserving the general shape and area of polygons
27
+ - Supporting all shapley geometry types
28
+ - Handling shapes with interior holes
29
+ - Efficiently processing large datasets with multiprocessing
30
+
31
+ <p align="left">
32
+ <img src="https://raw.githubusercontent.com/DPIRD-DMA/Smoothify/main/images/smoothify_hero.png" alt="Smoothify Hero Image" width="600">
33
+ </p>
34
+
35
+ ## Installation
36
+
37
+ ```bash
38
+ uv add smoothify
39
+ ```
40
+ or
41
+ ```bash
42
+ pip install smoothify
43
+ ```
44
+
45
+ ## Quick Start
46
+
47
+ ```python
48
+ import geopandas as gpd
49
+ from smoothify import smoothify
50
+
51
+ # Load your polygonized raster data
52
+ polygon_gdf = gpd.read_file("path/to/your/polygons.gpkg")
53
+
54
+ # Apply smoothing (segment_length auto-detected from geometry)
55
+ smoothed_gdf = smoothify(
56
+ geom=polygon_gdf,
57
+ smooth_iterations=3, # More iterations = smoother result
58
+ num_cores=4 # Use parallel processing for large datasets
59
+ )
60
+
61
+ # Or specify segment_length explicitly (generally recommended)
62
+ smoothed_gdf = smoothify(
63
+ geom=polygon_gdf,
64
+ segment_length=10.0, # Use the original raster resolution
65
+ smooth_iterations=3,
66
+ num_cores=4
67
+ )
68
+
69
+ # Save the result
70
+ smoothed_gdf.to_file("smoothed_polygons.gpkg")
71
+ ```
72
+
73
+ ## General Usage
74
+
75
+ The `smoothify()` function accepts three types of input:
76
+
77
+ ### 1. GeoDataFrame
78
+ ```python
79
+ import geopandas as gpd
80
+ from smoothify import smoothify
81
+
82
+ gdf = gpd.read_file("polygons.gpkg")
83
+ smoothed_gdf = smoothify(
84
+ geom=gdf,
85
+ segment_length=10.0,
86
+ smooth_iterations=3,
87
+ num_cores=4
88
+ )
89
+ ```
90
+
91
+ ### 2. Single Geometry
92
+ ```python
93
+ from shapely.geometry import Polygon
94
+ from smoothify import smoothify
95
+
96
+ polygon = Polygon([(0, 0), (10, 0), (10, 10), (0, 10)])
97
+ smoothed_polygon = smoothify(
98
+ geom=polygon,
99
+ smooth_iterations=3
100
+ )
101
+ ```
102
+
103
+ ### 3. List of Geometries or GeometryCollection
104
+ ```python
105
+ from shapely.geometry import Polygon, LineString
106
+ from smoothify import smoothify
107
+
108
+ geometries = [
109
+ Polygon([(0, 0), (10, 0), (10, 10), (0, 10)]),
110
+ LineString([(0, 0), (5, 5), (10, 0)])
111
+ ]
112
+ smoothed = smoothify(
113
+ geom=geometries,
114
+ segment_length=1.0,
115
+ smooth_iterations=3
116
+ )
117
+ ```
118
+
119
+ ## Parameters
120
+
121
+ | Parameter | Type | Default | Description |
122
+ |-----------|------|---------|-------------|
123
+ | `geom` | GeoDataFrame, BaseGeometry, or list[BaseGeometry] | Required | The geometry/geometries to smooth |
124
+ | `segment_length` | float | None | Resolution of the original raster data in map units. If None (default), automatically detects by finding the minimum segment length (from a data sample). Recommended to specify explicitly when known |
125
+ | `smooth_iterations` | int | 3 | Number of Chaikin corner-cutting iterations (typically 3-5). Higher values = smoother output with more vertices |
126
+ | `num_cores` | int | 0 | Number of CPU cores for parallel processing (0 = all available cores, 1 = serial) |
127
+ | `merge_collection` | bool | True | Whether to merge/dissolve adjacent geometries in collections before smoothing |
128
+ | `merge_multipolygons` | bool | True | Whether to merge adjacent polygons within MultiPolygons before smoothing |
129
+ | `preserve_area` | bool | True | Whether to restore original area after smoothing via buffering (applies to Polygons only) |
130
+ | `area_tolerance` | float | 0.01 | Percentage of original area allowed as error (e.g., 0.01 = 0.01% error = 99.99% preservation). Only affects Polygons when preserve_area=True |
131
+
132
+ ## How It Works
133
+
134
+ Smoothify uses an advanced multi-step smoothing pipeline:
135
+
136
+
137
+ 1. Adds intermediate vertices along line segments (segmentize)
138
+ 2. Generates multiple rotated variants (for Polygons) to avoid artifacts
139
+ 3. Simplifies each variant to remove noise
140
+ 4. Applies Chaikin corner cutting to smooth
141
+ 5. Merges all variants via union to eliminate start-point artifacts
142
+ 6. Applies final smoothing pass
143
+ 7. Optionally restores original area via buffering (for Polygons)
144
+
145
+ ## Performance Considerations
146
+
147
+ - **Parallel Processing**: For large GeoDataFrames or collections, use `num_cores` = 0 to enable parallel processing
148
+ - **Smoothing Iterations**: Values of 3-5 typically provide good results. Higher values create smoother output but increase processing time and vertex count
149
+ - **Memory Usage**: Scales with geometry complexity. The algorithm creates multiple variants during smoothing
150
+ - **Optimal segment_length**: Should match the original raster cell size (pixel size) or be slightly larger for best results
151
+
152
+ ## Contributing
153
+
154
+ Contributions are welcome! Please feel free to submit a Pull Request.
155
+
156
+ 1. Fork the repository
157
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
158
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
159
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
160
+ 5. Open a Pull Request
161
+
162
+ ## License
163
+
164
+ This project is licensed under the MIT License - see the [LICENSE](https://github.com/DPIRD-DMA/Smoothify/blob/main/LICENSE) file for details.
@@ -0,0 +1,43 @@
1
+ [build-system]
2
+ requires = ["setuptools >= 61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "smoothify"
7
+ authors = [{name = "Nick Wright", email = "nicholas.wright@dpird.wa.gov.au"}]
8
+ dynamic = ["version"]
9
+ description = "Transform pixelated geometries from raster data into smooth natural looking features"
10
+ requires-python = ">=3.10"
11
+ dependencies = [
12
+ "geopandas>=1.0.0",
13
+ "joblib>=1.4.0",
14
+ "numpy>=1.27.0",
15
+ "scipy>=1.11.0",
16
+ "shapely>=2.0.2",
17
+ ]
18
+
19
+ license = "MIT"
20
+ readme = {file = "README.md", content-type = "text/markdown"}
21
+ keywords = ["geometry", "smoothing", "smooth", "GIS", "raster", "vector", "chaikin", "shapely", "geopandas"]
22
+
23
+
24
+ [tool.setuptools.dynamic]
25
+ version = {attr = "smoothify.__version__.__version__"}
26
+
27
+ [dependency-groups]
28
+ dev = [
29
+ "descartes>=1.1.0",
30
+ "ipykernel>=6.29.5",
31
+ "matplotlib>=3.9.4",
32
+ "pytest>=8.4.2",
33
+ "pytest-cov>=6.0.0",
34
+ ]
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/DPIRD-DMA/Smoothify"
38
+
39
+ [tool.setuptools]
40
+ packages = ["smoothify"]
41
+
42
+ [tool.ruff.lint]
43
+ select = ["E", "F", "B"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,28 @@
1
+ """
2
+ Smoothify - Geometry Smoothing Package
3
+
4
+ A Python package for smoothing and refining geometries derived from raster data
5
+ classifications. Transforms jagged polygons and lines resulting from raster-to-vector
6
+ conversion into smooth, visually appealing features using an optimized implementation
7
+ of Chaikin's corner-cutting algorithm.
8
+
9
+ Supports:
10
+ - Polygons (including those with holes)
11
+ - LineStrings
12
+ - MultiPolygons
13
+ - MultiLineStrings
14
+ - GeometryCollections
15
+ - GeoDataFrames
16
+
17
+ Main function:
18
+ smoothify() - Apply Chaikin corner-cutting smoothing to geometries
19
+ """
20
+
21
+ from .__version__ import __version__
22
+ from .coordinator import smoothify
23
+
24
+ # Package-wide exports
25
+ __all__ = [
26
+ "smoothify",
27
+ "__version__",
28
+ ]
@@ -0,0 +1 @@
1
+ __version__ = "0.1.1"
@@ -0,0 +1,173 @@
1
+ from multiprocessing import cpu_count
2
+ from typing import Optional, Sequence, overload
3
+
4
+ import geopandas as gpd
5
+ from shapely.geometry import (
6
+ GeometryCollection,
7
+ LinearRing,
8
+ LineString,
9
+ MultiLineString,
10
+ MultiPolygon,
11
+ Polygon,
12
+ )
13
+ from shapely.geometry.base import BaseGeometry
14
+
15
+ from smoothify.geometry_ops import (
16
+ _auto_detect_segment_length,
17
+ _smoothify_bulk,
18
+ _smoothify_geodataframe,
19
+ _smoothify_single,
20
+ )
21
+
22
+
23
+ @overload
24
+ def smoothify(
25
+ geom: gpd.GeoDataFrame,
26
+ segment_length: Optional[float] = None,
27
+ num_cores: int = 0,
28
+ smooth_iterations: int = 3,
29
+ merge_collection: bool = True,
30
+ merge_multipolygons: bool = True,
31
+ preserve_area: bool = True,
32
+ area_tolerance: float = 0.01,
33
+ ) -> gpd.GeoDataFrame: ...
34
+
35
+
36
+ @overload
37
+ def smoothify(
38
+ geom: Sequence[BaseGeometry],
39
+ segment_length: Optional[float] = None,
40
+ num_cores: int = 0,
41
+ smooth_iterations: int = 3,
42
+ merge_collection: bool = True,
43
+ merge_multipolygons: bool = True,
44
+ preserve_area: bool = True,
45
+ area_tolerance: float = 0.01,
46
+ ) -> BaseGeometry: ...
47
+
48
+
49
+ @overload
50
+ def smoothify(
51
+ geom: BaseGeometry,
52
+ segment_length: Optional[float] = None,
53
+ num_cores: int = 0,
54
+ smooth_iterations: int = 3,
55
+ merge_collection: bool = True,
56
+ merge_multipolygons: bool = True,
57
+ preserve_area: bool = True,
58
+ area_tolerance: float = 0.01,
59
+ ) -> BaseGeometry: ...
60
+
61
+
62
+ def smoothify(
63
+ geom: BaseGeometry | Sequence[BaseGeometry] | gpd.GeoDataFrame,
64
+ segment_length: Optional[float] = None,
65
+ num_cores: int = 0,
66
+ smooth_iterations: int = 3,
67
+ merge_collection: bool = True,
68
+ merge_multipolygons: bool = True,
69
+ preserve_area: bool = True,
70
+ area_tolerance: float = 0.01,
71
+ ) -> BaseGeometry | Sequence[BaseGeometry] | gpd.GeoDataFrame:
72
+ """Smooth geometries derived from raster data using Chaikin's corner-cutting algorithm.
73
+
74
+ Main entry point for smoothing jagged polygons and lines resulting from
75
+ raster-to-vector conversion. Transforms pixelated edges into smooth, natural-looking
76
+ curves while preserving general shape and area characteristics.
77
+
78
+ Supports multiple input types:
79
+ - Single geometries (Polygon, LineString, LinearRing, MultiPolygon, MultiLineString)
80
+ - Lists of geometries or GeometryCollections
81
+ - GeoDataFrames
82
+
83
+ Args:
84
+ geom: Geometry, list of geometries, or GeoDataFrame to smooth.
85
+ segment_length: Resolution of the original raster data in map units. Used for
86
+ adding intermediate vertices and simplification. Should match or exceed
87
+ the original raster pixel size. If None (default), automatically detects
88
+ segment length by finding the minimum segment length, which represents the
89
+ true pixel size in pixelated geometries (corners retain minimum length even
90
+ when straight edges are simplified during polygonization).
91
+ num_cores: Number of CPU cores for parallel processing (0 = all available cores,
92
+ 1 = serial processing). Only applies to GeoDataFrames and collections.
93
+ smooth_iterations: Number of Chaikin corner-cutting iterations (typically 3-5).
94
+ Higher values produce smoother results but add more vertices.
95
+ merge_collection: Whether to merge/dissolve adjacent geometries in collections
96
+ before smoothing. Useful for joining polygons from adjacent raster cells.
97
+ merge_multipolygons: Whether to merge adjacent polygons within MultiPolygons
98
+ before smoothing.
99
+ preserve_area: Whether to restore original area after smoothing via buffering.
100
+ Applied to Polygons only.
101
+ area_tolerance: Percentage of original area allowed as error
102
+ (e.g., 0.01 = 0.01% error). Default is 0.01% (99.99% area preservation).
103
+ Smaller values = more accurate area preservation but slower.
104
+ Only affects Polygons when preserve_area=True.
105
+
106
+ Returns:
107
+ Smoothed geometry matching the input type:
108
+ - BaseGeometry for single geometry inputs
109
+ - list[BaseGeometry] for list inputs
110
+ - GeoDataFrame for GeoDataFrame inputs
111
+
112
+ Raises:
113
+ ValueError: If input is not a supported geometry type.
114
+
115
+ Examples:
116
+ >>> from smoothify import smoothify
117
+ >>> import geopandas as gpd
118
+ >>> from shapely.geometry import Polygon
119
+ >>>
120
+ >>> # Smooth a single polygon with default area tolerance (0.01%)
121
+ >>> polygon = Polygon([(0, 0), (10, 0), (10, 10), (0, 10)])
122
+ >>> smoothed = smoothify(polygon, segment_length=1.0, smooth_iterations=3)
123
+ >>>
124
+ >>> # Smooth with stricter area preservation (0.001% error)
125
+ >>> smoothed = smoothify(polygon, segment_length=1.0, area_tolerance=0.001)
126
+ >>>
127
+ >>> # Smooth a GeoDataFrame in parallel
128
+ >>> gdf = gpd.read_file("water_bodies.gpkg")
129
+ >>> smoothed_gdf = smoothify(gdf, segment_length=10.0, num_cores=4)
130
+ """ # noqa: E501
131
+ if num_cores <= 0:
132
+ num_cores = cpu_count()
133
+ if isinstance(geom, list):
134
+ geom = GeometryCollection(geom)
135
+
136
+ if segment_length is None:
137
+ segment_length = _auto_detect_segment_length(geom)
138
+
139
+ if isinstance(geom, GeometryCollection | MultiPolygon | MultiLineString):
140
+ return _smoothify_bulk(
141
+ geom=geom,
142
+ segment_length=segment_length,
143
+ num_cores=num_cores,
144
+ smooth_iterations=smooth_iterations,
145
+ merge_collection=merge_collection,
146
+ merge_multipolygons=merge_multipolygons,
147
+ preserve_area=preserve_area,
148
+ area_tolerance=area_tolerance,
149
+ )
150
+ elif isinstance(geom, Polygon | LineString | LinearRing):
151
+ return _smoothify_single(
152
+ geom=geom,
153
+ segment_length=segment_length,
154
+ smooth_iterations=smooth_iterations,
155
+ merge_multipolygons=merge_multipolygons,
156
+ preserve_area=preserve_area,
157
+ area_tolerance=area_tolerance,
158
+ )
159
+ elif isinstance(geom, gpd.GeoDataFrame):
160
+ return _smoothify_geodataframe(
161
+ gdf=geom,
162
+ segment_length=segment_length,
163
+ num_cores=num_cores,
164
+ smooth_iterations=smooth_iterations,
165
+ merge_collection=merge_collection,
166
+ merge_multipolygons=merge_multipolygons,
167
+ preserve_area=preserve_area,
168
+ area_tolerance=area_tolerance,
169
+ )
170
+ else:
171
+ raise ValueError(
172
+ f"Input geometry must be a BaseGeometry or list of BaseGeometry. Got {type(geom)}." # noqa: E501
173
+ )