shapely 1.2.7__py2.7-win32.egg
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- DLLs/geos.dll +0 -0
- DLLs/libgeos-3-2-2.dll +0 -0
- EGG-INFO/PKG-INFO +173 -0
- EGG-INFO/not-zip-safe +1 -0
- EGG-INFO/scripts/dissolve.py +53 -0
- EGG-INFO/scripts/intersect.py +81 -0
- shapely/__init__.py +1 -0
- shapely/__init__.pyc +0 -0
- shapely/coords.py +167 -0
- shapely/coords.pyc +0 -0
- shapely/ctypes_declarations.py +276 -0
- shapely/ctypes_declarations.pyc +0 -0
- shapely/geometry/__init__.py +20 -0
- shapely/geometry/__init__.pyc +0 -0
- shapely/geometry/base.py +595 -0
- shapely/geometry/base.pyc +0 -0
- shapely/geometry/collection.py +43 -0
- shapely/geometry/collection.pyc +0 -0
- shapely/geometry/geo.py +68 -0
- shapely/geometry/geo.pyc +0 -0
- shapely/geometry/linestring.py +230 -0
- shapely/geometry/linestring.pyc +0 -0
- shapely/geometry/multilinestring.py +133 -0
- shapely/geometry/multilinestring.pyc +0 -0
- shapely/geometry/multipoint.py +180 -0
- shapely/geometry/multipoint.pyc +0 -0
- shapely/geometry/multipolygon.py +154 -0
- shapely/geometry/multipolygon.pyc +0 -0
- shapely/geometry/point.py +241 -0
- shapely/geometry/point.pyc +0 -0
- shapely/geometry/polygon.py +449 -0
- shapely/geometry/polygon.pyc +0 -0
- shapely/geometry/proxy.py +54 -0
- shapely/geometry/proxy.pyc +0 -0
- shapely/geos.py +370 -0
- shapely/geos.pyc +0 -0
- shapely/impl.py +118 -0
- shapely/impl.pyc +0 -0
- shapely/iterops.py +53 -0
- shapely/iterops.pyc +0 -0
- shapely/linref.py +26 -0
- shapely/linref.pyc +0 -0
- shapely/ops.py +87 -0
- shapely/ops.pyc +0 -0
- shapely/predicates.py +24 -0
- shapely/predicates.pyc +0 -0
- shapely/prepared.py +58 -0
- shapely/prepared.pyc +0 -0
- shapely/topology.py +62 -0
- shapely/topology.pyc +0 -0
- shapely/validation.py +7 -0
- shapely/validation.pyc +0 -0
- shapely/wkb.py +39 -0
- shapely/wkb.pyc +0 -0
- shapely/wkt.py +34 -0
- shapely/wkt.pyc +0 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"""Points and related utilities
|
|
2
|
+
"""
|
|
3
|
+
|
|
4
|
+
from ctypes import c_double
|
|
5
|
+
from ctypes import cast, POINTER
|
|
6
|
+
|
|
7
|
+
from shapely.geos import lgeos, DimensionError
|
|
8
|
+
from shapely.geometry.base import BaseGeometry
|
|
9
|
+
from shapely.geometry.proxy import CachingGeometryProxy
|
|
10
|
+
|
|
11
|
+
__all__ = ['Point', 'asPoint']
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Point(BaseGeometry):
|
|
15
|
+
"""
|
|
16
|
+
A zero dimensional feature
|
|
17
|
+
|
|
18
|
+
A point has zero length and zero area.
|
|
19
|
+
|
|
20
|
+
Attributes
|
|
21
|
+
----------
|
|
22
|
+
x, y, z : float
|
|
23
|
+
Coordinate values
|
|
24
|
+
|
|
25
|
+
Example
|
|
26
|
+
-------
|
|
27
|
+
|
|
28
|
+
>>> p = Point(1.0, -1.0)
|
|
29
|
+
>>> print p
|
|
30
|
+
POINT (1.0000000000000000 -1.0000000000000000)
|
|
31
|
+
>>> p.y
|
|
32
|
+
-1.0
|
|
33
|
+
>>> p.x
|
|
34
|
+
1.0
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, *args):
|
|
38
|
+
"""
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
There are 2 cases:
|
|
42
|
+
|
|
43
|
+
1) 1 parameter: this must satisfy the numpy array protocol.
|
|
44
|
+
2) 2 or more parameters: x, y, z : float
|
|
45
|
+
Easting, northing, and elevation.
|
|
46
|
+
"""
|
|
47
|
+
BaseGeometry.__init__(self)
|
|
48
|
+
if len(args) > 0:
|
|
49
|
+
self._set_coords(*args)
|
|
50
|
+
|
|
51
|
+
# Coordinate getters and setters
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def x(self):
|
|
55
|
+
"""Return x coordinate."""
|
|
56
|
+
return self.coords[0][0]
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def y(self):
|
|
60
|
+
"""Return y coordinate."""
|
|
61
|
+
return self.coords[0][1]
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def z(self):
|
|
65
|
+
"""Return z coordinate."""
|
|
66
|
+
if self._ndim != 3:
|
|
67
|
+
raise DimensionError("This point has no z coordinate.")
|
|
68
|
+
return self.coords[0][2]
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def __geo_interface__(self):
|
|
72
|
+
return {
|
|
73
|
+
'type': 'Point',
|
|
74
|
+
'coordinates': self.coords[0]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def ctypes(self):
|
|
79
|
+
if not self._ctypes_data:
|
|
80
|
+
array_type = c_double * self._ndim
|
|
81
|
+
array = array_type()
|
|
82
|
+
xy = self.coords[0]
|
|
83
|
+
array[0] = xy[0]
|
|
84
|
+
array[1] = xy[1]
|
|
85
|
+
if self._ndim == 3:
|
|
86
|
+
array[2] = xy[2]
|
|
87
|
+
self._ctypes_data = array
|
|
88
|
+
return self._ctypes_data
|
|
89
|
+
|
|
90
|
+
def array_interface(self):
|
|
91
|
+
"""Provide the Numpy array protocol."""
|
|
92
|
+
ai = self.array_interface_base
|
|
93
|
+
ai.update({'shape': (self._ndim,)})
|
|
94
|
+
return ai
|
|
95
|
+
__array_interface__ = property(array_interface)
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def bounds(self):
|
|
99
|
+
xy = self.coords[0]
|
|
100
|
+
return (xy[0], xy[1], xy[0], xy[1])
|
|
101
|
+
|
|
102
|
+
# Coordinate access
|
|
103
|
+
|
|
104
|
+
def _set_coords(self, *args):
|
|
105
|
+
self.empty()
|
|
106
|
+
if len(args) == 1:
|
|
107
|
+
self._geom, self._ndim = geos_point_from_py(args[0])
|
|
108
|
+
else:
|
|
109
|
+
self._geom, self._ndim = geos_point_from_py(tuple(args))
|
|
110
|
+
|
|
111
|
+
coords = property(BaseGeometry._get_coords, _set_coords)
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def xy(self):
|
|
115
|
+
"""Separate arrays of X and Y coordinate values
|
|
116
|
+
|
|
117
|
+
Example:
|
|
118
|
+
|
|
119
|
+
>>> x, y = Point(0, 0).xy
|
|
120
|
+
>>> list(x)
|
|
121
|
+
[0.0]
|
|
122
|
+
>>> list(y)
|
|
123
|
+
[0.0]
|
|
124
|
+
"""
|
|
125
|
+
return self.coords.xy
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class PointAdapter(CachingGeometryProxy, Point):
|
|
129
|
+
|
|
130
|
+
_owned = False
|
|
131
|
+
|
|
132
|
+
def __init__(self, context):
|
|
133
|
+
self.context = context
|
|
134
|
+
self.factory = geos_point_from_py
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def _ndim(self):
|
|
138
|
+
try:
|
|
139
|
+
# From array protocol
|
|
140
|
+
array = self.context.__array_interface__
|
|
141
|
+
n = array['shape'][0]
|
|
142
|
+
assert n == 2 or n == 3
|
|
143
|
+
return n
|
|
144
|
+
except AttributeError:
|
|
145
|
+
# Fall back on list
|
|
146
|
+
return len(self.context)
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def __array_interface__(self):
|
|
150
|
+
"""Provide the Numpy array protocol."""
|
|
151
|
+
try:
|
|
152
|
+
return self.context.__array_interface__
|
|
153
|
+
except AttributeError:
|
|
154
|
+
return self.array_interface()
|
|
155
|
+
|
|
156
|
+
_get_coords = BaseGeometry._get_coords
|
|
157
|
+
|
|
158
|
+
def _set_coords(self, ob):
|
|
159
|
+
raise NotImplementedError("Adapters can not modify their sources")
|
|
160
|
+
|
|
161
|
+
coords = property(_get_coords)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def asPoint(context):
|
|
165
|
+
"""Adapt an object to the Point interface"""
|
|
166
|
+
return PointAdapter(context)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def geos_point_from_py(ob, update_geom=None, update_ndim=0):
|
|
170
|
+
"""Create a GEOS geom from an object that is a coordinate sequence
|
|
171
|
+
or that provides the array interface.
|
|
172
|
+
|
|
173
|
+
Returns the GEOS geometry and the number of its dimensions.
|
|
174
|
+
"""
|
|
175
|
+
try:
|
|
176
|
+
# From array protocol
|
|
177
|
+
array = ob.__array_interface__
|
|
178
|
+
assert len(array['shape']) == 1
|
|
179
|
+
n = array['shape'][0]
|
|
180
|
+
assert n == 2 or n == 3
|
|
181
|
+
|
|
182
|
+
dz = None
|
|
183
|
+
da = array['data']
|
|
184
|
+
if type(da) == type((0,)):
|
|
185
|
+
cdata = da[0]
|
|
186
|
+
cp = cast(cdata, POINTER(c_double))
|
|
187
|
+
dx = c_double(cp[0])
|
|
188
|
+
dy = c_double(cp[1])
|
|
189
|
+
if n == 3:
|
|
190
|
+
dz = c_double(cp[2])
|
|
191
|
+
ndim = 3
|
|
192
|
+
else:
|
|
193
|
+
dx, dy = da[0:2]
|
|
194
|
+
if n == 3:
|
|
195
|
+
dz = da[2]
|
|
196
|
+
ndim = 3
|
|
197
|
+
|
|
198
|
+
except AttributeError:
|
|
199
|
+
# Fall back on the case of Python sequence data
|
|
200
|
+
# Accept either (x, y) or [(x, y)]
|
|
201
|
+
if type(ob[0]) == type(tuple()):
|
|
202
|
+
coords = ob[0]
|
|
203
|
+
else:
|
|
204
|
+
coords = ob
|
|
205
|
+
n = len(coords)
|
|
206
|
+
dx = c_double(coords[0])
|
|
207
|
+
dy = c_double(coords[1])
|
|
208
|
+
dz = None
|
|
209
|
+
if n == 3:
|
|
210
|
+
dz = c_double(coords[2])
|
|
211
|
+
|
|
212
|
+
if update_geom:
|
|
213
|
+
cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
|
|
214
|
+
if n != update_ndim:
|
|
215
|
+
raise ValueError(
|
|
216
|
+
"Wrong coordinate dimensions; this geometry has dimensions: %d" \
|
|
217
|
+
% update_ndim)
|
|
218
|
+
else:
|
|
219
|
+
cs = lgeos.GEOSCoordSeq_create(1, n)
|
|
220
|
+
|
|
221
|
+
# Because of a bug in the GEOS C API, always set X before Y
|
|
222
|
+
lgeos.GEOSCoordSeq_setX(cs, 0, dx)
|
|
223
|
+
lgeos.GEOSCoordSeq_setY(cs, 0, dy)
|
|
224
|
+
if n == 3:
|
|
225
|
+
lgeos.GEOSCoordSeq_setZ(cs, 0, dz)
|
|
226
|
+
|
|
227
|
+
if update_geom:
|
|
228
|
+
return None
|
|
229
|
+
else:
|
|
230
|
+
return lgeos.GEOSGeom_createPoint(cs), n
|
|
231
|
+
|
|
232
|
+
def update_point_from_py(geom, ob):
|
|
233
|
+
geos_point_from_py(ob, geom._geom, geom._ndim)
|
|
234
|
+
|
|
235
|
+
# Test runner
|
|
236
|
+
def _test():
|
|
237
|
+
import doctest
|
|
238
|
+
doctest.testmod()
|
|
239
|
+
|
|
240
|
+
if __name__ == "__main__":
|
|
241
|
+
_test()
|
|
Binary file
|
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
"""Polygons and their linear ring components
|
|
2
|
+
"""
|
|
3
|
+
|
|
4
|
+
from ctypes import c_double, c_void_p, cast, POINTER
|
|
5
|
+
from ctypes import ArgumentError
|
|
6
|
+
import weakref
|
|
7
|
+
from shapely.geos import lgeos
|
|
8
|
+
from shapely.geometry.base import BaseGeometry
|
|
9
|
+
from shapely.geometry.linestring import LineString, LineStringAdapter
|
|
10
|
+
from shapely.geometry.proxy import PolygonProxy
|
|
11
|
+
|
|
12
|
+
__all__ = ['Polygon', 'asPolygon', 'LinearRing', 'asLinearRing']
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class LinearRing(LineString):
|
|
16
|
+
"""
|
|
17
|
+
A closed one-dimensional feature comprising one or more line segments
|
|
18
|
+
|
|
19
|
+
A LinearRing that crosses itself or touches itself at a single point is
|
|
20
|
+
invalid and operations on it may fail.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, coordinates=None):
|
|
24
|
+
"""
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
coordinates : sequence
|
|
28
|
+
A sequence of (x, y [,z]) numeric coordinate pairs or triples
|
|
29
|
+
|
|
30
|
+
Rings are implicitly closed. There is no need to specific a final
|
|
31
|
+
coordinate pair identical to the first.
|
|
32
|
+
|
|
33
|
+
Example
|
|
34
|
+
-------
|
|
35
|
+
Construct a square ring.
|
|
36
|
+
|
|
37
|
+
>>> ring = LinearRing( ((0, 0), (0, 1), (1 ,1 ), (1 , 0)) )
|
|
38
|
+
>>> ring.is_closed
|
|
39
|
+
True
|
|
40
|
+
>>> ring.length
|
|
41
|
+
4.0
|
|
42
|
+
"""
|
|
43
|
+
BaseGeometry.__init__(self)
|
|
44
|
+
if coordinates is not None:
|
|
45
|
+
self._set_coords(coordinates)
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def __geo_interface__(self):
|
|
49
|
+
return {
|
|
50
|
+
'type': 'LinearRing',
|
|
51
|
+
'coordinates': tuple(self.coords)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Coordinate access
|
|
55
|
+
|
|
56
|
+
_get_coords = BaseGeometry._get_coords
|
|
57
|
+
|
|
58
|
+
def _set_coords(self, coordinates):
|
|
59
|
+
self.empty()
|
|
60
|
+
self._geom, self._ndim = geos_linearring_from_py(coordinates)
|
|
61
|
+
|
|
62
|
+
coords = property(_get_coords, _set_coords)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class LinearRingAdapter(LineStringAdapter):
|
|
66
|
+
|
|
67
|
+
__p__ = None
|
|
68
|
+
|
|
69
|
+
def __init__(self, context):
|
|
70
|
+
self.context = context
|
|
71
|
+
self.factory = geos_linearring_from_py
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def __geo_interface__(self):
|
|
75
|
+
return {
|
|
76
|
+
'type': 'LinearRing',
|
|
77
|
+
'coordinates': tuple(self.coords)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
coords = property(BaseGeometry._get_coords)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def asLinearRing(context):
|
|
84
|
+
"""Adapt an object to the LinearRing interface"""
|
|
85
|
+
return LinearRingAdapter(context)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class InteriorRingSequence(object):
|
|
89
|
+
|
|
90
|
+
_factory = None
|
|
91
|
+
_geom = None
|
|
92
|
+
__p__ = None
|
|
93
|
+
_ndim = None
|
|
94
|
+
_index = 0
|
|
95
|
+
_length = 0
|
|
96
|
+
__rings__ = None
|
|
97
|
+
_gtag = None
|
|
98
|
+
|
|
99
|
+
def __init__(self, parent):
|
|
100
|
+
self.__p__ = parent
|
|
101
|
+
self._geom = parent._geom
|
|
102
|
+
self._ndim = parent._ndim
|
|
103
|
+
|
|
104
|
+
def __iter__(self):
|
|
105
|
+
self._index = 0
|
|
106
|
+
self._length = self.__len__()
|
|
107
|
+
return self
|
|
108
|
+
|
|
109
|
+
def next(self):
|
|
110
|
+
if self._index < self._length:
|
|
111
|
+
ring = self._get_ring(self._index)
|
|
112
|
+
self._index += 1
|
|
113
|
+
return ring
|
|
114
|
+
else:
|
|
115
|
+
raise StopIteration
|
|
116
|
+
|
|
117
|
+
def __len__(self):
|
|
118
|
+
return lgeos.GEOSGetNumInteriorRings(self._geom)
|
|
119
|
+
|
|
120
|
+
def __getitem__(self, i):
|
|
121
|
+
M = self.__len__()
|
|
122
|
+
if i + M < 0 or i >= M:
|
|
123
|
+
raise IndexError("index out of range")
|
|
124
|
+
if i < 0:
|
|
125
|
+
ii = M + i
|
|
126
|
+
else:
|
|
127
|
+
ii = i
|
|
128
|
+
return self._get_ring(i)
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def _longest(self):
|
|
132
|
+
max = 0
|
|
133
|
+
for g in iter(self):
|
|
134
|
+
l = len(g.coords)
|
|
135
|
+
if l > max:
|
|
136
|
+
max = l
|
|
137
|
+
|
|
138
|
+
def gtag(self):
|
|
139
|
+
return hash(repr(self.__p__))
|
|
140
|
+
|
|
141
|
+
def _get_ring(self, i):
|
|
142
|
+
gtag = self.gtag()
|
|
143
|
+
if gtag != self._gtag:
|
|
144
|
+
self.__rings__ = {}
|
|
145
|
+
if i not in self.__rings__:
|
|
146
|
+
g = lgeos.GEOSGetInteriorRingN(self._geom, i)
|
|
147
|
+
ring = LinearRing()
|
|
148
|
+
ring.__geom__ = g
|
|
149
|
+
ring.__p__ = self
|
|
150
|
+
ring._owned = True
|
|
151
|
+
ring._ndim = self._ndim
|
|
152
|
+
self.__rings__[i] = weakref.ref(ring)
|
|
153
|
+
return self.__rings__[i]()
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class Polygon(BaseGeometry):
|
|
157
|
+
"""
|
|
158
|
+
A two-dimensional figure bounded by a linear ring
|
|
159
|
+
|
|
160
|
+
A polygon has a non-zero area. It may have one or more negative-space
|
|
161
|
+
"holes" which are also bounded by linear rings. If any rings cross each
|
|
162
|
+
other, the feature is invalid and operations on it may fail.
|
|
163
|
+
|
|
164
|
+
Attributes
|
|
165
|
+
----------
|
|
166
|
+
exterior : LinearRing
|
|
167
|
+
The ring which bounds the positive space of the polygon.
|
|
168
|
+
interiors : sequence
|
|
169
|
+
A sequence of rings which bound all existing holes.
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
_exterior = None
|
|
173
|
+
_interiors = []
|
|
174
|
+
_ndim = 2
|
|
175
|
+
|
|
176
|
+
def __init__(self, shell=None, holes=None):
|
|
177
|
+
"""
|
|
178
|
+
Parameters
|
|
179
|
+
----------
|
|
180
|
+
shell : sequence
|
|
181
|
+
A sequence of (x, y [,z]) numeric coordinate pairs or triples
|
|
182
|
+
holes : sequence
|
|
183
|
+
A sequence of objects which satisfy the same requirements as the
|
|
184
|
+
shell parameters above
|
|
185
|
+
|
|
186
|
+
Example
|
|
187
|
+
-------
|
|
188
|
+
Create a square polygon with no holes
|
|
189
|
+
|
|
190
|
+
>>> coords = ((0., 0.), (0., 1.), (1., 1.), (1., 0.), (0., 0.))
|
|
191
|
+
>>> polygon = Polygon(coords)
|
|
192
|
+
>>> polygon.area
|
|
193
|
+
1.0
|
|
194
|
+
"""
|
|
195
|
+
BaseGeometry.__init__(self)
|
|
196
|
+
|
|
197
|
+
if shell is not None:
|
|
198
|
+
self._geom, self._ndim = geos_polygon_from_py(shell, holes)
|
|
199
|
+
|
|
200
|
+
@property
|
|
201
|
+
def exterior(self):
|
|
202
|
+
if self.is_empty:
|
|
203
|
+
return None
|
|
204
|
+
elif self._exterior is None or self._exterior() is None:
|
|
205
|
+
g = lgeos.GEOSGetExteriorRing(self._geom)
|
|
206
|
+
ring = LinearRing()
|
|
207
|
+
ring.__geom__ = g
|
|
208
|
+
ring.__p__ = self
|
|
209
|
+
ring._owned = True
|
|
210
|
+
ring._ndim = self._ndim
|
|
211
|
+
self._exterior = weakref.ref(ring)
|
|
212
|
+
return self._exterior()
|
|
213
|
+
|
|
214
|
+
@property
|
|
215
|
+
def interiors(self):
|
|
216
|
+
if self.is_empty:
|
|
217
|
+
return []
|
|
218
|
+
return InteriorRingSequence(self)
|
|
219
|
+
|
|
220
|
+
@property
|
|
221
|
+
def ctypes(self):
|
|
222
|
+
if not self._ctypes_data:
|
|
223
|
+
self._ctypes_data = self.exterior.ctypes
|
|
224
|
+
return self._ctypes_data
|
|
225
|
+
|
|
226
|
+
@property
|
|
227
|
+
def __array_interface__(self):
|
|
228
|
+
raise NotImplementedError(
|
|
229
|
+
"A polygon does not itself provide the array interface. Its rings do.")
|
|
230
|
+
|
|
231
|
+
def _get_coords(self):
|
|
232
|
+
raise NotImplementedError(
|
|
233
|
+
"Component rings have coordinate sequences, but the polygon does not")
|
|
234
|
+
|
|
235
|
+
def _set_coords(self, ob):
|
|
236
|
+
raise NotImplementedError(
|
|
237
|
+
"Component rings have coordinate sequences, but the polygon does not")
|
|
238
|
+
|
|
239
|
+
@property
|
|
240
|
+
def coords(self):
|
|
241
|
+
raise NotImplementedError(
|
|
242
|
+
"Component rings have coordinate sequences, but the polygon does not")
|
|
243
|
+
|
|
244
|
+
@property
|
|
245
|
+
def __geo_interface__(self):
|
|
246
|
+
coords = [tuple(self.exterior.coords)]
|
|
247
|
+
for hole in self.interiors:
|
|
248
|
+
coords.append(tuple(hole.coords))
|
|
249
|
+
return {
|
|
250
|
+
'type': 'Polygon',
|
|
251
|
+
'coordinates': tuple(coords)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class PolygonAdapter(PolygonProxy, Polygon):
|
|
256
|
+
|
|
257
|
+
def __init__(self, shell, holes=None):
|
|
258
|
+
self.shell = shell
|
|
259
|
+
self.holes = holes
|
|
260
|
+
self.context = (shell, holes)
|
|
261
|
+
self.factory = geos_polygon_from_py
|
|
262
|
+
|
|
263
|
+
@property
|
|
264
|
+
def _ndim(self):
|
|
265
|
+
try:
|
|
266
|
+
# From array protocol
|
|
267
|
+
array = self.shell.__array_interface__
|
|
268
|
+
n = array['shape'][1]
|
|
269
|
+
assert n == 2 or n == 3
|
|
270
|
+
return n
|
|
271
|
+
except AttributeError:
|
|
272
|
+
# Fall back on list
|
|
273
|
+
return len(self.shell[0])
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def asPolygon(shell, holes=None):
|
|
277
|
+
"""Adapt objects to the Polygon interface"""
|
|
278
|
+
return PolygonAdapter(shell, holes)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def geos_linearring_from_py(ob, update_geom=None, update_ndim=0):
|
|
282
|
+
try:
|
|
283
|
+
# From array protocol
|
|
284
|
+
array = ob.__array_interface__
|
|
285
|
+
assert len(array['shape']) == 2
|
|
286
|
+
m = array['shape'][0]
|
|
287
|
+
n = array['shape'][1]
|
|
288
|
+
if m < 3:
|
|
289
|
+
raise ValueError(
|
|
290
|
+
"A LinearRing must have at least 3 coordinate tuples")
|
|
291
|
+
assert n == 2 or n == 3
|
|
292
|
+
|
|
293
|
+
# Make pointer to the coordinate array
|
|
294
|
+
try:
|
|
295
|
+
cp = cast(array['data'][0], POINTER(c_double))
|
|
296
|
+
except ArgumentError:
|
|
297
|
+
cp = array['data']
|
|
298
|
+
|
|
299
|
+
# Add closing coordinates to sequence?
|
|
300
|
+
if cp[0] != cp[m*n-n] or cp[1] != cp[m*n-n+1]:
|
|
301
|
+
M = m + 1
|
|
302
|
+
else:
|
|
303
|
+
M = m
|
|
304
|
+
|
|
305
|
+
# Create a coordinate sequence
|
|
306
|
+
if update_geom is not None:
|
|
307
|
+
cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
|
|
308
|
+
if n != update_ndim:
|
|
309
|
+
raise ValueError(
|
|
310
|
+
"Wrong coordinate dimensions; this geometry has dimensions: %d" \
|
|
311
|
+
% update_ndim)
|
|
312
|
+
else:
|
|
313
|
+
cs = lgeos.GEOSCoordSeq_create(M, n)
|
|
314
|
+
|
|
315
|
+
# add to coordinate sequence
|
|
316
|
+
for i in xrange(m):
|
|
317
|
+
dx = c_double(cp[n*i])
|
|
318
|
+
dy = c_double(cp[n*i+1])
|
|
319
|
+
dz = None
|
|
320
|
+
if n == 3:
|
|
321
|
+
dz = c_double(cp[n*i+2])
|
|
322
|
+
|
|
323
|
+
# Because of a bug in the GEOS C API,
|
|
324
|
+
# always set X before Y
|
|
325
|
+
lgeos.GEOSCoordSeq_setX(cs, i, dx)
|
|
326
|
+
lgeos.GEOSCoordSeq_setY(cs, i, dy)
|
|
327
|
+
if n == 3:
|
|
328
|
+
lgeos.GEOSCoordSeq_setZ(cs, i, dz)
|
|
329
|
+
|
|
330
|
+
# Add closing coordinates to sequence?
|
|
331
|
+
if M > m:
|
|
332
|
+
dx = c_double(cp[0])
|
|
333
|
+
dy = c_double(cp[1])
|
|
334
|
+
dz = None
|
|
335
|
+
if n == 3:
|
|
336
|
+
dz = c_double(cp[2])
|
|
337
|
+
|
|
338
|
+
# Because of a bug in the GEOS C API,
|
|
339
|
+
# always set X before Y
|
|
340
|
+
lgeos.GEOSCoordSeq_setX(cs, M-1, dx)
|
|
341
|
+
lgeos.GEOSCoordSeq_setY(cs, M-1, dy)
|
|
342
|
+
if n == 3:
|
|
343
|
+
lgeos.GEOSCoordSeq_setZ(cs, M-1, dz)
|
|
344
|
+
|
|
345
|
+
except AttributeError:
|
|
346
|
+
# Fall back on list
|
|
347
|
+
m = len(ob)
|
|
348
|
+
n = len(ob[0])
|
|
349
|
+
if m < 3:
|
|
350
|
+
raise ValueError(
|
|
351
|
+
"A LinearRing must have at least 3 coordinate tuples")
|
|
352
|
+
assert (n == 2 or n == 3)
|
|
353
|
+
|
|
354
|
+
# Add closing coordinates if not provided
|
|
355
|
+
if m == 3 or ob[0][0] != ob[-1][0] or ob[0][1] != ob[-1][1]:
|
|
356
|
+
M = m + 1
|
|
357
|
+
else:
|
|
358
|
+
M = m
|
|
359
|
+
|
|
360
|
+
# Create a coordinate sequence
|
|
361
|
+
if update_geom is not None:
|
|
362
|
+
cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
|
|
363
|
+
if n != update_ndim:
|
|
364
|
+
raise ValueError(
|
|
365
|
+
"Wrong coordinate dimensions; this geometry has dimensions: %d" \
|
|
366
|
+
% update_ndim)
|
|
367
|
+
else:
|
|
368
|
+
cs = lgeos.GEOSCoordSeq_create(M, n)
|
|
369
|
+
|
|
370
|
+
# add to coordinate sequence
|
|
371
|
+
for i in xrange(m):
|
|
372
|
+
coords = ob[i]
|
|
373
|
+
dx = c_double(coords[0])
|
|
374
|
+
dy = c_double(coords[1])
|
|
375
|
+
dz = None
|
|
376
|
+
if n == 3:
|
|
377
|
+
dz = c_double(coords[2])
|
|
378
|
+
|
|
379
|
+
# Because of a bug in the GEOS C API,
|
|
380
|
+
# always set X before Y
|
|
381
|
+
lgeos.GEOSCoordSeq_setX(cs, i, dx)
|
|
382
|
+
lgeos.GEOSCoordSeq_setY(cs, i, dy)
|
|
383
|
+
if n == 3:
|
|
384
|
+
lgeos.GEOSCoordSeq_setZ(cs, i, dz)
|
|
385
|
+
|
|
386
|
+
# Add closing coordinates to sequence?
|
|
387
|
+
if M > m:
|
|
388
|
+
coords = ob[0]
|
|
389
|
+
dx = c_double(coords[0])
|
|
390
|
+
dy = c_double(coords[1])
|
|
391
|
+
dz = None
|
|
392
|
+
if n == 3:
|
|
393
|
+
dz = c_double(coords[2])
|
|
394
|
+
|
|
395
|
+
# Because of a bug in the GEOS C API,
|
|
396
|
+
# always set X before Y
|
|
397
|
+
lgeos.GEOSCoordSeq_setX(cs, M-1, dx)
|
|
398
|
+
lgeos.GEOSCoordSeq_setY(cs, M-1, dy)
|
|
399
|
+
if n == 3:
|
|
400
|
+
lgeos.GEOSCoordSeq_setZ(cs, M-1, dz)
|
|
401
|
+
|
|
402
|
+
if update_geom is not None:
|
|
403
|
+
return None
|
|
404
|
+
else:
|
|
405
|
+
return lgeos.GEOSGeom_createLinearRing(cs), n
|
|
406
|
+
|
|
407
|
+
def update_linearring_from_py(geom, ob):
|
|
408
|
+
geos_linearring_from_py(ob, geom._geom, geom._ndim)
|
|
409
|
+
|
|
410
|
+
def geos_polygon_from_py(shell, holes=None):
|
|
411
|
+
if shell is not None:
|
|
412
|
+
geos_shell, ndim = geos_linearring_from_py(shell)
|
|
413
|
+
if holes:
|
|
414
|
+
ob = holes
|
|
415
|
+
L = len(ob)
|
|
416
|
+
exemplar = ob[0]
|
|
417
|
+
try:
|
|
418
|
+
N = len(exemplar[0])
|
|
419
|
+
except TypeError:
|
|
420
|
+
N = exemplar._ndim
|
|
421
|
+
assert L >= 1
|
|
422
|
+
assert N == 2 or N == 3
|
|
423
|
+
|
|
424
|
+
# Array of pointers to ring geometries
|
|
425
|
+
geos_holes = (c_void_p * L)()
|
|
426
|
+
|
|
427
|
+
# add to coordinate sequence
|
|
428
|
+
for l in xrange(L):
|
|
429
|
+
geom, ndim = geos_linearring_from_py(ob[l])
|
|
430
|
+
geos_holes[l] = cast(geom, c_void_p)
|
|
431
|
+
else:
|
|
432
|
+
geos_holes = POINTER(c_void_p)()
|
|
433
|
+
L = 0
|
|
434
|
+
return (
|
|
435
|
+
lgeos.GEOSGeom_createPolygon(
|
|
436
|
+
c_void_p(geos_shell),
|
|
437
|
+
geos_holes,
|
|
438
|
+
L
|
|
439
|
+
),
|
|
440
|
+
ndim
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
# Test runner
|
|
444
|
+
def _test():
|
|
445
|
+
import doctest
|
|
446
|
+
doctest.testmod()
|
|
447
|
+
|
|
448
|
+
if __name__ == "__main__":
|
|
449
|
+
_test()
|
|
Binary file
|