1
0
mirror of https://github.com/django/django.git synced 2025-07-04 17:59:13 +00:00

gis: gdal: OSGeo sprint -- cleaned up spatial references associated with geometries (fixed a segfault); cleaned up Envelope module (thanks to ww for help), and added tests; added a test module for invoking all gdal tests; changed exception style.

git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@6436 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2007-09-29 14:02:41 +00:00
parent cfb807a1fb
commit 9eb115e304
14 changed files with 311 additions and 136 deletions

View File

@ -1,12 +1,39 @@
"""
This module houses ctypes interfaces for GDAL objects. The following GDAL
objects are supported:
CoordTransform: Used for coordinate transformations from one spatial
reference system to another.
Driver: Wraps an OGR data source driver.
DataSource: Wrapper for the OGR data source object, supports
OGR-supported data sources.
Envelope: A ctypes structure for bounding boxes (GDAL library
not required).
OGRGeometry: Layer for accessing OGR Geometry objects.
OGRGeomType: A class for representing the different OGR Geometry
types (GDAL library not required).
SpatialReference: Represents OSR Spatial Reference objects.
"""
# Attempting to import objects that depend on the GDAL library. The
# HAS_GDAL flag will be set to True if the library is present on
# the system.
try: try:
from django.contrib.gis.gdal.driver import Driver from django.contrib.gis.gdal.driver import Driver
from django.contrib.gis.gdal.envelope import Envelope
from django.contrib.gis.gdal.datasource import DataSource from django.contrib.gis.gdal.datasource import DataSource
from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform
from django.contrib.gis.gdal.geometries import OGRGeometry from django.contrib.gis.gdal.geometries import OGRGeometry
from django.contrib.gis.gdal.geomtype import OGRGeomType
from django.contrib.gis.gdal.error import check_err, OGRException, SRSException
HAS_GDAL = True HAS_GDAL = True
except: except:
HAS_GDAL = False HAS_GDAL = False
# The envelope, error, and geomtype modules do not actually require the
# GDAL library.
from django.contrib.gis.gdal.envelope import Envelope
from django.contrib.gis.gdal.error import check_err, OGRException, OGRIndexError, SRSException
from django.contrib.gis.gdal.geomtype import OGRGeomType

View File

@ -4,7 +4,7 @@ from ctypes import c_char_p, c_int, c_void_p, byref, string_at
# The GDAL C library, OGR exceptions, and the Layer object. # The GDAL C library, OGR exceptions, and the Layer object.
from django.contrib.gis.gdal.libgdal import lgdal from django.contrib.gis.gdal.libgdal import lgdal
from django.contrib.gis.gdal.error import OGRException, check_err from django.contrib.gis.gdal.error import OGRException, OGRIndexError, check_err
from django.contrib.gis.gdal.layer import Layer from django.contrib.gis.gdal.layer import Layer
from django.contrib.gis.gdal.driver import Driver from django.contrib.gis.gdal.driver import Driver
@ -60,7 +60,7 @@ class DataSource(object):
# Registering all the drivers, this needs to be done # Registering all the drivers, this needs to be done
# _before_ we try to open up a data source. # _before_ we try to open up a data source.
if not lgdal.OGRGetDriverCount() and not lgdal.OGRRegisterAll(): if not lgdal.OGRGetDriverCount() and not lgdal.OGRRegisterAll():
raise OGRException, 'Could not register all the OGR data source drivers!' raise OGRException('Could not register all the OGR data source drivers!')
if isinstance(ds_input, StringType): if isinstance(ds_input, StringType):
@ -72,12 +72,12 @@ class DataSource(object):
elif isinstance(ds_input, c_void_p) and isinstance(ds_driver, c_void_p): elif isinstance(ds_input, c_void_p) and isinstance(ds_driver, c_void_p):
ds = ds_input ds = ds_input
else: else:
raise OGRException, 'Invalid data source input type: %s' % str(type(ds_input)) raise OGRException('Invalid data source input type: %s' % str(type(ds_input)))
# Raise an exception if the returned pointer is NULL # Raise an exception if the returned pointer is NULL
if not ds: if not ds:
self._ds = False self._ds = False
raise OGRException, 'Invalid data source file "%s"' % ds_input raise OGRException('Invalid data source file "%s"' % ds_input)
else: else:
self._ds = ds self._ds = ds
self._driver = Driver(ds_driver) self._driver = Driver(ds_driver)
@ -95,10 +95,10 @@ class DataSource(object):
"Allows use of the index [] operator to get a layer at the index." "Allows use of the index [] operator to get a layer at the index."
if isinstance(index, StringType): if isinstance(index, StringType):
l = lgdal.OGR_DS_GetLayerByName(self._ds, c_char_p(index)) l = lgdal.OGR_DS_GetLayerByName(self._ds, c_char_p(index))
if not l: raise IndexError, 'invalid OGR Layer name given: "%s"' % index if not l: raise OGRIndexError('invalid OGR Layer name given: "%s"' % index)
else: else:
if index < 0 or index >= self.layer_count: if index < 0 or index >= self.layer_count:
raise IndexError, 'index out of range' raise OGRIndexError('index out of range')
l = lgdal.OGR_DS_GetLayer(self._ds, c_int(index)) l = lgdal.OGR_DS_GetLayer(self._ds, c_int(index))
return Layer(l) return Layer(l)

