geoplanar
geoplanar copied to clipboard
Resolve overlapping polygons by merging them
I am currently trying to use geoplanar to preprocess building footprints that have a ton of overlapping geometries. In some cases, trimming is not the right solution because the two polygons represent different height of parts of buildings. So the solution there is to merge then together.
I have a function that does that, would you be interested in a PR?
def merge_overlapping(gdf, merge_limit, overlap_limit, largest=None):
"""Merge overlapping polygons based on a set of conditions.
Overlapping polygons smaller than ``merge_limit`` are merged to a neighboring polygon.
If ``largest=None`` it picks one randomly, otherwise it picks the largest (True) or the
smallest (False).
Polygons larger than ``merge_limit`` are merged to neighboring if they share area larger
than ``area * overlap_limit``.
Parameters
----------
gdf : GeoDataFrame
GeoDataFrame with polygon or mutli polygon geometry
merge_limit : float
area of overlapping polygons that are to be merged with neighbors no matter the size
of the overlap
overlap_limit : float (0-1)
ratio of area of an overlapping polygon that has to be shared with other polygon
to merge both into a one
largest : bool (default None)
Merge each overlapping polygons smaller than merge_limit with its largest (True), or smallest (False) neighbor.
If None, merge with any neighbor non-deterministically but performantly.
Returns
-------
GeoDataFrame
"""
neighbors = {}
for i, poly in tqdm(gdf.geometry.iteritems(), total=len(gdf)):
hits = gdf.sindex.query(poly, predicate='intersects')
hits = hits[hits != i]
if hits.size == 0:
neighbors[i] = []
continue
if poly.area < merge_limit:
if hits.size == 0:
neighbors[i] = list(hits)
else:
if largest is None:
neighbors[i] = [hits[0]]
else:
sub = gdf.geometry.iloc[hits]
inters = sub.intersection(poly.exterior)
if largest:
neighbors[i] = [inters.length.idxmax()]
else:
neighbors[i] = [inters.length.idxmin()]
else:
sub = gdf.geometry.iloc[hits]
inters = sub.intersection(poly)
include = sub.index[inters.area > (sub.area * overlap_limit)]
neighbors[i] = list(include)
W = libpysal.weights.W(neighbors, silence_warnings=True)
return gdf.dissolve(W.component_labels)
This would definitely be a useful piece of functionality to have. +1 on a pr.
Closed by #50