How to simplify boundary geometries in shapely
Asked Answered
P

2

12

I'm working with shapely to do GIS, but I'm running into memory errors when loading the geometry of each zip code into memory because the geometry is so jagged and complicated.

I'd like to make the shape smaller in memory by reducing the number of boundary points as low as possible without distorting the shape too much. Using the convex hull seems like one potential answer, as could simply throwing away lots of points from the boundary. I'm wondering if there's something already out there that solves this problem.

Phantasy answered 3/4, 2014 at 14:1 Comment(0)
M
15

Try using the geometry's simplify method, specifying a tolerance distance.

Mannose answered 21/4, 2014 at 23:56 Comment(0)
T
2

I came across this old post as I had a similar problem. My solution was as follows based on the link from Mike T:

Generate polygons from an area mask.

Here it's two areas.

import matplotlib.pyplot as plt
import shapely.geometry
import cv2
import numpy as np

# gen. mask
mask=np.zeros((600,600),dtype=bool)
mask[300:500,300:500]=True
mask[:150,30:120]=True
mask[70:120,30:220]=True
mask[100:200,200:260]=True

# get contours == polygon
contours, _ = cv2.findContours(mask.astype(np.uint8),  # cv2 requires special types
                               cv2.RETR_TREE,
                               cv2.CHAIN_APPROX_NONE)
contours = [i.reshape((-1, 2)) for i in contours]

Simplify the polygon(s)

def simplify(polygon, tolerance = .1):
    """ Simplify a polygon with shapely.
    Polygon: ndarray
        ndarray of the polygon positions of N points with the shape (N,2)
    tolerance: float
        the tolerance
    """
    poly = shapely.geometry.Polygon(i)
    poly_s = poly.simplify(tolerance=tolerance)
    # convert it back to numpy
    return np.array(poly_s.boundary.coords[:])

# Simplify all contours
contours_s = []
for i in contours:
    contours_s.append(simplify(i))

Plot

plt.figure(figsize=(4,4))
plt.imshow(mask, label='2D mask')

for i, c_i in enumerate(contours_s):
    plt.plot(*c_i.T, '-', label=f'cv2 contours {i}')
    
for i, c_i in enumerate(contours_s):
    plt.plot(*c_i.T, 'o', label=f'shapely simplify {i}')
    
plt.legend()
plt.tight_layout()

Plot

Tubing answered 5/8, 2022 at 11:54 Comment(2)
Hi! I feel like you are missing a closing bracket in the "Simplify the polygon(s)" section. The line: contours_s.append(simplify(i). Shouldn't it be contours_s.append(simplify(i))?Mango
Hi Marci, thanks for pointing out this typo and providing the solution. I fixed it in the post.Tubing

© 2022 - 2024 — McMap. All rights reserved.