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

gis: GDAL improvements; added Driver class, improved tests, removed non-existent exception, updated geometries for tests, updated documentation.

git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@5478 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2007-06-15 06:02:04 +00:00
parent b0b7bbced7
commit 48c9f87e1f
14 changed files with 500 additions and 78 deletions

View File

@ -6,12 +6,20 @@ from ctypes import c_char_p, c_int, c_void_p, byref, string_at
from django.contrib.gis.gdal.libgdal import lgdal from django.contrib.gis.gdal.libgdal import lgdal
from django.contrib.gis.gdal.OGRError import OGRException, check_err from django.contrib.gis.gdal.OGRError import OGRException, 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
""" """
DataSource is a wrapper for the OGR Data Source object, which provides DataSource is a wrapper for the OGR Data Source object, which provides
an interface for reading vector geometry data from many different file an interface for reading vector geometry data from many different file
formats (including ESRI shapefiles). formats (including ESRI shapefiles).
When instantiating a DataSource object, use the filename of a
GDAL-supported data source. For example, a SHP file or a
TIGER/Line file from the government.
The ds_driver keyword is used internally when a ctypes pointer
is passed in directly.
Example: Example:
ds = DataSource('/home/foo/bar.shp') ds = DataSource('/home/foo/bar.shp')
for layer in ds: for layer in ds:
@ -22,13 +30,24 @@ from django.contrib.gis.gdal.Layer import Layer
# Getting the 'description' field for the feature. # Getting the 'description' field for the feature.
desc = feature['description'] desc = feature['description']
More documentation forthcoming. # We can also increment through all of the fields
# attached to this feature.
for field in feature:
# Get the name of the field (e.g. 'description')
nm = field.name
# Get the type (integer) of the field, e.g. 0 => OFTInteger
t = field.type
# Returns the value the field; OFTIntegers return ints,
# OFTReal returns floats, all else returns string.
val = field.value
""" """
# 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
# #
# The OGR_DS* routines are relevant here. # The OGR_DS_* routines are relevant here.
class DataSource(object): class DataSource(object):
"Wraps an OGR Data Source object." "Wraps an OGR Data Source object."
@ -36,26 +55,32 @@ class DataSource(object):
_ds = 0 # Initially NULL _ds = 0 # Initially NULL
#### Python 'magic' routines #### #### Python 'magic' routines ####
def __init__(self, ds_file): def __init__(self, ds_input, ds_driver=False):
# 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.OGRRegisterAll(): if not lgdal.OGRGetDriverCount() and not lgdal.OGRRegisterAll():
raise OGRException, 'Could not register all data source drivers!' raise OGRException, 'Could not register all the OGR data source drivers!'
# The data source driver is a void pointer. if isinstance(ds_input, StringType):
ds_driver = c_void_p()
# OGROpen will auto-detect the data source type. # The data source driver is a void pointer.
ds = lgdal.OGROpen(c_char_p(ds_file), c_int(0), byref(ds_driver)) ds_driver = c_void_p()
# OGROpen will auto-detect the data source type.
ds = lgdal.OGROpen(c_char_p(ds_input), c_int(0), byref(ds_driver))
elif isinstance(ds_input, c_void_p) and isinstance(ds_driver, c_void_p):
ds = ds_input
else:
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_file raise OGRException, 'Invalid data source file "%s"' % ds_input
else: else:
self._ds = ds self._ds = ds
self._driver = ds_driver self._driver = Driver(ds_driver)
def __del__(self): def __del__(self):
"This releases the reference to the data source (destroying it if it's the only one)." "This releases the reference to the data source (destroying it if it's the only one)."
@ -83,13 +108,13 @@ class DataSource(object):
def __str__(self): def __str__(self):
"Returns OGR GetName and Driver for the Data Source." "Returns OGR GetName and Driver for the Data Source."
return '%s (%s)' % (self.name, self.driver) return '%s (%s)' % (self.name, str(self.driver))
#### DataSource Properties #### #### DataSource Properties ####
@property @property
def driver(self): def driver(self):
"Returns the name of the data source driver." "Returns the Driver object for this Data Source."
return string_at(lgdal.OGR_Dr_GetName(self._driver)) return self._driver
@property @property
def layer_count(self): def layer_count(self):

View File

