shapely 1.0b1__py2.4.egg → 1.0b2__py2.4.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.
- EGG-INFO/PKG-INFO +1 -1
- shapely/geometry/__init__.py +9 -0
- shapely/geometry/__init__.pyc +0 -0
- shapely/geometry/base.py +358 -0
- shapely/geometry/base.pyc +0 -0
- shapely/geometry/collection.py +24 -0
- shapely/geometry/collection.pyc +0 -0
- shapely/geometry/geo.py +35 -0
- shapely/geometry/geo.pyc +0 -0
- shapely/geometry/linestring.py +228 -0
- shapely/geometry/linestring.pyc +0 -0
- shapely/geometry/multilinestring.py +155 -0
- shapely/geometry/multilinestring.pyc +0 -0
- shapely/geometry/multipoint.py +182 -0
- shapely/geometry/multipoint.pyc +0 -0
- shapely/geometry/multipolygon.py +141 -0
- shapely/geometry/multipolygon.pyc +0 -0
- shapely/geometry/point.py +242 -0
- shapely/geometry/point.pyc +0 -0
- shapely/geometry/polygon.py +414 -0
- shapely/geometry/polygon.pyc +0 -0
EGG-INFO/PKG-INFO
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from geo import asShape
|
|
2
|
+
from point import Point, asPoint
|
|
3
|
+
from linestring import LineString, asLineString
|
|
4
|
+
from polygon import Polygon, asPolygon
|
|
5
|
+
from multipoint import MultiPoint, asMultiPoint
|
|
6
|
+
from multilinestring import MultiLineString, asMultiLineString
|
|
7
|
+
from multipolygon import MultiPolygon, asMultiPolygon
|
|
8
|
+
from collection import GeometryCollection
|
|
9
|
+
|
|
Binary file
|
shapely/geometry/base.py
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base geometry class and utilities.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from ctypes import string_at, byref, c_int, c_size_t, c_char_p, c_double
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
from shapely.geos import lgeos
|
|
9
|
+
from shapely.predicates import BinaryPredicate, UnaryPredicate
|
|
10
|
+
from shapely.topology import BinaryTopologicalOp, UnaryTopologicalOp
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
GEOMETRY_TYPES = [
|
|
14
|
+
'Point',
|
|
15
|
+
'LineString',
|
|
16
|
+
'LinearRing',
|
|
17
|
+
'Polygon',
|
|
18
|
+
'MultiPoint',
|
|
19
|
+
'MultiLineString',
|
|
20
|
+
'MultiPolygon',
|
|
21
|
+
'GeometryCollection'
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
def geometry_type_name(g):
|
|
25
|
+
return GEOMETRY_TYPES[lgeos.GEOSGeomTypeId(g)]
|
|
26
|
+
|
|
27
|
+
# Abstract geometry factory for use with topological methods below
|
|
28
|
+
|
|
29
|
+
def geom_factory(g):
|
|
30
|
+
if not g:
|
|
31
|
+
raise ValueError, "No Shapely geometry can be created from this null value"
|
|
32
|
+
ob = BaseGeometry()
|
|
33
|
+
geom_type = geometry_type_name(g)
|
|
34
|
+
# TODO: check cost of dynamic import by profiling
|
|
35
|
+
mod = __import__(
|
|
36
|
+
'shapely.geometry',
|
|
37
|
+
globals(),
|
|
38
|
+
locals(),
|
|
39
|
+
[geom_type],
|
|
40
|
+
)
|
|
41
|
+
ob.__class__ = getattr(mod, geom_type)
|
|
42
|
+
ob._geom = g
|
|
43
|
+
ob._ndim = 2 # callers should be all from 2D worlds
|
|
44
|
+
return ob
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class CoordinateSequence(object):
|
|
48
|
+
|
|
49
|
+
_cseq = None
|
|
50
|
+
_ndim = None
|
|
51
|
+
_length = 0
|
|
52
|
+
index = 0
|
|
53
|
+
|
|
54
|
+
def __init__(self, geom):
|
|
55
|
+
self._cseq = lgeos.GEOSGeom_getCoordSeq(geom._geom)
|
|
56
|
+
self._ndim = geom._ndim
|
|
57
|
+
|
|
58
|
+
def __iter__(self):
|
|
59
|
+
self.index = 0
|
|
60
|
+
self._length = self.__len__()
|
|
61
|
+
return self
|
|
62
|
+
|
|
63
|
+
def next(self):
|
|
64
|
+
dx = c_double()
|
|
65
|
+
dy = c_double()
|
|
66
|
+
dz = c_double()
|
|
67
|
+
i = self.index
|
|
68
|
+
if i < self._length:
|
|
69
|
+
lgeos.GEOSCoordSeq_getX(self._cseq, i, byref(dx))
|
|
70
|
+
lgeos.GEOSCoordSeq_getY(self._cseq, i, byref(dy))
|
|
71
|
+
if self._ndim == 3: # TODO: use hasz
|
|
72
|
+
lgeos.GEOSCoordSeq_getZ(self._cseq, i, byref(dz))
|
|
73
|
+
self.index += 1
|
|
74
|
+
return (dx.value, dy.value, dz.value)
|
|
75
|
+
else:
|
|
76
|
+
self.index += 1
|
|
77
|
+
return (dx.value, dy.value)
|
|
78
|
+
else:
|
|
79
|
+
raise StopIteration
|
|
80
|
+
|
|
81
|
+
def __len__(self):
|
|
82
|
+
cs_len = c_int(0)
|
|
83
|
+
lgeos.GEOSCoordSeq_getSize(self._cseq, byref(cs_len))
|
|
84
|
+
return cs_len.value
|
|
85
|
+
|
|
86
|
+
def __getitem__(self, i):
|
|
87
|
+
M = self.__len__()
|
|
88
|
+
if i + M < 0 or i >= M:
|
|
89
|
+
raise IndexError, "index out of range"
|
|
90
|
+
if i < 0:
|
|
91
|
+
ii = M + i
|
|
92
|
+
else:
|
|
93
|
+
ii = i
|
|
94
|
+
dx = c_double()
|
|
95
|
+
dy = c_double()
|
|
96
|
+
dz = c_double()
|
|
97
|
+
lgeos.GEOSCoordSeq_getX(self._cseq, ii, byref(dx))
|
|
98
|
+
lgeos.GEOSCoordSeq_getY(self._cseq, ii, byref(dy))
|
|
99
|
+
if self._ndim == 3: # TODO: use hasz
|
|
100
|
+
lgeos.GEOSCoordSeq_getZ(self._cseq, ii, byref(dz))
|
|
101
|
+
return (dx.value, dy.value, dz.value)
|
|
102
|
+
else:
|
|
103
|
+
return (dx.value, dy.value)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class GeometrySequence(object):
|
|
107
|
+
|
|
108
|
+
_factory = None
|
|
109
|
+
_geom = None
|
|
110
|
+
_ndim = None
|
|
111
|
+
_index = 0
|
|
112
|
+
_length = 0
|
|
113
|
+
|
|
114
|
+
def __init__(self, geom, type):
|
|
115
|
+
self._factory = type
|
|
116
|
+
self._geom = geom._geom
|
|
117
|
+
self._ndim = geom._ndim
|
|
118
|
+
|
|
119
|
+
def __iter__(self):
|
|
120
|
+
self._index = 0
|
|
121
|
+
self._length = self.__len__()
|
|
122
|
+
return self
|
|
123
|
+
|
|
124
|
+
def next(self):
|
|
125
|
+
if self._index < self.__len__():
|
|
126
|
+
g = self._factory()
|
|
127
|
+
g._owned = True
|
|
128
|
+
g._geom = lgeos.GEOSGetGeometryN(self._geom, self._index)
|
|
129
|
+
self._index += 1
|
|
130
|
+
return g
|
|
131
|
+
else:
|
|
132
|
+
raise StopIteration
|
|
133
|
+
|
|
134
|
+
def __len__(self):
|
|
135
|
+
return lgeos.GEOSGetNumGeometries(self._geom)
|
|
136
|
+
|
|
137
|
+
def __getitem__(self, i):
|
|
138
|
+
M = self.__len__()
|
|
139
|
+
if i + M < 0 or i >= M:
|
|
140
|
+
raise IndexError, "index out of range"
|
|
141
|
+
if i < 0:
|
|
142
|
+
ii = M + i
|
|
143
|
+
else:
|
|
144
|
+
ii = i
|
|
145
|
+
g = self._factory()
|
|
146
|
+
g._owned = True
|
|
147
|
+
g._geom = lgeos.GEOSGetGeometryN(self._geom, ii)
|
|
148
|
+
return g
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def _longest(self):
|
|
152
|
+
max = 0
|
|
153
|
+
for g in iter(self):
|
|
154
|
+
l = len(g.coords)
|
|
155
|
+
if l > max:
|
|
156
|
+
max = l
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class BaseGeometry(object):
|
|
160
|
+
|
|
161
|
+
"""Provides GEOS spatial predicates and topological operations.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
_geom = None
|
|
165
|
+
_ctypes_data = None
|
|
166
|
+
_ndim = None
|
|
167
|
+
_crs = None
|
|
168
|
+
_owned = False
|
|
169
|
+
|
|
170
|
+
def __init__(self):
|
|
171
|
+
self._geom = lgeos.GEOSGeomFromWKT(c_char_p('GEOMETRYCOLLECTION EMPTY'))
|
|
172
|
+
|
|
173
|
+
def __del__(self):
|
|
174
|
+
if self._geom is not None and not self._owned:
|
|
175
|
+
lgeos.GEOSGeom_destroy(self._geom)
|
|
176
|
+
|
|
177
|
+
def __str__(self):
|
|
178
|
+
return self.to_wkt()
|
|
179
|
+
|
|
180
|
+
def __eq__(self, other):
|
|
181
|
+
return self.equals(other)
|
|
182
|
+
|
|
183
|
+
def __ne__(self, other):
|
|
184
|
+
return not self.equals(other)
|
|
185
|
+
|
|
186
|
+
# To support pickling
|
|
187
|
+
|
|
188
|
+
def __reduce__(self):
|
|
189
|
+
return (self.__class__, (), self.to_wkb())
|
|
190
|
+
|
|
191
|
+
def __setstate__(self, state):
|
|
192
|
+
self._geom = lgeos.GEOSGeomFromWKB_buf(
|
|
193
|
+
c_char_p(state),
|
|
194
|
+
c_size_t(len(state))
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
# Array and ctypes interfaces
|
|
198
|
+
|
|
199
|
+
@property
|
|
200
|
+
def ctypes(self):
|
|
201
|
+
"""Return a ctypes representation.
|
|
202
|
+
|
|
203
|
+
To be overridden by extension classes."""
|
|
204
|
+
raise NotImplementedError
|
|
205
|
+
|
|
206
|
+
@property
|
|
207
|
+
def array_interface_base(self):
|
|
208
|
+
if sys.byteorder == 'little':
|
|
209
|
+
typestr = '<f8'
|
|
210
|
+
elif sys.byteorder == 'big':
|
|
211
|
+
typestr = '>f8'
|
|
212
|
+
else:
|
|
213
|
+
raise ValueError, "Unsupported byteorder: neither little nor big-endian"
|
|
214
|
+
return {
|
|
215
|
+
'version': 3,
|
|
216
|
+
'typestr': typestr,
|
|
217
|
+
'data': self.ctypes,
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
@property
|
|
221
|
+
def __array_interface__(self):
|
|
222
|
+
"""Provide the Numpy array protocol."""
|
|
223
|
+
raise NotImplementedError
|
|
224
|
+
|
|
225
|
+
def get_coords(self):
|
|
226
|
+
return CoordinateSequence(self)
|
|
227
|
+
|
|
228
|
+
def set_coords(self, ob):
|
|
229
|
+
raise NotImplementedError, \
|
|
230
|
+
"set_coords must be provided by derived classes"
|
|
231
|
+
|
|
232
|
+
coords = property(get_coords, set_coords)
|
|
233
|
+
|
|
234
|
+
# Python feature protocol
|
|
235
|
+
|
|
236
|
+
@property
|
|
237
|
+
def __geo_interface__(self):
|
|
238
|
+
raise NotImplementedError
|
|
239
|
+
|
|
240
|
+
@property
|
|
241
|
+
def type(self):
|
|
242
|
+
return self.geometryType()
|
|
243
|
+
|
|
244
|
+
# Type of geometry and its representations
|
|
245
|
+
|
|
246
|
+
def geometryType(self):
|
|
247
|
+
"""Returns a string representing the geometry type, e.g. 'Polygon'."""
|
|
248
|
+
return geometry_type_name(self._geom)
|
|
249
|
+
|
|
250
|
+
def to_wkb(self):
|
|
251
|
+
"""Returns a WKB byte string representation of the geometry."""
|
|
252
|
+
size = c_int()
|
|
253
|
+
bytes = lgeos.GEOSGeomToWKB_buf(self._geom, byref(size))
|
|
254
|
+
return string_at(bytes, size.value)
|
|
255
|
+
|
|
256
|
+
def to_wkt(self):
|
|
257
|
+
"""Returns a WKT string representation of the geometry."""
|
|
258
|
+
return string_at(lgeos.GEOSGeomToWKT(self._geom))
|
|
259
|
+
|
|
260
|
+
geom_type = property(geometryType)
|
|
261
|
+
wkt = property(to_wkt)
|
|
262
|
+
wkb = property(to_wkb)
|
|
263
|
+
|
|
264
|
+
# Basic geometry properties
|
|
265
|
+
|
|
266
|
+
@property
|
|
267
|
+
def area(self):
|
|
268
|
+
a = c_double()
|
|
269
|
+
retval = lgeos.GEOSArea(self._geom, byref(a))
|
|
270
|
+
return a.value
|
|
271
|
+
|
|
272
|
+
@property
|
|
273
|
+
def length(self):
|
|
274
|
+
len = c_double()
|
|
275
|
+
retval = lgeos.GEOSLength(self._geom, byref(len))
|
|
276
|
+
return len.value
|
|
277
|
+
|
|
278
|
+
def distance(self, other):
|
|
279
|
+
d = c_double()
|
|
280
|
+
retval = lgeos.GEOSDistance(self._geom, other._geom, byref(d))
|
|
281
|
+
return d.value
|
|
282
|
+
|
|
283
|
+
# Topology operations
|
|
284
|
+
#
|
|
285
|
+
# These use descriptors to reduce the amount of boilerplate.
|
|
286
|
+
|
|
287
|
+
envelope = UnaryTopologicalOp(lgeos.GEOSEnvelope, geom_factory)
|
|
288
|
+
intersection = BinaryTopologicalOp(lgeos.GEOSIntersection, geom_factory)
|
|
289
|
+
convex_hull = UnaryTopologicalOp(lgeos.GEOSConvexHull, geom_factory)
|
|
290
|
+
difference = BinaryTopologicalOp(lgeos.GEOSDifference, geom_factory)
|
|
291
|
+
symmetric_difference = BinaryTopologicalOp(lgeos.GEOSSymDifference,
|
|
292
|
+
geom_factory)
|
|
293
|
+
boundary = UnaryTopologicalOp(lgeos.GEOSBoundary, geom_factory)
|
|
294
|
+
union = BinaryTopologicalOp(lgeos.GEOSUnion, geom_factory)
|
|
295
|
+
centroid = UnaryTopologicalOp(lgeos.GEOSGetCentroid, geom_factory)
|
|
296
|
+
|
|
297
|
+
# Buffer has a unique distance argument, so not a descriptor
|
|
298
|
+
def buffer(self, distance, quadsegs=16):
|
|
299
|
+
return geom_factory(
|
|
300
|
+
lgeos.GEOSBuffer(self._geom, c_double(distance), c_int(quadsegs))
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
# Relate has a unique string return value
|
|
304
|
+
def relate(self, other):
|
|
305
|
+
func = lgeos.GEOSRelate
|
|
306
|
+
func.restype = c_char_p
|
|
307
|
+
return lgeos.GEOSRelate(self._geom, other._geom)
|
|
308
|
+
|
|
309
|
+
# Binary predicates
|
|
310
|
+
#
|
|
311
|
+
# These use descriptors to reduce the amount of boilerplate.
|
|
312
|
+
|
|
313
|
+
# TODO: Relate Pattern?
|
|
314
|
+
disjoint = BinaryPredicate(lgeos.GEOSDisjoint)
|
|
315
|
+
touches = BinaryPredicate(lgeos.GEOSTouches)
|
|
316
|
+
intersects = BinaryPredicate(lgeos.GEOSIntersects)
|
|
317
|
+
crosses = BinaryPredicate(lgeos.GEOSCrosses)
|
|
318
|
+
within = BinaryPredicate(lgeos.GEOSWithin)
|
|
319
|
+
contains = BinaryPredicate(lgeos.GEOSContains)
|
|
320
|
+
overlaps = BinaryPredicate(lgeos.GEOSOverlaps)
|
|
321
|
+
equals = BinaryPredicate(lgeos.GEOSEquals)
|
|
322
|
+
|
|
323
|
+
# Unary predicates
|
|
324
|
+
#
|
|
325
|
+
# These use descriptors to reduce the amount of boilerplate.
|
|
326
|
+
|
|
327
|
+
is_empty = UnaryPredicate(lgeos.GEOSisEmpty)
|
|
328
|
+
is_valid = UnaryPredicate(lgeos.GEOSisValid)
|
|
329
|
+
is_simple = UnaryPredicate(lgeos.GEOSisSimple)
|
|
330
|
+
is_ring = UnaryPredicate(lgeos.GEOSisRing)
|
|
331
|
+
has_z = UnaryPredicate(lgeos.GEOSHasZ)
|
|
332
|
+
|
|
333
|
+
@property
|
|
334
|
+
def bounds(self):
|
|
335
|
+
env = self.envelope
|
|
336
|
+
if env.geom_type != 'Polygon':
|
|
337
|
+
raise ValueError, env.wkt
|
|
338
|
+
cs = lgeos.GEOSGeom_getCoordSeq(env.exterior._geom)
|
|
339
|
+
cs_len = c_int(0)
|
|
340
|
+
lgeos.GEOSCoordSeq_getSize(cs, byref(cs_len))
|
|
341
|
+
|
|
342
|
+
minx = 1.e+20
|
|
343
|
+
maxx = -1e+20
|
|
344
|
+
miny = 1.e+20
|
|
345
|
+
maxy = -1e+20
|
|
346
|
+
temp = c_double()
|
|
347
|
+
for i in xrange(cs_len.value):
|
|
348
|
+
lgeos.GEOSCoordSeq_getX(cs, i, byref(temp))
|
|
349
|
+
x = temp.value
|
|
350
|
+
if x < minx: minx = x
|
|
351
|
+
if x > maxx: maxx = x
|
|
352
|
+
lgeos.GEOSCoordSeq_getY(cs, i, byref(temp))
|
|
353
|
+
y = temp.value
|
|
354
|
+
if y < miny: miny = y
|
|
355
|
+
if y > maxy: maxy = y
|
|
356
|
+
|
|
357
|
+
return (minx, miny, maxx, maxy)
|
|
358
|
+
|
|
Binary file
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Geometry collections.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from shapely.geometry.base import BaseGeometry
|
|
6
|
+
|
|
7
|
+
class GeometryCollection(BaseGeometry):
|
|
8
|
+
|
|
9
|
+
"""A geometry collection.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
BaseGeometry.__init__(self)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Test runner
|
|
17
|
+
def _test():
|
|
18
|
+
import doctest
|
|
19
|
+
doctest.testmod()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
if __name__ == "__main__":
|
|
23
|
+
_test()
|
|
24
|
+
|
|
Binary file
|
shapely/geometry/geo.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
from point import PointAdapter
|
|
3
|
+
from linestring import LineStringAdapter
|
|
4
|
+
from polygon import PolygonAdapter
|
|
5
|
+
from multipoint import MultiPointAdapter
|
|
6
|
+
from multilinestring import MultiLineStringAdapter
|
|
7
|
+
from multipolygon import MultiPolygonAdapter
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def asShape(context):
|
|
11
|
+
"""Adapts the context to a geometry interface. The coordinates remain
|
|
12
|
+
stored in the context.
|
|
13
|
+
"""
|
|
14
|
+
if hasattr(context, "__geo_interface__"):
|
|
15
|
+
ob = context.__geo_interface__
|
|
16
|
+
else:
|
|
17
|
+
ob = context
|
|
18
|
+
|
|
19
|
+
geom_type = ob.get("type").lower()
|
|
20
|
+
|
|
21
|
+
if geom_type == "point":
|
|
22
|
+
return PointAdapter(ob["coordinates"])
|
|
23
|
+
elif geom_type == "linestring":
|
|
24
|
+
return LineStringAdapter(ob["coordinates"])
|
|
25
|
+
elif geom_type == "polygon":
|
|
26
|
+
return PolygonAdapter(ob["coordinates"][0], ob["coordinates"][1:])
|
|
27
|
+
elif geom_type == "multipoint":
|
|
28
|
+
return MultiPointAdapter(ob["coordinates"])
|
|
29
|
+
elif geom_type == "multilinestring":
|
|
30
|
+
return MultiLineStringAdapter(ob["coordinates"])
|
|
31
|
+
elif geom_type == "multipolygon":
|
|
32
|
+
return MultiPolygonAdapter(ob["coordinates"])
|
|
33
|
+
else:
|
|
34
|
+
raise ValueError, "Unknown geometry type: %s" % geom_type
|
|
35
|
+
|
shapely/geometry/geo.pyc
ADDED
|
Binary file
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Line strings.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from ctypes import byref, c_double, c_int, cast, POINTER, pointer
|
|
6
|
+
from ctypes import ArgumentError
|
|
7
|
+
|
|
8
|
+
from shapely.geos import lgeos
|
|
9
|
+
from shapely.geometry.base import BaseGeometry
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
|
|
13
|
+
try:
|
|
14
|
+
# From array protocol
|
|
15
|
+
array = ob.__array_interface__
|
|
16
|
+
assert len(array['shape']) == 2
|
|
17
|
+
m = array['shape'][0]
|
|
18
|
+
n = array['shape'][1]
|
|
19
|
+
assert m >= 2
|
|
20
|
+
assert n == 2 or n == 3
|
|
21
|
+
|
|
22
|
+
# Make pointer to the coordinate array
|
|
23
|
+
try:
|
|
24
|
+
cp = cast(array['data'][0], POINTER(c_double))
|
|
25
|
+
except ArgumentError:
|
|
26
|
+
cp = array['data']
|
|
27
|
+
|
|
28
|
+
# Create a coordinate sequence
|
|
29
|
+
if update_geom is not None:
|
|
30
|
+
cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
|
|
31
|
+
if n != update_ndim:
|
|
32
|
+
raise ValueError, \
|
|
33
|
+
"Wrong coordinate dimensions; this geometry has dimensions: %d" \
|
|
34
|
+
% update_ndim
|
|
35
|
+
else:
|
|
36
|
+
cs = lgeos.GEOSCoordSeq_create(m, n)
|
|
37
|
+
|
|
38
|
+
# add to coordinate sequence
|
|
39
|
+
for i in xrange(m):
|
|
40
|
+
dx = c_double(cp[n*i])
|
|
41
|
+
dy = c_double(cp[n*i+1])
|
|
42
|
+
dz = None
|
|
43
|
+
if n == 3:
|
|
44
|
+
dz = c_double(cp[n*i+2])
|
|
45
|
+
|
|
46
|
+
# Because of a bug in the GEOS C API,
|
|
47
|
+
# always set X before Y
|
|
48
|
+
lgeos.GEOSCoordSeq_setX(cs, i, dx)
|
|
49
|
+
lgeos.GEOSCoordSeq_setY(cs, i, dy)
|
|
50
|
+
if n == 3:
|
|
51
|
+
lgeos.GEOSCoordSeq_setZ(cs, i, dz)
|
|
52
|
+
|
|
53
|
+
except AttributeError:
|
|
54
|
+
# Fall back on list
|
|
55
|
+
m = len(ob)
|
|
56
|
+
n = len(ob[0])
|
|
57
|
+
assert m >= 2
|
|
58
|
+
assert n == 2 or n == 3
|
|
59
|
+
|
|
60
|
+
# Create a coordinate sequence
|
|
61
|
+
if update_geom is not None:
|
|
62
|
+
cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
|
|
63
|
+
if n != update_ndim:
|
|
64
|
+
raise ValueError, \
|
|
65
|
+
"Wrong coordinate dimensions; this geometry has dimensions: %d" \
|
|
66
|
+
% update_ndim
|
|
67
|
+
else:
|
|
68
|
+
cs = lgeos.GEOSCoordSeq_create(m, n)
|
|
69
|
+
|
|
70
|
+
# add to coordinate sequence
|
|
71
|
+
for i in xrange(m):
|
|
72
|
+
coords = ob[i]
|
|
73
|
+
dx = c_double(coords[0])
|
|
74
|
+
dy = c_double(coords[1])
|
|
75
|
+
dz = None
|
|
76
|
+
if n == 3:
|
|
77
|
+
dz = c_double(coords[2])
|
|
78
|
+
|
|
79
|
+
# Because of a bug in the GEOS C API,
|
|
80
|
+
# always set X before Y
|
|
81
|
+
lgeos.GEOSCoordSeq_setX(cs, i, dx)
|
|
82
|
+
lgeos.GEOSCoordSeq_setY(cs, i, dy)
|
|
83
|
+
if n == 3:
|
|
84
|
+
lgeos.GEOSCoordSeq_setZ(cs, i, dz)
|
|
85
|
+
|
|
86
|
+
if update_geom is not None:
|
|
87
|
+
return None
|
|
88
|
+
else:
|
|
89
|
+
return (lgeos.GEOSGeom_createLineString(cs), n)
|
|
90
|
+
|
|
91
|
+
def update_linestring_from_py(geom, ob):
|
|
92
|
+
geos_linestring_from_py(ob, geom._geom, geom._ndim)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class LineString(BaseGeometry):
|
|
96
|
+
|
|
97
|
+
"""A line string, also known as a polyline.
|
|
98
|
+
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
def __init__(self, coordinates=None):
|
|
102
|
+
"""Initialize.
|
|
103
|
+
|
|
104
|
+
Parameters
|
|
105
|
+
----------
|
|
106
|
+
|
|
107
|
+
coordinates : sequence or array
|
|
108
|
+
This may be an object that satisfies the numpy array protocol,
|
|
109
|
+
providing an M x 2 or M x 3 (with z) array, or it may be a sequence
|
|
110
|
+
of x, y (,z) coordinate sequences.
|
|
111
|
+
|
|
112
|
+
Example
|
|
113
|
+
-------
|
|
114
|
+
|
|
115
|
+
>>> line = LineString([[0.0, 0.0], [1.0, 2.0]])
|
|
116
|
+
>>> line = LineString(array([[0.0, 0.0], [1.0, 2.0]]))
|
|
117
|
+
|
|
118
|
+
Each result in a line string from (0.0, 0.0) to (1.0, 2.0).
|
|
119
|
+
"""
|
|
120
|
+
BaseGeometry.__init__(self)
|
|
121
|
+
|
|
122
|
+
if coordinates is None:
|
|
123
|
+
# allow creation of null lines, to support unpickling
|
|
124
|
+
pass
|
|
125
|
+
else:
|
|
126
|
+
self._geom, self._ndim = geos_linestring_from_py(coordinates)
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def __geo_interface__(self):
|
|
130
|
+
return {
|
|
131
|
+
'type': 'LineString',
|
|
132
|
+
'coordinates': tuple(self.coords)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def ctypes(self):
|
|
137
|
+
if not self._ctypes_data:
|
|
138
|
+
cs = lgeos.GEOSGeom_getCoordSeq(self._geom)
|
|
139
|
+
cs_len = c_int(0)
|
|
140
|
+
lgeos.GEOSCoordSeq_getSize(cs, byref(cs_len))
|
|
141
|
+
temp = c_double()
|
|
142
|
+
n = self._ndim
|
|
143
|
+
m = cs_len.value
|
|
144
|
+
array_type = c_double * (m * n)
|
|
145
|
+
data = array_type()
|
|
146
|
+
for i in xrange(m):
|
|
147
|
+
lgeos.GEOSCoordSeq_getX(cs, i, byref(temp))
|
|
148
|
+
data[n*i] = temp.value
|
|
149
|
+
lgeos.GEOSCoordSeq_getY(cs, i, byref(temp))
|
|
150
|
+
data[n*i+1] = temp.value
|
|
151
|
+
if n == 3: # TODO: use hasz
|
|
152
|
+
lgeos.GEOSCoordSeq_getZ(cs, i, byref(temp))
|
|
153
|
+
data[n*i+2] = temp.value
|
|
154
|
+
self._ctypes_data = data
|
|
155
|
+
return self._ctypes_data
|
|
156
|
+
|
|
157
|
+
def array_interface(self):
|
|
158
|
+
"""Provide the Numpy array protocol."""
|
|
159
|
+
ai = self.array_interface_base
|
|
160
|
+
ai.update({'shape': (len(self.coords), self._ndim)})
|
|
161
|
+
return ai
|
|
162
|
+
__array_interface__ = property(array_interface)
|
|
163
|
+
|
|
164
|
+
# Coordinate access
|
|
165
|
+
|
|
166
|
+
def set_coords(self, coordinates):
|
|
167
|
+
update_linestring_from_py(self, coordinates)
|
|
168
|
+
|
|
169
|
+
coords = property(BaseGeometry.get_coords, set_coords)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class LineStringAdapter(LineString):
|
|
173
|
+
|
|
174
|
+
"""Adapts a Python coordinate pair or a numpy array to the line string
|
|
175
|
+
interface.
|
|
176
|
+
"""
|
|
177
|
+
|
|
178
|
+
context = None
|
|
179
|
+
|
|
180
|
+
def __init__(self, context):
|
|
181
|
+
self.context = context
|
|
182
|
+
|
|
183
|
+
# Override base class __del__
|
|
184
|
+
def __del__(self):
|
|
185
|
+
pass
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def _ndim(self):
|
|
189
|
+
try:
|
|
190
|
+
# From array protocol
|
|
191
|
+
array = self.context.__array_interface__
|
|
192
|
+
n = array['shape'][1]
|
|
193
|
+
assert n == 2 or n == 3
|
|
194
|
+
return n
|
|
195
|
+
except AttributeError:
|
|
196
|
+
# Fall back on list
|
|
197
|
+
return len(self.context[0])
|
|
198
|
+
|
|
199
|
+
@property
|
|
200
|
+
def _geom(self):
|
|
201
|
+
"""Keeps the GEOS geometry in synch with the context."""
|
|
202
|
+
return geos_linestring_from_py(self.context)[0]
|
|
203
|
+
|
|
204
|
+
@property
|
|
205
|
+
def __array_interface__(self):
|
|
206
|
+
"""Provide the Numpy array protocol."""
|
|
207
|
+
try:
|
|
208
|
+
return self.context.__array_interface__
|
|
209
|
+
except AttributeError:
|
|
210
|
+
return self.array_interface()
|
|
211
|
+
|
|
212
|
+
coords = property(BaseGeometry.get_coords)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def asLineString(context):
|
|
216
|
+
"""Factory for PointAdapter instances."""
|
|
217
|
+
return LineStringAdapter(context)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
# Test runner
|
|
221
|
+
def _test():
|
|
222
|
+
import doctest
|
|
223
|
+
doctest.testmod()
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
if __name__ == "__main__":
|
|
227
|
+
_test()
|
|
228
|
+
|
|
Binary file
|