I have a KML file that contains a polygon with holes in it. It looks similar to this KML:
<?xml version="1.0" encoding="utf-8" ?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document id="root_doc">
<Folder><name>polygon_with_holes</name>
<Placemark>
<Style><LineStyle><color>ff0000ff</color></LineStyle><PolyStyle><fill>0</fill></PolyStyle></Style>
<Polygon>
<outerBoundaryIs>
<LinearRing>
<coordinates>
-95,45 -95.0,45.007 -94.99,45.007 -94.99,45.0 -95,45
</coordinates>
</LinearRing>
</outerBoundaryIs>
<innerBoundaryIs>
<LinearRing>
<coordinates>
-94.999,45.003 -94.999,45.005 -94.997,45.005 -94.997,45.003 -94.999,45.003
</coordinates>
</LinearRing>
<LinearRing>
<coordinates>
-94.996,45.001 -94.996,45.003 -94.994,45.003 -94.994,45.001 -94.996,45.001
</coordinates>
</LinearRing>
<LinearRing>
<coordinates>
-94.993,45.004 -94.993,45.006 -94.991,45.006 -94.991,45.004 -94.993,45.004
</coordinates>
</LinearRing>
</innerBoundaryIs>
</Polygon>
</Placemark>
</Folder>
</Document></kml>
This KML renders fine within Google Earth:
However, when I read this KML into R using st_read
from the sf package, only the first hole gets read in:
> polygon1 <- st_read(dsn = '/path/to/file.kml', layer = 'polygon_with_holes')
> st_geometry(polygon1)[[1]]
# POLYGON ((-95 45, -95 45.007, -94.99 45.007, -94.99 45, -95 45), (-94.999 45.003, -94.999 45.005, -94.997 45.005, -94.997 45.003, -94.999 45.003))
> plot(st_geometry(polygon1), col = 'red')
Note that the same behavior occurs with readOGR
from the sp package, which leads me to believe this behavior is specific to GDAL. I've determined that wrapping each inner LinearRing
with innerBoundaryIs
tags solves the problem. In other words, the following KML is properly parsed by GDAL:
<?xml version="1.0" encoding="utf-8" ?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document id="root_doc">
<Folder><name>polygon_with_holes</name>
<Placemark>
<Style><LineStyle><color>ff0000ff</color></LineStyle><PolyStyle><fill>0</fill></PolyStyle></Style>
<Polygon>
<outerBoundaryIs>
<LinearRing>
<coordinates>
-95,45 -95.0,45.007 -94.99,45.007 -94.99,45.0 -95,45
</coordinates>
</LinearRing>
</outerBoundaryIs>
<innerBoundaryIs>
<LinearRing>
<coordinates>
-94.999,45.003 -94.999,45.005 -94.997,45.005 -94.997,45.003 -94.999,45.003
</coordinates>
</LinearRing>
</innerBoundaryIs>
<innerBoundaryIs>
<LinearRing>
<coordinates>
-94.996,45.001 -94.996,45.003 -94.994,45.003 -94.994,45.001 -94.996,45.001
</coordinates>
</LinearRing>
</innerBoundaryIs>
<innerBoundaryIs>
<LinearRing>
<coordinates>
-94.993,45.004 -94.993,45.006 -94.991,45.006 -94.991,45.004 -94.993,45.004
</coordinates>
</LinearRing>
</innerBoundaryIs>
</Polygon>
</Placemark>
</Folder>
</Document></kml>
When this new KML is read into R:
> polygon2 <- st_read(dsn = '/path/to/newfile.kml', layer = 'polygon_with_holes')
> st_geometry(polygon2)[[1]]
# POLYGON ((-95 45, -95 45.007, -94.99 45.007, -94.99 45, -95 45), (-94.999 45.003, -94.999 45.005, -94.997 45.005, -94.997 45.003, -94.999 45.003), (-94.996 45.001, -94.996 45.003, -94.994 45.003, -94.994 45.001, -94.996 45.001), (-94.993 45.004, -94.993 45.006, -94.991 45.006, -94.991 45.004, -94.993 45.004))
> plot(st_geometry(polygon2), col = 'red')
Unfortunately, the original offending file is part of an automated process, so fixing that file (and others like it) by hand is not a feasible option. So my questions are twofold:
- Is this the expected behavior of GDAL when parsing KML polygons with holes?
- Is there a GDAL option that can be invoked so that the original KML file is correctly parsed? (Preferrably something that can be passed in via the
options
argument tost_read
.)
<innerBoundaryIs> Contains a <LinearRing> element. A Polygon can contain multiple <innerBoundaryIs> elements, which create multiple cut-outs inside the Polygon.
Unfortunately the specification is not very exact about inner boundaries and linearrings.