@ -0,0 +1,86 @@
# types and ctypes
from types import StringType
from ctypes import c_char_p, c_int, c_void_p, byref, string_at
# The GDAL C library, OGR exceptions, and the Layer object.
from django.contrib.gis.gdal.libgdal import lgdal
from django.contrib.gis.gdal.OGRError import OGRException
# For more information, see the OGR C API source code:
# http://www.gdal.org/ogr/ogr__api_8h.html
#
# The OGR_Dr_* routines are relevant here.
class Driver(object):
"Wraps an OGR Data Source Driver."
_dr = 0 # Initially NULL
# Case-insensitive aliases for OGR Drivers.
_alias = {'esri' : 'ESRI Shapefile',
'shp' : 'ESRI Shapefile',
'shape' : 'ESRI Shapefile',
'tiger' : 'TIGER',
'tiger/line' : 'TIGER',
}
def __init__(self, input, ptr=False):
"Initializes an OGR driver on either a string or integer input."
if isinstance(input, StringType):
# If a string name of the driver was passed in
self._register()
# Checking the alias dictionary (case-insensitive) to see if an alias
# exists for the given driver.
if input.lower() in self._alias:
name = c_char_p(self._alias[input.lower()])
else:
name = c_char_p(input)
# Attempting to get the OGR driver by the string name.
dr = lgdal.OGRGetDriverByName(name)
elif isinstance(input, int):
self._register()
dr = lgdal.OGRGetDriver(c_int(input))
elif isinstance(input, c_void_p):
dr = input
else:
raise OGRException, 'Unrecognized input type for OGR Driver: %s' % str(type(input))
# Making sure we get a valid pointer to the OGR Driver
if not dr:
raise OGRException, 'Could not initialize OGR Driver on input: %s' % str(input)
self._dr = dr
def __str__(self):
"Returns the string name of the OGR Driver."
return string_at(lgdal.OGR_Dr_GetName(self._dr))
def _register(self):
"Attempts to register all the data source drivers."
# Only register all if the driver count is 0 (or else all drivers
# will be registered over and over again)
if not self.driver_count and not lgdal.OGRRegisterAll():
raise OGRException, 'Could not register all the OGR data source drivers!'
# Driver properties
@property
def driver_count(self):
"Returns the number of OGR data source drivers registered."
return lgdal.OGRGetDriverCount()
def create_ds(self, **kwargs):
"Creates a data source using the keyword args as name value options."
raise NotImplementedError
# Getting the options string
#options = ''
#n_opts = len(kwargs)
#for i in xrange(n_opts):
# options += '%s=%s' % (str(k), str(v))
# if i < n_opts-1: options += ','
#opts = c_char_p(options)

View File

@ -1,5 +1,5 @@
# types and ctypes # types and ctypes
import types from types import StringType
from ctypes import c_char_p, c_int, string_at from ctypes import c_char_p, c_int, string_at
# The GDAL C library, OGR exception, and the Field object # The GDAL C library, OGR exception, and the Field object
@ -31,7 +31,7 @@ class Feature(object):
def __getitem__(self, index): def __getitem__(self, index):
"Gets the Field at the specified index." "Gets the Field at the specified index."
if isinstance(index, types.StringType): if isinstance(index, StringType):
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:

View File

@ -8,7 +8,7 @@ from django.contrib.gis.gdal.OGRError import OGRException
# #
# The OGR_Fld_* routines are relevant here. # The OGR_Fld_* routines are relevant here.
class Field(object): class Field(object):
"A class that wraps an OGR Field." "A class that wraps an OGR Field, needs to be instantiated from a Feature object."
_fld = 0 # Initially NULL _fld = 0 # Initially NULL

View File

