~ubuntu-branches/ubuntu/quantal/python-django/quantal-security

« back to all changes in this revision

Viewing changes to django/contrib/gis/gdal/geometries.py

  • Committer: Bazaar Package Importer
  • Author(s): Chris Lamb
  • Date: 2010-05-21 07:52:55 UTC
  • mfrom: (1.3.6 upstream)
  • mto: This revision was merged to the branch mainline in revision 28.
  • Revision ID: james.westby@ubuntu.com-20100521075255-ii78v1dyfmyu3uzx
Tags: upstream-1.2
ImportĀ upstreamĀ versionĀ 1.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
  +proj=longlat +ellps=clrk66 +datum=NAD27 +no_defs
30
30
  >>> print mpnt
31
31
  MULTIPOINT (-89.999930378602485 29.999797886557641,-89.999930378602485 29.999797886557641)
32
 
  
 
32
 
33
33
  The OGRGeomType class is to make it easy to specify an OGR geometry type:
34
34
  >>> from django.contrib.gis.gdal import OGRGeomType
35
35
  >>> gt1 = OGRGeomType(3) # Using an integer for the type
39
39
  True
40
40
"""
41
41
# Python library requisites.
42
 
import re, sys
 
42
import sys
43
43
from binascii import a2b_hex
44
44
from ctypes import byref, string_at, c_char_p, c_double, c_ubyte, c_void_p
45
45
 
48
48
from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope
49
49
from django.contrib.gis.gdal.error import OGRException, OGRIndexError, SRSException
50
50
from django.contrib.gis.gdal.geomtype import OGRGeomType
 
51
from django.contrib.gis.gdal.libgdal import GEOJSON, GDAL_VERSION
51
52
from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform
52
53
 
53
54
# Getting the ctypes prototype functions that interface w/the GDAL C library.
54
55
from django.contrib.gis.gdal.prototypes import geom as capi, srs as srs_api
55
 
GEOJSON = capi.GEOJSON
 
56
 
 
57
# For recognizing geometry input.
 
58
from django.contrib.gis.geometry.regex import hex_regex, wkt_regex, json_regex
56
59
 
57
60
# For more information, see the OGR C API source code:
58
61
#  http://www.gdal.org/ogr/ogr__api_8h.html
59
62
#
60
63
# The OGR_G_* routines are relevant here.
61
64
 
62
 
# Regular expressions for recognizing HEXEWKB and WKT.
63
 
hex_regex = re.compile(r'^[0-9A-F]+$', re.I)
64
 
wkt_regex = re.compile(r'^(?P<type>POINT|LINESTRING|LINEARRING|POLYGON|MULTIPOINT|MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION)[ACEGIMLONPSRUTY\d,\.\-\(\) ]+$', re.I)
65
 
json_regex = re.compile(r'^(\s+)?\{[\s\w,\[\]\{\}\-\."\':]+\}(\s+)?$')
66
 
 
67
65
#### OGRGeometry Class ####
68
66
class OGRGeometry(GDALBase):
69
67
    "Generally encapsulates an OGR geometry."
78
76
            geom_input = buffer(a2b_hex(geom_input.upper()))
79
77
            str_instance = False
80
78
 
81
 
        # Constructing the geometry, 
 
79
        # Constructing the geometry,
82
80
        if str_instance:
83
81
            # Checking if unicode
84
82
            if isinstance(geom_input, unicode):
88
86
            wkt_m = wkt_regex.match(geom_input)
89
87
            json_m = json_regex.match(geom_input)
90
88
            if wkt_m:
 
89
                if wkt_m.group('srid'):
 
90
                    # If there's EWKT, set the SRS w/value of the SRID.
 
91
                    srs = int(wkt_m.group('srid'))
91
92
                if wkt_m.group('type').upper() == 'LINEARRING':
92
93
                    # OGR_G_CreateFromWkt doesn't work with LINEARRING WKT.
93
94
                    #  See http://trac.osgeo.org/gdal/ticket/1992.
94
95
                    g = capi.create_geom(OGRGeomType(wkt_m.group('type')).num)
95
 
                    capi.import_wkt(g, byref(c_char_p(geom_input)))
 
96
                    capi.import_wkt(g, byref(c_char_p(wkt_m.group('wkt'))))
96
97
                else:
97
 
                    g = capi.from_wkt(byref(c_char_p(geom_input)), None, byref(c_void_p()))
 
98
                    g = capi.from_wkt(byref(c_char_p(wkt_m.group('wkt'))), None, byref(c_void_p()))
98
99
            elif json_m:
99
100
                if GEOJSON:
100
101
                    g = capi.from_json(geom_input)
129
130
        # Setting the class depending upon the OGR Geometry Type
130
131
        self.__class__ = GEO_CLASSES[self.geom_type.num]
131
132
 
 
133
    def __del__(self):
 
134
        "Deletes this Geometry."
 
135
        if self._ptr: capi.destroy_geom(self._ptr)
 
136
 
 
137
    # Pickle routines
 
138
    def __getstate__(self):
 
139
        srs = self.srs
 
140
        if srs:
 
141
            srs = srs.wkt
 
142
        else:
 
143
            srs = None
 
144
        return str(self.wkb), srs
 
145
 
 
146
    def __setstate__(self, state):
 
147
        wkb, srs = state
 
148
        ptr = capi.from_wkb(wkb, None, byref(c_void_p()), len(wkb))
 
149
        if not ptr: raise OGRException('Invalid OGRGeometry loaded from pickled state.')
 
150
        self.ptr = ptr
 
151
        self.srs = srs
 
152
 
132
153
    @classmethod
133
 
    def from_bbox(cls, bbox):   
 
154
    def from_bbox(cls, bbox):
134
155
        "Constructs a Polygon from a bounding box (4-tuple)."
135
156
        x0, y0, x1, y1 = bbox
136
157
        return OGRGeometry( 'POLYGON((%s %s, %s %s, %s %s, %s %s, %s %s))' %  (
137
158
                x0, y0, x0, y1, x1, y1, x1, y0, x0, y0) )
138
 
 
139
 
    def __del__(self):
140
 
        "Deletes this Geometry."
141
 
        if self._ptr: capi.destroy_geom(self._ptr)
142
159
 
143
160
    ### Geometry set-like operations ###
144
161
    # g = g1 | g2
163
180
 
164
181
    def __eq__(self, other):
165
182
        "Is this Geometry equal to the other?"
166
 
        return self.equals(other)
 
183
        if isinstance(other, OGRGeometry):
 
184
            return self.equals(other)
 
185
        else:
 
186
            return False
167
187
 
168
188
    def __ne__(self, other):
169
189
        "Tests for inequality."
170
 
        return not self.equals(other)
 
190
        return not (self == other)
171
191
 
172
192
    def __str__(self):
173
193
        "WKT is used for the string representation."
179
199
        "Returns 0 for points, 1 for lines, and 2 for surfaces."
180
200
        return capi.get_dims(self.ptr)
181
201
 
182
 
    @property
183
 
    def coord_dim(self):
 
202
    def _get_coord_dim(self):
184
203
        "Returns the coordinate dimension of the Geometry."
185
 
        return capi.get_coord_dims(self.ptr)
 
204
        if isinstance(self, GeometryCollection) and GDAL_VERSION < (1, 5, 2):
 
205
            # On GDAL versions prior to 1.5.2, there exists a bug in which
 
206
            # the coordinate dimension of geometry collections is always 2:
 
207
            #   http://trac.osgeo.org/gdal/ticket/2334
 
208
            # Here we workaround by returning the coordinate dimension of the
 
209
            # first geometry in the collection instead.
 
210
            if len(self):
 
211
                return capi.get_coord_dim(capi.get_geom_ref(self.ptr, 0))
 
212
        return capi.get_coord_dim(self.ptr)
 
213
 
 
214
    def _set_coord_dim(self, dim):
 
215
        "Sets the coordinate dimension of this Geometry."
 
216
        if not dim in (2, 3):
 
217
            raise ValueError('Geometry dimension must be either 2 or 3')
 
218
        capi.set_coord_dim(self.ptr, dim)
 
219
 
 
220
    coord_dim = property(_get_coord_dim, _set_coord_dim)
186
221
 
187
222
    @property
188
223
    def geom_count(self):
207
242
    @property
208
243
    def geom_type(self):
209
244
        "Returns the Type for this Geometry."
210
 
        try:
211
 
            return OGRGeomType(capi.get_geom_type(self.ptr))
212
 
        except OGRException:
213
 
            # VRT datasources return an invalid geometry type
214
 
            # number, but a valid name -- we'll try that instead.
215
 
            # See: http://trac.osgeo.org/gdal/ticket/2491
216
 
            return OGRGeomType(capi.get_geom_name(self.ptr))
 
245
        return OGRGeomType(capi.get_geom_type(self.ptr))
217
246
 
218
247
    @property
219
248
    def geom_name(self):
237
266
        return self.envelope.tuple
238
267
 
239
268
    #### SpatialReference-related Properties ####
240
 
    
 
269
 
241
270
    # The SRS property
242
271
    def _get_srs(self):
243
272
        "Returns the Spatial Reference for this Geometry."
249
278
 
250
279
    def _set_srs(self, srs):
251
280
        "Sets the SpatialReference for this geometry."
 
281
        # Do not have to clone the `SpatialReference` object pointer because
 
282
        # when it is assigned to this `OGRGeometry` it's internal OGR
 
283
        # reference count is incremented, and will likewise be released
 
284
        # (decremented) when this geometry's destructor is called.
252
285
        if isinstance(srs, SpatialReference):
253
 
            srs_ptr = srs_api.clone_srs(srs.ptr)
 
286
            srs_ptr = srs.ptr
254
287
        elif isinstance(srs, (int, long, basestring)):
255
288
            sr = SpatialReference(srs)
256
 
            srs_ptr = srs_api.clone_srs(sr.ptr)
 
289
            srs_ptr = sr.ptr
257
290
        else:
258
291
            raise TypeError('Cannot assign spatial reference with object of type: %s' % type(srs))
259
292
        capi.assign_srs(self.ptr, srs_ptr)
298
331
        Returns the GeoJSON representation of this Geometry (requires
299
332
        GDAL 1.5+).
300
333
        """
