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:
parent
c953f188dd
commit
8648cf78cd
112
django/contrib/gis/gdal/Envelope.py
Normal file
112
django/contrib/gis/gdal/Envelope.py
Normal 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)
|
||||
|
@ -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."
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user