@ -1,14 +1,53 @@
# types & ctypes # types & ctypes
from types import StringType from types import IntType, StringType
from ctypes import \ from ctypes import byref, string_at, c_char_p, c_double, c_int, c_void_p
byref, string_at, create_string_buffer, POINTER, \
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.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
"""
The OGRGeometry is a wrapper for using the OGR Geometry class
(see http://www.gdal.org/ogr/classOGRGeometry.html). OGRGeometry
may be instantiated when reading geometries from OGR Data Sources
(e.g. SHP files), or when given OGC WKT (a string).
While the 'full' API is not present yet, the API is "pythonic" unlike
the traditional and "next-generation" OGR Python bindings. One major
advantage OGR Geometries have over their GEOS counterparts is support
for spatial reference systems and their transformation.
Example:
>>> from django.contrib.gis.gdal import OGRGeometry, OGRGeomType, SpatialReference
>>> wkt1, wkt2 = 'POINT(-90 30)', 'POLYGON((0 0, 5 0, 5 5, 0 5)'
>>> pnt = OGRGeometry(wkt1)
>>> print pnt
POINT (-90 30)
>>> mpnt = OGRGeometry(OGRGeomType('MultiPoint'), SpatialReference('WGS84'))
>>> mpnt.add(wkt1)
>>> mpnt.add(wkt1)
>>> print mpnt
MULTIPOINT (-90 30,-90 30)
>>> print mpnt.srs.name
WGS 84
>>> print mpnt.srs.proj
+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs
>>> mpnt.transform_to(SpatialReference('NAD27'))
>>> print mpnt.proj
+proj=longlat +ellps=clrk66 +datum=NAD27 +no_defs
>>> print mpnt
MULTIPOINT (-89.999930378602485 29.999797886557641,-89.999930378602485 29.999797886557641)
The OGRGeomType class is to make it easy to specify an OGR geometry type:
>>> from django.contrib.gis.gdal import OGRGeomType
>>> gt1 = OGRGeomType(3) # Using an integer for the type
>>> gt2 = OGRGeomType('Polygon') # Using a string
>>> gt3 = OGRGeomType('POLYGON') # It's case-insensitive
>>> print gt1 == 3, gt1 == 'Polygon' # Equivalence works w/non-OGRGeomType objects
True
"""
# 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
# #
@ -29,10 +68,10 @@ class OGRGeomType(object):
"Encapulates OGR Geometry Types." "Encapulates OGR Geometry Types."
# Ordered array of acceptable strings and their corresponding OGRwkbGeometryType # Ordered array of acceptable strings and their corresponding OGRwkbGeometryType
__ogr_str = ['Unknown', 'Point', 'LineString', 'Polygon', 'MultiPoint', __ogr_str = ['Point', 'LineString', 'Polygon', 'MultiPoint',
'MultiLineString', 'MultiPolygon', 'GeometryCollection', 'MultiLineString', 'MultiPolygon', 'GeometryCollection',
'None', 'LinearRing'] 'LinearRing']
__ogr_int = [0, 1, 2, 3, 4, 5, 6, 7, 100, 101] __ogr_int = [1, 2, 3, 4, 5, 6, 7, 101]
def __init__(self, input): def __init__(self, input):
"Figures out the correct OGR Type based upon the input." "Figures out the correct OGR Type based upon the input."
@ -40,7 +79,7 @@ class OGRGeomType(object):
self._index = input._index self._index = input._index
elif isinstance(input, StringType): elif isinstance(input, StringType):
idx = self._has_str(self.__ogr_str, input) idx = self._has_str(self.__ogr_str, input)
if not idx: if idx == None:
raise OGRException, 'Invalid OGR String Type "%s"' % input raise OGRException, 'Invalid OGR String Type "%s"' % input
self._index = idx self._index = idx
elif isinstance(input, int): elif isinstance(input, int):
@ -61,7 +100,7 @@ class OGRGeomType(object):
return self._index == other._index return self._index == other._index
elif isinstance(other, StringType): elif isinstance(other, StringType):
idx = self._has_str(self.__ogr_str, other) idx = self._has_str(self.__ogr_str, other)
if idx: return self._index == idx if not (idx == None): return self._index == idx
return False return False
elif isinstance(other, int): elif isinstance(other, int):
if not other in self.__ogr_int: return False if not other in self.__ogr_int: return False
@ -70,9 +109,10 @@ class OGRGeomType(object):
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):
slow = s.lower() "Case-insensitive search of the string array for the given pattern."
s_low = s.lower()
for i in xrange(len(arr)): for i in xrange(len(arr)):
if slow == arr[i].lower(): return i if s_low == arr[i].lower(): return i
return None return None
@property @property
@ -99,18 +139,15 @@ class OGRGeometry(object):
"Initializes Geometry on either WKT or an OGR pointer as input." "Initializes Geometry on either WKT or an OGR pointer as input."
if isinstance(input, StringType): if isinstance(input, StringType):
# Getting the spatial reference
self._init_srs(srs)
# First, trying the input as WKT # First, trying the input as WKT
buf = c_char_p(input) buf = c_char_p(input)
g = c_void_p() g = c_void_p()
# Getting the spatial
if not isinstance(srs, SpatialReference):
s = SpatialReference() # creating an empty spatial reference
else:
s = srs.clone() # cloning the given spatial reference
try: try:
check_err(lgdal.OGR_G_CreateFromWkt(byref(buf), s._srs, byref(g))) check_err(lgdal.OGR_G_CreateFromWkt(byref(buf), self._s._srs, byref(g)))
except OGRException, msg: except OGRException, msg:
try: try:
ogr_t = OGRGeomType(input) # Seeing if the input is a valid short-hand string ogr_t = OGRGeomType(input) # Seeing if the input is a valid short-hand string
@ -118,8 +155,10 @@ class OGRGeometry(object):
except: except:
raise OGRException, 'Could not initialize on WKT "%s"' % input raise OGRException, 'Could not initialize on WKT "%s"' % input
elif isinstance(input, OGRGeomType): elif isinstance(input, OGRGeomType):
self._init_srs(srs)
g = lgdal.OGR_G_CreateGeometry(input.num) g = lgdal.OGR_G_CreateGeometry(input.num)
elif isinstance(input, int): lgdal.OGR_G_AssignSpatialReference(g, self._s._srs)
elif isinstance(input, IntType):
# OGR Pointer (integer) was the input # OGR Pointer (integer) was the input
g = input g = input
else: else:
@ -133,13 +172,24 @@ class OGRGeometry(object):
# Setting the class depending upon the OGR Geometry Type # Setting the class depending upon the OGR Geometry Type
self.__class__ = GEO_CLASSES[self.geom_type.num] self.__class__ = GEO_CLASSES[self.geom_type.num]
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
def __add__(self, other):
"Returns the union of the two geometries."
return self.union(other)
def __del__(self): def __del__(self):
"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 __eq__(self, other): def __eq__(self, other):
"Is this Geometry equal to the other?" "Is this Geometry equal to the other?"
return lgdal.OGR_G_Equals(self._g, other._g) return self.equals(other)
def __str__(self): def __str__(self):
"WKT is used for the string representation." "WKT is used for the string representation."
@ -163,9 +213,14 @@ class OGRGeometry(object):
@property @property
def point_count(self): def point_count(self):
"The number of Points in this Geometry." "Returns the number of Points in this Geometry."
return lgdal.OGR_G_GetPointCount(self._g) return lgdal.OGR_G_GetPointCount(self._g)
@property
def num_coords(self):
"Returns the number of Points in this Geometry."
return self.point_count
@property @property
def srs(self): def srs(self):
"Returns the Spatial Reference for this Geometry." "Returns the Spatial Reference for this Geometry."
@ -187,12 +242,27 @@ class OGRGeometry(object):
buf = c_char_p() buf = c_char_p()
check_err(lgdal.OGR_G_ExportToWkt(self._g, byref(buf))) check_err(lgdal.OGR_G_ExportToWkt(self._g, byref(buf)))
return string_at(buf) return string_at(buf)
@property
def area(self):
"Returns the area for a LinearRing, Polygon, or MultiPolygon; 0 otherwise."
a = lgdal.OGR_G_GetArea(self._g)
return a.value
#### Geometry Methods #### #### Geometry Methods ####
def clone(self): def clone(self):
"Clones this OGR Geometry." "Clones this OGR Geometry."
return OGRGeometry(lgdal.OGR_G_Clone(self._g)) return OGRGeometry(lgdal.OGR_G_Clone(self._g))
def close_rings(self):
"""If there are any rings within this geometry that have not been
closed, this routine will do so by adding the starting point at the
end."""
# Closing the open rings.
lgdal.OGR_G_CloseRings(self._g)
# This "fixes" a GDAL bug. See http://trac.osgeo.org/gdal/ticket/1673
foo = self.wkt
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):
@ -205,6 +275,78 @@ class OGRGeometry(object):
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 ####
def _topology(self, topo_func, other):
"""A generalized function for topology operations, takes a GDAL function and
the other geometry to perform the operation on."""
if not isinstance(other, OGRGeometry):
raise OGRException, 'Must use another OGRGeometry object for topology operations!'
# Calling the passed-in topology function with the other geometry
status = topo_func(self._g, other._g)
# Returning based on the status code (an integer)
if status: return True
else: return False
def intersects(self, other):
"Returns True if this geometry intersects with the other."
return self._topology(lgdal.OGR_G_Intersects, other)
def equals(self, other):
"Returns True if this geometry is equivalent to the other."
return self._topology(lgdal.OGR_G_Equals, other)
def disjoint(self, other):
"Returns True if this geometry and the other are spatially disjoint."
return self._topology(lgdal.OGR_G_Disjoint, other)
def touches(self, other):
"Returns True if this geometry touches the other."
return self._topology(lgdal.OGR_G_Touches, other)
def crosses(self, other):
"Returns True if this geometry crosses the other."
return self._topology(lgdal.OGR_G_Crosses, other)
def within(self, other):
"Returns True if this geometry is within the other."
return self._topology(lgdal.OGR_G_Within, other)
def contains(self, other):
"Returns True if this geometry contains the other."
return self._topology(lgdal.OGR_G_Contains, other)
def overlaps(self, other):
"Returns True if this geometry overlaps the other."
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 union(self, other):
"""Returns a new geometry consisting of the region which is the union of
this geometry and the other."""
return self._geomgen(lgdal.OGR_G_Union, other)
def difference(self, other):
"""Returns a new geometry consisting of the region which is the difference
of this geometry and the other."""
return self._geomgen(lgdal.OGR_G_Difference, other)
def sym_difference(self, other):
"""Returns a new geometry which is the symmetric difference of this
geometry and the other."""
return self._geomgen(lgdal.OGR_G_SymmetricDifference, other)
def intersection(self, other):
"""Returns a new geometry consisting of the region of intersection of this
geometry and the other."""
return self._geomgen(lgdal.OGR_G_Intersection, other)
# The subclasses for OGR Geometry. # The subclasses for OGR Geometry.
class Point(OGRGeometry): class Point(OGRGeometry):
@ -298,6 +440,20 @@ class Polygon(OGRGeometry):
"Returns a tuple of LinearRing coordinate tuples." "Returns a tuple of LinearRing coordinate tuples."
return tuple(self.__getitem__(i).tuple for i in xrange(self.geom_count)) return tuple(self.__getitem__(i).tuple for i in xrange(self.geom_count))
@property
def point_count(self):
"The number of Points in this Polygon."
# Summing up the number of points in each ring of the Polygon.
return sum([self.__getitem__(i).point_count for i in xrange(self.geom_count)])
@property
def centroid(self):
"Returns the centroid (a Point) of this Polygon."
# The centroid is a Point, create a geometry for this.
p = OGRGeometry(OGRGeomType('Point'))
check_err(lgdal.OGR_G_Centroid(self._g, p._g))
return p
# Geometry Collection base class. # Geometry Collection base class.
class GeometryCollection(OGRGeometry): class GeometryCollection(OGRGeometry):
"The Geometry Collection class." "The Geometry Collection class."
@ -320,9 +476,25 @@ class GeometryCollection(OGRGeometry):
def add(self, geom): def add(self, geom):
"Add the geometry to this Geometry Collection." "Add the geometry to this Geometry Collection."
if not isinstance(geom, OGRGeometry): if isinstance(geom, OGRGeometry):
ptr = geom._g
elif isinstance(geom, StringType):
tmp = OGRGeometry(geom)
ptr = tmp._g
else:
raise OGRException, 'Must add an OGRGeometry.' raise OGRException, 'Must add an OGRGeometry.'
lgdal.OGR_G_AddGeometry(self._g, geom._g) lgdal.OGR_G_AddGeometry(self._g, ptr)
@property
def point_count(self):
"The number of Points in this Geometry Collection."
# Summing up the number of points in each geometry in this collection
return sum([self.__getitem__(i).point_count for i in xrange(self.geom_count)])
@property
def tuple(self):
"Returns a tuple representation of this Geometry Collection."
return tuple(self.__getitem__(i).tuple for i in xrange(self.geom_count))
# Multiple Geometry types. # Multiple Geometry types.
class MultiPoint(GeometryCollection): pass class MultiPoint(GeometryCollection): pass