301
 
        if GEOJSON: 
 
334
        if GEOJSON:
302
335
            return capi.to_json(self.ptr)
303
336
        else:
304
337
            raise NotImplementedError('GeoJSON output only supported on GDAL 1.5+.')
335
368
    def wkt(self):
336
369
        "Returns the WKT representation of the Geometry."
337
370
        return capi.to_wkt(self.ptr, byref(c_char_p()))
338
 
    
 
371
 
 
372
    @property
 
373
    def ewkt(self):
 
374
        "Returns the EWKT representation of the Geometry."
 
375
        srs = self.srs
 
376
        if srs and srs.srid:
 
377
            return 'SRID=%s;%s' % (srs.srid, self.wkt)
 
378
        else:
 
379
            return self.wkt
 
380
 
339
381
    #### Geometry Methods ####
340
382
    def clone(self):
341
383
        "Clones this OGR Geometry."
363
405
            klone = self.clone()
364
406
            klone.transform(coord_trans)
365
407
            return klone
 
408
 
 
409
        # Have to get the coordinate dimension of the original geometry
 
410
        # so it can be used to reset the transformed geometry's dimension
 
411
        # afterwards.  This is done because of GDAL bug (in versions prior
 
412
        # to 1.7) that turns geometries 3D after transformation, see:
 
