You can use the shape function of Shapely:
from shapely.geometry import shape
c = fiona.open('data/boroughs/boroughs_n.shp')
pol = c.next()
geom = shape(pol['geometry'])
and a MultiPolygon is a list of Polygons,so
Multi = MultiPolygon([shape(pol['geometry']) for pol in fiona.open('data/boroughs/boroughs_n.shp')])
Example with one of my data:
# the dictionaries
for pol in fiona.open('poly.shp'):
print pol['geometry']
{'type': 'Polygon', 'coordinates': [[(249744.23153029341, 142798.16434689672), (250113.79108725351, 142132.95714436853), (250062.62130244367, 141973.76225829343), (249607.77877080048, 141757.71205576291), (249367.77424759799, 142304.68402918623), (249367.77424759799, 142304.68402918623), (249744.23153029341, 142798.16434689672)]]}
{'type': 'Polygon', 'coordinates': [[(249175.78991730965, 142292.53526406409), (249367.77424759799, 142304.68402918623), (249607.77877080048, 141757.71205576291), (249014.45396077307, 141876.13484290778), (249175.78991730965, 142292.53526406409)]]}
{'type': 'Polygon', 'coordinates': [[(249026.74622412826, 142549.13626160321), (249223.42243781092, 142496.89414234375), (249175.78991730965, 142292.53526406409), (249026.74622412826, 142549.13626160321)]]}
...
and
# MultiPolygon from the list of Polygons
Multi = MultiPolygon([shape(pol['geometry']) for pol in fiona.open('poly.shp')])
Multi.wkt
'MULTIPOLYGON (((249744.2315302934148349 142798.1643468967231456, 250113.7910872535139788 142132.9571443685272243, 250062.6213024436729029 141973.7622582934272941, 249607.7787708004761953 141757.7120557629095856, 249367.7742475979903247 142304.6840291862317827, 249367.7742475979903247 142304.6840291862317827, 249744.2315302934148349 142798.1643468967231456)), ((249175.7899173096520826 142292.5352640640921891, 249367.7742475979903247 142304.6840291862317827, 249607.7787708004761953 141757.7120557629095856, 249014.4539607730694115 141876.1348429077770561, 249175.7899173096520826 142292.5352640640921891)), ((249026.7462241282628383 142549.1362616032129154, 249223.4224378109211102 142496.8941423437499907, 249175.7899173096520826 142292.5352640640921891, 249026.7462241282628383 142549.1362616032129154)), ((249244.9338986824732274 142733.5202119307068642, 249744.2315302934148349 142798.1643468967231456, 249367.7742475979903247 142304.6840291862317827, 249367.7742475979903247 142304.6840291862317827, 249367.7742475979903247 142304.6840291862317827, 249175.7899173096520826 142292.5352640640921891, 249223.4224378109211102 142496.8941423437499907, 249244.9338986824732274 142733.5202119307068642)), ((249870.8182051893090829 142570.3083320840960369, 250034.3015973484434653 142613.6706442178401630, 250152.6146321419219021 142438.5058914067049045, 250015.3392731740023009 142310.1704097116598859, 249870.8182051893090829 142570.3083320840960369)))'
see also Append support for MultiPolygons in shapefiles
Solution for now: ignore the LineStrings, these are probably produced by an "st_touches" type spatial join where only the perimeters of the multipolygons are touching
RGeo allows you to break out the pieces of a Geometry Collection so I wrote some code to catch the intersect shape if its a GeometryCollection type, sending it to a different area to pluck out the polygons, sum up their size, mark that as the intersect size, and move on
x = shape.intersection(p.proj_shape_2264)
if x.geometry_type.to_s == "GeometryCollection"
area = 0
(0..(x.count - 1)).to_a.each do |k|
collection_shape = x.geometry_n(k)
next if collection_shape.geometry_type.to_s == "LineString"
area += collection_shape.area
end
z.intersect_size = area
else
Best Answer
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..
The geometry should be output as follows:
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.