View File

@ -11,6 +11,36 @@ from django.contrib.gis.gdal.libgdal import lgdal
# Getting the error checking routine and exceptions # Getting the error checking routine and exceptions
from django.contrib.gis.gdal.OGRError import check_err, OGRException, SRSException from django.contrib.gis.gdal.OGRError import check_err, OGRException, SRSException
"""
The Spatial Reference class, represensents OGR Spatial Reference objects.
Example:
>>> from django.contrib.gis.gdal import SpatialReference
>>> srs = SpatialReference('WGS84')
>>> print srs
GEOGCS["WGS 84",
DATUM["WGS_1984",
SPHEROID["WGS 84",6378137,298.257223563,
AUTHORITY["EPSG","7030"]],
TOWGS84[0,0,0,0,0,0,0],
AUTHORITY["EPSG","6326"]],
PRIMEM["Greenwich",0,
AUTHORITY["EPSG","8901"]],
UNIT["degree",0.01745329251994328,
AUTHORITY["EPSG","9122"]],
AUTHORITY["EPSG","4326"]]
>>> print srs.proj
+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs
>>> print srs.ellipsoid
(6378137.0, 6356752.3142451793, 298.25722356300003)
>>> print srs.projected, srs.geographic
False True
>>> srs.import_epsg(32140)
>>> print srs.name
NAD83 / Texas South Central
"""
#### ctypes function prototypes #### #### ctypes function prototypes ####
def ellipsis_func(f): def ellipsis_func(f):
"""Creates a ctypes function prototype for OSR ellipsis property functions, """Creates a ctypes function prototype for OSR ellipsis property functions,
@ -183,7 +213,13 @@ class SpatialReference(object):
self._cache_angular() self._cache_angular()
return self._angular_units return self._angular_units
#### Spheroid/Ellipsis Properties #### #### Spheroid/Ellipsoid Properties ####
@property
def ellipsoid(self):
"""Returns a tuple of the ellipsoid parameters:
(semimajor axis, semiminor axis, and inverse flattening)."""
return (self.semi_major, self.semi_minor, self.inverse_flattening)
@property @property
def semi_major(self): def semi_major(self):
"Gets the Semi Major Axis for this Spatial Reference." "Gets the Semi Major Axis for this Spatial Reference."
@ -267,6 +303,10 @@ class SpatialReference(object):
check_err(lgdal.OSRExportToProj4(self._srs, byref(w))) check_err(lgdal.OSRExportToProj4(self._srs, byref(w)))
return string_at(w) return string_at(w)
def proj4(self):
"Alias for proj()."
return self.proj
@property @property
def xml(self, dialect=''): def xml(self, dialect=''):
"Returns the XML representation of this Spatial Reference." "Returns the XML representation of this Spatial Reference."

