Author: jbronn
Date: 2007-06-24 20:29:01 -0500 (Sun, 24 Jun 2007)
New Revision: 5527

Added:
   django/branches/gis/django/contrib/gis/gdal/Envelope.py
Modified:
   django/branches/gis/django/contrib/gis/gdal/Layer.py
   django/branches/gis/django/contrib/gis/gdal/OGRGeometry.py
   django/branches/gis/django/contrib/gis/gdal/__init__.py
   django/branches/gis/django/contrib/gis/tests/test_gdal_ds.py
Log:
gis: added the Envelope class, used in getting layer and geometry extents; also 
added boundary and convex_hull properties.


Added: django/branches/gis/django/contrib/gis/gdal/Envelope.py
===================================================================
--- django/branches/gis/django/contrib/gis/gdal/Envelope.py                     
        (rev 0)
+++ django/branches/gis/django/contrib/gis/gdal/Envelope.py     2007-06-25 
01:29:01 UTC (rev 5527)
@@ -0,0 +1,112 @@
+from ctypes import Structure, c_double
+from types import TupleType
+
+"""
+ The GDAL/OGR library uses an Envelope structure to hold the bounding
+  box information for a geometry.  The envelope (bounding box) contains
+  two pairs of coordinates, one for the lower left coordinate and one
+  for the upper right coordinate:
+
+                            +----------o Upper right; (max_x, max_y)
+                            |          |
+                            |          |
+                            |          |
+  Lower left (min_x, min_y) o----------+
+  
+"""
+
+# The OGR definition of an Envelope is a C structure containing four doubles.
+#  See the 'ogr_core.h' source file for more information:
+#   http://www.gdal.org/ogr/ogr__core_8h-source.html
+class OGREnvelope(Structure):
+    "Represents the OGREnvelope C Structure."
+    _fields_ = [("MinX", c_double),
+                ("MaxX", c_double),
+                ("MinY", c_double),
+                ("MaxY", c_double),
+                ]
+
+class Envelope(object):
+    "A class that will wrap an OGR Envelope structure."
+
+    def __init__(self, *args):
+        if len(args) == 1:
+            if isinstance(args[0], OGREnvelope):
+                # OGREnvelope (a ctypes Structure) was passed in.
+                self._envelope = args[0]
+            elif isinstance(args[0], TupleType) and len(args[0]) == 4:
+                # A Tuple was passed in
+                self._from_tuple(args[0])
+            else:
+                raise OGRException, 'Incorrect type of argument: %s' % 
str(type(args[0]))
+        elif len(args) == 4:
+            self._from_tuple(args)
+        else:
+            raise OGRException, 'Incorrect number of arguments!'
+
+    def __eq__(self, other):
+        "Returns true if the envelopes are equivalent; can compare against 
other Envelopes and 4-tuples."
+        if isinstance(other, Envelope):
+            return (self.min_x == other.min_x) and (self.min_y == other.min_y) 
and \
+                   (self.max_x == other.max_x) and (self.max_y == other.max_y)
+        elif isinstance(other, TupleType) and len(other) == 4:
+            return (self.min_x == other[0]) and (self.min_y == other[1]) and \
+                   (self.max_x == other[2]) and (self.max_y == other[3])
+        else:
+            raise OGRException, 'Equivalence testing only works with other 
Envelopes.'
+
+    def __str__(self):
+        "Returns a string representation of the tuple."
+        return str(self.tuple)
+
+    def _from_tuple(self, tup):
+        "Initializes the C OGR Envelope structure from the given tuple."
+        self._envelope = OGREnvelope()
+        self._envelope.MinX = tup[0]
+        self._envelope.MinY = tup[1]
+        self._envelope.MaxX = tup[2]
+        self._envelope.MaxY = tup[3]
+    
+    @property
+    def min_x(self):
+        "Returns the value of the minimum X coordinate."
+        return self._envelope.MinX
+
+    @property
+    def min_y(self):
+        "Returns the value of the minimum Y coordinate."
+        return self._envelope.MinY
+
+    @property
+    def max_x(self):
+        "Returns the value of the maximum X coordinate."
+        return self._envelope.MaxX
+
+    @property
+    def max_y(self):
+        "Returns the value of the maximum Y coordinate."
+        return self._envelope.MaxY
+
+    @property
+    def ur(self):
+        "Returns the upper-right coordinate."
+        return (self.max_x, self.max_y)
+
+    @property
+    def ll(self):
+        "Returns the lower-left coordinate."
+        return (self.min_x, self.min_y)
+
+    @property
+    def tuple(self):
+        "Returns a tuple representing the envelope."
+        return (self.min_x, self.min_y, self.max_x, self.max_y)
+
+    @property
+    def wkt(self):
+        "Returns WKT representing a Polygon for this envelope."
+        # TODO: Fix significant figures.
+        return 'POLYGON((%f %f,%f %f,%f %f,%f %f,%f %f))' % (self.min_x, 
self.min_y, self.min_x, self.max_y,
+                                                             self.max_x, 
self.max_y, self.max_x, self.min_y,
+                                                             self.min_x, 
self.min_y)
+