View File

@ -45,11 +45,11 @@ class Driver(object):
elif isinstance(input, c_void_p): elif isinstance(input, c_void_p):
dr = input dr = input
else: else:
raise OGRException, 'Unrecognized input type for OGR Driver: %s' % str(type(input)) raise OGRException('Unrecognized input type for OGR Driver: %s' % str(type(input)))
# Making sure we get a valid pointer to the OGR Driver # Making sure we get a valid pointer to the OGR Driver
if not dr: if not dr:
raise OGRException, 'Could not initialize OGR Driver on input: %s' % str(input) raise OGRException('Could not initialize OGR Driver on input: %s' % str(input))
self._dr = dr self._dr = dr
def __str__(self): def __str__(self):
@ -61,7 +61,7 @@ class Driver(object):
# Only register all if the driver count is 0 (or else all drivers # Only register all if the driver count is 0 (or else all drivers
# will be registered over and over again) # will be registered over and over again)
if not self.driver_count and not lgdal.OGRRegisterAll(): if not self.driver_count and not lgdal.OGRRegisterAll():
raise OGRException, 'Could not register all the OGR data source drivers!' raise OGRException('Could not register all the OGR data source drivers!')
# Driver properties # Driver properties
@property @property

View File

@ -1,6 +1,3 @@
from ctypes import Structure, c_double
from types import TupleType
""" """
The GDAL/OGR library uses an Envelope structure to hold the bounding The GDAL/OGR library uses an Envelope structure to hold the bounding
box information for a geometry. The envelope (bounding box) contains box information for a geometry. The envelope (bounding box) contains
@ -14,6 +11,9 @@ from types import TupleType
Lower left (min_x, min_y) o----------+ Lower left (min_x, min_y) o----------+
""" """
from ctypes import Structure, c_double
from types import TupleType, ListType
from django.contrib.gis.gdal.error import OGRException
# The OGR definition of an Envelope is a C structure containing four doubles. # The OGR definition of an Envelope is a C structure containing four doubles.
# See the 'ogr_core.h' source file for more information: # See the 'ogr_core.h' source file for more information:
@ -27,25 +27,48 @@ class OGREnvelope(Structure):
] ]
class Envelope(object): class Envelope(object):
"A class that will wrap an OGR Envelope structure." """
The Envelope object is a C structure that contains the minimum and
maximum X, Y coordinates for a rectangle bounding box. The naming
of the variables is compatible with the OGR Envelope structure.
"""
def __init__(self, *args): def __init__(self, *args):
"""
The initialization function may take an OGREnvelope structure, 4-element
tuple or list, or 4 individual arguments.
"""
if len(args) == 1: if len(args) == 1:
if isinstance(args[0], OGREnvelope): if isinstance(args[0], OGREnvelope):
# OGREnvelope (a ctypes Structure) was passed in. # OGREnvelope (a ctypes Structure) was passed in.
self._envelope = args[0] self._envelope = args[0]
elif isinstance(args[0], TupleType) and len(args[0]) == 4: elif isinstance(args[0], (TupleType, ListType)):
# A Tuple was passed in # A tuple was passed in.
self._from_tuple(args[0]) if len(args[0]) != 4:
raise OGRException('Incorrect number of tuple elements (%d).' % len(args[0]))
else: else:
raise OGRException, 'Incorrect type of argument: %s' % str(type(args[0])) self._from_sequence(args[0])
else:
raise TypeError('Incorrect type of argument: %s' % str(type(args[0])))
elif len(args) == 4: elif len(args) == 4:
self._from_tuple(args) # Individiual parameters passed in.
# Thanks to ww for the help
self._from_sequence(map(float, args))
else: else:
raise OGRException, 'Incorrect number of arguments!' raise OGRException('Incorrect number (%d) of arguments.' % len(args))
# Checking the x,y coordinates
if self.min_x >= self.max_x:
raise OGRException('Envelope minimum X >= maximum X.')
if self.min_y >= self.max_y:
raise OGRException('Envelope minimum Y >= maximum Y.')
def __eq__(self, other): def __eq__(self, other):
"Returns true if the envelopes are equivalent; can compare against other Envelopes and 4-tuples." """
Returns True if the envelopes are equivalent; can compare against
other Envelopes and 4-tuples.
"""
if isinstance(other, Envelope): if isinstance(other, Envelope):
return (self.min_x == other.min_x) and (self.min_y == other.min_y) and \ 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) (self.max_x == other.max_x) and (self.max_y == other.max_y)
@ -53,19 +76,19 @@ class Envelope(object):
return (self.min_x == other[0]) and (self.min_y == other[1]) and \ return (self.min_x == other[0]) and (self.min_y == other[1]) and \
(self.max_x == other[2]) and (self.max_y == other[3]) (self.max_x == other[2]) and (self.max_y == other[3])
else: else:
raise OGRException, 'Equivalence testing only works with other Envelopes.' raise OGRException('Equivalence testing only works with other Envelopes.')
def __str__(self): def __str__(self):
"Returns a string representation of the tuple." "Returns a string representation of the tuple."
return str(self.tuple) return str(self.tuple)
def _from_tuple(self, tup): def _from_sequence(self, seq):
"Initializes the C OGR Envelope structure from the given tuple." "Initializes the C OGR Envelope structure from the given sequence."
self._envelope = OGREnvelope() self._envelope = OGREnvelope()
self._envelope.MinX = tup[0] self._envelope.MinX = seq[0]
self._envelope.MinY = tup[1] self._envelope.MinY = seq[1]
self._envelope.MaxX = tup[2] self._envelope.MaxX = seq[2]
self._envelope.MaxY = tup[3] self._envelope.MaxY = seq[3]
@property @property
def min_x(self): def min_x(self):
@ -106,7 +129,7 @@ class Envelope(object):
def wkt(self): def wkt(self):
"Returns WKT representing a Polygon for this envelope." "Returns WKT representing a Polygon for this envelope."
# TODO: Fix significant figures. # TODO: Fix significant figures.
return 'POLYGON((%s %s,%s %s,%s %s,%s %s,%s %s))' % (self.min_x, self.min_y, self.min_x, self.max_y, return 'POLYGON((%s %s,%s %s,%s %s,%s %s,%s %s))' % \
(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.max_x, self.max_y, self.max_x, self.min_y,
self.min_x, self.min_y) self.min_x, self.min_y)

