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
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:
@ -57,6 +58,13 @@ class Layer(object):
return self.name
#### 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."

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
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
@ -249,6 +250,13 @@ class OGRGeometry(object):
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):
"Clones this OGR Geometry."
@ -322,10 +330,22 @@ class OGRGeometry(object):
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

View File

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

View File

@ -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 @@ class TestSHP:
# 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,8 +70,15 @@ class DataSourceTest(unittest.TestCase):
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