Modified: django/branches/gis/django/contrib/gis/gdal/Layer.py
===================================================================
--- django/branches/gis/django/contrib/gis/gdal/Layer.py        2007-06-24 
12:38:11 UTC (rev 5526)
+++ django/branches/gis/django/contrib/gis/gdal/Layer.py        2007-06-25 
01:29:01 UTC (rev 5527)
@@ -1,12 +1,13 @@
 # Needed ctypes routines
-from ctypes import c_int, c_long, c_void_p, string_at
+from ctypes import c_int, c_long, c_void_p, byref, string_at
 
 # The GDAL C Library
 from django.contrib.gis.gdal.libgdal import lgdal
 
 # Other GDAL imports.
+from django.contrib.gis.gdal.Envelope import Envelope, OGREnvelope
 from django.contrib.gis.gdal.Feature import Feature
-from django.contrib.gis.gdal.OGRError import OGRException
+from django.contrib.gis.gdal.OGRError import OGRException, check_err
 from django.contrib.gis.gdal.SpatialReference import SpatialReference
 
 # For more information, see the OGR C API source code:
@@ -58,6 +59,13 @@
 
     #### Layer properties ####
     @property
+    def extent(self):
+        "Returns the extent (an Envelope) of this layer."
+        env = OGREnvelope()
+        check_err(lgdal.OGR_L_GetExtent(self._layer, byref(env), c_int(1)))
+        return Envelope(env)
+
+    @property
     def name(self):
         "Returns the name of this layer in the Data Source."
         return string_at(lgdal.OGR_FD_GetName(self._ldefn))

Modified: django/branches/gis/django/contrib/gis/gdal/OGRGeometry.py
===================================================================
--- django/branches/gis/django/contrib/gis/gdal/OGRGeometry.py  2007-06-24 
12:38:11 UTC (rev 5526)
+++ django/branches/gis/django/contrib/gis/gdal/OGRGeometry.py  2007-06-25 
01:29:01 UTC (rev 5527)
@@ -4,6 +4,7 @@
 
 # Getting the GDAL C library and error checking facilities
 from django.contrib.gis.gdal.libgdal import lgdal
+from django.contrib.gis.gdal.Envelope import Envelope, OGREnvelope
 from django.contrib.gis.gdal.OGRError import check_err, OGRException
 from django.contrib.gis.gdal.SpatialReference import SpatialReference, 
CoordTransform
 
@@ -248,6 +249,13 @@
         "Returns the area for a LinearRing, Polygon, or MultiPolygon; 0 
otherwise."
         a = lgdal.OGR_G_GetArea(self._g)
         return a.value
+
+    @property
+    def envelope(self):
+        "Returns the envelope for this Geometry."
+        env = OGREnvelope()
+        lgdal.OGR_G_GetEnvelope(self._g, byref(env))
+        return Envelope(env)
     
     #### Geometry Methods ####
     def clone(self):
@@ -322,11 +330,23 @@
         return self._topology(lgdal.OGR_G_Overlaps, other)
 
     #### Geometry-generation Methods ####
