shapely 1.0b1__py2.4.egg → 1.0b3__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,242 @@
1
+ """
2
+ Points.
3
+ """
4
+
5
+ from ctypes import string_at, create_string_buffer, \
6
+ c_char_p, c_double, c_float, c_int, c_uint, c_size_t, c_ubyte, \
7
+ c_void_p, byref
8
+ from ctypes import cast, POINTER
9
+
10
+ from shapely.geos import lgeos, DimensionError
11
+ from shapely.geometry.base import BaseGeometry, CoordinateSequence
12
+
13
+
14
+ def geos_point_from_py(ob, update_geom=None, update_ndim=0):
15
+ """Create a GEOS geom from an object that is a coordinate sequence
16
+ or that provides the array interface.
17
+
18
+ Returns the GEOS geometry and the number of its dimensions.
19
+ """
20
+ try:
21
+ # From array protocol
22
+ array = ob.__array_interface__
23
+ assert len(array['shape']) == 1
24
+ n = array['shape'][0]
25
+ assert n == 2 or n == 3
26
+
27
+ cdata = array['data'][0]
28
+ cp = cast(cdata, POINTER(c_double))
29
+ dx = c_double(cp[0])
30
+ dy = c_double(cp[1])
31
+ dz = None
32
+ if n == 3:
33
+ dz = c_double(cp[2])
34
+ ndim = 3
35
+ except AttributeError:
36
+ # Fall back on list
37
+ coords = ob
38
+ n = len(coords)
39
+ dx = c_double(coords[0])
40
+ dy = c_double(coords[1])
41
+ dz = None
42
+ if n == 3:
43
+ dz = c_double(coords[2])
44
+
45
+ if update_geom:
46
+ cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
47
+ if n != update_ndim:
48
+ raise ValueError, \
49
+ "Wrong coordinate dimensions; this geometry has dimensions: %d" \
50
+ % update_ndim
51
+ else:
52
+ cs = lgeos.GEOSCoordSeq_create(1, n)
53
+
54
+ # Because of a bug in the GEOS C API, always set X before Y
55
+ lgeos.GEOSCoordSeq_setX(cs, 0, dx)
56
+ lgeos.GEOSCoordSeq_setY(cs, 0, dy)
57
+ if n == 3:
58
+ lgeos.GEOSCoordSeq_setZ(cs, 0, dz)
59
+
60
+ if update_geom:
61
+ return None
62
+ else:
63
+ return (lgeos.GEOSGeom_createPoint(cs), n)
64
+
65
+ def update_point_from_py(geom, ob):
66
+ geos_point_from_py(ob, geom._geom, geom._ndim)
67
+
68
+
69
+ class Point(BaseGeometry):
70
+
71
+ """A point geometry.
72
+
73
+ Attributes
74
+ ----------
75
+ x, y, z : float
76
+ Coordinate values
77
+
78
+ Example
79
+ -------
80
+ >>> p = Point(1.0, -1.0)
81
+ >>> str(p)
82
+ 'POINT (1.0000000000000000 -1.0000000000000000)'
83
+ >>> p.y = 0.0
84
+ >>> p.y
85
+ 0.0
86
+ >>> p.x
87
+ 1.0
88
+ >>> p.array
89
+ [[1.0, 0.0]]
90
+ """
91
+
92
+ def __init__(self, *args):
93
+ """This *copies* the given data to a new GEOS geometry.
94
+
95
+ Parameters
96
+ ----------
97
+
98
+ There are 2 cases:
99
+
100
+ 1) 1 parameter: this must satisfy the numpy array protocol.
101
+ 2) 2 or more parameters: x, y, z : float
102
+ Easting, northing, and elevation.
103
+ """
104
+ BaseGeometry.__init__(self)
105
+
106
+ if len(args) == 0:
107
+ # allow creation of null points, to support unpickling
108
+ pass
109
+ else:
110
+ if len(args) == 1:
111
+ self._geom, self._ndim = geos_point_from_py(args[0])
112
+ else:
113
+ self._geom, self._ndim = geos_point_from_py(tuple(args))
114
+
115
+ # Coordinate getters and setters
116
+
117
+ @property
118
+ def x(self):
119
+ """Return x coordinate."""
120
+ cs = lgeos.GEOSGeom_getCoordSeq(self._geom)
121
+ d = c_double()
122
+ lgeos.GEOSCoordSeq_getX(cs, 0, byref(d))
123
+ return d.value
124
+
125
+ @property
126
+ def y(self):
127
+ """Return y coordinate."""
128
+ cs = lgeos.GEOSGeom_getCoordSeq(self._geom)
129
+ d = c_double()
130
+ lgeos.GEOSCoordSeq_getY(cs, 0, byref(d))
131
+ return d.value
132
+
133
+ @property
134
+ def z(self):
135
+ """Return z coordinate."""
136
+ if self._ndim != 3:
137
+ raise DimensionError, "This point has no z coordinate."
138
+ cs = lgeos.GEOSGeom_getCoordSeq(self._geom)
139
+ d = c_double()
140
+ lgeos.GEOSCoordSeq_getZ(cs, 0, byref(d))
141
+ return d.value
142
+
143
+ @property
144
+ def __geo_interface__(self):
145
+ return {
146
+ 'type': 'Point',
147
+ 'coordinates': self.coords[0]
148
+ }
149
+
150
+ @property
151
+ def ctypes(self):
152
+ if not self._ctypes_data:
153
+ array_type = c_double * self._ndim
154
+ array = array_type()
155
+ array[0] = self.x
156
+ array[1] = self.y
157
+ if self._ndim == 3:
158
+ array[2] = self.z
159
+ self._ctypes_data = array
160
+ return self._ctypes_data
161
+
162
+ def array_interface(self):
163
+ """Provide the Numpy array protocol."""
164
+ ai = self.array_interface_base
165
+ ai.update({'shape': (self._ndim,)})
166
+ return ai
167
+ __array_interface__ = property(array_interface)
168
+
169
+ @property
170
+ def bounds(self):
171
+ cs = lgeos.GEOSGeom_getCoordSeq(self._geom)
172
+ x = c_double()
173
+ y = c_double()
174
+ lgeos.GEOSCoordSeq_getX(cs, 0, byref(x))
175
+ lgeos.GEOSCoordSeq_getY(cs, 0, byref(y))
176
+ return (x.value, y.value, x.value, y.value)
177
+
178
+ # Coordinate access
179
+
180
+ def set_coords(self, coordinates):
181
+ update_point_from_py(self, coordinates)
182
+
183
+ coords = property(BaseGeometry.get_coords, set_coords)
184
+
185
+
186
+ class PointAdapter(Point):
187
+
188
+ """Adapts a Python coordinate pair or a numpy array to the point interface.
189
+ """
190
+
191
+ context = None
192
+
193
+ def __init__(self, context):
194
+ self.context = context
195
+
196
+ # Override base class __del__
197
+ def __del__(self):
198
+ pass
199
+
200
+ @property
201
+ def _ndim(self):
202
+ try:
203
+ # From array protocol
204
+ array = self.context.__array_interface__
205
+ n = array['shape'][0]
206
+ assert n == 2 or n == 3
207
+ return n
208
+ except AttributeError:
209
+ # Fall back on list
210
+ return len(self.context)
211
+
212
+ @property
213
+ def _geom(self):
214
+ """Keeps the GEOS geometry in synch with the context."""
215
+ return geos_point_from_py(self.context)[0]
216
+
217
+ # TODO: reimplement x, y, z properties without calling invoking _geom
218
+
219
+ @property
220
+ def __array_interface__(self):
221
+ """Provide the Numpy array protocol."""
222
+ try:
223
+ return self.context.__array_interface__
224
+ except AttributeError:
225
+ return self.array_interface()
226
+
227
+ coords = property(BaseGeometry.get_coords)
228
+
229
+
230
+ def asPoint(context):
231
+ """Factory for PointAdapter instances."""
232
+ return PointAdapter(context)
233
+
234
+
235
+ # Test runner
236
+ def _test():
237
+ import doctest
238
+ doctest.testmod()
239
+
240
+ if __name__ == "__main__":
241
+ _test()
242
+
Binary file
@@ -0,0 +1,414 @@
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
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
+
145
+ def __init__(self, coordinates=None):
146
+ """Initialize.
147
+
148
+ Parameters
149
+ ----------
150
+ coordinates : sequence or array
151
+ This may be an object that satisfies the numpy array protocol,
152
+ providing an M x 2 or M x 3 (with z) array, or it may be a sequence
153
+ of x, y (,z) coordinate sequences.
154
+
155
+ Rings are implicitly closed. There is no need to specific a final
156
+ coordinate pair identical to the first.
157
+
158
+ Example
159
+ -------
160
+ >>> ring = LinearRing( ((0.,0.), (0.,1.), (1.,1.), (1.,0.)) )
161
+
162
+ Produces a 1x1 square.
163
+ """
164
+ BaseGeometry.__init__(self)
165
+
166
+ if coordinates is None:
167
+ # allow creation of null lines, to support unpickling
168
+ pass
169
+ else:
170
+ self._geom, self._ndims = geos_linearring_from_py(coordinates)
171
+
172
+ @property
173
+ def __geo_interface__(self):
174
+ return {
175
+ 'type': 'LinearRing',
176
+ 'coordinates': tuple(self.coords)
177
+ }
178
+
179
+ # Coordinate access
180
+
181
+ def set_coords(self, coordinates):
182
+ update_linearring_from_py(self, coordinates)
183
+
184
+ coords = property(BaseGeometry.get_coords, set_coords)
185
+
186
+
187
+ class LinearRingAdapter(LineStringAdapter):
188
+
189
+ @property
190
+ def _geom(self):
191
+ """Keeps the GEOS geometry in synch with the context."""
192
+ return geos_linearring_from_py(self.context)[0]
193
+
194
+ @property
195
+ def __geo_interface__(self):
196
+ return {
197
+ 'type': 'LinearRing',
198
+ 'coordinates': tuple(self.coords)
199
+ }
200
+
201
+ coords = property(BaseGeometry.get_coords)
202
+
203
+
204
+ def asLinearRing(context):
205
+ return LinearRingAdapter(context)
206
+
207
+
208
+ class InteriorRingSequence(object):
209
+
210
+ _factory = None
211
+ _geom = None
212
+ _ndim = None
213
+ _index = 0
214
+ _length = 0
215
+
216
+ def __init__(self, geom):
217
+ self._geom = geom._geom
218
+ self._ndim = geom._ndim
219
+
220
+ def __iter__(self):
221
+ self._index = 0
222
+ self._length = self.__len__()
223
+ return self
224
+
225
+ def next(self):
226
+ if self._index < self._length:
227
+ g = LinearRing()
228
+ g._owned = True
229
+ g._geom = lgeos.GEOSGetInteriorRingN(self._geom, self._index)
230
+ self._index += 1
231
+ return g
232
+ else:
233
+ raise StopIteration
234
+
235
+ def __len__(self):
236
+ return lgeos.GEOSGetNumInteriorRings(self._geom)
237
+
238
+ def __getitem__(self, i):
239
+ M = self.__len__()
240
+ if i + M < 0 or i >= M:
241
+ raise IndexError, "index out of range"
242
+ if i < 0:
243
+ ii = M + i
244
+ else:
245
+ ii = i
246
+ g = LinearRing()
247
+ g._owned = True
248
+ g._geom = lgeos.GEOSGetInteriorRingN(self._geom, ii)
249
+ return g
250
+
251
+ @property
252
+ def _longest(self):
253
+ max = 0
254
+ for g in iter(self):
255
+ l = len(g.coords)
256
+ if l > max:
257
+ max = l
258
+
259
+
260
+ def geos_polygon_from_py(shell, holes=None):
261
+ if shell is not None:
262
+ geos_shell, ndims = geos_linearring_from_py(shell)
263
+ ## Polygon geometry takes ownership of the ring
264
+ #self._exterior._owned = True
265
+
266
+ if holes:
267
+ ob = holes
268
+ L = len(ob)
269
+ try:
270
+ N = len(ob[0][0])
271
+ except:
272
+ import pdb; pdb.set_trace()
273
+ assert L >= 1
274
+ assert N == 2 or N == 3
275
+
276
+ # Array of pointers to ring geometries
277
+ geos_holes = (c_void_p * L)()
278
+
279
+ # add to coordinate sequence
280
+ for l in xrange(L):
281
+ geom, ndims = geos_linearring_from_py(ob[l])
282
+ geos_holes[l] = cast(geom, c_void_p)
283
+
284
+ else:
285
+ geos_holes = POINTER(c_void_p)()
286
+ L = 0
287
+
288
+ return (
289
+ lgeos.GEOSGeom_createPolygon(
290
+ c_void_p(geos_shell),
291
+ geos_holes,
292
+ L
293
+ ),
294
+ ndims
295
+ )
296
+
297
+ class Polygon(BaseGeometry):
298
+
299
+ """A line string, also known as a polyline.
300
+ """
301
+
302
+ _exterior = None
303
+ _interiors = []
304
+ _ndim = 2
305
+
306
+ def __init__(self, shell=None, holes=None):
307
+ """Initialize.
308
+
309
+ Parameters
310
+ ----------
311
+ exterior : sequence or array
312
+ This may be an object that satisfies the numpy array protocol,
313
+ providing an M x 2 or M x 3 (with z) array, or it may be a sequence
314
+ of x, y (,z) coordinate sequences.
315
+
316
+ Example
317
+ -------
318
+ >>> coords = ((0., 0.), (0., 1.), (1., 1.), (1., 0.), (0., 0.))
319
+ >>> polygon = Polygon(coords)
320
+ """
321
+ BaseGeometry.__init__(self)
322
+
323
+ if shell is not None:
324
+ self._geom, self._ndims = geos_polygon_from_py(shell, holes)
325
+
326
+ @property
327
+ def exterior(self):
328
+ if self._exterior is None:
329
+ # A polygon created from the abstract factory will have a null
330
+ # _exterior attribute.
331
+ ring = lgeos.GEOSGetExteriorRing(self._geom)
332
+ self._exterior = LinearRing()
333
+ self._exterior._geom = ring
334
+ self._exterior._owned = True
335
+ return self._exterior
336
+
337
+ @property
338
+ def interiors(self):
339
+ return InteriorRingSequence(self)
340
+
341
+ @property
342
+ def ctypes(self):
343
+ if not self._ctypes_data:
344
+ self._ctypes_data = self.exterior.ctypes
345
+ return self._ctypes_data
346
+
347
+ @property
348
+ def __array_interface__(self):
349
+ raise NotImplementedError, \
350
+ "A polygon does not itself provide the array interface. Its rings do."
351
+
352
+ @property
353
+ def coords(self):
354
+ raise NotImplementedError, \
355
+ "Component rings have coordinate sequences, but the polygon does not"
356
+
357
+ @property
358
+ def __geo_interface__(self):
359
+ coords = [tuple(self.exterior.coords)]
360
+ for hole in self.interiors:
361
+ coords.append(tuple(hole.coords))
362
+ return {
363
+ 'type': 'Polygon',
364
+ 'coordinates': tuple(coords)
365
+ }
366
+
367
+
368
+ class PolygonAdapter(Polygon):
369
+
370
+ """Adapts sequences of sequences or numpy arrays to the polygon
371
+ interface.
372
+ """
373
+
374
+ context = None
375
+
376
+ def __init__(self, shell, holes=None):
377
+ self.shell = shell
378
+ self.holes = holes
379
+
380
+ # Override base class __del__
381
+ def __del__(self):
382
+ pass
383
+
384
+ @property
385
+ def _ndim(self):
386
+ try:
387
+ # From array protocol
388
+ array = self.shell.__array_interface__
389
+ n = array['shape'][1]
390
+ assert n == 2 or n == 3
391
+ return n
392
+ except AttributeError:
393
+ # Fall back on list
394
+ return len(self.shell[0])
395
+
396
+ @property
397
+ def _geom(self):
398
+ """Keeps the GEOS geometry in synch with the context."""
399
+ return geos_polygon_from_py(self.shell, self.holes)[0]
400
+
401
+
402
+ def asPolygon(shell, holes=None):
403
+ """Factory for PolygonAdapter instances."""
404
+ return PolygonAdapter(shell, holes)
405
+
406
+
407
+ # Test runner
408
+ def _test():
409
+ import doctest
410
+ doctest.testmod()
411
+
412
+ if __name__ == "__main__":
413
+ _test()
414
+
Binary file