View File

@ -7,6 +7,7 @@ from django.contrib.gis.gdal.libgdal import lgdal
from django.contrib.gis.gdal.error import OGRException, OGRIndexError from django.contrib.gis.gdal.error import OGRException, OGRIndexError
from django.contrib.gis.gdal.field import Field from django.contrib.gis.gdal.field import Field
from django.contrib.gis.gdal.geometries import OGRGeometry, OGRGeomType from django.contrib.gis.gdal.geometries import OGRGeometry, OGRGeomType
from django.contrib.gis.gdal.srs import SpatialReference
# For more information, see the OGR C API source code: # For more information, see the OGR C API source code:
# http://www.gdal.org/ogr/ogr__api_8h.html # http://www.gdal.org/ogr/ogr__api_8h.html
@ -21,7 +22,7 @@ class Feature(object):
self._feat = 0 # Initially NULL self._feat = 0 # Initially NULL
self._fdefn = 0 self._fdefn = 0
if not f: if not f:
raise OGRException, 'Cannot create OGR Feature, invalid pointer given.' raise OGRException('Cannot create OGR Feature, invalid pointer given.')
self._feat = f self._feat = f
self._fdefn = lgdal.OGR_F_GetDefnRef(f) self._fdefn = lgdal.OGR_F_GetDefnRef(f)
@ -35,7 +36,7 @@ class Feature(object):
i = self.index(index) i = self.index(index)
else: else:
if index < 0 or index > self.num_fields: if index < 0 or index > self.num_fields:
raise OGRIndexError, 'index out of range' raise OGRIndexError('index out of range')
i = index i = index
return Field(lgdal.OGR_F_GetFieldDefnRef(self._feat, c_int(i)), return Field(lgdal.OGR_F_GetFieldDefnRef(self._feat, c_int(i)),
string_at(lgdal.OGR_F_GetFieldAsString(self._feat, c_int(i)))) string_at(lgdal.OGR_F_GetFieldAsString(self._feat, c_int(i))))
@ -84,8 +85,20 @@ class Feature(object):
@property @property
def geom(self): def geom(self):
"Returns the OGR Geometry for this Feature." "Returns the OGR Geometry for this Feature."
# A clone is used, so destruction of the Geometry won't bork the Feature. # Retrieving the geometry pointer for the feature.
return OGRGeometry(lgdal.OGR_G_Clone(lgdal.OGR_F_GetGeometryRef(self._feat))) geom_ptr = lgdal.OGR_F_GetGeometryRef(self._feat)
if not geom_ptr:
raise OGRException('Cannot retrieve Geometry from the feature.')
# Attempting to retrieve the Spatial Reference for the geometry.
srs_ptr = lgdal.OGR_G_GetSpatialReference(geom_ptr)
if srs_ptr:
srs = SpatialReference(srs_ptr, 'ogr')
else:
srs = None
# Geometry is cloned so the feature isn't invalidated.
return OGRGeometry(lgdal.OGR_G_Clone(geom_ptr), srs)
@property @property
def geom_type(self): def geom_type(self):
@ -105,7 +118,7 @@ class Feature(object):
def index(self, field_name): def index(self, field_name):
"Returns the index of the given field name." "Returns the index of the given field name."
i = lgdal.OGR_F_GetFieldIndex(self._feat, c_char_p(field_name)) i = lgdal.OGR_F_GetFieldIndex(self._feat, c_char_p(field_name))
if i < 0: raise OGRIndexError, 'invalid OFT field name given: "%s"' % field_name if i < 0: raise OGRIndexError('invalid OFT field name given: "%s"' % field_name)
return i return i
def clone(self): def clone(self):

View File

