shapely 1.0__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.
@@ -0,0 +1,429 @@
1
+ """
2
+ Polygons and their linear ring components.
3
+ """
4
+
5
+ from ctypes import byref, c_double, c_int, c_void_p, cast, POINTER, pointer
6
+
7
+ from shapely.geos import lgeos
8
+ from shapely.geometry.base import BaseGeometry, exceptNull
9
+ from shapely.geometry.linestring import LineString, LineStringAdapter
10
+
11
+
12
+ def geos_linearring_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
+ # Add closing coordinates to sequence?
29
+ if cp[0] != cp[m*n-n] or cp[1] != cp[m*n-n+1]:
30
+ M = m + 1
31
+ else:
32
+ M = m
33
+
34
+ # Create a coordinate sequence
35
+ if update_geom is not None:
36
+ cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
37
+ if n != update_ndim:
38
+ raise ValueError, \
39
+ "Wrong coordinate dimensions; this geometry has dimensions: %d" \
40
+ % update_ndim
41
+ else:
42
+ cs = lgeos.GEOSCoordSeq_create(M, n)
43
+
44
+ # add to coordinate sequence
45
+ for i in xrange(m):
46
+ dx = c_double(cp[n*i])
47
+ dy = c_double(cp[n*i+1])
48
+ dz = None
49
+ if n == 3:
50
+ dz = c_double(cp[n*i+2])
51
+
52
+ # Because of a bug in the GEOS C API,
53
+ # always set X before Y
54
+ lgeos.GEOSCoordSeq_setX(cs, i, dx)
55
+ lgeos.GEOSCoordSeq_setY(cs, i, dy)
56
+ if n == 3:
57
+ lgeos.GEOSCoordSeq_setZ(cs, i, dz)
58
+
59
+ # Add closing coordinates to sequence?
60
+ if M > m:
61
+ dx = c_double(cp[0])
62
+ dy = c_double(cp[1])
63
+ dz = None
64
+ if n == 3:
65
+ dz = c_double(cp[2])
66
+
67
+ # Because of a bug in the GEOS C API,
68
+ # always set X before Y
69
+ lgeos.GEOSCoordSeq_setX(cs, M-1, dx)
70
+ lgeos.GEOSCoordSeq_setY(cs, M-1, dy)
71
+ if n == 3:
72
+ lgeos.GEOSCoordSeq_setZ(cs, M-1, dz)
73
+
74
+ except AttributeError:
75
+ # Fall back on list
76
+ m = len(ob)
77
+ n = len(ob[0])
78
+ assert m >= 2
79
+ assert n == 2 or n == 3
80
+
81
+ # Add closing coordinates if not provided
82
+ if ob[0][0] != ob[-1][0] or ob[0][1] != ob[-1][1]:
83
+ M = m + 1
84
+ else:
85
+ M = m
86
+
87
+ # Create a coordinate sequence
88
+ if update_geom is not None:
89
+ cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
90
+ if n != update_ndim:
91
+ raise ValueError, \
92
+ "Wrong coordinate dimensions; this geometry has dimensions: %d" \
93
+ % update_ndim
94
+ else:
95
+ cs = lgeos.GEOSCoordSeq_create(M, n)
96
+
97
+ # add to coordinate sequence
98
+ for i in xrange(m):
99
+ coords = ob[i]
100
+ dx = c_double(coords[0])
101
+ dy = c_double(coords[1])
102
+ dz = None
103
+ if n == 3:
104
+ dz = c_double(coords[2])
105
+
106
+ # Because of a bug in the GEOS C API,
107
+ # always set X before Y
108
+ lgeos.GEOSCoordSeq_setX(cs, i, dx)
109
+ lgeos.GEOSCoordSeq_setY(cs, i, dy)
110
+ if n == 3:
111
+ lgeos.GEOSCoordSeq_setZ(cs, i, dz)
112
+
113
+ # Add closing coordinates to sequence?
114
+ if M > m:
115
+ coords = ob[0]
116
+ dx = c_double(coords[0])
117
+ dy = c_double(coords[1])
118
+ dz = None
119
+ if n == 3:
120
+ dz = c_double(coords[2])
121
+
122
+ # Because of a bug in the GEOS C API,
123
+ # always set X before Y
124
+ lgeos.GEOSCoordSeq_setX(cs, M-1, dx)
125
+ lgeos.GEOSCoordSeq_setY(cs, M-1, dy)
126
+ if n == 3:
127
+ lgeos.GEOSCoordSeq_setZ(cs, M-1, dz)
128
+
129
+ if update_geom is not None:
130
+ return None
131
+ else:
132
+ return lgeos.GEOSGeom_createLinearRing(cs), n
133
+
134
+ def update_linearring_from_py(geom, ob):
135
+ geos_linearring_from_py(ob, geom._geom, geom._ndim)
136
+
137
+
138
+ class LinearRing(LineString):
139
+
140
+ """A linear ring.
141
+ """
142
+
143
+ _ndim = 2
144
+ __geom = None
145
+ _owned = False
146
+
147
+ def __init__(self, coordinates=None):
148
+ """Initialize.
149
+
150
+ Parameters
151
+ ----------
152
+ coordinates : sequence or array
153
+ This may be an object that satisfies the numpy array protocol,
154
+ providing an M x 2 or M x 3 (with z) array, or it may be a sequence
155
+ of x, y (,z) coordinate sequences.
156
+
157
+ Rings are implicitly closed. There is no need to specific a final
158
+ coordinate pair identical to the first.
159
+
160
+ Example
161
+ -------
162
+ >>> ring = LinearRing( ((0.,0.), (0.,1.), (1.,1.), (1.,0.)) )
163
+
164
+ Produces a 1x1 square.
165
+ """
166
+ BaseGeometry.__init__(self)
167
+ self._init_geom(coordinates)
168
+
169
+ def _init_geom(self, coordinates):
170
+ if coordinates is None:
171
+ # allow creation of null lines, to support unpickling
172
+ pass
173
+ else:
174
+ self._geom, self._ndims = geos_linearring_from_py(coordinates)
175
+
176
+ @property
177
+ def __geo_interface__(self):
178
+ return {
179
+ 'type': 'LinearRing',
180
+ 'coordinates': tuple(self.coords)
181
+ }
182
+
183
+ # Coordinate access
184
+
185
+ def set_coords(self, coordinates):
186
+ if self._geom is None:
187
+ self._init_geom(coordinates)
188
+ update_linearring_from_py(self, coordinates)
189
+
190
+ coords = property(BaseGeometry.get_coords, set_coords)
191
+
192
+
193
+ class LinearRingAdapter(LineStringAdapter):
194
+
195
+ context = None
196
+ __geom = None
197
+ _owned = False
198
+
199
+ @property
200
+ def _geom(self):
201
+ """Keeps the GEOS geometry in synch with the context."""
202
+ if self.__geom is not None:
203
+ lgeos.GEOSGeom_destroy(self.__geom)
204
+ self.__geom, n = geos_linearring_from_py(self.context)
205
+ return self.__geom
206
+
207
+ @property
208
+ def __geo_interface__(self):
209
+ return {
210
+ 'type': 'LinearRing',
211
+ 'coordinates': tuple(self.coords)
212
+ }
213
+
214
+ coords = property(BaseGeometry.get_coords)
215
+
216
+
217
+ def asLinearRing(context):
218
+ return LinearRingAdapter(context)
219
+
220
+
221
+ class InteriorRingSequence(object):
222
+
223
+ _factory = None
224
+ _geom = None
225
+ _ndim = None
226
+ _index = 0
227
+ _length = 0
228
+
229
+ def __init__(self, geom):
230
+ self._geom = geom._geom
231
+ self._ndim = geom._ndim
232
+
233
+ def __iter__(self):
234
+ self._index = 0
235
+ self._length = self.__len__()
236
+ return self
237
+
238
+ def next(self):
239
+ if self._index < self._length:
240
+ g = LinearRing()
241
+ g._owned = True
242
+ g._geom = lgeos.GEOSGetInteriorRingN(self._geom, self._index)
243
+ self._index += 1
244
+ return g
245
+ else:
246
+ raise StopIteration
247
+
248
+ def __len__(self):
249
+ return lgeos.GEOSGetNumInteriorRings(self._geom)
250
+
251
+ def __getitem__(self, i):
252
+ M = self.__len__()
253
+ if i + M < 0 or i >= M:
254
+ raise IndexError, "index out of range"
255
+ if i < 0:
256
+ ii = M + i
257
+ else:
258
+ ii = i
259
+ g = LinearRing()
260
+ g._owned = True
261
+ g._geom = lgeos.GEOSGetInteriorRingN(self._geom, ii)
262
+ return g
263
+
264
+ @property
265
+ def _longest(self):
266
+ max = 0
267
+ for g in iter(self):
268
+ l = len(g.coords)
269
+ if l > max:
270
+ max = l
271
+
272
+
273
+ def geos_polygon_from_py(shell, holes=None):
274
+ if shell is not None:
275
+ geos_shell, ndims = geos_linearring_from_py(shell)
276
+ if holes:
277
+ ob = holes
278
+ L = len(ob)
279
+ try:
280
+ N = len(ob[0][0])
281
+ except:
282
+ import pdb; pdb.set_trace()
283
+ assert L >= 1
284
+ assert N == 2 or N == 3
285
+
286
+ # Array of pointers to ring geometries
287
+ geos_holes = (c_void_p * L)()
288
+
289
+ # add to coordinate sequence
290
+ for l in xrange(L):
291
+ geom, ndims = geos_linearring_from_py(ob[l])
292
+ geos_holes[l] = cast(geom, c_void_p)
293
+
294
+ else:
295
+ geos_holes = POINTER(c_void_p)()
296
+ L = 0
297
+
298
+ return (
299
+ lgeos.GEOSGeom_createPolygon(
300
+ c_void_p(geos_shell),
301
+ geos_holes,
302
+ L
303
+ ),
304
+ ndims
305
+ )
306
+
307
+ class Polygon(BaseGeometry):
308
+
309
+ """A line string, also known as a polyline.
310
+ """
311
+
312
+ _exterior = None
313
+ _interiors = []
314
+ _ndim = 2
315
+ __geom = None
316
+ _owned = False
317
+
318
+ def __init__(self, shell=None, holes=None):
319
+ """Initialize.
320
+
321
+ Parameters
322
+ ----------
323
+ exterior : sequence or array
324
+ This may be an object that satisfies the numpy array protocol,
325
+ providing an M x 2 or M x 3 (with z) array, or it may be a sequence
326
+ of x, y (,z) coordinate sequences.
327
+
328
+ Example
329
+ -------
330
+ >>> coords = ((0., 0.), (0., 1.), (1., 1.), (1., 0.), (0., 0.))
331
+ >>> polygon = Polygon(coords)
332
+ """
333
+ BaseGeometry.__init__(self)
334
+
335
+ if shell is not None:
336
+ self._geom, self._ndims = geos_polygon_from_py(shell, holes)
337
+
338
+ @property
339
+ @exceptNull
340
+ def exterior(self):
341
+ if self._exterior is None:
342
+ # A polygon created from the abstract factory will have a null
343
+ # _exterior attribute.
344
+ ring = lgeos.GEOSGetExteriorRing(self._geom)
345
+ self._exterior = LinearRing()
346
+ self._exterior._geom = ring
347
+ self._exterior._owned = True
348
+ return self._exterior
349
+
350
+ @property
351
+ @exceptNull
352
+ def interiors(self):
353
+ return InteriorRingSequence(self)
354
+
355
+ @property
356
+ def ctypes(self):
357
+ if not self._ctypes_data:
358
+ self._ctypes_data = self.exterior.ctypes
359
+ return self._ctypes_data
360
+
361
+ @property
362
+ def __array_interface__(self):
363
+ raise NotImplementedError, \
364
+ "A polygon does not itself provide the array interface. Its rings do."
365
+
366
+ @property
367
+ def coords(self):
368
+ raise NotImplementedError, \
369
+ "Component rings have coordinate sequences, but the polygon does not"
370
+
371
+ @property
372
+ def __geo_interface__(self):
373
+ coords = [tuple(self.exterior.coords)]
374
+ for hole in self.interiors:
375
+ coords.append(tuple(hole.coords))
376
+ return {
377
+ 'type': 'Polygon',
378
+ 'coordinates': tuple(coords)
379
+ }
380
+
381
+
382
+ class PolygonAdapter(Polygon):
383
+
384
+ """Adapts sequences of sequences or numpy arrays to the polygon
385
+ interface.
386
+ """
387
+
388
+ context = None
389
+ __geom = None
390
+ _owned = False
391
+
392
+ def __init__(self, shell, holes=None):
393
+ self.shell = shell
394
+ self.holes = holes
395
+
396
+ @property
397
+ def _ndim(self):
398
+ try:
399
+ # From array protocol
400
+ array = self.shell.__array_interface__
401
+ n = array['shape'][1]
402
+ assert n == 2 or n == 3
403
+ return n
404
+ except AttributeError:
405
+ # Fall back on list
406
+ return len(self.shell[0])
407
+
408
+ @property
409
+ def _geom(self):
410
+ """Keeps the GEOS geometry in synch with the context."""
411
+ if self.__geom is not None:
412
+ lgeos.GEOSGeom_destroy(self.__geom)
413
+ self.__geom = geos_polygon_from_py(self.shell, self.holes)[0]
414
+ return self.__geom
415
+
416
+
417
+ def asPolygon(shell, holes=None):
418
+ """Factory for PolygonAdapter instances."""
419
+ return PolygonAdapter(shell, holes)
420
+
421
+
422
+ # Test runner
423
+ def _test():
424
+ import doctest
425
+ doctest.testmod()
426
+
427
+ if __name__ == "__main__":
428
+ _test()
429
+
Binary file
shapely/geos.py ADDED
@@ -0,0 +1,80 @@
1
+ """
2
+ Exports the libgeos_c shared lib, GEOS-specific exceptions, and utilities.
3
+ """
4
+
5
+ import atexit
6
+ from ctypes import cdll, CDLL, CFUNCTYPE, c_char_p
7
+ from ctypes.util import find_library
8
+ import os
9
+ import sys
10
+
11
+ import shapely
12
+
13
+ if sys.platform == 'win32':
14
+ try:
15
+ local_dlls = os.path.abspath(os.__file__ + "../../../DLLs")
16
+ original_path = os.environ['PATH']
17
+ os.environ['PATH'] = "%s;%s" % (local_dlls, original_path)
18
+ lgeos = CDLL("geos.dll")
19
+ except (ImportError, WindowsError):
20
+ raise
21
+ def free(m):
22
+ try:
23
+ cdll.msvcrt.free(m)
24
+ except WindowsError:
25
+ # XXX: See http://trac.gispython.org/projects/PCL/ticket/149
26
+ pass
27
+
28
+ elif sys.platform == 'darwin':
29
+ lgeos = CDLL(find_library('geos_c'))
30
+ free = CDLL(find_library('libc')).free
31
+ else:
32
+ # Try the major versioned name first, falling back on the unversioned name.
33
+ try:
34
+ lgeos = CDLL('libgeos_c.so.1')
35
+ except ImportError:
36
+ lgeos = CDLL('libgeos_c.so')
37
+ except:
38
+ raise
39
+ free = CDLL('libc.so.6').free
40
+
41
+
42
+ class allocated_c_char_p(c_char_p):
43
+ pass
44
+
45
+
46
+ # Exceptions
47
+
48
+ class ReadingError(Exception):
49
+ pass
50
+
51
+ class DimensionError(Exception):
52
+ pass
53
+
54
+ class TopologicalError(Exception):
55
+ pass
56
+
57
+ class PredicateError(Exception):
58
+ pass
59
+
60
+
61
+ # GEOS error handlers, which currently do nothing.
62
+
63
+ def error_handler(fmt, list):
64
+ pass
65
+ error_h = CFUNCTYPE(None, c_char_p, c_char_p)(error_handler)
66
+
67
+ def notice_handler(fmt, list):
68
+ pass
69
+ notice_h = CFUNCTYPE(None, c_char_p, c_char_p)(notice_handler)
70
+
71
+ # Register a cleanup function
72
+
73
+ def cleanup():
74
+ lgeos.finishGEOS()
75
+
76
+ atexit.register(cleanup)
77
+
78
+ lgeos.initGEOS(notice_h, error_h)
79
+
80
+
shapely/geos.pyc ADDED
Binary file
shapely/iterops.py ADDED
@@ -0,0 +1,56 @@
1
+ """
2
+ """
3
+
4
+ from ctypes import c_char_p, c_size_t
5
+ from shapely.geos import lgeos, free
6
+
7
+
8
+ def geos_from_geometry(geom):
9
+ data = geom.to_wkb()
10
+ return lgeos.GEOSGeomFromWKB_buf(
11
+ c_char_p(data),
12
+ c_size_t(len(data))
13
+ )
14
+
15
+ class BinaryPredicateIterator(object):
16
+
17
+ """A generating non-data descriptor.
18
+ """
19
+
20
+ fn = None
21
+ context = None
22
+
23
+ def __init__(self, fn):
24
+ self.fn = fn
25
+
26
+ def __get__(self, obj, objtype=None):
27
+ self.context = obj
28
+ return self
29
+
30
+ def __call__(self, geom, iterator, value=True):
31
+ if geom._geom is None:
32
+ raise ValueError, "Null geometry supports no operations"
33
+ for item in iterator:
34
+ try:
35
+ this_geom, ob = item
36
+ except TypeError:
37
+ this_geom = item
38
+ ob = this_geom
39
+ if this_geom._geom is None:
40
+ raise ValueError, "Null geometry supports no operations"
41
+ retval = self.fn(geom._geom, this_geom._geom)
42
+ if retval == 2:
43
+ raise PredicateError, "Failed to evaluate %s" % repr(self.fn)
44
+ elif bool(retval) == value:
45
+ yield ob
46
+
47
+ # utilities
48
+ disjoint = BinaryPredicateIterator(lgeos.GEOSDisjoint)
49
+ touches = BinaryPredicateIterator(lgeos.GEOSTouches)
50
+ intersects = BinaryPredicateIterator(lgeos.GEOSIntersects)
51
+ crosses = BinaryPredicateIterator(lgeos.GEOSCrosses)
52
+ within = BinaryPredicateIterator(lgeos.GEOSWithin)
53
+ contains = BinaryPredicateIterator(lgeos.GEOSContains)
54
+ overlaps = BinaryPredicateIterator(lgeos.GEOSOverlaps)
55
+ equals = BinaryPredicateIterator(lgeos.GEOSEquals)
56
+
shapely/iterops.pyc ADDED
Binary file
shapely/kml.py ADDED
File without changes
shapely/kml.pyc ADDED
Binary file
shapely/predicates.py ADDED
@@ -0,0 +1,58 @@
1
+ """
2
+ Support for GEOS spatial predicates.
3
+ """
4
+
5
+ from shapely.geos import PredicateError
6
+
7
+ # Predicates
8
+
9
+ class BinaryPredicate(object):
10
+
11
+ """A callable non-data descriptor.
12
+ """
13
+
14
+ fn = None
15
+ context = None
16
+
17
+ def __init__(self, fn):
18
+ self.fn = fn
19
+ def errcheck(result, func, argtuple):
20
+ if result == 2:
21
+ raise PredicateError, "Failed to evaluate %s" % repr(self.fn)
22
+ return result
23
+ self.fn.errcheck = errcheck
24
+
25
+ def __get__(self, obj, objtype=None):
26
+ self.context = obj
27
+ return self
28
+
29
+ def __call__(self, other):
30
+ if self.context._geom is None or other._geom is None:
31
+ raise ValueError, "Null geometry supports no operations"
32
+ return bool(self.fn(self.context._geom, other._geom))
33
+
34
+
35
+ # A data descriptor
36
+ class UnaryPredicate(object):
37
+
38
+ """A data descriptor.
39
+ """
40
+
41
+ fn = None
42
+
43
+ def __init__(self, fn):
44
+ self.fn = fn
45
+ def errcheck(result, func, argtuple):
46
+ if result == 2:
47
+ raise PredicateError, "Failed to evaluate %s" % repr(self.fn)
48
+ return result
49
+ self.fn.errcheck = errcheck
50
+
51
+ def __get__(self, obj, objtype=None):
52
+ if obj._geom is None:
53
+ raise ValueError, "Null geometry supports no operations"
54
+ return bool(self.fn(obj._geom))
55
+
56
+ def __set__(self, obj, value=None):
57
+ raise AttributeError, "Attribute is read-only"
58
+
shapely/predicates.pyc ADDED
Binary file
shapely/topology.py ADDED
@@ -0,0 +1,66 @@
1
+ """
2
+ Support for GEOS topological operations.
3
+ """
4
+
5
+ from shapely.geos import TopologicalError
6
+
7
+
8
+ class BinaryTopologicalOp(object):
9
+
10
+ """A callable non-data descriptor.
11
+
12
+ Wraps a GEOS function. The factory is a callable which wraps results in
13
+ the appropriate shapely geometry class.
14
+ """
15
+
16
+ fn = None
17
+ context = None
18
+ factory = None
19
+
20
+ def __init__(self, fn, factory):
21
+ self.fn = fn
22
+ self.factory = factory
23
+
24
+ def __get__(self, obj, objtype=None):
25
+ self.context = obj
26
+ return self
27
+
28
+ def __call__(self, other):
29
+ if self.context._geom is None or other._geom is None:
30
+ raise ValueError, "Null geometry supports no operations"
31
+ product = self.fn(self.context._geom, other._geom)
32
+ if not product:
33
+ # Check validity of geometries
34
+ if not self.context.is_valid:
35
+ raise TopologicalError, \
36
+ "The operation '%s' produced a null geometry. Likely cause is invalidity of the geometry %s" % (self.fn.__name__, repr(self.context))
37
+ elif not other.is_valid:
38
+ raise TopologicalError, \
39
+ "The operation '%s' produced a null geometry. Likely cause is invalidity of the 'other' geometry %s" % (self.fn.__name__, repr(other))
40
+
41
+ return self.factory(product)
42
+
43
+
44
+ class UnaryTopologicalOp(object):
45
+
46
+ """A data descriptor.
47
+
48
+ Wraps a GEOS function. The factory is a callable which wraps results in
49
+ the appropriate shapely geometry class.
50
+ """
51
+
52
+ fn = None
53
+ factory = None
54
+
55
+ def __init__(self, fn, factory):
56
+ self.fn = fn
57
+ self.factory = factory
58
+
59
+ def __get__(self, obj, objtype=None):
60
+ if obj._geom is None:
61
+ raise ValueError, "Null geometry supports no operations"
62
+ return self.factory(self.fn(obj._geom))
63
+
64
+ def __set__(self, obj, value=None):
65
+ raise AttributeError, "Attribute is read-only"
66
+
shapely/topology.pyc ADDED
Binary file