View File

@ -1,3 +1,4 @@
from Driver import Driver
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,8 +1,9 @@
import os, sys import os, sys
from ctypes import CDLL from ctypes import CDLL
from django.contrib.gis.gdal.OGRError import OGRException
if os.name == 'nt': if os.name == 'nt':
# Windows NT library # Windows NT shared library
lib_name = 'libgdal-1.dll' lib_name = 'libgdal-1.dll'
elif os.name == 'posix': elif os.name == 'posix':
platform = os.uname()[0] platform = os.uname()[0]
@ -10,13 +11,13 @@ elif os.name == 'posix':
# Linux shared library # Linux shared library
lib_name = 'libgdal.so' lib_name = 'libgdal.so'
elif platform == 'Darwin': elif platform == 'Darwin':
# Mac OSX Shared Library # Mac OSX shared library
lib_name = 'libgdal.dylib' lib_name = 'libgdal.dylib'
else: else:
raise GDALException, 'Unknown POSIX platform "%s"' % platform raise OGRException, 'Unknown POSIX platform "%s"' % platform
else: else:
raise GDALException, 'Unsupported OS "%s"' % os.name raise OGRException, 'Unsupported OS "%s"' % os.name
# The GDAL C library # This loads the GDAL/OGR C library
lgdal = CDLL(lib_name) lgdal = CDLL(lib_name)