@ -1,5 +1,4 @@
from ctypes import string_at from ctypes import string_at
from django.contrib.gis.gdal.libgdal import lgdal from django.contrib.gis.gdal.libgdal import lgdal
from django.contrib.gis.gdal.error import OGRException from django.contrib.gis.gdal.error import OGRException
@ -16,7 +15,7 @@ class Field(object):
self._fld = 0 # Initially NULL self._fld = 0 # Initially NULL
if not fld: if not fld:
raise OGRException, 'Cannot create OGR Field, invalid pointer given.' raise OGRException('Cannot create OGR Field, invalid pointer given.')
self._fld = fld self._fld = fld
self._val = val self._val = val
@ -64,7 +63,10 @@ class OFTReal(Field):
return None return None
class OFTRealList(Field): pass class OFTRealList(Field): pass
class OFTString(Field): pass class OFTString(Field):
def __str__(self):
return '%s ("%s")' % (self.name, self.value)
class OFTStringList(Field): pass class OFTStringList(Field): pass
class OFTWideString(Field): pass class OFTWideString(Field): pass
class OFTWideStringList(Field): pass class OFTWideStringList(Field): pass

View File

@ -1,14 +1,3 @@
# types & ctypes
from types import IntType, StringType
from ctypes import byref, string_at, c_char_p, c_double, c_int, c_void_p
# Getting geodjango gdal prerequisites
from django.contrib.gis.gdal.libgdal import lgdal
from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope
from django.contrib.gis.gdal.error import check_err, OGRException, OGRIndexError
from django.contrib.gis.gdal.geomtype import OGRGeomType
from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform
""" """
The OGRGeometry is a wrapper for using the OGR Geometry class The OGRGeometry is a wrapper for using the OGR Geometry class
(see http://www.gdal.org/ogr/classOGRGeometry.html). OGRGeometry (see http://www.gdal.org/ogr/classOGRGeometry.html). OGRGeometry
@ -49,6 +38,16 @@ from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform
>>> print gt1 == 3, gt1 == 'Polygon' # Equivalence works w/non-OGRGeomType objects >>> print gt1 == 3, gt1 == 'Polygon' # Equivalence works w/non-OGRGeomType objects
True True
""" """
# types & ctypes
from types import IntType, StringType
from ctypes import byref, string_at, c_char_p, c_double, c_int, c_void_p
# Getting GDAL prerequisites
from django.contrib.gis.gdal.libgdal import lgdal
from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope
from django.contrib.gis.gdal.error import check_err, OGRException, OGRIndexError
from django.contrib.gis.gdal.geomtype import OGRGeomType
from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform
# For more information, see the OGR C API source code: # For more information, see the OGR C API source code:
# http://www.gdal.org/ogr/ogr__api_8h.html # http://www.gdal.org/ogr/ogr__api_8h.html
@ -75,37 +74,45 @@ get_area.argtypes = [c_void_p]
class OGRGeometry(object): class OGRGeometry(object):
"Generally encapsulates an OGR geometry." "Generally encapsulates an OGR geometry."
def __init__(self, input, srs=False): def __init__(self, geom_input, srs=None):
"Initializes Geometry on either WKT or an OGR pointer as input." "Initializes Geometry on either WKT or an OGR pointer as input."
self._g = 0 # Initially NULL self._g = 0 # Initially NULL
self._init_srs(srs)
if isinstance(input, StringType): if isinstance(geom_input, StringType):
# First, trying the input as WKT # First, trying the input as WKT
buf = c_char_p(input) buf = c_char_p(geom_input)
g = c_void_p() g = c_void_p()
try: try:
check_err(lgdal.OGR_G_CreateFromWkt(byref(buf), self._s._srs, byref(g))) check_err(lgdal.OGR_G_CreateFromWkt(byref(buf), c_void_p(), byref(g)))
except OGRException, msg: except OGRException:
try: try:
ogr_t = OGRGeomType(input) # Seeing if the input is a valid short-hand string # Seeing if the input is a valid short-hand string
ogr_t = OGRGeomType(geom_input)
g = lgdal.OGR_G_CreateGeometry(ogr_t.num) g = lgdal.OGR_G_CreateGeometry(ogr_t.num)
except: except:
raise OGRException, 'Could not initialize on WKT "%s"' % input raise OGRException('Could not initialize OGR Geometry from: %s' % geom_input)
elif isinstance(input, OGRGeomType): elif isinstance(geom_input, OGRGeomType):
g = lgdal.OGR_G_CreateGeometry(input.num) g = lgdal.OGR_G_CreateGeometry(geom_input.num)
lgdal.OGR_G_AssignSpatialReference(g, self._s._srs) elif isinstance(geom_input, IntType):
elif isinstance(input, IntType):
# OGR Pointer (integer) was the input # OGR Pointer (integer) was the input
g = input g = geom_input
else: else:
raise OGRException, 'Type of input cannot be determined!' raise OGRException('Type of input cannot be determined!')
# Assigning the SpatialReference object to the geometry, if valid.
if bool(srs):
if isinstance(srs, SpatialReference):
srs_ptr = srs._srs
else:
sr = SpatialReference(srs)
srs_ptr = sr._srs
lgdal.OGR_G_AssignSpatialReference(g, srs_ptr)
# Now checking the Geometry pointer before finishing initialization # Now checking the Geometry pointer before finishing initialization
if not g: if not g:
raise OGRException, 'Cannot create OGR Geometry from input: %s' % str(input) raise OGRException('Cannot create OGR Geometry from input: %s' % str(geom_input))
self._g = g self._g = g
# Setting the class depending upon the OGR Geometry Type # Setting the class depending upon the OGR Geometry Type
@ -115,13 +122,6 @@ class OGRGeometry(object):
"Deletes this Geometry." "Deletes this Geometry."
if self._g: lgdal.OGR_G_DestroyGeometry(self._g) if self._g: lgdal.OGR_G_DestroyGeometry(self._g)
def _init_srs(self, srs):
# Getting the spatial
if not isinstance(srs, SpatialReference):
self._s = SpatialReference() # creating an empty spatial reference
else:
self._s = srs.clone() # cloning the given spatial reference
### Geometry set-like operations ### ### Geometry set-like operations ###
# g = g1 | g2 # g = g1 | g2
def __or__(self, other): def __or__(self, other):
@ -184,7 +184,11 @@ class OGRGeometry(object):
@property @property
def srs(self): def srs(self):
"Returns the Spatial Reference for this Geometry." "Returns the Spatial Reference for this Geometry."
return SpatialReference(lgdal.OSRClone(lgdal.OGR_G_GetSpatialReference(self._g)), 'ogr') srs_ptr = lgdal.OGR_G_GetSpatialReference(self._g)
if srs_ptr:
return SpatialReference(lgdal.OSRClone(srs_ptr), 'ogr')
else:
return None
@property @property
def geom_type(self): def geom_type(self):
@ -196,13 +200,6 @@ class OGRGeometry(object):
"Returns the Name of this Geometry." "Returns the Name of this Geometry."
return string_at(lgdal.OGR_G_GetGeometryName(self._g)) return string_at(lgdal.OGR_G_GetGeometryName(self._g))
@property
def wkt(self):
"Returns the WKT form of the Geometry."
buf = c_char_p()
check_err(lgdal.OGR_G_ExportToWkt(self._g, byref(buf)))
return string_at(buf)
@property @property
def area(self): def area(self):
"Returns the area for a LinearRing, Polygon, or MultiPolygon; 0 otherwise." "Returns the area for a LinearRing, Polygon, or MultiPolygon; 0 otherwise."
@ -215,6 +212,21 @@ class OGRGeometry(object):
lgdal.OGR_G_GetEnvelope(self._g, byref(env)) lgdal.OGR_G_GetEnvelope(self._g, byref(env))
return Envelope(env) return Envelope(env)
#### Output Methods ####
@property
def gml(self):
"Returns the GML representation of the Geometry."
buf = c_char_p()
check_err(lgdal.OGR_G_ExportToGML(self._g, byref(buf)))
return string_at(buf)
@property
def wkt(self):
"Returns the WKT representation of the Geometry."
buf = c_char_p()
check_err(lgdal.OGR_G_ExportToWkt(self._g, byref(buf)))
return string_at(buf)
#### Geometry Methods #### #### Geometry Methods ####
def clone(self): def clone(self):
"Clones this OGR Geometry." "Clones this OGR Geometry."
@ -230,13 +242,13 @@ class OGRGeometry(object):
def transform(self, coord_trans): def transform(self, coord_trans):
"Transforms this Geometry with the given CoordTransform object." "Transforms this Geometry with the given CoordTransform object."
if not isinstance(coord_trans, CoordTransform): if not isinstance(coord_trans, CoordTransform):
raise OGRException, 'CoordTransform object required for transform.' raise OGRException('CoordTransform object required for transform.')
check_err(lgdal.OGR_G_Transform(self._g, coord_trans._ct)) check_err(lgdal.OGR_G_Transform(self._g, coord_trans._ct))
def transform_to(self, srs): def transform_to(self, srs):
"Transforms this Geometry with the given SpatialReference." "Transforms this Geometry with the given SpatialReference."
if not isinstance(srs, SpatialReference): if not isinstance(srs, SpatialReference):
raise OGRException, 'SpatialReference object required for transform_to.' raise OGRException('SpatialReference object required for transform_to.')
check_err(lgdal.OGR_G_TransformTo(self._g, srs._srs)) check_err(lgdal.OGR_G_TransformTo(self._g, srs._srs))
#### Topology Methods #### #### Topology Methods ####
@ -244,7 +256,7 @@ class OGRGeometry(object):
"""A generalized function for topology operations, takes a GDAL function and """A generalized function for topology operations, takes a GDAL function and
the other geometry to perform the operation on.""" the other geometry to perform the operation on."""
if not isinstance(other, OGRGeometry): if not isinstance(other, OGRGeometry):
raise OGRException, 'Must use another OGRGeometry object for topology operations!' raise OGRException('Must use another OGRGeometry object for topology operations!')
# Calling the passed-in topology function with the other geometry # Calling the passed-in topology function with the other geometry
status = topo_func(self._g, other._g) status = topo_func(self._g, other._g)
@ -368,7 +380,7 @@ class LineString(OGRGeometry):
elif self.coord_dim == 3: elif self.coord_dim == 3:
return (x.value, y.value, z.value) return (x.value, y.value, z.value)
else: else:
raise OGRIndexError, 'index out of range: %s' % str(index) raise OGRIndexError('index out of range: %s' % str(index))
def __iter__(self): def __iter__(self):
"Iterates over each point in the LineString." "Iterates over each point in the LineString."
@ -401,9 +413,9 @@ class Polygon(OGRGeometry):
def __getitem__(self, index): def __getitem__(self, index):
"Gets the ring at the specified index." "Gets the ring at the specified index."
if index < 0 or index >= self.geom_count: if index < 0 or index >= self.geom_count:
raise OGRIndexError, 'index out of range: %s' % str(index) raise OGRIndexError('index out of range: %s' % str(index))
else: else:
return OGRGeometry(lgdal.OGR_G_Clone(lgdal.OGR_G_GetGeometryRef(self._g, c_int(index)))) return OGRGeometry(lgdal.OGR_G_Clone(lgdal.OGR_G_GetGeometryRef(self._g, c_int(index))), self.srs)
# Polygon Properties # Polygon Properties
@property @property
@ -437,9 +449,9 @@ class GeometryCollection(OGRGeometry):
def __getitem__(self, index): def __getitem__(self, index):
"Gets the Geometry at the specified index." "Gets the Geometry at the specified index."
if index < 0 or index >= self.geom_count: if index < 0 or index >= self.geom_count:
raise OGRIndexError, 'index out of range: %s' % str(index) raise OGRIndexError('index out of range: %s' % str(index))
else: else:
return OGRGeometry(lgdal.OGR_G_Clone(lgdal.OGR_G_GetGeometryRef(self._g, c_int(index)))) return OGRGeometry(lgdal.OGR_G_Clone(lgdal.OGR_G_GetGeometryRef(self._g, c_int(index))), self.srs)
def __iter__(self): def __iter__(self):
"Iterates over each Geometry." "Iterates over each Geometry."
@ -458,7 +470,7 @@ class GeometryCollection(OGRGeometry):
tmp = OGRGeometry(geom) tmp = OGRGeometry(geom)
ptr = tmp._g ptr = tmp._g
else: else:
raise OGRException, 'Must add an OGRGeometry.' raise OGRException('Must add an OGRGeometry.')
lgdal.OGR_G_AddGeometry(self._g, ptr) lgdal.OGR_G_AddGeometry(self._g, ptr)
@property @property

View File

@ -11,21 +11,21 @@ class OGRGeomType(object):
'LinearRing'] 'LinearRing']
__ogr_int = [1, 2, 3, 4, 5, 6, 7, 101] __ogr_int = [1, 2, 3, 4, 5, 6, 7, 101]
def __init__(self, input): def __init__(self, type_input):
"Figures out the correct OGR Type based upon the input." "Figures out the correct OGR Type based upon the input."
if isinstance(input, OGRGeomType): if isinstance(type_input, OGRGeomType):
self._index = input._index self._index = type_input._index
elif isinstance(input, StringType): elif isinstance(type_input, StringType):
idx = self._has_str(self.__ogr_str, input) idx = self._has_str(self.__ogr_str, type_input)
if idx == None: if idx == None:
raise OGRException, 'Invalid OGR String Type "%s"' % input raise OGRException('Invalid OGR String Type "%s"' % type_input)
self._index = idx self._index = idx
elif isinstance(input, int): elif isinstance(type_input, int):
if not input in self.__ogr_int: if not type_input in self.__ogr_int:
raise OGRException, 'Invalid OGR Integer Type: %d' % input raise OGRException('Invalid OGR Integer Type: %d' % type_input)
self._index = self.__ogr_int.index(input) self._index = self.__ogr_int.index(type_input)
else: else:
raise TypeError, 'Invalid OGR Input type given!' raise TypeError('Invalid OGR Input type given!')
def __str__(self): def __str__(self):
"Returns a short-hand string form of the OGR Geometry type." "Returns a short-hand string form of the OGR Geometry type."
@ -44,7 +44,7 @@ class OGRGeomType(object):
if not other in self.__ogr_int: return False if not other in self.__ogr_int: return False
return self.__ogr_int.index(other) == self._index return self.__ogr_int.index(other) == self._index
else: else:
raise TypeError, 'Cannot compare with type: %s' % str(type(other)) raise TypeError('Cannot compare with type: %s' % str(type(other)))
def _has_str(self, arr, s): def _has_str(self, arr, s):
"Case-insensitive search of the string array for the given pattern." "Case-insensitive search of the string array for the given pattern."

