1
0
mirror of https://github.com/django/django.git synced 2025-07-04 09:49:12 +00:00

gis: added the Envelope class, used in getting layer and geometry extents; also added boundary and convex_hull properties.

git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@5527 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2007-06-25 01:29:01 +00:00
parent c953f188dd
commit 8648cf78cd
5 changed files with 160 additions and 9 deletions

View File

@ -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)

View File

@ -1,12 +1,13 @@
# Needed ctypes routines # 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 # The GDAL C Library
from django.contrib.gis.gdal.libgdal import lgdal from django.contrib.gis.gdal.libgdal import lgdal
# Other GDAL imports. # 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.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 from django.contrib.gis.gdal.SpatialReference import SpatialReference
# For more information, see the OGR C API source code: # For more information, see the OGR C API source code:
@ -57,6 +58,13 @@ class Layer(object):
return self.name return self.name
#### Layer properties #### #### 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 @property
def name(self): def name(self):
"Returns the name of this layer in the Data Source." "Returns the name of this layer in the Data Source."

View File

@ -4,6 +4,7 @@ from ctypes import byref, string_at, c_char_p, c_double, c_int, c_void_p
# Getting the GDAL C library and error checking facilities # Getting the GDAL C library and error checking facilities
from django.contrib.gis.gdal.libgdal import lgdal 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.OGRError import check_err, OGRException
from django.contrib.gis.gdal.SpatialReference import SpatialReference, CoordTransform from django.contrib.gis.gdal.SpatialReference import SpatialReference, CoordTransform
@ -249,6 +250,13 @@ class OGRGeometry(object):
a = lgdal.OGR_G_GetArea(self._g) a = lgdal.OGR_G_GetArea(self._g)
return a.value 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 #### #### Geometry Methods ####
def clone(self): def clone(self):
"Clones this OGR Geometry." "Clones this OGR Geometry."
@ -322,10 +330,22 @@ class OGRGeometry(object):
return self._topology(lgdal.OGR_G_Overlaps, other) return self._topology(lgdal.OGR_G_Overlaps, other)
#### Geometry-generation Methods #### #### Geometry-generation Methods ####
def _geomgen(self, gen_func, other): def _geomgen(self, gen_func, other=None):
if not isinstance(other, OGRGeometry): "A helper routine for the OGR routines that generate geometries."
raise OGRException, 'Must use another OGRGeometry object for geometry-generating operations!' if isinstance(other, OGRGeometry):
return OGRGeometry(gen_func(self._g, other._g)) 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): def union(self, other):
"""Returns a new geometry consisting of the region which is the union of """Returns a new geometry consisting of the region which is the union of

View File

@ -1,4 +1,5 @@
from Driver import Driver from Driver import Driver
from Envelope import Envelope
from DataSource import DataSource from DataSource import DataSource
from SpatialReference import SpatialReference, CoordTransform from SpatialReference import SpatialReference, CoordTransform
from OGRGeometry import OGRGeometry, OGRGeomType from OGRGeometry import OGRGeometry, OGRGeomType

View File

@ -1,5 +1,6 @@
import os, os.path, unittest import os, os.path, unittest
from django.contrib.gis.gdal import DataSource, OGRException 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 from django.contrib.gis.gdal.Field import OFTReal, OFTInteger, OFTString
# Path for SHP files # Path for SHP files
@ -16,8 +17,10 @@ class TestSHP:
# List of acceptable data sources. # 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,}, 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]]'), 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,}, 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]]'), srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]'),
) )
@ -67,8 +70,15 @@ class DataSourceTest(unittest.TestCase):
self.assertEqual(len(layer), source.nfeat) self.assertEqual(len(layer), source.nfeat)
# Making sure we get the number of fields we expect # Making sure we get the number of fields we expect
self.assertEqual(layer.num_fields, source.nfld) self.assertEqual(source.nfld, layer.num_fields)
self.assertEqual(len(layer.fields), source.nfld) 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. # Now checking the field names.
flds = layer.fields flds = layer.fields