View File

@ -1,14 +1,15 @@
from unittest import TestSuite, makeSuite, TextTestRunner from unittest import TestSuite, makeSuite, TextTestRunner
import test_geos, test_gdal_ds, test_gdal_srs, test_gdal_geom, test_spatialrefsys import test_geos, test_gdal_ds, test_gdal_driver, test_gdal_srs, test_gdal_geom, test_spatialrefsys
def suite(): def suite():
s = TestSuite() s = TestSuite()
s.addTest(test_geos.suite()) s.addTest(test_geos.suite())
s.addTest(test_gdal_ds.suite()) s.addTest(test_gdal_ds.suite())
s.addTest(test_gdal_driver.suite())
s.addTest(test_gdal_srs.suite()) s.addTest(test_gdal_srs.suite())
s.addTest(test_gdal_geom.suite()) s.addTest(test_gdal_geom.suite())
s.addTest(test_spatialrefsys.suite()) s.addTest(test_spatialrefsys.suite())
return s return s
def run(verbosity=2): def run(verbosity=1):
TextTestRunner(verbosity=verbosity).run(suite()) TextTestRunner(verbosity=verbosity).run(suite())

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,40 @@
import os, os.path, unittest
from django.contrib.gis.gdal import Driver, OGRException
valid_drivers = ('ESRI Shapefile', 'MapInfo File', 'TIGER', 'S57', 'DGN',
'Memory', 'CSV', 'GML', 'KML')
invalid_drivers = ('Foo baz', 'clucka', 'ESRI Shp')
aliases = {'eSrI' : 'ESRI Shapefile',
'TigER/linE' : 'TIGER',
'SHAPE' : 'ESRI Shapefile',
'sHp' : 'ESRI Shapefile',
}
class DriverTest(unittest.TestCase):
def test01_valid_driver(self):
"Testing valid OGR Data Source Drivers."
for d in valid_drivers:
dr = Driver(d)
self.assertEqual(d, str(dr))
def test02_invalid_driver(self):
"Testing invalid OGR Data Source Drivers."
for i in invalid_drivers:
self.assertRaises(OGRException, Driver, i)
def test03_aliases(self):
"Testing driver aliases."
for alias, full_name in aliases.items():
dr = Driver(alias)
self.assertEqual(full_name, str(dr))
def suite():
s = unittest.TestSuite()
s.addTest(unittest.makeSuite(DriverTest))
return s
def run(verbosity=2):
unittest.TextTestRunner(verbosity=verbosity).run(suite())