View File

@ -79,7 +79,7 @@ class SpatialReference(object):
_epsg_regex = re.compile('^EPSG:(?P<epsg>\d+)$', re.I) _epsg_regex = re.compile('^EPSG:(?P<epsg>\d+)$', re.I)
#### Python 'magic' routines #### #### Python 'magic' routines ####
def __init__(self, input='', srs_type='wkt'): def __init__(self, srs_input='', srs_type='wkt'):
"Creates a spatial reference object from the given OGC Well Known Text (WKT)." "Creates a spatial reference object from the given OGC Well Known Text (WKT)."
self._srs = 0 # Initially NULL self._srs = 0 # Initially NULL
@ -87,45 +87,46 @@ class SpatialReference(object):
# Creating an initial empty string buffer. # Creating an initial empty string buffer.
buf = c_char_p('') buf = c_char_p('')
if isinstance(input, UnicodeType): # Encoding to ASCII if unicode passed in.
input = input.encode('ascii') if isinstance(srs_input, UnicodeType):
srs_input = srs_input.encode('ascii')
if isinstance(input, StringType): if isinstance(srs_input, StringType):
# Is this an EPSG well known name? # Is this an EPSG well known name?
m = self._epsg_regex.match(input) m = self._epsg_regex.match(srs_input)
if m: if m:
srs_type = 'epsg' srs_type = 'epsg'
input = int(m.group('epsg')) srs_input = int(m.group('epsg'))
# Is this a short-hand well known name? # Is this a short-hand well known name?
elif input in self._well_known: elif srs_input in self._well_known:
srs_type = 'epsg' srs_type = 'epsg'
input = self._well_known[input] srs_input = self._well_known[srs_input]
elif srs_type == 'proj': elif srs_type == 'proj':
pass pass
else: else:
buf = c_char_p(input) buf = c_char_p(srs_input)
elif isinstance(input, int): elif isinstance(srs_input, int):
if srs_type == 'wkt': srs_type = 'epsg' # want to try epsg if only integer provided if srs_type == 'wkt': srs_type = 'epsg' # want to try epsg if only integer provided
if srs_type not in ('epsg', 'ogr'): if srs_type not in ('epsg', 'ogr'):
raise SRSException, 'Integer input requires SRS type of "ogr" or "epsg".' raise SRSException('Integer input requires SRS type of "ogr" or "epsg".')
else: else:
raise TypeError, 'Invalid SRS type "%s"' % srs_type raise TypeError('Invalid SRS type "%s"' % srs_type)
# Calling OSRNewSpatialReference with the string buffer. # Calling OSRNewSpatialReference with the string buffer.
if srs_type == 'ogr': if srs_type == 'ogr':
srs = input # Input is OGR pointer srs = srs_input # SRS input is OGR pointer
else: else:
srs = lgdal.OSRNewSpatialReference(buf) srs = lgdal.OSRNewSpatialReference(buf)
# If the pointer is NULL, throw an exception. # If the pointer is NULL, throw an exception.
if not srs: if not srs:
raise SRSException, 'Could not create spatial reference from WKT! (%s)' % input raise SRSException('Could not create spatial reference from: %s' % srs_input)
else: else:
self._srs = srs self._srs = srs
# Post-processing if in PROJ.4 or EPSG formats. # Post-processing if in PROJ.4 or EPSG formats.
if srs_type == 'proj': self.import_proj(input) if srs_type == 'proj': self.import_proj(srs_input)
elif srs_type == 'epsg': self.import_epsg(input) elif srs_type == 'epsg': self.import_epsg(srs_input)
def __del__(self): def __del__(self):
"Destroys this spatial reference." "Destroys this spatial reference."
@ -142,6 +143,14 @@ class SpatialReference(object):
else: else:
return self.attr_value(target) return self.attr_value(target)
def __nonzero__(self):
"Returns True if this SpatialReference object is valid."
try:
self.validate()
return True
except OGRException:
return False
def __str__(self): def __str__(self):
"The string representation uses 'pretty' WKT." "The string representation uses 'pretty' WKT."
return self.pretty_wkt return self.pretty_wkt
@ -189,6 +198,14 @@ class SpatialReference(object):
elif self.local: return self.attr_value('LOCAL_CS') elif self.local: return self.attr_value('LOCAL_CS')
else: return None else: return None
@property
def srid(self):
"""
Returns the EPSG SRID of this Spatial Reference, will be None if
if undefined.
"""
return self.srs['AUTHORITY', 1]
#### Unit Properties #### #### Unit Properties ####
def _cache_linear(self): def _cache_linear(self):
"Caches the linear units value and name." "Caches the linear units value and name."
@ -345,10 +362,10 @@ class CoordTransform(object):
"Initializes on a source and target SpatialReference objects." "Initializes on a source and target SpatialReference objects."
self._ct = 0 # Initially NULL self._ct = 0 # Initially NULL
if not isinstance(source, SpatialReference) or not isinstance(target, SpatialReference): if not isinstance(source, SpatialReference) or not isinstance(target, SpatialReference):
raise SRSException, 'source and target must be of type SpatialReference' raise SRSException('source and target must be of type SpatialReference')
ct = lgdal.OCTNewCoordinateTransformation(source._srs, target._srs) ct = lgdal.OCTNewCoordinateTransformation(source._srs, target._srs)
if not ct: if not ct:
raise SRSException, 'could not intialize CoordTransform object' raise SRSException('could not intialize CoordTransform object')
self._ct = ct self._ct = ct
self._srs1_name = source.name self._srs1_name = source.name
self._srs2_name = target.name self._srs2_name = target.name

