mirror of
https://github.com/django/django.git
synced 2025-07-04 01:39:20 +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:
parent
b0b7bbced7
commit
48c9f87e1f
@ -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.OGRError import OGRException, check_err
|
||||
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
|
||||
an interface for reading vector geometry data from many different file
|
||||
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:
|
||||
ds = DataSource('/home/foo/bar.shp')
|
||||
for layer in ds:
|
||||
@ -22,13 +30,24 @@ from django.contrib.gis.gdal.Layer import Layer
|
||||
# Getting the 'description' field for the feature.
|
||||
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:
|
||||
# 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):
|
||||
"Wraps an OGR Data Source object."
|
||||
@ -36,26 +55,32 @@ class DataSource(object):
|
||||
_ds = 0 # Initially NULL
|
||||
|
||||
#### 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
|
||||
# _before_ we try to open up a data source.
|
||||
if not lgdal.OGRRegisterAll():
|
||||
raise OGRException, 'Could not register all data source drivers!'
|
||||
if not lgdal.OGRGetDriverCount() and not lgdal.OGRRegisterAll():
|
||||
raise OGRException, 'Could not register all the OGR data source drivers!'
|
||||
|
||||
# The data source driver is a void pointer.
|
||||
ds_driver = c_void_p()
|
||||
if isinstance(ds_input, StringType):
|
||||
|
||||
# OGROpen will auto-detect the data source type.
|
||||
ds = lgdal.OGROpen(c_char_p(ds_file), c_int(0), byref(ds_driver))
|
||||
# The data source driver is a void pointer.
|
||||
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
|
||||
if not ds:
|
||||
self._ds = False
|
||||
raise OGRException, 'Invalid data source file "%s"' % ds_file
|
||||
raise OGRException, 'Invalid data source file "%s"' % ds_input
|
||||
else:
|
||||
self._ds = ds
|
||||
self._driver = ds_driver
|
||||
self._driver = Driver(ds_driver)
|
||||
|
||||
def __del__(self):
|
||||
"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):
|
||||
"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 ####
|
||||
@property
|
||||
def driver(self):
|
||||
"Returns the name of the data source driver."
|
||||
return string_at(lgdal.OGR_Dr_GetName(self._driver))
|
||||
"Returns the Driver object for this Data Source."
|
||||
return self._driver
|
||||
|
||||
@property
|
||||
def layer_count(self):
|
||||
|
86
django/contrib/gis/gdal/Driver.py
Normal file
86
django/contrib/gis/gdal/Driver.py
Normal 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)
|
||||
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
# types and ctypes
|
||||
import types
|
||||
from types import StringType
|
||||
from ctypes import c_char_p, c_int, string_at
|
||||
|
||||
# The GDAL C library, OGR exception, and the Field object
|
||||
@ -31,7 +31,7 @@ class Feature(object):
|
||||
|
||||
def __getitem__(self, index):
|
||||
"Gets the Field at the specified index."
|
||||
if isinstance(index, types.StringType):
|
||||
if isinstance(index, StringType):
|
||||
i = self.index(index)
|
||||
else:
|
||||
if index < 0 or index > self.num_fields:
|
||||
|
@ -8,7 +8,7 @@ from django.contrib.gis.gdal.OGRError import OGRException
|
||||
#
|
||||
# The OGR_Fld_* routines are relevant here.
|
||||
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
|
||||
|
||||
|
@ -1,14 +1,53 @@
|
||||
# types & ctypes
|
||||
from types import StringType
|
||||
from ctypes import \
|
||||
byref, string_at, create_string_buffer, POINTER, \
|
||||
c_char_p, c_double, c_int, c_void_p
|
||||
from types import IntType, StringType
|
||||
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.OGRError import check_err, OGRException
|
||||
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:
|
||||
# http://www.gdal.org/ogr/ogr__api_8h.html
|
||||
#
|
||||
@ -29,10 +68,10 @@ class OGRGeomType(object):
|
||||
"Encapulates OGR Geometry Types."
|
||||
|
||||
# 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',
|
||||
'None', 'LinearRing']
|
||||
__ogr_int = [0, 1, 2, 3, 4, 5, 6, 7, 100, 101]
|
||||
'LinearRing']
|
||||
__ogr_int = [1, 2, 3, 4, 5, 6, 7, 101]
|
||||
|
||||
def __init__(self, input):
|
||||
"Figures out the correct OGR Type based upon the input."
|
||||
@ -40,7 +79,7 @@ class OGRGeomType(object):
|
||||
self._index = input._index
|
||||
elif isinstance(input, StringType):
|
||||
idx = self._has_str(self.__ogr_str, input)
|
||||
if not idx:
|
||||
if idx == None:
|
||||
raise OGRException, 'Invalid OGR String Type "%s"' % input
|
||||
self._index = idx
|
||||
elif isinstance(input, int):
|
||||
@ -61,7 +100,7 @@ class OGRGeomType(object):
|
||||
return self._index == other._index
|
||||
elif isinstance(other, StringType):
|
||||
idx = self._has_str(self.__ogr_str, other)
|
||||
if idx: return self._index == idx
|
||||
if not (idx == None): return self._index == idx
|
||||
return False
|
||||
elif isinstance(other, int):
|
||||
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))
|
||||
|
||||
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)):
|
||||
if slow == arr[i].lower(): return i
|
||||
if s_low == arr[i].lower(): return i
|
||||
return None
|
||||
|
||||
@property
|
||||
@ -99,18 +139,15 @@ class OGRGeometry(object):
|
||||
"Initializes Geometry on either WKT or an OGR pointer as input."
|
||||
|
||||
if isinstance(input, StringType):
|
||||
# Getting the spatial reference
|
||||
self._init_srs(srs)
|
||||
|
||||
# First, trying the input as WKT
|
||||
buf = c_char_p(input)
|
||||
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:
|
||||
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:
|
||||
try:
|
||||
ogr_t = OGRGeomType(input) # Seeing if the input is a valid short-hand string
|
||||
@ -118,8 +155,10 @@ class OGRGeometry(object):
|
||||
except:
|
||||
raise OGRException, 'Could not initialize on WKT "%s"' % input
|
||||
elif isinstance(input, OGRGeomType):
|
||||
self._init_srs(srs)
|
||||
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
|
||||
g = input
|
||||
else:
|
||||
@ -133,13 +172,24 @@ class OGRGeometry(object):
|
||||
# Setting the class depending upon the OGR Geometry Type
|
||||
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):
|
||||
"Deletes this Geometry."
|
||||
if self._g: lgdal.OGR_G_DestroyGeometry(self._g)
|
||||
|
||||
def __eq__(self, other):
|
||||
"Is this Geometry equal to the other?"
|
||||
return lgdal.OGR_G_Equals(self._g, other._g)
|
||||
return self.equals(other)
|
||||
|
||||
def __str__(self):
|
||||
"WKT is used for the string representation."
|
||||
@ -163,9 +213,14 @@ class OGRGeometry(object):
|
||||
|
||||
@property
|
||||
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)
|
||||
|
||||
@property
|
||||
def num_coords(self):
|
||||
"Returns the number of Points in this Geometry."
|
||||
return self.point_count
|
||||
|
||||
@property
|
||||
def srs(self):
|
||||
"Returns the Spatial Reference for this Geometry."
|
||||
@ -187,12 +242,27 @@ class OGRGeometry(object):
|
||||
buf = c_char_p()
|
||||
check_err(lgdal.OGR_G_ExportToWkt(self._g, byref(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 ####
|
||||
def clone(self):
|
||||
"Clones this OGR Geometry."
|
||||
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):
|
||||
"Transforms this Geometry with the given CoordTransform object."
|
||||
if not isinstance(coord_trans, CoordTransform):
|
||||
@ -205,6 +275,78 @@ class OGRGeometry(object):
|
||||
raise OGRException, 'SpatialReference object required for transform_to.'
|
||||
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.
|
||||
class Point(OGRGeometry):
|
||||
|
||||
@ -298,6 +440,20 @@ class Polygon(OGRGeometry):
|
||||
"Returns a tuple of LinearRing coordinate tuples."
|
||||
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.
|
||||
class GeometryCollection(OGRGeometry):
|
||||
"The Geometry Collection class."
|
||||
@ -320,9 +476,25 @@ class GeometryCollection(OGRGeometry):
|
||||
|
||||
def add(self, geom):
|
||||
"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.'
|
||||
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.
|
||||
class MultiPoint(GeometryCollection): pass
|
||||
|
@ -11,6 +11,36 @@ from django.contrib.gis.gdal.libgdal import lgdal
|
||||
# Getting the error checking routine and exceptions
|
||||
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 ####
|
||||
def ellipsis_func(f):
|
||||
"""Creates a ctypes function prototype for OSR ellipsis property functions,
|
||||
@ -183,7 +213,13 @@ class SpatialReference(object):
|
||||
self._cache_angular()
|
||||
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
|
||||
def semi_major(self):
|
||||
"Gets the Semi Major Axis for this Spatial Reference."
|
||||
@ -267,6 +303,10 @@ class SpatialReference(object):
|
||||
check_err(lgdal.OSRExportToProj4(self._srs, byref(w)))
|
||||
return string_at(w)
|
||||
|
||||
def proj4(self):
|
||||
"Alias for proj()."
|
||||
return self.proj
|
||||
|
||||
@property
|
||||
def xml(self, dialect=''):
|
||||
"Returns the XML representation of this Spatial Reference."
|
||||
|
@ -1,3 +1,4 @@
|
||||
from Driver import Driver
|
||||
from DataSource import DataSource
|
||||
from SpatialReference import SpatialReference, CoordTransform
|
||||
from OGRGeometry import OGRGeometry, OGRGeomType
|
||||
|
@ -1,8 +1,9 @@
|
||||
import os, sys
|
||||
from ctypes import CDLL
|
||||
from django.contrib.gis.gdal.OGRError import OGRException
|
||||
|
||||
if os.name == 'nt':
|
||||
# Windows NT library
|
||||
# Windows NT shared library
|
||||
lib_name = 'libgdal-1.dll'
|
||||
elif os.name == 'posix':
|
||||
platform = os.uname()[0]
|
||||
@ -10,13 +11,13 @@ elif os.name == 'posix':
|
||||
# Linux shared library
|
||||
lib_name = 'libgdal.so'
|
||||
elif platform == 'Darwin':
|
||||
# Mac OSX Shared Library
|
||||
# Mac OSX shared library
|
||||
lib_name = 'libgdal.dylib'
|
||||
else:
|
||||
raise GDALException, 'Unknown POSIX platform "%s"' % platform
|
||||
raise OGRException, 'Unknown POSIX platform "%s"' % platform
|
||||
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)
|
||||
|
||||
|
@ -1,14 +1,15 @@
|
||||
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():
|
||||
s = TestSuite()
|
||||
s.addTest(test_geos.suite())
|
||||
s.addTest(test_gdal_ds.suite())
|
||||
s.addTest(test_gdal_driver.suite())
|
||||
s.addTest(test_gdal_srs.suite())
|
||||
s.addTest(test_gdal_geom.suite())
|
||||
s.addTest(test_spatialrefsys.suite())
|
||||
return s
|
||||
|
||||
def run(verbosity=2):
|
||||
def run(verbosity=1):
|
||||
TextTestRunner(verbosity=verbosity).run(suite())
|
||||
|
File diff suppressed because one or more lines are too long
40
django/contrib/gis/tests/test_gdal_driver.py
Normal file
40
django/contrib/gis/tests/test_gdal_driver.py
Normal 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())
|
@ -40,7 +40,7 @@ class DataSourceTest(unittest.TestCase):
|
||||
self.assertEqual(source.ds, ds.name)
|
||||
|
||||
# Making sure the driver name matches up
|
||||
self.assertEqual('ESRI Shapefile', ds.driver)
|
||||
self.assertEqual('ESRI Shapefile', str(ds.driver))
|
||||
|
||||
# Making sure indexing works
|
||||
try:
|
||||
|
@ -10,11 +10,11 @@ class OGRGeomTest(unittest.TestCase):
|
||||
|
||||
# OGRGeomType should initialize on all these inputs.
|
||||
try:
|
||||
g = OGRGeomType(0)
|
||||
g = OGRGeomType(1)
|
||||
g = OGRGeomType(7)
|
||||
g = OGRGeomType('point')
|
||||
g = OGRGeomType('GeometrycollectioN')
|
||||
g = OGRGeomType('LINearrING')
|
||||
except:
|
||||
self.fail('Could not create an OGRGeomType object!')
|
||||
|
||||
@ -42,15 +42,54 @@ class OGRGeomTest(unittest.TestCase):
|
||||
for p in points:
|
||||
if not hasattr(p, 'z'): # No 3D
|
||||
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.y, pnt.y)
|
||||
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."
|
||||
for p in polygons:
|
||||
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
|
||||
for r in poly:
|
||||
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(p.ext_ring_cs, r.tuple)
|
||||
|
||||
def test04_multipoints(self):
|
||||
"Testing MultiPoint objects."
|
||||
def test07_closepolygons(self):
|
||||
"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():
|
||||
s = unittest.TestSuite()
|
||||
s.addTest(unittest.makeSuite(OGRGeomTest))
|
||||
|
@ -115,8 +115,9 @@ class GeosTest2(unittest.TestCase):
|
||||
self.assertEqual(mp.valid, mpoly.valid)
|
||||
|
||||
if mp.valid:
|
||||
self.assertEqual(mp.n_p, mpoly.num_geom)
|
||||
self.assertEqual(mp.n_p, len(mpoly))
|
||||
self.assertEqual(mp.num_geom, mpoly.num_geom)
|
||||
self.assertEqual(mp.n_p, mpoly.num_coords)
|
||||
self.assertEqual(mp.num_geom, len(mpoly))
|
||||
for p in mpoly:
|
||||
self.assertEqual(p.geom_type, 'Polygon')
|
||||
self.assertEqual(p.geom_typeid, 3)
|
||||
|
Loading…
x
Reference in New Issue
Block a user