413
        #  http://trac.osgeo.org/gdal/changeset/17792
 
414
        if GDAL_VERSION < (1, 7):
 
415
            orig_dim = self.coord_dim
 
416
 
 
417
        # Depending on the input type, use the appropriate OGR routine
 
418
        # to perform the transformation.
366
419
        if isinstance(coord_trans, CoordTransform):
367
420
            capi.geom_transform(self.ptr, coord_trans.ptr)
368
421
        elif isinstance(coord_trans, SpatialReference):
371
424
            sr = SpatialReference(coord_trans)
372
425
            capi.geom_transform_to(self.ptr, sr.ptr)
373
426
        else:
374
 
            raise TypeError('Transform only accepts CoordTransform, SpatialReference, string, and integer objects.')
 
427
            raise TypeError('Transform only accepts CoordTransform, '
 
428
                            'SpatialReference, string, and integer objects.')
 
429
 
 
430
        # Setting with original dimension, see comment above.
 
431
        if GDAL_VERSION < (1, 7):
 
432
            if isinstance(self, GeometryCollection):
 
433
                # With geometry collections have to set dimension on
 
434
                # each internal geometry reference, as the collection
 
435
                # dimension isn't affected.
 
436
                for i in xrange(len(self)):
 
437
                    internal_ptr = capi.get_geom_ref(self.ptr, i)
 
