12

I'm using Python, Shapely and Fiona to process some shapefiles.

Is there an easy way to count the number of points in a polygon/multipolygon in Shapely and Fiona?

Amandasaurus
  • 892
  • 3
  • 10
  • 20

3 Answers3

11

1) With Fiona, you don't need shapely to count the number of points in a polygon/multipolygon. Simply use the resulting GeoJSON format (= a Python dictionary).

Polygon simple:

>>> import fiona
>>> shape = fiona.open("simplePoly.shp")
>>> # first feature
>>> feature = shape.next()
>>> geom = feature['geometry']
>>> print geom
{'type': 'Polygon', 'coordinates': [[(1.0, 1.0), (1.0, 7.0), (7.0, 7.0), (7.0, 1.0), (1.0, 1.0)]]}
>>> print geom['coordinates']
[[(1.0, 1.0), (1.0, 7.0), (7.0, 7.0), (7.0, 1.0), (1.0, 1.0)]]
>>> print len(geom['coordinates'])
1 # -> only one polygon
>>> print "number of points =", len(geom['coordinates'][0])
number of points = 5 

or simply:

>>> shape = fiona.open("simplePoly.shp")
>>> print "number of points =", len(shape.next()['geometry']['coordinates'][0]
number of points = 5

Polygon with one hole

>>> shape = fiona.open("Poly_hole.shp")
>>> geom = shape.next()['geometry']['coordinates']
[[(1.0, 1.0), (1.0, 7.0), (7.0, 7.0), (7.0, 1.0), (1.0, 1.0)], [(2.0, 3.0), (4.0, 3.0), (4.0, 5.0), (2.0, 5.0), (2.0, 3.0)]]
>>> print len(geom)
2 # -> polygon and 1 hole
>>> print "number of exterior points =", len(geom)[0])
number of exterior points =5
>>> print "number of interior points =", len(geom)[1])
number of interior points =5 

MultiPolygon

>>> shape = fiona.open("multiPoly.shp")
>>> geom = shape.next()['geometry']['coordinates']
>>> print geom
{'type': 'MultiPolygon', 'coordinates': [[[(1.0, 1.0), (1.0, 7.0), (7.0, 7.0), (7.0, 1.0), (1.0, 1.0)]], [[(2.0, 3.0), (4.0, 3.0), (4.0, 5.0), (2.0, 5.0), (2.0, 3.0)]]]}
>>> print "number of points =", sum([len(poly[0]) for poly in geom])
number of points =10

So the solution is

if shape.next()['geometry']['type'] == 'Polygon':
      if len(shape.next()['geometry']) == 1: # -> polygon simple
      if len(shape.next()['geometry']) > 1: # -> polygon with holes
if shape.next()['geometry']['type'] == 'MultiPolygon': # -> Multipolygon

2) if you want to use the solution of againstflow with Fiona and shapely, use the shapely shape function (Python Geo Interface):

>>> from shapely.geometry import shape
>>> shape = fiona.open("simplePoly.shp")
>>> geom_shapely = shape(shape.next())
>>> print type(geom_shapely)
<class 'shapely.geometry.polygon.Polygon'>
>>> print geom_shapely
POLYGON ((1 1, 1 7, 7 7, 7 1, 1 1))
gene
  • 54,868
  • 3
  • 110
  • 187
7

You can get polygon exterior and interior points coordinates this way:

def extract_poly_coords(geom):
    if geom.type == 'Polygon':
        exterior_coords = geom.exterior.coords[:]
        interior_coords = []
        for interior in geom.interiors:
            interior_coords += interior.coords[:]
    elif geom.type == 'MultiPolygon':
        exterior_coords = []
        interior_coords = []
        for part in geom:
            epc = extract_poly_coords(part)  # Recursive call
            exterior_coords += epc['exterior_coords']
            interior_coords += epc['interior_coords']
    else:
        raise ValueError('Unhandled geometry type: ' + repr(geom.type))
    return {'exterior_coords': exterior_coords,
            'interior_coords': interior_coords}

Then you can count for example all exterior coords:

polyPoints = len(exterior_coords);
Barbarossa
  • 5,797
  • 27
  • 61
againstflow
  • 5,034
  • 18
  • 56
  • 87
2

Flatten geometry and count coordinates

I'm a fan of this flatten function, so here's the shapely geometry equivalent, and a coords length calculator.

def geometry_flatten(geom):
  if hasattr(geom, 'geoms'):  # Multi<Type> / GeometryCollection
    for g in geom.geoms:
      yield from geometry_flatten(g)
  elif hasattr(geom, 'interiors'):  # Polygon
    yield geom.exterior
    yield from geom.interiors
  else:  # Point / LineString
    yield geom

def geometry_length(geom): return sum(len(g.coords) for g in geometry_flatten(geom))

Example Usage

df = gpd.read_file(gpd.datasets.get_path('nybb'))
g = df.loc[0, 'geometry']

geometry_length(g) # 8991 df['geometry'].apply(geometry_length) # 8991, 29219, 22986, 6362, 8505 geometry_length(df.unary_union) # 75545

similar geopandas question
geopandas question on coordinates

A. West
  • 121
  • 4