View File

@ -13,6 +13,7 @@ if HAS_GDAL:
test_suite_names += [ test_suite_names += [
'test_gdal_driver', 'test_gdal_driver',
'test_gdal_ds', 'test_gdal_ds',
'test_gdal_envelope',
'test_gdal_geom', 'test_gdal_geom',
'test_gdal_srs', 'test_gdal_srs',
'test_spatialrefsys', 'test_spatialrefsys',

View File

@ -0,0 +1,24 @@
from unittest import TestSuite, TextTestRunner
# Importing the GDAL test modules.
from django.contrib.gis.tests import \
test_gdal_driver, test_gdal_ds, test_gdal_envelope, \
test_gdal_geom, test_gdal_srs
test_suites = [test_gdal_driver.suite(),
test_gdal_ds.suite(),
test_gdal_envelope.suite(),
test_gdal_geom.suite(),
test_gdal_srs.suite(),
]
def suite():
"Builds a test suite for the GDAL tests."
s = TestSuite()
map(s.addTest, test_suites)
return s
def run(verbosity=1):
"Runs the tests that do not require geographic (GEOS, GDAL, etc.) models."
TextTestRunner(verbosity=verbosity).run(suite())

View File

@ -1,6 +1,5 @@
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, Envelope, OGRException, OGRIndexError
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
@ -48,7 +47,7 @@ class DataSourceTest(unittest.TestCase):
# Making sure indexing works # Making sure indexing works
try: try:
ds[len(ds)] ds[len(ds)]
except IndexError: except OGRIndexError:
pass pass
else: else:
self.fail('Expected an IndexError!') self.fail('Expected an IndexError!')
@ -106,7 +105,9 @@ class DataSourceTest(unittest.TestCase):
# Asserting the string representation, and making sure we get # Asserting the string representation, and making sure we get
# the proper OGR Field instance. # the proper OGR Field instance.
self.assertEqual('%s (%s)' % (k, fld.value), str(fld)) if isinstance(fld, OFTString): fmt = '%s ("%s")'
else: fmt = '%s (%s)'
self.assertEqual(fmt % (k, fld.value), str(fld))
self.assertEqual(True, isinstance(fld, v)) self.assertEqual(True, isinstance(fld, v))
# Testing __iter__ on the Feature # Testing __iter__ on the Feature
@ -136,5 +137,3 @@ def suite():
def run(verbosity=2): def run(verbosity=2):
unittest.TextTestRunner(verbosity=verbosity).run(suite()) unittest.TextTestRunner(verbosity=verbosity).run(suite())

View File

@ -0,0 +1,45 @@
import unittest
from django.contrib.gis.gdal import Envelope, OGRException
class EnvelopeTest(unittest.TestCase):
def test01_init(self):
"Testing Envelope initilization."
e1 = Envelope((0, 0, 5, 5))
e2 = Envelope(0, 0, 5, 5)
e3 = Envelope(0, '0', '5', 5) # Thanks to ww for this
e4 = Envelope(e1._envelope)
self.assertRaises(OGRException, Envelope, (5, 5, 0, 0))
self.assertRaises(OGRException, Envelope, 5, 5, 0, 0)
self.assertRaises(OGRException, Envelope, (0, 0, 5, 5, 3))
self.assertRaises(OGRException, Envelope, ())
self.assertRaises(ValueError, Envelope, 0, 'a', 5, 5)
self.assertRaises(TypeError, Envelope, u'foo')
def test02_properties(self):
"Testing Envelope properties."
e = Envelope(0, 0, 2, 3)
self.assertEqual(0, e.min_x)
self.assertEqual(0, e.min_y)
self.assertEqual(2, e.max_x)
self.assertEqual(3, e.max_y)
self.assertEqual((0, 0), e.ll)
self.assertEqual((2, 3), e.ur)
self.assertEqual((0, 0, 2, 3), e.tuple)
self.assertEqual('POLYGON((0.0 0.0,0.0 3.0,2.0 3.0,2.0 0.0,0.0 0.0))', e.wkt)
self.assertEqual('(0.0, 0.0, 2.0, 3.0)', str(e))
def test03_equivalence(self):
"Testing Envelope equivalence."
e1 = Envelope(0.523, 0.217, 253.23, 523.69)
e2 = Envelope((0.523, 0.217, 253.23, 523.69))
self.assertEqual(e1, e2)
self.assertEqual((0.523, 0.217, 253.23, 523.69), e1)
def suite():
s = unittest.TestSuite()
s.addTest(unittest.makeSuite(EnvelopeTest))
return s
def run(verbosity=2):
unittest.TextTestRunner(verbosity=verbosity).run(suite())

View File

@ -1,5 +1,5 @@
import unittest import unittest
from django.contrib.gis.gdal import OGRGeometry, OGRGeomType, OGRException from django.contrib.gis.gdal import OGRGeometry, OGRGeomType, OGRException, SpatialReference
from geometries import * from geometries import *
class OGRGeomTest(unittest.TestCase): class OGRGeomTest(unittest.TestCase):
@ -130,6 +130,18 @@ class OGRGeomTest(unittest.TestCase):
self.assertEqual(mp.n_p, mpoly.point_count) self.assertEqual(mp.n_p, mpoly.point_count)
self.assertEqual(mp.num_geom, len(mpoly)) self.assertEqual(mp.num_geom, len(mpoly))
def test09_srs(self):
"Testing OGR Geometries with Spatial Reference objects."
for mp in multipolygons:
sr = SpatialReference('WGS84')
mpoly = OGRGeometry(mp.wkt, sr)
self.assertEqual(sr.wkt, mpoly.srs.wkt)
for poly in mpoly:
self.assertEqual(sr.wkt, poly.srs.wkt)
for ring in poly:
self.assertEqual(sr.wkt, ring.srs.wkt)
def suite(): def suite():
s = unittest.TestSuite() s = unittest.TestSuite()
s.addTest(unittest.makeSuite(OGRGeomTest)) s.addTest(unittest.makeSuite(OGRGeomTest))