The node should work similar like the https://help.alteryx.com/current/en/designer/tools/spatial/smooth-tool.html#smooth-tool from Alteryx. This could be done by implementing an algorithm using the Shapely library.
`import geopandas as gpd
from shapely.geometry import (
LineString,
MultiLineString,
Polygon,
MultiPolygon,
LinearRing,
GeometryCollection,
)
def _chaikin_coords(coords, iterations=3, closed=False):
"""
Smooth a coordinate sequence using Chaikin's corner-cutting algorithm.
"""
coords = list(coords)
if closed and coords[0] == coords[-1]:
coords = coords[:-1]
for _ in range(iterations):
new_coords = []
pairs = (
zip(coords, coords[1:] + coords[:1])
if closed
else zip(coords[:-1], coords[1:])
)
if not closed:
new_coords.append(coords[0])
for p0, p1 in pairs:
x0, y0 = p0[:2]
x1, y1 = p1[:2]
q = (0.75 * x0 + 0.25 * x1, 0.75 * y0 + 0.25 * y1)
r = (0.25 * x0 + 0.75 * x1, 0.25 * y0 + 0.75 * y1)
new_coords.extend([q, r])
if not closed:
new_coords.append(coords[-1])
coords = new_coords
if closed:
coords.append(coords[0])
return coords
def smooth_linestring(line, iterations=3):
if len(line.coords) < 3:
return line
return LineString(
_chaikin_coords(line.coords, iterations=iterations, closed=False)
)
def smooth_polygon(poly, iterations=3):
if poly.is_empty:
return poly
exterior = LinearRing(
_chaikin_coords(poly.exterior.coords, iterations=iterations, closed=True)
)
interiors = [
LinearRing(
_chaikin_coords(ring.coords, iterations=iterations, closed=True)
)
for ring in poly.interiors
]
smoothed = Polygon(exterior, interiors)
# Optional validity repair
if not smoothed.is_valid:
smoothed = smoothed.buffer(0)
return smoothed
def smooth_geometry(geom, iterations=3):
"""
Smooth Shapely geometries while preserving GeoPandas compatibility.
Supports LineString, MultiLineString, Polygon, MultiPolygon,
and GeometryCollection.
"""
if geom is None or geom.is_empty:
return geom
geom_type = geom.geom_type
if geom_type == "LineString":
return smooth_linestring(geom, iterations)
if geom_type == "MultiLineString":
return MultiLineString(
[smooth_linestring(part, iterations) for part in geom.geoms]
)
if geom_type == "Polygon":
return smooth_polygon(geom, iterations)
if geom_type == "MultiPolygon":
return MultiPolygon(
[smooth_polygon(part, iterations) for part in geom.geoms]
)
if geom_type == "GeometryCollection":
return GeometryCollection(
[smooth_geometry(part, iterations) for part in geom.geoms]
)
# Points and unsupported geometries are returned unchanged
return geom
def smooth_geodataframe(gdf, iterations=3, inplace=False):
"""
Smooth geometries in a GeoDataFrame.
Parameters
----------
gdf : geopandas.GeoDataFrame
iterations : int
More iterations means smoother geometry, but more vertices.
inplace : bool
If True, modifies the input GeoDataFrame.
Returns
-------
geopandas.GeoDataFrame
"""
result = gdf if inplace else gdf.copy()
result.geometry = result.geometry.apply(
lambda geom: smooth_geometry(geom, iterations=iterations)
)
return result`
Usage
`# Recommended: use a projected CRS, not latitude/longitude
gdf_projected = gdf.to_crs("EPSG:3857")
gdf_smooth = smooth_geodataframe(
gdf_projected,
iterations=3
)
Convert back if needed
gdf_smooth = gdf_smooth.to_crs(gdf.crs)`
Details: https://chatgpt.com/share/e/69f359d5-02e4-800c-bb84-5bcb74882ae6
The node should work similar like the https://help.alteryx.com/current/en/designer/tools/spatial/smooth-tool.html#smooth-tool from Alteryx. This could be done by implementing an algorithm using the Shapely library.
`import geopandas as gpd
from shapely.geometry import (
LineString,
MultiLineString,
Polygon,
MultiPolygon,
LinearRing,
GeometryCollection,
)
def _chaikin_coords(coords, iterations=3, closed=False):
"""
Smooth a coordinate sequence using Chaikin's corner-cutting algorithm.
"""
coords = list(coords)
def smooth_linestring(line, iterations=3):
if len(line.coords) < 3:
return line
def smooth_polygon(poly, iterations=3):
if poly.is_empty:
return poly
def smooth_geometry(geom, iterations=3):
"""
Smooth Shapely geometries while preserving GeoPandas compatibility.
Supports LineString, MultiLineString, Polygon, MultiPolygon,
and GeometryCollection.
"""
if geom is None or geom.is_empty:
return geom
def smooth_geodataframe(gdf, iterations=3, inplace=False):
"""
Smooth geometries in a GeoDataFrame.
Usage
`# Recommended: use a projected CRS, not latitude/longitude
gdf_projected = gdf.to_crs("EPSG:3857")
gdf_smooth = smooth_geodataframe(
gdf_projected,
iterations=3
)
Convert back if needed
gdf_smooth = gdf_smooth.to_crs(gdf.crs)`
Details: https://chatgpt.com/share/e/69f359d5-02e4-800c-bb84-5bcb74882ae6