438
                    if orig_dim != capi.get_coord_dim(internal_ptr):
 
439
                        capi.set_coord_dim(internal_ptr, orig_dim)
 
440
            else:
 
441
                if self.coord_dim != orig_dim:
 
442
                    self.coord_dim = orig_dim
375
443
 
376
444
    def transform_to(self, srs):
377
445
        "For backwards-compatibility."
391
459
    def intersects(self, other):
392
460
        "Returns True if this geometry intersects with the other."
393
461
        return self._topology(capi.ogr_intersects, other)
394
 
    
 
462
 
395
463
    def equals(self, other):
396
464
        "Returns True if this geometry is equivalent to the other."
397
465
        return self._topology(capi.ogr_equals, other)
436
504
    @property
437
505
    def convex_hull(self):
438
506
        """
439
 
        Returns the smallest convex Polygon that contains all the points in 
 
507
        Returns the smallest convex Polygon that contains all the points in
440
508
        this Geometry.
441
509
        """
442
510
        return self._geomgen(capi.geom_convex_hull)
456
524
        return self._geomgen(capi.geom_intersection, other)
457
525
 
458
526
    def sym_difference(self, other):
459
 
        """                                                                                                                                                
 
527
        """
460
528
        Returns a new geometry which is the symmetric difference of this
461
529
        geometry and the other.
462
530
        """
545
613
    def y(self):
546
614
        "Returns the Y coordinates in a list."
547
615
        return self._listarr(capi.gety)
548
 
    
 
616
 
549
617
    @property
550
618
    def z(self):
551
619
        "Returns the Z coordinates in a list."
610
678
            raise OGRIndexError('index out of range: %s' % index)
611
679
        else:
612
680
            return OGRGeometry(capi.clone_geom(capi.get_geom_ref(self.ptr, index)), self.srs)
613
 
        
 
681
 
614
682
    def __iter__(self):
615
683
        "Iterates over each Geometry."
616
684
        for i in xrange(self.geom_count):
658
726
               5 : MultiLineString,
659
727
               6 : MultiPolygon,
660
728
               7 : GeometryCollection,
661
 
               101: LinearRing, 
 
729
               101: LinearRing,
 
730
               1 + OGRGeomType.wkb25bit : Point,
 
731
               2 + OGRGeomType.wkb25bit : LineString,
 
732
               3 + OGRGeomType.wkb25bit : Polygon,
 
733
               4 + OGRGeomType.wkb25bit : MultiPoint,
 
734
               5 + OGRGeomType.wkb25bit : MultiLineString,
 
735
               6 + OGRGeomType.wkb25bit : MultiPolygon,
 
736
               7 + OGRGeomType.wkb25bit : GeometryCollection,
662
737
               }