44
44
from ctypes import byref, string_at, c_char_p, c_double, c_ubyte, c_void_p
46
46
# Getting GDAL prerequisites
47
from django.contrib.gis.gdal.base import GDALBase
47
48
from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope
48
49
from django.contrib.gis.gdal.error import OGRException, OGRIndexError, SRSException
49
50
from django.contrib.gis.gdal.geomtype import OGRGeomType
50
51
from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform
52
53
# Getting the ctypes prototype functions that interface w/the GDAL C library.
53
from django.contrib.gis.gdal.prototypes.geom import *
54
from django.contrib.gis.gdal.prototypes.srs import clone_srs
54
from django.contrib.gis.gdal.prototypes import geom as capi, srs as srs_api
55
GEOJSON = capi.GEOJSON
56
57
# For more information, see the OGR C API source code:
57
58
# http://www.gdal.org/ogr/ogr__api_8h.html
64
65
json_regex = re.compile(r'^(\s+)?\{[\s\w,\[\]\{\}\-\."\':]+\}(\s+)?$')
66
67
#### OGRGeometry Class ####
67
class OGRGeometry(object):
68
class OGRGeometry(GDALBase):
68
69
"Generally encapsulates an OGR geometry."
70
71
def __init__(self, geom_input, srs=None):
71
72
"Initializes Geometry on either WKT or an OGR pointer as input."
73
self._ptr = c_void_p(None) # Initially NULL
74
74
str_instance = isinstance(geom_input, basestring)
76
76
# If HEX, unpack input to to a binary buffer.
91
91
if wkt_m.group('type').upper() == 'LINEARRING':
92
92
# OGR_G_CreateFromWkt doesn't work with LINEARRING WKT.
93
93
# See http://trac.osgeo.org/gdal/ticket/1992.
94
g = create_geom(OGRGeomType(wkt_m.group('type')).num)
95
import_wkt(g, byref(c_char_p(geom_input)))
94
g = capi.create_geom(OGRGeomType(wkt_m.group('type')).num)
95
capi.import_wkt(g, byref(c_char_p(geom_input)))
97
g = from_wkt(byref(c_char_p(geom_input)), None, byref(c_void_p()))
97
g = capi.from_wkt(byref(c_char_p(geom_input)), None, byref(c_void_p()))
100
g = from_json(geom_input)
100
g = capi.from_json(geom_input)
102
102
raise NotImplementedError('GeoJSON input only supported on GDAL 1.5+.')
104
104
# Seeing if the input is a valid short-hand string
105
105
# (e.g., 'Point', 'POLYGON').
106
106
ogr_t = OGRGeomType(geom_input)
107
g = create_geom(OGRGeomType(geom_input).num)
107
g = capi.create_geom(OGRGeomType(geom_input).num)
108
108
elif isinstance(geom_input, buffer):
109
109
# WKB was passed in
110
g = from_wkb(str(geom_input), None, byref(c_void_p()), len(geom_input))
110
g = capi.from_wkb(str(geom_input), None, byref(c_void_p()), len(geom_input))
111
111
elif isinstance(geom_input, OGRGeomType):
112
112
# OGRGeomType was passed in, an empty geometry will be created.
113
g = create_geom(geom_input.num)
114
elif isinstance(geom_input, c_void_p):
113
g = capi.create_geom(geom_input.num)
114
elif isinstance(geom_input, self.ptr_type):
115
115
# OGR pointer (c_void_p) was the input.
129
129
# Setting the class depending upon the OGR Geometry Type
130
130
self.__class__ = GEO_CLASSES[self.geom_type.num]
133
def from_bbox(cls, bbox):
134
"Constructs a Polygon from a bounding box (4-tuple)."
135
x0, y0, x1, y1 = bbox
136
return OGRGeometry( 'POLYGON((%s %s, %s %s, %s %s, %s %s, %s %s))' % (
137
x0, y0, x0, y1, x1, y1, x1, y0, x0, y0) )
132
139
def __del__(self):
133
140
"Deletes this Geometry."
134
if self._ptr: destroy_geom(self._ptr)
141
if self._ptr: capi.destroy_geom(self._ptr)
136
143
### Geometry set-like operations ###
171
178
def dimension(self):
172
179
"Returns 0 for points, 1 for lines, and 2 for surfaces."
173
return get_dims(self._ptr)
180
return capi.get_dims(self.ptr)
176
183
def coord_dim(self):
177
184
"Returns the coordinate dimension of the Geometry."
178
return get_coord_dims(self._ptr)
185
return capi.get_coord_dims(self.ptr)
181
188
def geom_count(self):
182
189
"The number of elements in this Geometry."
183
return get_geom_count(self._ptr)
190
return capi.get_geom_count(self.ptr)
186
193
def point_count(self):
187
194
"Returns the number of Points in this Geometry."
188
return get_point_count(self._ptr)
195
return capi.get_point_count(self.ptr)
191
198
def num_points(self):
201
208
def geom_type(self):
202
209
"Returns the Type for this Geometry."
204
return OGRGeomType(get_geom_type(self._ptr))
211
return OGRGeomType(capi.get_geom_type(self.ptr))
205
212
except OGRException:
206
213
# VRT datasources return an invalid geometry type
207
214
# number, but a valid name -- we'll try that instead.
208
215
# See: http://trac.osgeo.org/gdal/ticket/2491
209
return OGRGeomType(get_geom_name(self._ptr))
216
return OGRGeomType(capi.get_geom_name(self.ptr))
212
219
def geom_name(self):
213
220
"Returns the Name of this Geometry."
214
return get_geom_name(self._ptr)
221
return capi.get_geom_name(self.ptr)
218
225
"Returns the area for a LinearRing, Polygon, or MultiPolygon; 0 otherwise."
219
return get_area(self._ptr)
226
return capi.get_area(self.ptr)
222
229
def envelope(self):
223
230
"Returns the envelope for this Geometry."
224
231
# TODO: Fix Envelope() for Point geometries.
225
return Envelope(get_envelope(self._ptr, byref(OGREnvelope())))
232
return Envelope(capi.get_envelope(self.ptr, byref(OGREnvelope())))
228
235
def extent(self):
232
239
#### SpatialReference-related Properties ####
234
241
# The SRS property
236
243
"Returns the Spatial Reference for this Geometry."
238
srs_ptr = get_geom_srs(self._ptr)
239
return SpatialReference(clone_srs(srs_ptr))
245
srs_ptr = capi.get_geom_srs(self.ptr)
246
return SpatialReference(srs_api.clone_srs(srs_ptr))
240
247
except SRSException:
243
def set_srs(self, srs):
250
def _set_srs(self, srs):
244
251
"Sets the SpatialReference for this geometry."
245
252
if isinstance(srs, SpatialReference):
246
srs_ptr = clone_srs(srs._ptr)
253
srs_ptr = srs_api.clone_srs(srs.ptr)
247
254
elif isinstance(srs, (int, long, basestring)):
248
255
sr = SpatialReference(srs)
249
srs_ptr = clone_srs(sr._ptr)
256
srs_ptr = srs_api.clone_srs(sr.ptr)
251
258
raise TypeError('Cannot assign spatial reference with object of type: %s' % type(srs))
252
assign_srs(self._ptr, srs_ptr)
259
capi.assign_srs(self.ptr, srs_ptr)
254
srs = property(get_srs, set_srs)
261
srs = property(_get_srs, _set_srs)
256
263
# The SRID property
258
if self.srs: return self.srs.srid
266
if srs: return srs.srid
261
def set_srid(self, srid):
269
def _set_srid(self, srid):
262
270
if isinstance(srid, (int, long)):
265
273
raise TypeError('SRID must be set with an integer.')
267
srid = property(get_srid, set_srid)
275
srid = property(_get_srid, _set_srid)
269
277
#### Output Methods ####
298
Returns the GeoJSON representation of this Geometry (requires
290
return to_json(self._ptr)
302
return capi.to_json(self.ptr)
292
304
raise NotImplementedError('GeoJSON output only supported on GDAL 1.5+.')
309
"Returns the KML representation of the Geometry."
311
return capi.to_kml(self.ptr, None)
313
raise NotImplementedError('KML output only supported on GDAL 1.5+.')
296
316
def wkb_size(self):
297
317
"Returns the size of the WKB buffer."
298
return get_wkbsize(self._ptr)
318
return capi.get_wkbsize(self.ptr)
307
327
sz = self.wkb_size
308
328
# Creating the unsigned character buffer, and passing it in by reference.
309
329
buf = (c_ubyte * sz)()
310
wkb = to_wkb(self._ptr, byteorder, byref(buf))
330
wkb = capi.to_wkb(self.ptr, byteorder, byref(buf))
311
331
# Returning a buffer of the string at the pointer.
312
332
return buffer(string_at(buf, sz))
316
336
"Returns the WKT representation of the Geometry."
317
return to_wkt(self._ptr, byref(c_char_p()))
337
return capi.to_wkt(self.ptr, byref(c_char_p()))
319
339
#### Geometry Methods ####
321
341
"Clones this OGR Geometry."
322
return OGRGeometry(clone_geom(self._ptr), self.srs)
342
return OGRGeometry(capi.clone_geom(self.ptr), self.srs)
324
344
def close_rings(self):
344
364
klone.transform(coord_trans)
346
366
if isinstance(coord_trans, CoordTransform):
347
geom_transform(self._ptr, coord_trans._ptr)
367
capi.geom_transform(self.ptr, coord_trans.ptr)
348
368
elif isinstance(coord_trans, SpatialReference):
349
geom_transform_to(self._ptr, coord_trans._ptr)
369
capi.geom_transform_to(self.ptr, coord_trans.ptr)
350
370
elif isinstance(coord_trans, (int, long, basestring)):
351
371
sr = SpatialReference(coord_trans)
352
geom_transform_to(self._ptr, sr._ptr)
372
capi.geom_transform_to(self.ptr, sr.ptr)
354
374
raise TypeError('Transform only accepts CoordTransform, SpatialReference, string, and integer objects.')
367
387
# Returning the output of the given function with the other geometry's
369
return func(self._ptr, other._ptr)
389
return func(self.ptr, other.ptr)
371
391
def intersects(self, other):
372
392
"Returns True if this geometry intersects with the other."
373
return self._topology(ogr_intersects, other)
393
return self._topology(capi.ogr_intersects, other)
375
395
def equals(self, other):
376
396
"Returns True if this geometry is equivalent to the other."
377
return self._topology(ogr_equals, other)
397
return self._topology(capi.ogr_equals, other)
379
399
def disjoint(self, other):
380
400
"Returns True if this geometry and the other are spatially disjoint."
381
return self._topology(ogr_disjoint, other)
401
return self._topology(capi.ogr_disjoint, other)
383
403
def touches(self, other):
384
404
"Returns True if this geometry touches the other."
385
return self._topology(ogr_touches, other)
405
return self._topology(capi.ogr_touches, other)
387
407
def crosses(self, other):
388
408
"Returns True if this geometry crosses the other."
389
return self._topology(ogr_crosses, other)
409
return self._topology(capi.ogr_crosses, other)
391
411
def within(self, other):
392
412
"Returns True if this geometry is within the other."
393
return self._topology(ogr_within, other)
413
return self._topology(capi.ogr_within, other)
395
415
def contains(self, other):
396
416
"Returns True if this geometry contains the other."
397
return self._topology(ogr_contains, other)
417
return self._topology(capi.ogr_contains, other)
399
419
def overlaps(self, other):
400
420
"Returns True if this geometry overlaps the other."
401
return self._topology(ogr_overlaps, other)
421
return self._topology(capi.ogr_overlaps, other)
403
423
#### Geometry-generation Methods ####
404
424
def _geomgen(self, gen_func, other=None):
405
425
"A helper routine for the OGR routines that generate geometries."
406
426
if isinstance(other, OGRGeometry):
407
return OGRGeometry(gen_func(self._ptr, other._ptr), self.srs)
427
return OGRGeometry(gen_func(self.ptr, other.ptr), self.srs)
409
return OGRGeometry(gen_func(self._ptr), self.srs)
429
return OGRGeometry(gen_func(self.ptr), self.srs)
412
432
def boundary(self):
413
433
"Returns the boundary of this geometry."
414
return self._geomgen(get_boundary)
434
return self._geomgen(capi.get_boundary)
417
437
def convex_hull(self):
419
439
Returns the smallest convex Polygon that contains all the points in
422
return self._geomgen(geom_convex_hull)
442
return self._geomgen(capi.geom_convex_hull)
424
444
def difference(self, other):
426
446
Returns a new geometry consisting of the region which is the difference
427
447
of this geometry and the other.
429
return self._geomgen(geom_diff, other)
449
return self._geomgen(capi.geom_diff, other)
431
451
def intersection(self, other):
433
453
Returns a new geometry consisting of the region of intersection of this
434
454
geometry and the other.
436
return self._geomgen(geom_intersection, other)
456
return self._geomgen(capi.geom_intersection, other)
438
458
def sym_difference(self, other):
440
460
Returns a new geometry which is the symmetric difference of this
441
461
geometry and the other.
443
return self._geomgen(geom_sym_diff, other)
463
return self._geomgen(capi.geom_sym_diff, other)
445
465
def union(self, other):
447
467
Returns a new geometry consisting of the region which is the union of
448
468
this geometry and the other.
450
return self._geomgen(geom_union, other)
470
return self._geomgen(capi.geom_union, other)
452
472
# The subclasses for OGR Geometry.
453
473
class Point(OGRGeometry):
457
477
"Returns the X coordinate for this Point."
458
return getx(self._ptr, 0)
478
return capi.getx(self.ptr, 0)
462
482
"Returns the Y coordinate for this Point."
463
return gety(self._ptr, 0)
483
return capi.gety(self.ptr, 0)
467
487
"Returns the Z coordinate for this Point."
468
488
if self.coord_dim == 3:
469
return getz(self._ptr, 0)
489
return capi.getz(self.ptr, 0)
483
503
"Returns the Point at the given index."
484
504
if index >= 0 and index < self.point_count:
485
505
x, y, z = c_double(), c_double(), c_double()
486
get_point(self._ptr, index, byref(x), byref(y), byref(z))
506
capi.get_point(self.ptr, index, byref(x), byref(y), byref(z))
487
507
dim = self.coord_dim
489
509
return (x.value,)
514
534
Internal routine that returns a sequence (list) corresponding with
515
535
the given function.
517
return [func(self._ptr, i) for i in xrange(len(self))]
537
return [func(self.ptr, i) for i in xrange(len(self))]
521
541
"Returns the X coordinates in a list."
522
return self._listarr(getx)
542
return self._listarr(capi.getx)
526
546
"Returns the Y coordinates in a list."
527
return self._listarr(gety)
547
return self._listarr(capi.gety)
531
551
"Returns the Z coordinates in a list."
532
552
if self.coord_dim == 3:
533
return self._listarr(getz)
553
return self._listarr(capi.getz)
535
555
# LinearRings are used in Polygons.
536
556
class LinearRing(LineString): pass
604
624
"Add the geometry to this Geometry Collection."
605
625
if isinstance(geom, OGRGeometry):
606
626
if isinstance(geom, self.__class__):
607
for g in geom: add_geom(self._ptr, g._ptr)
627
for g in geom: capi.add_geom(self.ptr, g.ptr)
609
add_geom(self._ptr, geom._ptr)
629
capi.add_geom(self.ptr, geom.ptr)
610
630
elif isinstance(geom, basestring):
611
631
tmp = OGRGeometry(geom)
612
add_geom(self._ptr, tmp._ptr)
632
capi.add_geom(self.ptr, tmp.ptr)
614
634
raise OGRException('Must add an OGRGeometry.')