View File

@ -40,7 +40,7 @@ class DataSourceTest(unittest.TestCase):
self.assertEqual(source.ds, ds.name) self.assertEqual(source.ds, ds.name)
# Making sure the driver name matches up # Making sure the driver name matches up
self.assertEqual('ESRI Shapefile', ds.driver) self.assertEqual('ESRI Shapefile', str(ds.driver))
# Making sure indexing works # Making sure indexing works
try: try:

View File

@ -10,11 +10,11 @@ class OGRGeomTest(unittest.TestCase):
# OGRGeomType should initialize on all these inputs. # OGRGeomType should initialize on all these inputs.
try: try:
g = OGRGeomType(0)
g = OGRGeomType(1) g = OGRGeomType(1)
g = OGRGeomType(7) g = OGRGeomType(7)
g = OGRGeomType('point') g = OGRGeomType('point')
g = OGRGeomType('GeometrycollectioN') g = OGRGeomType('GeometrycollectioN')
g = OGRGeomType('LINearrING')
except: except:
self.fail('Could not create an OGRGeomType object!') self.fail('Could not create an OGRGeomType object!')
@ -42,15 +42,54 @@ class OGRGeomTest(unittest.TestCase):
for p in points: for p in points:
if not hasattr(p, 'z'): # No 3D if not hasattr(p, 'z'): # No 3D
pnt = OGRGeometry(p.wkt) pnt = OGRGeometry(p.wkt)
self.assertEqual(pnt.geom_type, 1) self.assertEqual(1, pnt.geom_type)
self.assertEqual('POINT', pnt.geom_name)
self.assertEqual(p.x, pnt.x) self.assertEqual(p.x, pnt.x)
self.assertEqual(p.y, pnt.y) self.assertEqual(p.y, pnt.y)
self.assertEqual((p.x, p.y), pnt.tuple) self.assertEqual((p.x, p.y), pnt.tuple)
def test03_polygons(self): def test03_multipoints(self):
"Testing MultiPoint objects."
for mp in multipoints:
mgeom1 = OGRGeometry(mp.wkt) # First one from WKT
self.assertEqual(4, mgeom1.geom_type)
self.assertEqual('MULTIPOINT', mgeom1.geom_name)
mgeom2 = OGRGeometry('MULTIPOINT') # Creating empty multipoint
mgeom3 = OGRGeometry('MULTIPOINT')
for g in mgeom1:
mgeom2.add(g) # adding each point from the multipoints
mgeom3.add(g.wkt) # should take WKT as well
self.assertEqual(mgeom1, mgeom2) # they should equal
self.assertEqual(mgeom1, mgeom3)
self.assertEqual(mp.points, mgeom2.tuple)
self.assertEqual(mp.n_p, mgeom2.point_count)
def test04_linestring(self):
"Testing LineString objects."
for ls in linestrings:
linestr = OGRGeometry(ls.wkt)
self.assertEqual(2, linestr.geom_type)
self.assertEqual('LINESTRING', linestr.geom_name)
self.assertEqual(ls.n_p, linestr.point_count)
self.assertEqual(ls.tup, linestr.tuple)
def test05_multilinestring(self):
"Testing MultiLineString objects."
for mls in multilinestrings:
mlinestr = OGRGeometry(mls.wkt)
self.assertEqual(5, mlinestr.geom_type)
self.assertEqual('MULTILINESTRING', mlinestr.geom_name)
self.assertEqual(mls.n_p, mlinestr.point_count)
self.assertEqual(mls.tup, mlinestr.tuple)
def test06_polygons(self):
"Testing Polygon objects." "Testing Polygon objects."
for p in polygons: for p in polygons:
poly = OGRGeometry(p.wkt) poly = OGRGeometry(p.wkt)
self.assertEqual(3, poly.geom_type)
self.assertEqual('POLYGON', poly.geom_name)
self.assertEqual(p.n_p, poly.point_count)
first = True first = True
for r in poly: for r in poly:
if first and p.ext_ring_cs: if first and p.ext_ring_cs:
@ -60,16 +99,35 @@ class OGRGeomTest(unittest.TestCase):
self.assertEqual(len(p.ext_ring_cs), r.point_count) self.assertEqual(len(p.ext_ring_cs), r.point_count)
self.assertEqual(p.ext_ring_cs, r.tuple) self.assertEqual(p.ext_ring_cs, r.tuple)
def test04_multipoints(self): def test07_closepolygons(self):
"Testing MultiPoint objects." "Testing closing Polygon objects."
# Both rings in this geometry are not closed.
poly = OGRGeometry('POLYGON((0 0, 5 0, 5 5, 0 5), (1 1, 2 1, 2 2, 2 1))')
self.assertEqual(8, poly.point_count)
try:
c = poly.centroid
except OGRException:
# Should raise an OGR exception, rings are not closed
pass
else:
self.fail('Should have raised an OGRException!')
# Closing the rings
poly.close_rings()
self.assertEqual(10, poly.point_count) # Two closing points should've been added
self.assertEqual(OGRGeometry('POINT(2.5 2.5)'), poly.centroid)
def test08_multipolygons(self):
"Testing MultiPolygon objects."
for mp in multipolygons:
mpoly = OGRGeometry(mp.wkt)
self.assertEqual(6, mpoly.geom_type)
self.assertEqual('MULTIPOLYGON', mpoly.geom_name)
if mp.valid:
self.assertEqual(mp.n_p, mpoly.point_count)
self.assertEqual(mp.num_geom, len(mpoly))
for mp in multipoints:
mgeom1 = OGRGeometry(mp.wkt) # First one from WKT
mgeom2 = OGRGeometry('MULTIPOINT') # Creating empty multipoint
for g in mgeom1:
mgeom2.add(g) # adding each point from the multipoint
self.assertEqual(mgeom1, mgeom2) # they should equal
def suite(): def suite():
s = unittest.TestSuite() s = unittest.TestSuite()
s.addTest(unittest.makeSuite(OGRGeomTest)) s.addTest(unittest.makeSuite(OGRGeomTest))

View File

@ -115,8 +115,9 @@ class GeosTest2(unittest.TestCase):
self.assertEqual(mp.valid, mpoly.valid) self.assertEqual(mp.valid, mpoly.valid)
if mp.valid: if mp.valid:
self.assertEqual(mp.n_p, mpoly.num_geom) self.assertEqual(mp.num_geom, mpoly.num_geom)
self.assertEqual(mp.n_p, len(mpoly)) self.assertEqual(mp.n_p, mpoly.num_coords)
self.assertEqual(mp.num_geom, len(mpoly))
for p in mpoly: for p in mpoly:
self.assertEqual(p.geom_type, 'Polygon') self.assertEqual(p.geom_type, 'Polygon')
self.assertEqual(p.geom_typeid, 3) self.assertEqual(p.geom_typeid, 3)