/usr/lib/python3/dist-packages/shapely/geometry/multipolygon.py is in python3-shapely 1.6.4-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | """Collections of polygons and related utilities
"""
import sys
if sys.version_info[0] < 3:
range = xrange
from ctypes import c_void_p, cast
from shapely.geos import lgeos
from shapely.geometry.base import BaseMultipartGeometry, geos_geom_from_py
from shapely.geometry import polygon
from shapely.geometry.proxy import CachingGeometryProxy
__all__ = ['MultiPolygon', 'asMultiPolygon']
class MultiPolygon(BaseMultipartGeometry):
"""A collection of one or more polygons
If component polygons overlap the collection is `invalid` and some
operations on it may fail.
Attributes
----------
geoms : sequence
A sequence of `Polygon` instances
"""
def __init__(self, polygons=None, context_type='polygons'):
"""
Parameters
----------
polygons : sequence
A sequence of (shell, holes) tuples where shell is the sequence
representation of a linear ring (see linearring.py) and holes is
a sequence of such linear rings
Example
-------
Construct a collection from a sequence of coordinate tuples
>>> ob = MultiPolygon( [
... (
... ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)),
... [((0.1,0.1), (0.1,0.2), (0.2,0.2), (0.2,0.1))]
... )
... ] )
>>> len(ob.geoms)
1
>>> type(ob.geoms[0]) == Polygon
True
"""
super(MultiPolygon, self).__init__()
if not polygons:
# allow creation of empty multipolygons, to support unpickling
pass
elif context_type == 'polygons':
self._geom, self._ndim = geos_multipolygon_from_polygons(polygons)
elif context_type == 'geojson':
self._geom, self._ndim = geos_multipolygon_from_py(polygons)
def shape_factory(self, *args):
return polygon.Polygon(*args)
@property
def __geo_interface__(self):
allcoords = []
for geom in self.geoms:
coords = []
coords.append(tuple(geom.exterior.coords))
for hole in geom.interiors:
coords.append(tuple(hole.coords))
allcoords.append(tuple(coords))
return {
'type': 'MultiPolygon',
'coordinates': allcoords
}
def svg(self, scale_factor=1., fill_color=None):
"""Returns group of SVG path elements for the MultiPolygon geometry.
Parameters
==========
scale_factor : float
Multiplication factor for the SVG stroke-width. Default is 1.
fill_color : str, optional
Hex string for fill color. Default is to use "#66cc99" if
geometry is valid, and "#ff3333" if invalid.
"""
if self.is_empty:
return '<g />'
if fill_color is None:
fill_color = "#66cc99" if self.is_valid else "#ff3333"
return '<g>' + \
''.join(p.svg(scale_factor, fill_color) for p in self) + \
'</g>'
class MultiPolygonAdapter(CachingGeometryProxy, MultiPolygon):
context = None
_other_owned = False
def __init__(self, context, context_type='polygons'):
self.context = context
if context_type == 'geojson':
self.factory = geos_multipolygon_from_py
elif context_type == 'polygons':
self.factory = geos_multipolygon_from_polygons
@property
def _ndim(self):
try:
# From array protocol
array = self.context[0][0].__array_interface__
n = array['shape'][1]
assert n == 2 or n == 3
return n
except AttributeError:
# Fall back on list
return len(self.context[0][0][0])
def asMultiPolygon(context):
"""Adapts a sequence of objects to the MultiPolygon interface"""
return MultiPolygonAdapter(context)
def geos_multipolygon_from_py(ob):
"""ob must provide Python geo interface coordinates."""
L = len(ob)
assert L >= 1
N = len(ob[0][0][0])
assert N == 2 or N == 3
subs = (c_void_p * L)()
for l in range(L):
geom, ndims = polygon.geos_polygon_from_py(ob[l][0], ob[l][1:])
subs[l] = cast(geom, c_void_p)
return (lgeos.GEOSGeom_createCollection(6, subs, L), N)
def geos_multipolygon_from_polygons(arg):
"""
ob must be either a MultiPolygon, sequence or array of sequences
or arrays.
"""
if isinstance(arg, MultiPolygon):
return geos_geom_from_py(arg)
obs = getattr(arg, 'geoms', arg)
obs = [ob for ob in obs
if ob and not (isinstance(ob, polygon.Polygon) and ob.is_empty)]
L = len(obs)
# Bail immediately if we have no input points.
if L <= 0:
return (lgeos.GEOSGeom_createEmptyCollection(6), 3)
exemplar = obs[0]
try:
N = len(exemplar[0][0])
except TypeError:
N = exemplar._ndim
assert N == 2 or N == 3
subs = (c_void_p * L)()
for i, ob in enumerate(obs):
if isinstance(ob, polygon.Polygon):
shell = ob.exterior
holes = ob.interiors
else:
shell = ob[0]
holes = ob[1]
geom, ndims = polygon.geos_polygon_from_py(shell, holes)
subs[i] = cast(geom, c_void_p)
return (lgeos.GEOSGeom_createCollection(6, subs, L), N)
# Test runner
def _test():
import doctest
doctest.testmod()
if __name__ == "__main__":
_test()
|