-    def _geomgen(self, gen_func, other):
-        if not isinstance(other, OGRGeometry):
-            raise OGRException, 'Must use another OGRGeometry object for 
geometry-generating operations!'
-        return OGRGeometry(gen_func(self._g, other._g))
+    def _geomgen(self, gen_func, other=None):
+        "A helper routine for the OGR routines that generate geometries."
+        if isinstance(other, OGRGeometry):
+            return OGRGeometry(gen_func(self._g, other._g))
+        else:
+            return OGRGeometry(gen_func(self._g))
 
+    @property
+    def boundary(self):
+        "Returns the boundary of this geometry."
+        return self._geomgen(lgdal.OGR_G_GetBoundary)
+
+    @property
+    def convex_hull(self):
+        "Returns the smallest convex Polygon that contains all the points in 
the Geometry."
+        return self._geomgen(lgdal.OGR_G_ConvexHull)
+
     def union(self, other):
         """Returns a new geometry consisting of the region which is the union 
of
         this geometry and the other."""

Modified: django/branches/gis/django/contrib/gis/gdal/__init__.py
===================================================================
--- django/branches/gis/django/contrib/gis/gdal/__init__.py     2007-06-24 
12:38:11 UTC (rev 5526)
+++ django/branches/gis/django/contrib/gis/gdal/__init__.py     2007-06-25 
01:29:01 UTC (rev 5527)
@@ -1,4 +1,5 @@
 from Driver import Driver
+from Envelope import Envelope
 from DataSource import DataSource
 from SpatialReference import SpatialReference, CoordTransform
 from OGRGeometry import OGRGeometry, OGRGeomType

Modified: django/branches/gis/django/contrib/gis/tests/test_gdal_ds.py
===================================================================
--- django/branches/gis/django/contrib/gis/tests/test_gdal_ds.py        
2007-06-24 12:38:11 UTC (rev 5526)
+++ django/branches/gis/django/contrib/gis/tests/test_gdal_ds.py        
2007-06-25 01:29:01 UTC (rev 5527)
@@ -1,5 +1,6 @@
 import os, os.path, unittest
 from django.contrib.gis.gdal import DataSource, OGRException
+from django.contrib.gis.gdal.Envelope import Envelope
 from django.contrib.gis.gdal.Field import OFTReal, OFTInteger, OFTString
 
 # Path for SHP files
@@ -16,8 +17,10 @@
 
 # List of acceptable data sources.
 ds_list = (TestSHP('test_point', nfeat=5, nfld=3, geom='POINT', gtype=1, 
fields={'dbl' : OFTReal, 'int' : OFTReal, 'str' : OFTString,},
+                   extent=(-1.35011,0.166623,-0.524093,0.824508), # Got extent 
from QGIS
                    
srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]'),
            TestSHP('test_poly', nfeat=3, nfld=3, geom='POLYGON', gtype=3, 
fields={'float' : OFTReal, 'int' : OFTReal, 'str' : OFTString,},
+                   extent=(-1.01513,-0.558245,0.161876,0.839637), # Got extent 
from QGIS
                    
srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]'),
            )
 
@@ -67,9 +70,16 @@
                 self.assertEqual(len(layer), source.nfeat)
 
                 # Making sure we get the number of fields we expect
-                self.assertEqual(layer.num_fields, source.nfld)
-                self.assertEqual(len(layer.fields), source.nfld)
+                self.assertEqual(source.nfld, layer.num_fields)
+                self.assertEqual(source.nfld, len(layer.fields))
 
+                # Testing the layer's extent (an Envelope), and it's properties
+                self.assertEqual(True, isinstance(layer.extent, Envelope))
+                self.assertAlmostEqual(source.extent[0], layer.extent.min_x, 5)
+                self.assertAlmostEqual(source.extent[1], layer.extent.min_y, 5)
+                self.assertAlmostEqual(source.extent[2], layer.extent.max_x, 5)
+                self.assertAlmostEqual(source.extent[3], layer.extent.max_y, 5)
+
                 # Now checking the field names.
                 flds = layer.fields
                 for f in flds: self.assertEqual(True, f in source.fields)
@@ -113,7 +123,7 @@
 
                     # Making sure the SpatialReference is as expected.
                     self.assertEqual(source.srs_wkt, g.srs.wkt)
-                    
+                        
 def suite():
     s = unittest.TestSuite()
     s.addTest(unittest.makeSuite(DataSourceTest))


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to