Gaps#

import geopandas
import numpy
import matplotlib.pyplot as plt
import geoplanar
from shapely.geometry import box, Polygon
p1 = box(0,0,10,10)
p2 = Polygon([(10,10), (12,8), (10,6), (12,4), (10,2), (20,5)])

gdf = geopandas.GeoDataFrame(geometry=[p1,p2])
gdf.plot(edgecolor='k')
<Axes: >
_images/24eae6ebb1f3a88523af1f7cdfb0c29e4470f9b8acf28674f9df70b19048fa01.png
geoplanar.gaps(gdf)
0     POLYGON ((10 2, 10 6, 12 4, 10 2))
1    POLYGON ((10 6, 10 10, 12 8, 10 6))
dtype: geometry
g = geoplanar.gaps(gdf)
g.area.values
array([4., 4.])
gdf1 = geoplanar.fill_gaps(gdf)
gdf1.plot(edgecolor='k')
<Axes: >
_images/f6eda2ad9aca6690242c285b79d147c23c56bcf26de621e716d870fb3ea6def1.png
gdf1.area
0    108.0
1     32.0
dtype: float64
gdf.area
0    100.0
1     32.0
dtype: float64
geoplanar.gaps(gdf1)
GeoSeries([], dtype: geometry)

The default is to merge the gap with the largest neighboring feature.

To merge the gap with the smallest neighboring feature strategy=’smallest’:

geoplanar.fill_gaps(gdf, strategy='smallest').plot(edgecolor='k')
<Axes: >
_images/ef0c5e521fff8bb176cc9c53fea70021d21e1e46e1b9d54046974b74d8b69fc6.png
geoplanar.fill_gaps(gdf, strategy='smallest').area
0    100.0
1     40.0
dtype: float64

Maximizing compactness when filling gaps#

p1 = box(9,0,10,5)
p2 = box(10,0, 40,2)
p3 = box(10,3, 40,10)
p4 = box(40,0, 100, 10)
gdf = geopandas.GeoDataFrame(geometry=[p1,p2, p3, p4])
gdf.plot(edgecolor='k');
_images/44bf5271de02b7796dbf08b7a27d5f68b36993fddf5c8ccd37f201bfe5d65957.png

The default will result in attaching the gap to the largest polygon:

geoplanar.fill_gaps(gdf).plot(edgecolor='k').plot
<bound method Axes.plot of <Axes: >>
_images/263b179b968a24f92b24b479e0a339769e5c79ab6513b153a93c43abd9d8f369.png

Attaching to the smallest candidate polygon also results in a non-compact polygon.

geoplanar.fill_gaps(gdf,strategy='smallest').plot(edgecolor='k').plot
<bound method Axes.plot of <Axes: >>
_images/55139137127c8d324e022dbec82c9871bff2211da4479822d0d206572ce9378e.png

Setting strategy="compact" will instead add the gap to the candidate polygon that results in the new polygon with the highest compactness (measured by the isoperimetric quotient).

geoplanar.fill_gaps(gdf, strategy='compact').plot(edgecolor='k')
<Axes: >
_images/8ffd4f3bc4aab6c1a7ed243e52f690757ca65bb07d57a642d1a3785b1231dc91.png

Checking edge case#

p1 = box(0,0,10,10)
p2 = Polygon([(10,10), (12,8), (10,6), (12,4), (10,2), (20,5)])
p3 = box(17,0,20,2)

gdf = geopandas.GeoDataFrame(geometry=[p1,p2,p3])
gdf.plot(edgecolor='k')
<Axes: >
_images/0f419b32b44fe590b377cbce4ddecc9b033f9f986d4fdc3c16ce0aa7de9009f7.png
g = geoplanar.gaps(gdf)
g.plot()
<Axes: >
_images/6adb913a6e1512edb2d87f60a9310135ba57b0383345dd229e21047a31c6b170.png
geoplanar.fill_gaps(gdf, strategy='smallest').plot(edgecolor='k')
<Axes: >
_images/e95b863e402fe18158eb1e5c7a634cba72c7b1c3bb192e6d88367f45173c7afc.png
geoplanar.fill_gaps(gdf, strategy='largest').plot(edgecolor='k')
<Axes: >
_images/2dab4b7e610ec30e616a933289c9a45fa444759f3ee3b25324600fc6ecc054d0.png
geoplanar.fill_gaps(gdf, strategy=None).plot(edgecolor='k')
<Axes: >
_images/2dab4b7e610ec30e616a933289c9a45fa444759f3ee3b25324600fc6ecc054d0.png
gdf.plot()
<Axes: >
_images/bc5ce0ca183d935cdc853f3e5c9c3aa53e4f4619d6341bbe7d99fe9938e44507.png

Gap with an inlet (non-gap)#

p1 = box(0,0,10,10)
p2 = Polygon([(10,10), (12,8), (10,6), (12,4), (11,2), (20,5)])

