Further to relet's answer on how to get individual polygons, you can then run an intersection on all the polygons to create the holes. If your dataset contains overlapping polygons though you're out of luck.
Explain again what is wrong with existing shapefile readers?
Would it not be easier to export feature IDs and M values from the shapefile and then join them back to the polygons after using an existing shapefile reader?
For multipatches you can use the same technique of assigning polygon IDs to a "patch ID" and then adding this attribute back to the features.
Edit: Whilst you say you don't want to use OGR, just in case you change your mind..
import ogr
# Get the driver
driver = ogr.GetDriverByName('ESRI Shapefile')
# Open a shapefile
shapefileName = "D:/temp/myshapefile.shp"
dataset = driver.Open(shapefileName, 0)
layer = dataset.GetLayer()
for index in xrange(layer.GetFeatureCount()):
feature = layer.GetFeature(index)
geometry = feature.GetGeometryRef()
#geometry for polygon as WKT, inner rings, outer rings etc.
print geometry
The geometry should be output as follows:
POLYGON ((79285 57742,78741 54273...),(76087 55694,78511 55088,..))
The first bracket contains the coords of the exterior ring, subsequent brackets the coords of interior rings.
If you have Z values points should be in the format 79285 57742 10 (where the last coord is a height).
Otherwise you could use the Shapely Contains and Within functions to assess every polygon with each other and apply a spatial index beforehand - http://pypi.python.org/pypi/Rtree/ to speed up processing.
When you start with a Python module, there are several solutions to find the available functions. One of them is dir
:
geom = feat.GetGeometryRef()
print dir(geom)
['AddGeometry', 'AddGeometryDirectly', 'AddPoint', 'AddPoint_2D', 'Area', 'AssignSpatialReference', 'Boundary', 'Buffer', 'Centroid', 'Clone', 'CloseRings', 'Contains', 'ConvexHull', 'Crosses', 'Destroy', 'Difference', 'Disjoint', 'Distance', 'Empty', 'Equal', 'Equals', 'ExportToGML', 'ExportToJson', 'ExportToKML', 'ExportToWkb', 'ExportToWkt', 'FlattenTo2D', 'GetArea', 'GetBoundary', 'GetCoordinateDimension', 'GetDimension', 'GetEnvelope', 'GetEnvelope3D', 'GetGeometryCount', 'GetGeometryName', 'GetGeometryRef', 'GetGeometryType', 'GetPoint', 'GetPointCount', 'GetPoint_2D', 'GetPoints', 'GetSpatialReference', 'GetX', 'GetY', 'GetZ', 'Intersect', 'Intersection', 'Intersects', 'IsEmpty', 'IsRing', 'IsSimple', 'IsValid', 'Length', 'Overlaps', 'PointOnSurface', 'Segmentize', 'SetCoordinateDimension', 'SetPoint', 'SetPoint_2D', 'Simplify', 'SimplifyPreserveTopology', 'SymDifference', 'SymmetricDifference', 'Touches', 'Transform', 'TransformTo', 'Union', 'UnionCascaded', 'Within', 'WkbSize', '__class__', '__del__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattr__', '__getattribute__', '__hash__', '__init__', '__iter__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__swig_destroy__', '__swig_getmethods__', '__swig_setmethods__', '__weakref__', 'next', 'this']
and no getNumInteriorRings
: apparently this method is not exposed in the python bindings.
But if you look at [gdal-dev] Polygon topology:
In fact you have to use the Geometry.GetGeometryCount() that
returns 1 (the exterior ring) + the number of interior rings. So
polygon.GetGeometryCount() - 1 should return the number of interior rings
So a polygon only:
geom.GetGeometryCount()
1
For a polygon whith one hole:
![enter image description here](https://i.stack.imgur.com/qm9h1.jpg)
geom.GetGeometryCount()
2 # -> one hole
and
geom.GetGeometryRef(0) # -> exterior ring = LinearRing, in red
geom.GetGeometryRef(1) # -> interior ring = LinearRing
![enter image description here](https://i.stack.imgur.com/vg0xc.jpg)
exterior = Geometry(ogr.wkbPolygon)
exterior.AddGeometry(geom.GetGeometryRef(0))
![enter image description here](https://i.stack.imgur.com/EfIIc.jpg)
interior = Geometry(ogr.wkbPolygon)
exterior.AddGeometry(geom.GetGeometryRef(1))
![enter image description here](https://i.stack.imgur.com/LAYWp.jpg)
But if you look at the geo_interface protocol (a python object property that returns a geojson dictionary, look at Python Geo_interface applications):
print geom.ExportToJson()
{ "type": "Polygon", "coordinates": [ [ [ 1149490.109727980103344, 691044.609108003089204 ], [ 1191579.109752570046112, 691044.609108003089204 ], [ 1191579.109752570046112, 648030.576115839648992 ], [ 1149490.109727980103344, 648030.576115839648992 ], [ 1149490.109727980103344, 691044.609108003089204 ] ], [ [ 1154115.274565846892074, 686419.444270136067644 ], [ 1154115.274565846892074, 653118.257437493419275 ], [ 1165678.186660513980314, 653118.257437493419275 ], [ 1165678.186660513980314, 686419.444270136067644 ], [ 1154115.274565846892074, 686419.444270136067644 ] ] ] }
it is easier with fiona (also based on GDAL/OGR)
import fiona
layer = fiona.open("myshape.shp")
feat = layer.next()
print feat
{'geometry': {'type': 'Polygon', 'coordinates': [[(1149490.1097279801, 691044.60910800309), (1191579.10975257, 691044.60910800309), (1191579.10975257, 648030.57611583965), (1149490.1097279801, 648030.57611583965), (1149490.1097279801, 691044.60910800309)], [(1154115.2745658469, 686419.44427013607), (1154115.2745658469, 653118.25743749342), (1165678.186660514, 653118.25743749342), (1165678.186660514, 686419.44427013607), (1154115.2745658469, 686419.44427013607)]]}, 'type': 'Feature', 'id': '0', 'properties': OrderedDict([(u'FID', 0.0)])}
print feat['geometry']['type'])
Polygon
print len(feat['geometry']['coordinates']
2
# outer ring
print feat['geometry']['coordinates'][0]
[(1149490.1097279801, 691044.60910800309), (1191579.10975257, 691044.60910800309), (1191579.10975257, 648030.57611583965), (1149490.1097279801, 648030.57611583965), (1149490.1097279801, 691044.60910800309)]
# inner ring
print feat['geometry']['coordinates'][1]
[(1154115.2745658469, 686419.44427013607), (1154115.2745658469, 653118.25743749342), (1165678.186660514, 653118.25743749342), (1165678.186660514, 686419.44427013607), (1154115.2745658469, 686419.44427013607)]
Best Answer
I think the only issue here is that you need to pass the coordinates to the Polygon constructor, not the list of coordinates. So in trial one, instead of
do:
As I don't have all of your code, here is an example to demonstrate the solution, which returns the same polygon as inputPolygon: