question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Plotting polygon with hole goes wrong in case of identical exterior/interior coordinates order

See original GitHub issue

I am playing around with the gdf.plot method which is supposed to facilitate plotting geodataframes using geopandas. The reason is that I would like to avoid jumping from QGIS to python while developing scripts and just focus on the programming.

Unfortunately for a simple polygon (granted it has a hole inside) this method seems to have problems. On QGIS I get the following plot:

enter image description here

On matplotlib I get the following plot:

enter image description here

I am quite sure that the QGIS version is correct. The geojson file for which this all applies contains the following:

{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 1.0, 2.0 ], [ 1.0, 1.5 ], [ 0.5, 1.5 ], [ 0.0, 1.5 ], [ 0.0, 0.0 ], [ 1.0, 0.0 ], [ 1.0, 0.5 ], [ 1.5, 0.5 ], [ 2.0, 0.5 ], [ 2.0, 2.0 ], [ 1.0, 2.0 ] ], [ [ 1.0, 1.5 ], [ 1.0, 1.0 ], [ 1.5, 1.0 ], [ 1.5, 1.5 ], [ 1.0, 1.5 ] ] ] } }
]
}

Why does geopandas not recognize that there must be a hole in there?

Thanks

Issue Analytics

  • State:open
  • Created 4 years ago
  • Comments:6 (6 by maintainers)

github_iconTop GitHub Comments

3reactions
jorisvandenbosschecommented, Mar 22, 2019

@awa5114 Thanks for opening the issue! After some investigation, this seems to be due to how descartes is handling the interior (and thus not related to the Collections PR). See https://bitbucket.org/sgillies/descartes/issues/3/plotting-issue-with-interior-polygons

Longer explanation:

Converting the geojson to a GeoDataFrame and plotting it (making the edges visible)

fc = { 
 "type": "FeatureCollection", 
 "features": [ 
 { "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 1.0, 2.0 ], [ 1.0, 1.5 ], [ 0.5, 1.5 ], [ 0.0, 1.5 ], [ 0.0, 0.0 ], [ 1.0,
  0.0 ], [ 1.0, 0.5 ], [ 1.5, 0.5 ], [ 2.0, 0.5 ], [ 2.0, 2.0 ], [ 1.0, 2.0 ] ], [ [ 1.0, 1.5 ], [ 1.0, 1.0 ], [ 1.5, 1.0 ], [ 1.5, 1.5 ], [ 1.0, 1.5 ] ] ] } } 
 ] 

import geopandas
gdf = geopandas.GeoDataFrame.from_features(fc)
gdf.plot(edgecolor='black', facecolor='red', alpha=.5) 

gives:

Figure_1

So you can see that the interior edges are there, only also (incorrectly) filled with a color.

It is the descartes library that is converting the the polgyon into a matplotlib Patch / Path, and from looking there, I saw an issue about interiors, indicating the the order of the interior coords is apparently relevant (https://bitbucket.org/sgillies/descartes/issues/3/plotting-issue-with-interior-polygons).

Creating a new polygon from the old one, and only inverting the order of the coordinates of the interior, and plotting it then again, indeed gives the desired result:

polygon = gdf.squeeze()
exterior = polygon.exterior
interior = list(polygon.interiors)[0]
from shapely.geometry import Polygon
polygon2 = Polygon(exterior, [[c for c in list(interior.coords)[::-1]]])

gdf2 = geopandas.GeoDataFrame(geometry=[polygon2])
gdf2.plot()

gives

Figure_1


So that is at least an explanation of why the issue is happening. But that said, I personally find it strange that a correct order of the coordinates is important for the plotting, and for sure it is very surprising / difficult to know for a user (and also a bit cumbersome to solve).

1reaction
jorisvandenbosschecommented, Mar 22, 2019

@ljwolf interesting, thanks for that link! Now, what I see there, is that Shapefiles and GeoJSON do the opposite, but both still say that exterior and interior is the opposite direction. For plotting, matplotlib/svg doesn’t care if the exterior is CCW and interior CW or the other way round, as long as the direction is different for exterior/interior.

So although Shapefiles and GeoJSON somewhat enforces this opposite winding for exterior/interiors (althoug differently), this does not seem to be a given in general? (at least in shapely / geos, a polygon with the same winding for exterior/interior is considered “valid”). Or at least not enforced. Eg saving / reloading to geopackage gives no problem at all with identical winding (while saving to a shapefile “corrects” it).

Another interesting read: https://gis.stackexchange.com/questions/119150/order-of-polygon-vertices-in-general-gis-clockwise-or-counterclockwise. From reading that link, it seems this opposite winding is described in the OGC Simple Features standard.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Creating Shapely Polygons with holes - GIS Stack Exchange
I think the only issue here is that you need to pass the coordinates to the Polygon constructor, not the list of coordinates....
Read more >
Plotting Shapely Polygons with Interiors (holes) - CodersLegacy
Your exterior coordinates must be counter-clockwise order, and the interior coordinates must be in clockwise-order (starting point doesn't really matter). Once ...
Read more >
Why is polygon "hole" being shaded in by Google Maps ...
The winding direction of the outer polygon is wrong. ... Reversing the order of the outer polygon's coordinates makes the hole work.
Read more >
Shapely and geometric objects - Read the Docs
Geometric objects consist of coordinate tuples where: ... This is because Polygon can also have holes inside of it.
Read more >
sp: Classes and Methods for Spatial Data
plotting data as maps, spatial selection, as well as methods for ... The ordering of the grid cells is as in coordinates() of...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found