# a true gap with an inlet
gdf = geopandas.GeoDataFrame(geometry=[p1,p2])
gdf.plot(edgecolor='k')
<Axes: >
_images/4719f21465556fe26a36df2cbaec25c1923d856657ef04e3972a04aff058266d.png
geoplanar.gaps(gdf)
0    POLYGON ((10 6, 10 10, 12 8, 10 6))
dtype: geometry
geoplanar.fill_gaps(gdf, strategy='smallest').plot(edgecolor='k')
<Axes: >
_images/3b15e885960931b27ab6820a23edb081728ef7d9992ecc42bce98cd5282fc0d4.png
geoplanar.fill_gaps(gdf, strategy='largest').plot(edgecolor='k')
<Axes: >
_images/8b98c771de707f8ef577c160f87ca5157b62c2ad3ee74a29717ba00758a7d41c.png

Selective Correction#

p1 = box(0,0,10,10)
p2 = Polygon([(10,10), (12,8), (10,6), (12,4), (10,2), (20,5)])
p3 = box(17,0,20,2)

gdf = geopandas.GeoDataFrame(geometry=[p1,p2,p3])
gdf.plot(edgecolor='k')
<Axes: >
_images/0f419b32b44fe590b377cbce4ddecc9b033f9f986d4fdc3c16ce0aa7de9009f7.png
gaps = geoplanar.gaps(gdf)
base = gdf.plot()
gaps.plot(color='red', ax=base)
<Axes: >
_images/bdb715cecd57176891a219a741b66af1a3fc66982652e3a41c59ede3474dc4ac.png
gaps
0     POLYGON ((10 2, 10 6, 12 4, 10 2))
1    POLYGON ((10 6, 10 10, 12 8, 10 6))
dtype: geometry
g2 = gaps.loc[[1]]
g2
1    POLYGON ((10 6, 10 10, 12 8, 10 6))
dtype: geometry
filled = geoplanar.fill_gaps(gdf,g2)
base = filled.plot()
g2.plot(color='red', ax=base)
<Axes: >
_images/1cd660b2df2db8e1dc4cc20f159743d47d714604900991f2d93a2fc8bb1ecdd6.png
filled.area
0    104.0
1     32.0
2      6.0
dtype: float64
filled.shape
(3, 1)
(filled.area==[104, 32,6]).all()
np.True_

gap coincindent with > 2 polygons#

p1 = box(0,0,10,10)
p2 = Polygon([(10,10), (12,8), (10,6), (12,4), (10,2), (20,5)])
p3 = box(17,0,20,2)
p4 = box(10,0, 17, 2)
p5 = Polygon([(17, 2), (20,5), (20, 2), (17,2)])

gdf = geopandas.GeoDataFrame(geometry=[p1,p2,p3, p4, p5])
gdf.plot(edgecolor='k')
<Axes: >
_images/11d655ea16d9a35ba7d1297ba1a770d34c0cb0debae85600fb9115c6b3cbbd16.png
g = geoplanar.gaps(gdf)
gl = geoplanar.fill_gaps(gdf, strategy='largest')
gl.plot(edgecolor='k')
<Axes: >
_images/4a48de64b43d8df4fdf3c8ecd84720999758f86def2a5dc6294e7a23288fb20c.png
gr = geoplanar.fill_gaps(gdf, strategy='smallest')
gr.plot(edgecolor='k')
<Axes: >
_images/7b405618fd3776b5a7edb4a7925edb45ee1cf91cf0f567d267c61fdf671edaf1.png
gr = geoplanar.fill_gaps(gdf, strategy=None)
gr.plot(edgecolor='k')
<Axes: >
_images/8fed27a8eb2a8691fd2e3734c560f43238020f9ce450f4086aef8a6d47dffbeb.png
gl.geometry
0    MULTIPOLYGON (((17 2, 10 2, 20 5, 17 2)), ((12...
1    POLYGON ((10 10, 12 8, 10 6, 12 4, 10 2, 20 5,...
2             POLYGON ((20 0, 20 2, 17 2, 17 0, 20 0))
3             POLYGON ((17 0, 17 2, 10 2, 10 0, 17 0))
4                   POLYGON ((17 2, 20 5, 20 2, 17 2))
Name: geometry, dtype: geometry
gr.geometry
0    POLYGON ((10 0, 0 0, 0 10, 10 10, 12 8, 10 6, ...
1    POLYGON ((10 10, 12 8, 10 6, 12 4, 10 2, 20 5,...
2             POLYGON ((20 0, 20 2, 17 2, 17 0, 20 0))
3    MULTIPOLYGON (((17 0, 10 0, 10 2, 20 5, 17 2, ...
4                   POLYGON ((17 2, 20 5, 20 2, 17 2))
Name: geometry, dtype: geometry
gdf.geometry
0             POLYGON ((10 0, 10 10, 0 10, 0 0, 10 0))
1    POLYGON ((10 10, 12 8, 10 6, 12 4, 10 2, 20 5,...
2             POLYGON ((20 0, 20 2, 17 2, 17 0, 20 0))
3             POLYGON ((17 0, 17 2, 10 2, 10 0, 17 0))
4                   POLYGON ((17 2, 20 5, 20 2, 17 2))
Name: geometry, dtype: geometry
gc = geoplanar.fill_gaps(gdf, strategy='compact')
gc.plot(edgecolor='k')
<Axes: >
_images/806625ed95928cff4a0bbfcfa337195b86a3efdfcc08d8a6f46c67f2a28f4407.png