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

gis: gdal: refactor of the GDAL ctypes interface

(1) All interactions with the GDAL library take place through predefined ctypes prototypes, abstracting away error-checking.
 (2) Fixed memory leaks by properly freeing pointers allocated w/in GDAL.
 (3) Improved OFTField support, and added support for the OGR date/time fields.
 (4) Significantly improved the OGRGeometry tests.


git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@6686 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2007-11-17 21:38:36 +00:00
parent f66821deae
commit ef32f913a0
20 changed files with 1226 additions and 617 deletions

View File

@ -21,8 +21,8 @@
SpatialReference: Represents OSR Spatial Reference objects. SpatialReference: Represents OSR Spatial Reference objects.
""" """
# Attempting to import objects that depend on the GDAL library. The # 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 # HAS_GDAL flag will be set to True if the library is present on
# the system. # 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.datasource import DataSource from django.contrib.gis.gdal.datasource import DataSource

View File

@ -1,105 +1,113 @@
# types and ctypes """
from types import StringType DataSource is a wrapper for the OGR Data Source object, which provides
from ctypes import c_char_p, c_int, c_void_p, byref, string_at 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:
for feature in layer:
# Getting the geometry for the feature.
g = feature.geom
# Getting the 'description' field for the feature.
desc = feature['description']
# 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
"""
# ctypes prerequisites.
from ctypes import byref, c_void_p
# 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.error import OGRException, OGRIndexError, check_err
from django.contrib.gis.gdal.layer import Layer
from django.contrib.gis.gdal.driver import Driver from django.contrib.gis.gdal.driver import Driver
from django.contrib.gis.gdal.error import OGRException, OGRIndexError
from django.contrib.gis.gdal.layer import Layer
""" # Getting the ctypes prototypes for the DataSource.
DataSource is a wrapper for the OGR Data Source object, which provides from django.contrib.gis.gdal.prototypes.ds import \
an interface for reading vector geometry data from many different file destroy_ds, get_driver_count, register_all, open_ds, release_ds, \
formats (including ESRI shapefiles). get_ds_name, get_layer, get_layer_count, get_layer_by_name
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:
for feature in layer:
# Getting the geometry for the feature.
g = feature.geom
# Getting the 'description' field for the feature.
desc = feature['description']
# 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."
#### Python 'magic' routines #### #### Python 'magic' routines ####
def __init__(self, ds_input, ds_driver=False): def __init__(self, ds_input, ds_driver=False, write=False):
self._ds = None # Initially NULL # DataSource pointer is initially NULL.
self._ptr = None
# The write flag.
if write:
self._write = 1
else:
self._write = 0
# 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 get_driver_count() and not register_all():
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, basestring):
# The data source driver is a void pointer. # The data source driver is a void pointer.
ds_driver = c_void_p() ds_driver = c_void_p()
# OGROpen will auto-detect the data source type. # OGROpen will auto-detect the data source type.
ds = lgdal.OGROpen(c_char_p(ds_input), c_int(0), byref(ds_driver)) ds = open_ds(ds_input, self._write, byref(ds_driver))
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' % type(ds_input))
# Raise an exception if the returned pointer is NULL if bool(ds):
if not ds: self._ptr = ds
self._ds = False
raise OGRException('Invalid data source file "%s"' % ds_input)
else:
self._ds = ds
self._driver = Driver(ds_driver) self._driver = Driver(ds_driver)
else:
# Raise an exception if the returned pointer is NULL
raise OGRException('Invalid data source file "%s"' % ds_input)
def __del__(self): def __del__(self):
"This releases the reference to the data source (destroying it if it's the only one)." "Destroys this DataStructure object."
if self._ds: lgdal.OGRReleaseDataSource(self._ds) if self._ptr: destroy_ds(self._ptr)
def __iter__(self): def __iter__(self):
"Allows for iteration over the layers in a data source." "Allows for iteration over the layers in a data source."
for i in xrange(self.layer_count): for i in xrange(self.layer_count):
yield self.__getitem__(i) yield self[i]
def __getitem__(self, index): def __getitem__(self, index):
"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, basestring):
l = lgdal.OGR_DS_GetLayerByName(self._ds, c_char_p(index)) l = get_layer_by_name(self._ptr, index)
if not l: raise OGRIndexError('invalid OGR Layer name given: "%s"' % index) if not l: raise OGRIndexError('invalid OGR Layer name given: "%s"' % index)
else: elif isinstance(index, int):
if index < 0 or index >= self.layer_count: if index < 0 or index >= self.layer_count:
raise OGRIndexError('index out of range') raise OGRIndexError('index out of range')
l = lgdal.OGR_DS_GetLayer(self._ds, c_int(index)) l = get_layer(self._ptr, index)
else:
raise TypeError('Invalid index type: %s' % type(index))
return Layer(l) return Layer(l)
def __len__(self): def __len__(self):
@ -119,10 +127,9 @@ class DataSource(object):
@property @property
def layer_count(self): def layer_count(self):
"Returns the number of layers in the data source." "Returns the number of layers in the data source."
return lgdal.OGR_DS_GetLayerCount(self._ds) return get_layer_count(self._ptr)
@property @property
def name(self): def name(self):
"Returns the name of the data source." "Returns the name of the data source."
return string_at(lgdal.OGR_DS_GetName(self._ds)) return get_ds_name(self._ptr)

View File

@ -1,16 +1,13 @@
# types and ctypes # prerequisites imports
from types import StringType from ctypes import c_void_p
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.error import OGRException from django.contrib.gis.gdal.error import OGRException
from django.contrib.gis.gdal.prototypes.ds import \
get_driver, get_driver_by_name, get_driver_count, get_driver_name, register_all
# 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_Dr_* routines are relevant here. # The OGR_Dr_* routines are relevant here.
class Driver(object): class Driver(object):
"Wraps an OGR Data Source Driver." "Wraps an OGR Data Source Driver."
@ -22,64 +19,49 @@ class Driver(object):
'tiger/line' : 'TIGER', 'tiger/line' : 'TIGER',
} }
def __init__(self, input, ptr=False): def __init__(self, dr_input):
"Initializes an OGR driver on either a string or integer input." "Initializes an OGR driver on either a string or integer input."
if isinstance(input, StringType): if isinstance(dr_input, basestring):
# If a string name of the driver was passed in # If a string name of the driver was passed in
self._dr = None # Initially NULL self._ptr = None # Initially NULL
self._register() self._register()
# Checking the alias dictionary (case-insensitive) to see if an alias # Checking the alias dictionary (case-insensitive) to see if an alias
# exists for the given driver. # exists for the given driver.
if input.lower() in self._alias: if dr_input.lower() in self._alias:
name = c_char_p(self._alias[input.lower()]) name = self._alias[dr_input.lower()]
else: else:
name = c_char_p(input) name = dr_input
# Attempting to get the OGR driver by the string name. # Attempting to get the OGR driver by the string name.
dr = lgdal.OGRGetDriverByName(name) dr = get_driver_by_name(name)
elif isinstance(input, int): elif isinstance(dr_input, int):
self._register() self._register()
dr = lgdal.OGRGetDriver(c_int(input)) dr = get_driver(dr_input)
elif isinstance(input, c_void_p): elif isinstance(dr_input, c_void_p):
dr = input dr = 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(dr_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(dr_input))
self._dr = dr self._ptr = dr
def __str__(self): def __str__(self):
"Returns the string name of the OGR Driver." "Returns the string name of the OGR Driver."
return string_at(lgdal.OGR_Dr_GetName(self._dr)) return get_driver_name(self._ptr)
def _register(self): def _register(self):
"Attempts to register all the data source drivers." "Attempts to register all the data source drivers."
# 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 register_all():
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
def driver_count(self): def driver_count(self):
"Returns the number of OGR data source drivers registered." "Returns the number of OGR data source drivers registered."
return lgdal.OGRGetDriverCount() return get_driver_count()
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,15 +1,14 @@
""" """
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
two pairs of coordinates, one for the lower left coordinate and one two pairs of coordinates, one for the lower left coordinate and one
for the upper right coordinate: for the upper right coordinate:
+----------o Upper right; (max_x, max_y)
| |
| |
| |
Lower left (min_x, min_y) o----------+
+----------o Upper right; (max_x, max_y)
| |
| |
| |
Lower left (min_x, min_y) o----------+
""" """
from ctypes import Structure, c_double from ctypes import Structure, c_double
from types import TupleType, ListType from types import TupleType, ListType
@ -29,14 +28,14 @@ class OGREnvelope(Structure):
class Envelope(object): class Envelope(object):
""" """
The Envelope object is a C structure that contains the minimum and The Envelope object is a C structure that contains the minimum and
maximum X, Y coordinates for a rectangle bounding box. The naming maximum X, Y coordinates for a rectangle bounding box. The naming
of the variables is compatible with the OGR Envelope structure. 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 The initialization function may take an OGREnvelope structure, 4-element
tuple or list, or 4 individual arguments. tuple or list, or 4 individual arguments.
""" """
if len(args) == 1: if len(args) == 1:

View File

@ -1,10 +1,9 @@
""" """
This module houses the OGR & SRS Exception objects, and the This module houses the OGR & SRS Exception objects, and the
check_err() routine which checks the status code returned by check_err() routine which checks the status code returned by
OGR methods. OGR methods.
""" """
#### OGR & SRS Exceptions ####
# OGR & SRS Exceptions
class OGRException(Exception): pass class OGRException(Exception): pass
class SRSException(Exception): pass class SRSException(Exception): pass
class OGRIndexError(OGRException, KeyError): class OGRIndexError(OGRException, KeyError):
@ -16,6 +15,8 @@ class OGRIndexError(OGRException, KeyError):
""" """
silent_variable_failure = True silent_variable_failure = True
#### OGR error checking codes and routine ####
# OGR Error Codes # OGR Error Codes
OGRERR_DICT = { 1 : (OGRException, 'Not enough data.'), OGRERR_DICT = { 1 : (OGRException, 'Not enough data.'),
2 : (OGRException, 'Not enough memory.'), 2 : (OGRException, 'Not enough memory.'),
@ -36,4 +37,4 @@ def check_err(code):
e, msg = OGRERR_DICT[code] e, msg = OGRERR_DICT[code]
raise e, msg raise e, msg
else: else:
raise OGRException, 'Unknown error code: "%s"' % code raise OGRException('Unknown error code: "%s"' % code)

View File

@ -1,14 +1,17 @@
# types and ctypes
from types import StringType
from ctypes import c_char_p, c_int, c_void_p, string_at
# The GDAL C library, OGR exception, and the Field object # The GDAL C library, OGR exception, and the Field object
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 from django.contrib.gis.gdal.srs import SpatialReference
# ctypes function prototypes
from django.contrib.gis.gdal.prototypes.ds import \
destroy_feature, feature_equal, get_fd_geom_type, get_feat_geom_ref, \
get_feat_name, get_feat_field_count, get_fid, get_field_defn, \
get_field_index
from django.contrib.gis.gdal.prototypes.geom import clone_geom, get_geom_srs
from django.contrib.gis.gdal.prototypes.srs import clone_srs
# 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
# #
@ -18,33 +21,31 @@ class Feature(object):
#### Python 'magic' routines #### #### Python 'magic' routines ####
def __init__(self, feat, fdefn): def __init__(self, feat, fdefn):
"Needs a C pointer (Python integer in ctypes) in order to initialize." "Initializes on the pointers for the feature and the layer definition."
self._feat = None # Initially NULL self._ptr = None # Initially NULL
self._fdefn = None
if not feat or not fdefn: if not feat or not fdefn:
raise OGRException('Cannot create OGR Feature, invalid pointer given.') raise OGRException('Cannot create OGR Feature, invalid pointer given.')
self._feat = feat self._ptr = feat
self._fdefn = fdefn self._fdefn = fdefn
def __del__(self): def __del__(self):
"Releases a reference to this object." "Releases a reference to this object."
if self._feat: lgdal.OGR_F_Destroy(self._feat) if self._ptr: destroy_feature(self._ptr)
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, StringType): if isinstance(index, basestring):
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(self._ptr, i)
string_at(lgdal.OGR_F_GetFieldAsString(self._feat, c_int(i))))
def __iter__(self): def __iter__(self):
"Iterates over each field in the Feature." "Iterates over each field in the Feature."
for i in xrange(self.num_fields): for i in xrange(self.num_fields):
yield self.__getitem__(i) yield self[i]
def __len__(self): def __len__(self):
"Returns the count of fields in this feature." "Returns the count of fields in this feature."
@ -56,54 +57,49 @@ class Feature(object):
def __eq__(self, other): def __eq__(self, other):
"Does equivalence testing on the features." "Does equivalence testing on the features."
if lgdal.OGR_F_Equal(self._feat, other._feat): return bool(feature_equal(self._ptr, other._ptr))
return True
else:
return False
#### Feature Properties #### #### Feature Properties ####
@property @property
def fid(self): def fid(self):
"Returns the feature identifier." "Returns the feature identifier."
return lgdal.OGR_F_GetFID(self._feat) return get_fid(self._ptr)
@property @property
def layer_name(self): def layer_name(self):
"Returns the name of the layer for the feature." "Returns the name of the layer for the feature."
return string_at(lgdal.OGR_FD_GetName(self._fdefn)) return get_feat_name(self._fdefn)
@property @property
def num_fields(self): def num_fields(self):
"Returns the number of fields in the Feature." "Returns the number of fields in the Feature."
return lgdal.OGR_F_GetFieldCount(self._feat) return get_feat_field_count(self._ptr)
@property @property
def fields(self): def fields(self):
"Returns a list of fields in the Feature." "Returns a list of fields in the Feature."
return [ string_at(lgdal.OGR_Fld_GetNameRef(lgdal.OGR_FD_GetFieldDefn(self._fdefn, i))) return [get_field_name(get_field_defn(self._fdefn, i))
for i in xrange(self.num_fields) ] for i in xrange(self.num_fields)]
@property @property
def geom(self): def geom(self):
"Returns the OGR Geometry for this Feature." "Returns the OGR Geometry for this Feature."
# Retrieving the geometry pointer for the feature. # Retrieving the geometry pointer for the feature.
geom_ptr = lgdal.OGR_F_GetGeometryRef(self._feat) geom_ptr = get_feat_geom_ref(self._ptr)
if not geom_ptr:
raise OGRException('Cannot retrieve Geometry from the feature.')
# Attempting to retrieve the Spatial Reference for the geometry. # Attempting to retrieve the Spatial Reference for the geometry.
srs_ptr = lgdal.OSRClone(lgdal.OGR_G_GetSpatialReference(geom_ptr)) try:
if srs_ptr: srs_ptr = get_geom_srs(geom_ptr)
srs = SpatialReference(srs_ptr, 'ogr') srs = SpatialReference(clone_srs(srs_ptr))
else: except OGRException:
srs = None srs = None
# Geometry is cloned so the feature isn't invalidated. # Geometry is cloned so the feature isn't invalidated.
return OGRGeometry(c_void_p(lgdal.OGR_G_Clone(geom_ptr)), srs) return OGRGeometry(clone_geom(geom_ptr), srs)
@property @property
def geom_type(self): def geom_type(self):
"Returns the OGR Geometry Type for this Feture." "Returns the OGR Geometry Type for this Feture."
return OGRGeomType(lgdal.OGR_FD_GetGeomType(self._fdefn)) return OGRGeomType(get_fd_geom_type(self._fdefn))
#### Feature Methods #### #### Feature Methods ####
def get(self, field): def get(self, field):
@ -113,14 +109,10 @@ class Feature(object):
parameters. parameters.
""" """
field_name = getattr(field, 'name', field) field_name = getattr(field, 'name', field)
return self.__getitem__(field_name).value return self[field_name].value
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 = get_field_index(self._ptr, 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):
"Clones this Feature."
return Feature(lgdal.OGR_F_Clone(self._feat))

View File

@ -1,6 +1,10 @@
from ctypes import string_at from ctypes import byref, c_int
from django.contrib.gis.gdal.libgdal import lgdal from datetime import date, datetime, time
from django.contrib.gis.gdal.error import OGRException from django.contrib.gis.gdal.error import OGRException
from django.contrib.gis.gdal.prototypes.ds import \
get_feat_field_defn, get_field_as_datetime, get_field_as_double, \
get_field_as_integer, get_field_as_string, get_field_name, get_field_precision, \
get_field_type, get_field_type_name, get_field_width
# 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
@ -10,70 +14,145 @@ class Field(object):
"A class that wraps an OGR Field, needs to be instantiated from a Feature object." "A class that wraps an OGR Field, needs to be instantiated from a Feature object."
#### Python 'magic' routines #### #### Python 'magic' routines ####
def __init__(self, fld, val=''): def __init__(self, feat, index):
"Needs a C pointer (Python integer in ctypes) in order to initialize." """
self._fld = None # Initially NULL Initializes on the feature pointer and the integer index of
the field within the feature.
"""
# Setting the feature pointer and index.
self._feat = feat
self._index = index
# Getting the pointer for this field.
fld = get_feat_field_defn(feat, index)
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._ptr = fld
self._val = val
# Setting the class depending upon the OGR Field Type (OFT) # Setting the class depending upon the OGR Field Type (OFT)
self.__class__ = FIELD_CLASSES[self.type] self.__class__ = FIELD_CLASSES[self.type]
# OFTReal with no precision should be an OFTInteger.
if isinstance(self, OFTReal) and self.precision == 0:
self.__class__ = OFTInteger
def __str__(self): def __str__(self):
"Returns the string representation of the Field." "Returns the string representation of the Field."
return '%s (%s)' % (self.name, self.value) return str(self.value).strip()
#### Field Methods ####
def as_double(self):
"Retrieves the Field's value as a double (float)."
return get_field_as_double(self._feat, self._index)
def as_int(self):
"Retrieves the Field's value as an integer."
return get_field_as_integer(self._feat, self._index)
def as_string(self):
"Retrieves the Field's value as a string."
return get_field_as_string(self._feat, self._index)
def as_datetime(self):
"Retrieves the Field's value as a tuple of date & time components."
yy, mm, dd, hh, mn, ss, tz = [c_int() for i in range(7)]
status = get_field_as_datetime(self._feat, self._index, byref(yy), byref(mm), byref(dd),
byref(hh), byref(mn), byref(ss), byref(tz))
if status:
return (yy, mm, dd, hh, mn, ss, tz)
else:
raise OGRException('Unable to retrieve date & time information from the field.')
#### Field Properties #### #### Field Properties ####
@property @property
def name(self): def name(self):
"Returns the name of the field." "Returns the name of this Field."
return string_at(lgdal.OGR_Fld_GetNameRef(self._fld)) return get_field_name(self._ptr)
@property
def precision(self):
"Returns the precision of this Field."
return get_field_precision(self._ptr)
@property @property
def type(self): def type(self):
"Returns the type of this field." "Returns the OGR type of this Field."
return lgdal.OGR_Fld_GetType(self._fld) return get_field_type(self._ptr)
@property
def type_name(self):
"Return the OGR field type name for this Field."
return get_field_type_name(self.type)
@property @property
def value(self): def value(self):
"Returns the value of this type of field." "Returns the value of this Field."
return self._val # Default is to get the field as a string.
return self.as_string()
# The Field sub-classes for each OGR Field type. @property
def width(self):
"Returns the width of this Field."
return get_field_width(self._ptr)
### The Field sub-classes for each OGR Field type. ###
class OFTInteger(Field): class OFTInteger(Field):
@property @property
def value(self): def value(self):
"Returns an integer contained in this field." "Returns an integer contained in this field."
try: return self.as_int()
return int(self._val)
except ValueError: @property
return None def type(self):
class OFTIntegerList(Field): pass """
GDAL uses OFTReals to represent OFTIntegers in created
shapefiles -- forcing the type here since the underlying field
type may actually be OFTReal.
"""
return 0
class OFTReal(Field): class OFTReal(Field):
@property @property
def value(self): def value(self):
"Returns a float contained in this field." "Returns a float contained in this field."
try: return self.as_double()
return float(self._val)
except ValueError:
return None
class OFTRealList(Field): pass
class OFTString(Field): # String & Binary fields, just subclasses
def __str__(self): class OFTString(Field): pass
return '%s ("%s")' % (self.name, self.value)
class OFTStringList(Field): pass
class OFTWideString(Field): pass class OFTWideString(Field): pass
class OFTWideStringList(Field): pass
class OFTBinary(Field): pass class OFTBinary(Field): pass
class OFTDate(Field): pass
class OFTTime(Field): pass # OFTDate, OFTTime, OFTDateTime fields.
class OFTDateTime(Field): pass class OFTDate(Field):
@property
def value(self):
"Returns a Python `date` object for the OFTDate field."
yy, mm, dd, hh, mn, ss, tz = self.as_datetime()
return date(yy.value, mm.value, dd.value)
class OFTDateTime(Field):
@property
def value(self):
"Returns a Python `datetime` object for this OFTDateTime field."
yy, mm, dd, hh, mn, ss, tz = self.as_datetime()
# TODO: Adapt timezone information.
# See http://lists.maptools.org/pipermail/gdal-dev/2006-February/007990.html
# The `tz` variable has values of: 0=unknown, 1=localtime (ambiguous),
# 100=GMT, 104=GMT+1, 80=GMT-5, etc.
return datetime(yy.value, mm.value, dd.value, hh.value, mn.value, ss.value)
class OFTTime(Field):
@property
def value(self):
"Returns a Python `time` object for this OFTTime field."
yy, mm, dd, hh, mn, ss, tz = self.as_datetime()
return time(hh.value, mn.value, ss.value)
# List fields are also just subclasses
class OFTIntegerList(Field): pass
class OFTRealList(Field): pass
class OFTStringList(Field): pass
class OFTWideStringList(Field): pass
# Class mapping dictionary for OFT Types # Class mapping dictionary for OFT Types
FIELD_CLASSES = { 0 : OFTInteger, FIELD_CLASSES = { 0 : OFTInteger,

View File

@ -1,79 +1,67 @@
""" """
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
may be instantiated when reading geometries from OGR Data Sources may be instantiated when reading geometries from OGR Data Sources
(e.g. SHP files), or when given OGC WKT (a string). (e.g. SHP files), or when given OGC WKT (a string).
While the 'full' API is not present yet, the API is "pythonic" unlike While the 'full' API is not present yet, the API is "pythonic" unlike
the traditional and "next-generation" OGR Python bindings. One major the traditional and "next-generation" OGR Python bindings. One major
advantage OGR Geometries have over their GEOS counterparts is support advantage OGR Geometries have over their GEOS counterparts is support
for spatial reference systems and their transformation. for spatial reference systems and their transformation.
Example: Example:
>>> from django.contrib.gis.gdal import OGRGeometry, OGRGeomType, SpatialReference >>> from django.contrib.gis.gdal import OGRGeometry, OGRGeomType, SpatialReference
>>> wkt1, wkt2 = 'POINT(-90 30)', 'POLYGON((0 0, 5 0, 5 5, 0 5)' >>> wkt1, wkt2 = 'POINT(-90 30)', 'POLYGON((0 0, 5 0, 5 5, 0 5)'
>>> pnt = OGRGeometry(wkt1) >>> pnt = OGRGeometry(wkt1)
>>> print pnt >>> print pnt
POINT (-90 30) POINT (-90 30)
>>> mpnt = OGRGeometry(OGRGeomType('MultiPoint'), SpatialReference('WGS84')) >>> mpnt = OGRGeometry(OGRGeomType('MultiPoint'), SpatialReference('WGS84'))
>>> mpnt.add(wkt1) >>> mpnt.add(wkt1)
>>> mpnt.add(wkt1) >>> mpnt.add(wkt1)
>>> print mpnt >>> print mpnt
MULTIPOINT (-90 30,-90 30) MULTIPOINT (-90 30,-90 30)
>>> print mpnt.srs.name >>> print mpnt.srs.name
WGS 84 WGS 84
>>> print mpnt.srs.proj >>> print mpnt.srs.proj
+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs
>>> mpnt.transform_to(SpatialReference('NAD27')) >>> mpnt.transform_to(SpatialReference('NAD27'))
>>> print mpnt.proj >>> print mpnt.proj
+proj=longlat +ellps=clrk66 +datum=NAD27 +no_defs +proj=longlat +ellps=clrk66 +datum=NAD27 +no_defs
>>> print mpnt >>> print mpnt
MULTIPOINT (-89.999930378602485 29.999797886557641,-89.999930378602485 29.999797886557641) MULTIPOINT (-89.999930378602485 29.999797886557641,-89.999930378602485 29.999797886557641)
The OGRGeomType class is to make it easy to specify an OGR geometry type: The OGRGeomType class is to make it easy to specify an OGR geometry type:
>>> from django.contrib.gis.gdal import OGRGeomType >>> from django.contrib.gis.gdal import OGRGeomType
>>> gt1 = OGRGeomType(3) # Using an integer for the type >>> gt1 = OGRGeomType(3) # Using an integer for the type
>>> gt2 = OGRGeomType('Polygon') # Using a string >>> gt2 = OGRGeomType('Polygon') # Using a string
>>> gt3 = OGRGeomType('POLYGON') # It's case-insensitive >>> gt3 = OGRGeomType('POLYGON') # It's case-insensitive
>>> print gt1 == 3, gt1 == 'Polygon' # Equivalence works w/non-OGRGeomType objects >>> print gt1 == 3, gt1 == 'Polygon' # Equivalence works w/non-OGRGeomType objects
True True
""" """
# Python library imports # Python library requisites.
import re, sys import re, sys
from binascii import a2b_hex, b2a_hex from binascii import a2b_hex
from ctypes import byref, create_string_buffer, string_at, c_char_p, c_double, c_int, c_void_p from ctypes import byref, string_at, c_char_p, c_double, c_ubyte, c_void_p
from types import BufferType, IntType, StringType, UnicodeType from types import BufferType, IntType, StringType, UnicodeType
# Getting GDAL prerequisites # 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.envelope import Envelope, OGREnvelope
from django.contrib.gis.gdal.error import check_err, OGRException, OGRIndexError from django.contrib.gis.gdal.error import OGRException, OGRIndexError, SRSException
from django.contrib.gis.gdal.geomtype import OGRGeomType from django.contrib.gis.gdal.geomtype import OGRGeomType
from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform
# Getting the ctypes prototype functions that interface w/the GDAL C library.
from django.contrib.gis.gdal.prototypes.geom import *
from django.contrib.gis.gdal.prototypes.srs import clone_srs
# 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_G_* routines are relevant here. # The OGR_G_* routines are relevant here.
#### ctypes prototypes for functions that return double values #### # Regular expressions for recognizing HEXEWKB and WKT.
def pnt_func(f):
"For accessing point information."
f.restype = c_double
f.argtypes = [c_void_p, c_int]
return f
# GetX, GetY, GetZ all return doubles.
getx = pnt_func(lgdal.OGR_G_GetX)
gety = pnt_func(lgdal.OGR_G_GetY)
getz = pnt_func(lgdal.OGR_G_GetZ)
# GetArea returns a double.
get_area = lgdal.OGR_G_GetArea
get_area.restype = c_double
get_area.argtypes = [c_void_p]
# Regular expression for determining whether the input is HEXEWKB.
hex_regex = re.compile(r'^[0-9A-F]+$', re.I) hex_regex = re.compile(r'^[0-9A-F]+$', re.I)
wkt_regex = re.compile(r'^(?P<type>POINT|LINESTRING|LINEARRING|POLYGON|MULTIPOINT|MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION)[ACEGIMLONPSRUTY\d,\.\-\(\) ]+$', re.I)
#### OGRGeometry Class #### #### OGRGeometry Class ####
class OGRGeometry(object): class OGRGeometry(object):
@ -82,7 +70,7 @@ class OGRGeometry(object):
def __init__(self, geom_input, srs=None): 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 = c_void_p(None) # Initially NULL self._ptr = c_void_p(None) # Initially NULL
# Checking if unicode # Checking if unicode
if isinstance(geom_input, UnicodeType): if isinstance(geom_input, UnicodeType):
@ -94,52 +82,47 @@ class OGRGeometry(object):
geom_input = buffer(a2b_hex(geom_input.upper())) geom_input = buffer(a2b_hex(geom_input.upper()))
if isinstance(geom_input, StringType): if isinstance(geom_input, StringType):
# First, trying the input as WKT m = wkt_regex.match(geom_input)
buf = c_char_p(geom_input) if m:
g = c_void_p() if m.group('type').upper() == 'LINEARRING':
# OGR_G_CreateFromWkt doesn't work with LINEARRING WKT.
try: # See http://trac.osgeo.org/gdal/ticket/1992.
check_err(lgdal.OGR_G_CreateFromWkt(byref(buf), c_void_p(), byref(g))) g = create_geom(OGRGeomType(m.group('type')).num)
except OGRException: import_wkt(g, byref(c_char_p(geom_input)))
try: else:
# Seeing if the input is a valid short-hand string g = from_wkt(byref(c_char_p(geom_input)), None, byref(c_void_p()))
ogr_t = OGRGeomType(geom_input) else:
g = lgdal.OGR_G_CreateGeometry(ogr_t.num) # Seeing if the input is a valid short-hand string
except: # (e.g., 'Point', 'POLYGON').
raise OGRException('Could not initialize OGR Geometry from: %s' % geom_input) ogr_t = OGRGeomType(geom_input)
g = create_geom(OGRGeomType(geom_input).num)
elif isinstance(geom_input, BufferType): elif isinstance(geom_input, BufferType):
# WKB was passed in # WKB was passed in
g = c_void_p() g = from_wkb(str(geom_input), None, byref(c_void_p()), len(geom_input))
check_err(lgdal.OGR_G_CreateFromWkb(c_char_p(str(geom_input)), c_void_p(), byref(g), len(geom_input)))
elif isinstance(geom_input, OGRGeomType): elif isinstance(geom_input, OGRGeomType):
# OGRGeomType was passed in, an empty geometry will be created. # OGRGeomType was passed in, an empty geometry will be created.
g = lgdal.OGR_G_CreateGeometry(geom_input.num) g = create_geom(geom_input.num)
elif isinstance(geom_input, c_void_p): elif isinstance(geom_input, c_void_p):
# OGR pointer (c_void_p) was the input. # OGR pointer (c_void_p) was the input.
g = geom_input g = geom_input
else: else:
raise OGRException('Type of input cannot be determined!') raise OGRException('Invalid input type for OGR Geometry construction: %s' % type(geom_input))
# 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
# by setting the pointer for the object.
if not g: if not g:
raise OGRException('Cannot create OGR Geometry from input: %s' % str(geom_input)) raise OGRException('Cannot create OGR Geometry from input: %s' % str(geom_input))
self._g = g self._ptr = g
# Assigning the SpatialReference object to the geometry, if valid.
if bool(srs): self.srs = srs
# 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 __del__(self): def __del__(self):
"Deletes this Geometry." "Deletes this Geometry."
if self._g: lgdal.OGR_G_DestroyGeometry(self._g) if self._ptr: destroy_geom(self._ptr)
### Geometry set-like operations ### ### Geometry set-like operations ###
# g = g1 | g2 # g = g1 | g2
@ -178,71 +161,74 @@ class OGRGeometry(object):
@property @property
def dimension(self): def dimension(self):
"Returns 0 for points, 1 for lines, and 2 for surfaces." "Returns 0 for points, 1 for lines, and 2 for surfaces."
return lgdal.OGR_G_GetDimension(self._g) return get_dims(self._ptr)
@property @property
def coord_dim(self): def coord_dim(self):
"Returns the coordinate dimension of the Geometry." "Returns the coordinate dimension of the Geometry."
return lgdal.OGR_G_GetCoordinateDimension(self._g) return get_coord_dims(self._ptr)
@property @property
def geom_count(self): def geom_count(self):
"The number of elements in this Geometry." "The number of elements in this Geometry."
return lgdal.OGR_G_GetGeometryCount(self._g) return get_geom_count(self._ptr)
@property @property
def point_count(self): def point_count(self):
"Returns the number of Points in this Geometry." "Returns the number of Points in this Geometry."
return lgdal.OGR_G_GetPointCount(self._g) return get_point_count(self._ptr)
@property
def num_points(self):
"Alias for `point_count` (same name method in GEOS API.)"
return self.point_count
@property @property
def num_coords(self): def num_coords(self):
"Returns the number of Points in this Geometry." "Alais for `point_count`."
return self.point_count return self.point_count
@property @property
def geom_type(self): def geom_type(self):
"Returns the Type for this Geometry." "Returns the Type for this Geometry."
return OGRGeomType(lgdal.OGR_G_GetGeometryType(self._g)) return OGRGeomType(get_geom_type(self._ptr))
@property @property
def geom_name(self): def geom_name(self):
"Returns the Name of this Geometry." "Returns the Name of this Geometry."
return string_at(lgdal.OGR_G_GetGeometryName(self._g)) return get_geom_name(self._ptr)
@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."
return get_area(self._g) return get_area(self._ptr)
@property @property
def envelope(self): def envelope(self):
"Returns the envelope for this Geometry." "Returns the envelope for this Geometry."
env = OGREnvelope() return Envelope(get_envelope(self._ptr, byref(OGREnvelope())))
lgdal.OGR_G_GetEnvelope(self._g, byref(env))
return Envelope(env)
#### SpatialReference-related Properties #### #### SpatialReference-related Properties ####
# The SRS property # The SRS property
def get_srs(self): def get_srs(self):
"Returns the Spatial Reference for this Geometry." "Returns the Spatial Reference for this Geometry."
srs_ptr = lgdal.OGR_G_GetSpatialReference(self._g) try:
if srs_ptr: srs_ptr = get_geom_srs(self._ptr)
return SpatialReference(lgdal.OSRClone(srs_ptr), 'ogr') return SpatialReference(clone_srs(srs_ptr))
else: except SRSException:
return None return None
def set_srs(self, srs): def set_srs(self, srs):
"Sets the SpatialReference for this geometry." "Sets the SpatialReference for this geometry."
if isinstance(srs, SpatialReference): if isinstance(srs, SpatialReference):
srs_ptr = lgdal.OSRClone(srs._srs) srs_ptr = clone_srs(srs._ptr)
elif isinstance(srs, (StringType, UnicodeType, IntType)): elif isinstance(srs, (StringType, UnicodeType, IntType)):
sr = SpatialReference(srs) sr = SpatialReference(srs)
srs_ptr = lgdal.OSRClone(sr._srs) srs_ptr = clone_srs(sr._ptr)
else: else:
raise TypeError('Cannot assign spatial reference with object of type: %s' % type(srs)) raise TypeError('Cannot assign spatial reference with object of type: %s' % type(srs))
lgdal.OGR_G_AssignSpatialReference(self._g, srs_ptr) assign_srs(self._ptr, srs_ptr)
srs = property(get_srs, set_srs) srs = property(get_srs, set_srs)
@ -260,151 +246,168 @@ class OGRGeometry(object):
srid = property(get_srid, set_srid) srid = property(get_srid, set_srid)
#### Output Methods #### #### Output Methods ####
@property
def geos(self):
"Returns a GEOSGeometry object from this OGRGeometry."
from django.contrib.gis.geos import GEOSGeometry
return GEOSGeometry(self.wkb, self.srid)
@property @property
def gml(self): def gml(self):
"Returns the GML representation of the Geometry." "Returns the GML representation of the Geometry."
buf = lgdal.OGR_G_ExportToGML(self._g) return to_gml(self._ptr)
if buf: return string_at(buf)
else: return None
@property @property
def hex(self): def hex(self):
"Returns the hexadecimal representation of the WKB (a string)." "Returns the hexadecimal representation of the WKB (a string)."
return b2a_hex(self.wkb).upper() return str(self.wkb).encode('hex').upper()
#return b2a_hex(self.wkb).upper()
@property @property
def wkb_size(self): def wkb_size(self):
"Returns the size of the WKB buffer." "Returns the size of the WKB buffer."
return lgdal.OGR_G_WkbSize(self._g) return get_wkbsize(self._ptr)
@property @property
def wkb(self): def wkb(self):
"Returns the WKB representation of the Geometry." "Returns the WKB representation of the Geometry."
if sys.byteorder == 'little': if sys.byteorder == 'little':
byteorder = c_int(1) # wkbNDR (from ogr_core.h) byteorder = 1 # wkbNDR (from ogr_core.h)
else: else:
byteorder = c_int(0) # wkbXDR (from ogr_core.h) byteorder = 0 # wkbXDR
# Creating a mutable string buffer of the given size, exporting
# to WKB, and returning a Python buffer of the WKB.
sz = self.wkb_size sz = self.wkb_size
wkb = create_string_buffer(sz) # Creating the unsigned character buffer, and passing it in by reference.
check_err(lgdal.OGR_G_ExportToWkb(self._g, byteorder, byref(wkb))) buf = (c_ubyte * sz)()
return buffer(string_at(wkb, sz)) wkb = to_wkb(self._ptr, byteorder, byref(buf))
# Returning a buffer of the string at the pointer.
return buffer(string_at(buf, sz))
@property @property
def wkt(self): def wkt(self):
"Returns the WKT representation of the Geometry." "Returns the WKT representation of the Geometry."
buf = c_char_p() return to_wkt(self._ptr, byref(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."
return OGRGeometry(c_void_p(lgdal.OGR_G_Clone(self._g))) return OGRGeometry(clone_geom(self._ptr), self.srs)
def close_rings(self): def close_rings(self):
"""If there are any rings within this geometry that have not been """
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 closed, this routine will do so by adding the starting point at the
end.""" end.
"""
# Closing the open rings. # Closing the open rings.
lgdal.OGR_G_CloseRings(self._g) geom_close_rings(self._ptr)
def transform(self, coord_trans): def transform(self, coord_trans):
"Transforms this Geometry with the given CoordTransform object." """
if not isinstance(coord_trans, CoordTransform): Transforms this geometry to a different spatial reference system. May take
raise OGRException('CoordTransform object required for transform.') either a CoordTransform object or a SpatialReference object.
check_err(lgdal.OGR_G_Transform(self._g, coord_trans._ct)) """
if isinstance(coord_trans, CoordTransform):
geom_transform(self._ptr, coord_trans._ptr)
elif isinstance(coord_trans, SpatialReference):
geom_transform_to(self._ptr, coord_trans._ptr)
else:
raise TypeError('Either a CoordTransform or a SpatialReference object required for transformation.')
def transform_to(self, srs): def transform_to(self, srs):
"Transforms this Geometry with the given SpatialReference." "For backwards-compatibility."
if not isinstance(srs, SpatialReference): self.transform(srs)
raise OGRException('SpatialReference object required for transform_to.')
check_err(lgdal.OGR_G_TransformTo(self._g, srs._srs))
#### Topology Methods #### #### Topology Methods ####
def _topology(self, topo_func, other): def _topology(self, func, other):
"""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 TypeError('Must use another OGRGeometry object for topology operations!')
# Calling the passed-in topology function with the other geometry # Returning the output of the given function with the other geometry's
status = topo_func(self._g, other._g) # pointer.
return func(self._ptr, other._ptr)
# Returning based on the status code (an integer)
if status: return True
else: return False
def intersects(self, other): def intersects(self, other):
"Returns True if this geometry intersects with the other." "Returns True if this geometry intersects with the other."
return self._topology(lgdal.OGR_G_Intersects, other) return self._topology(ogr_intersects, other)
def equals(self, other): def equals(self, other):
"Returns True if this geometry is equivalent to the other." "Returns True if this geometry is equivalent to the other."
return self._topology(lgdal.OGR_G_Equals, other) return self._topology(ogr_equals, other)
def disjoint(self, other): def disjoint(self, other):
"Returns True if this geometry and the other are spatially disjoint." "Returns True if this geometry and the other are spatially disjoint."
return self._topology(lgdal.OGR_G_Disjoint, other) return self._topology(ogr_disjoint, other)
def touches(self, other): def touches(self, other):
"Returns True if this geometry touches the other." "Returns True if this geometry touches the other."
return self._topology(lgdal.OGR_G_Touches, other) return self._topology(ogr_touches, other)
def crosses(self, other): def crosses(self, other):
"Returns True if this geometry crosses the other." "Returns True if this geometry crosses the other."
return self._topology(lgdal.OGR_G_Crosses, other) return self._topology(ogr_crosses, other)
def within(self, other): def within(self, other):
"Returns True if this geometry is within the other." "Returns True if this geometry is within the other."
return self._topology(lgdal.OGR_G_Within, other) return self._topology(ogr_within, other)
def contains(self, other): def contains(self, other):
"Returns True if this geometry contains the other." "Returns True if this geometry contains the other."
return self._topology(lgdal.OGR_G_Contains, other) return self._topology(ogr_contains, other)
def overlaps(self, other): def overlaps(self, other):
"Returns True if this geometry overlaps the other." "Returns True if this geometry overlaps the other."
return self._topology(lgdal.OGR_G_Overlaps, other) return self._topology(ogr_overlaps, other)
#### Geometry-generation Methods #### #### Geometry-generation Methods ####
def _geomgen(self, gen_func, other=None): def _geomgen(self, gen_func, other=None):
"A helper routine for the OGR routines that generate geometries." "A helper routine for the OGR routines that generate geometries."
if isinstance(other, OGRGeometry): if isinstance(other, OGRGeometry):
return OGRGeometry(c_void_p(gen_func(self._g, other._g))) return OGRGeometry(gen_func(self._ptr, other._ptr), self.srs)
else: else:
return OGRGeometry(c_void_p(gen_func(self._g))) return OGRGeometry(gen_func(self._ptr), self.srs)
@property @property
def boundary(self): def boundary(self):
"Returns the boundary of this geometry." "Returns the boundary of this geometry."
return self._geomgen(lgdal.OGR_G_GetBoundary) return self._geomgen(get_boundary)
@property @property
def convex_hull(self): def convex_hull(self):
"Returns the smallest convex Polygon that contains all the points in the Geometry." """
return self._geomgen(lgdal.OGR_G_ConvexHull) Returns the smallest convex Polygon that contains all the points in
this Geometry.
def union(self, other): """
"""Returns a new geometry consisting of the region which is the union of return self._geomgen(geom_convex_hull)
this geometry and the other."""
return self._geomgen(lgdal.OGR_G_Union, other)
def difference(self, other): def difference(self, other):
"""Returns a new geometry consisting of the region which is the difference """
of this geometry and the other.""" Returns a new geometry consisting of the region which is the difference
return self._geomgen(lgdal.OGR_G_Difference, other) of this geometry and the other.
"""
def sym_difference(self, other): return self._geomgen(geom_diff, 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): def intersection(self, other):
"""Returns a new geometry consisting of the region of intersection of this """
geometry and the other.""" Returns a new geometry consisting of the region of intersection of this
return self._geomgen(lgdal.OGR_G_Intersection, other) geometry and the other.
"""
return self._geomgen(geom_intersection, other)
def sym_difference(self, other):
"""
Returns a new geometry which is the symmetric difference of this
geometry and the other.
"""
return self._geomgen(geom_sym_diff, other)
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(geom_union, other)
# The subclasses for OGR Geometry. # The subclasses for OGR Geometry.
class Point(OGRGeometry): class Point(OGRGeometry):
@ -412,17 +415,17 @@ class Point(OGRGeometry):
@property @property
def x(self): def x(self):
"Returns the X coordinate for this Point." "Returns the X coordinate for this Point."
return getx(self._g, c_int(0)) return getx(self._ptr, 0)
@property @property
def y(self): def y(self):
"Returns the Y coordinate for this Point." "Returns the Y coordinate for this Point."
return gety(self._g, c_int(0)) return gety(self._ptr, 0)
@property @property
def z(self): def z(self):
"Returns the Z coordinate for this Point." "Returns the Z coordinate for this Point."
return getz(self._g, c_int(0)) return getz(self._ptr, 0)
@property @property
def tuple(self): def tuple(self):
@ -436,17 +439,15 @@ class LineString(OGRGeometry):
def __getitem__(self, index): def __getitem__(self, index):
"Returns the Point at the given index." "Returns the Point at the given index."
if index > 0 or index < self.point_count: if index >= 0 and index < self.point_count:
x = c_double() x, y, z = c_double(), c_double(), c_double()
y = c_double() get_point(self._ptr, index, byref(x), byref(y), byref(z))
z = c_double() dim = self.coord_dim
lgdal.OGR_G_GetPoint(self._g, c_int(index), if dim == 1:
byref(x), byref(y), byref(z))
if self.coord_dim == 1:
return (x.value,) return (x.value,)
elif self.coord_dim == 2: elif dim == 2:
return (x.value, y.value) return (x.value, y.value)
elif self.coord_dim == 3: elif 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))
@ -454,16 +455,16 @@ class LineString(OGRGeometry):
def __iter__(self): def __iter__(self):
"Iterates over each point in the LineString." "Iterates over each point in the LineString."
for i in xrange(self.point_count): for i in xrange(self.point_count):
yield self.__getitem__(i) yield self[i]
def __len__(self, index): def __len__(self):
"The length returns the number of points in the LineString." "The length returns the number of points in the LineString."
return self.point_count return self.point_count
@property @property
def tuple(self): def tuple(self):
"Returns the tuple representation of this LineString." "Returns the tuple representation of this LineString."
return tuple(self.__getitem__(i) for i in xrange(self.point_count)) return tuple(self[i] for i in xrange(len(self)))
# LinearRings are used in Polygons. # LinearRings are used in Polygons.
class LinearRing(LineString): pass class LinearRing(LineString): pass
@ -477,38 +478,38 @@ class Polygon(OGRGeometry):
def __iter__(self): def __iter__(self):
"Iterates through each ring in the Polygon." "Iterates through each ring in the Polygon."
for i in xrange(self.geom_count): for i in xrange(self.geom_count):
yield self.__getitem__(i) yield self[i]
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' % index)
else: else:
return OGRGeometry(c_void_p(lgdal.OGR_G_Clone(lgdal.OGR_G_GetGeometryRef(self._g, c_int(index)))), self.srs) return OGRGeometry(clone_geom(get_geom_ref(self._ptr, index)), self.srs)
# Polygon Properties # Polygon Properties
@property @property
def shell(self): def shell(self):
"Returns the shell of this Polygon." "Returns the shell of this Polygon."
return self.__getitem__(0) # First ring is the shell return self[0] # First ring is the shell
@property @property
def tuple(self): def tuple(self):
"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[i].tuple for i in xrange(self.geom_count))
@property @property
def point_count(self): def point_count(self):
"The number of Points in this Polygon." "The number of Points in this Polygon."
# Summing up the number of points in each ring of the 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)]) return sum([self[i].point_count for i in xrange(self.geom_count)])
@property @property
def centroid(self): def centroid(self):
"Returns the centroid (a Point) of this Polygon." "Returns the centroid (a Point) of this Polygon."
# The centroid is a Point, create a geometry for this. # The centroid is a Point, create a geometry for this.
p = OGRGeometry(OGRGeomType('Point')) p = OGRGeometry(OGRGeomType('Point'))
check_err(lgdal.OGR_G_Centroid(self._g, p._g)) get_centroid(self._ptr, p._ptr)
return p return p
# Geometry Collection base class. # Geometry Collection base class.
@ -518,14 +519,14 @@ 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' % index)
else: else:
return OGRGeometry(c_void_p(lgdal.OGR_G_Clone(lgdal.OGR_G_GetGeometryRef(self._g, c_int(index)))), self.srs) return OGRGeometry(clone_geom(get_geom_ref(self._ptr, index)), self.srs)
def __iter__(self): def __iter__(self):
"Iterates over each Geometry." "Iterates over each Geometry."
for i in xrange(self.geom_count): for i in xrange(self.geom_count):
yield self.__getitem__(i) yield self[i]
def __len__(self): def __len__(self):
"The number of geometries in this Geometry Collection." "The number of geometries in this Geometry Collection."
@ -534,24 +535,24 @@ 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 isinstance(geom, OGRGeometry): if isinstance(geom, OGRGeometry):
ptr = geom._g ptr = geom._ptr
elif isinstance(geom, (StringType, UnicodeType)): elif isinstance(geom, (StringType, UnicodeType)):
tmp = OGRGeometry(geom) tmp = OGRGeometry(geom)
ptr = tmp._g ptr = tmp._ptr
else: else:
raise OGRException('Must add an OGRGeometry.') raise OGRException('Must add an OGRGeometry.')
lgdal.OGR_G_AddGeometry(self._g, ptr) add_geom(self._ptr, ptr)
@property @property
def point_count(self): def point_count(self):
"The number of Points in this Geometry Collection." "The number of Points in this Geometry Collection."
# Summing up the number of points in each geometry in this 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)]) return sum([self[i].point_count for i in xrange(self.geom_count)])
@property @property
def tuple(self): def tuple(self):
"Returns a tuple representation of this Geometry Collection." "Returns a tuple representation of this Geometry Collection."
return tuple(self.__getitem__(i).tuple for i in xrange(self.geom_count)) return tuple(self[i].tuple for i in xrange(self.geom_count))
# Multiple Geometry types. # Multiple Geometry types.
class MultiPoint(GeometryCollection): pass class MultiPoint(GeometryCollection): pass
@ -566,4 +567,5 @@ GEO_CLASSES = {1 : Point,
5 : MultiLineString, 5 : MultiLineString,
6 : MultiPolygon, 6 : MultiPolygon,
7 : GeometryCollection, 7 : GeometryCollection,
101: LinearRing,
} }

View File

@ -1,4 +1,3 @@
from types import StringType
from django.contrib.gis.gdal.error import OGRException from django.contrib.gis.gdal.error import OGRException
#### OGRGeomType #### #### OGRGeomType ####
@ -15,7 +14,7 @@ class OGRGeomType(object):
"Figures out the correct OGR Type based upon the input." "Figures out the correct OGR Type based upon the input."
if isinstance(type_input, OGRGeomType): if isinstance(type_input, OGRGeomType):
self._index = type_input._index self._index = type_input._index
elif isinstance(type_input, StringType): elif isinstance(type_input, basestring):
idx = self._has_str(self.__ogr_str, type_input) idx = self._has_str(self.__ogr_str, type_input)
if idx == None: if idx == None:
raise OGRException('Invalid OGR String Type "%s"' % type_input) raise OGRException('Invalid OGR String Type "%s"' % type_input)
@ -25,18 +24,20 @@ class OGRGeomType(object):
raise OGRException('Invalid OGR Integer Type: %d' % type_input) raise OGRException('Invalid OGR Integer Type: %d' % type_input)
self._index = self.__ogr_int.index(type_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."
return self.__ogr_str[self._index] return self.__ogr_str[self._index]
def __eq__(self, other): def __eq__(self, other):
"""Does an equivalence test on the OGR type with the given """
other OGRGeomType, the short-hand string, or the integer.""" Does an equivalence test on the OGR type with the given
other OGRGeomType, the short-hand string, or the integer.
"""
if isinstance(other, OGRGeomType): if isinstance(other, OGRGeomType):
return self._index == other._index return self._index == other._index
elif isinstance(other, StringType): elif isinstance(other, basestring):
idx = self._has_str(self.__ogr_str, other) idx = self._has_str(self.__ogr_str, other)
if not (idx == None): return self._index == idx if not (idx == None): return self._index == idx
return False return False

View File

@ -1,38 +1,36 @@
# Needed ctypes routines # Needed ctypes routines
from ctypes import c_int, c_long, c_void_p, byref, string_at from ctypes import byref
# The GDAL C Library
from django.contrib.gis.gdal.libgdal import lgdal
# Other GDAL imports. # Other GDAL imports.
from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope
from django.contrib.gis.gdal.error import OGRException, OGRIndexError
from django.contrib.gis.gdal.feature import Feature from django.contrib.gis.gdal.feature import Feature
from django.contrib.gis.gdal.geometries import OGRGeomType from django.contrib.gis.gdal.geometries import OGRGeomType
from django.contrib.gis.gdal.error import OGRException, OGRIndexError, check_err
from django.contrib.gis.gdal.srs import SpatialReference from django.contrib.gis.gdal.srs import SpatialReference
# GDAL ctypes function prototypes.
from django.contrib.gis.gdal.prototypes.ds import \
get_extent, get_fd_geom_type, get_fd_name, get_feature, get_feature_count, \
get_field_count, get_field_defn, get_field_name, get_field_precision, \
get_field_width, get_layer_defn, get_layer_srs, get_next_feature, \
reset_reading
from django.contrib.gis.gdal.prototypes.srs import clone_srs
# 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_L_* routines are relevant here. # The OGR_L_* routines are relevant here.
# function prototype for obtaining the spatial reference system
get_srs = lgdal.OGR_L_GetSpatialRef
get_srs.restype = c_void_p
get_srs.argtypes = [c_void_p]
class Layer(object): class Layer(object):
"A class that wraps an OGR Layer, needs to be instantiated from a DataSource object." "A class that wraps an OGR Layer, needs to be instantiated from a DataSource object."
#### Python 'magic' routines #### #### Python 'magic' routines ####
def __init__(self, l): def __init__(self, layer_ptr):
"Needs a C pointer (Python/ctypes integer) in order to initialize." "Needs a C pointer (Python/ctypes integer) in order to initialize."
self._layer = None # Initially NULL self._ptr = None # Initially NULL
self._ldefn = None if not layer_ptr:
if not l: raise OGRException('Cannot create Layer, invalid pointer given')
raise OGRException, 'Cannot create Layer, invalid pointer given' self._ptr = layer_ptr
self._layer = l self._ldefn = get_layer_defn(self._ptr)
self._ldefn = lgdal.OGR_L_GetLayerDefn(l)
def __getitem__(self, index): def __getitem__(self, index):
"Gets the Feature at the specified index." "Gets the Feature at the specified index."
@ -44,7 +42,7 @@ class Layer(object):
if index < 0: if index < 0:
index = end - index index = end - index
if index < 0 or index >= self.num_feat: if index < 0 or index >= self.num_feat:
raise OGRIndexError, 'index out of range' raise OGRIndexError('index out of range')
return self._make_feature(index) return self._make_feature(index)
else: else:
# A slice was given # A slice was given
@ -54,9 +52,9 @@ class Layer(object):
def __iter__(self): def __iter__(self):
"Iterates over each Feature in the Layer." "Iterates over each Feature in the Layer."
# ResetReading() must be called before iteration is to begin. # ResetReading() must be called before iteration is to begin.
lgdal.OGR_L_ResetReading(self._layer) reset_reading(self._ptr)
for i in range(self.num_feat): for i in range(self.num_feat):
yield Feature(lgdal.OGR_L_GetNextFeature(self._layer), self._ldefn) yield Feature(get_next_feature(self._ptr), self._ldefn)
def __len__(self): def __len__(self):
"The length is the number of features." "The length is the number of features."
@ -68,76 +66,80 @@ class Layer(object):
def _make_feature(self, offset): def _make_feature(self, offset):
"Helper routine for __getitem__ that makes a feature from an offset." "Helper routine for __getitem__ that makes a feature from an offset."
return Feature(lgdal.OGR_L_GetFeature(self._layer, c_long(offset)), self._ldefn) return Feature(get_feature(self._ptr, offset), self._ldefn)
#### Layer properties #### #### Layer properties ####
@property @property
def extent(self): def extent(self):
"Returns the extent (an Envelope) of this layer." "Returns the extent (an Envelope) of this layer."
env = OGREnvelope() env = OGREnvelope()
check_err(lgdal.OGR_L_GetExtent(self._layer, byref(env), c_int(1))) get_extent(self._ptr, byref(env), 1)
return Envelope(env) return Envelope(env)
@property @property
def name(self): def name(self):
"Returns the name of this layer in the Data Source." "Returns the name of this layer in the Data Source."
return string_at(lgdal.OGR_FD_GetName(self._ldefn)) return get_fd_name(self._ldefn)
@property @property
def num_feat(self, force=1): def num_feat(self, force=1):
"Returns the number of features in the Layer." "Returns the number of features in the Layer."
return lgdal.OGR_L_GetFeatureCount(self._layer, c_int(force)) return get_feature_count(self._ptr, force)
@property @property
def num_fields(self): def num_fields(self):
"Returns the number of fields in the Layer." "Returns the number of fields in the Layer."
return lgdal.OGR_FD_GetFieldCount(self._ldefn) return get_field_count(self._ldefn)
@property @property
def geom_type(self): def geom_type(self):
"Returns the geometry type (OGRGeomType) of the Layer." "Returns the geometry type (OGRGeomType) of the Layer."
return OGRGeomType(lgdal.OGR_FD_GetGeomType(self._ldefn)) return OGRGeomType(get_fd_geom_type(self._ldefn))
@property @property
def srs(self): def srs(self):
"Returns the Spatial Reference used in this Layer." "Returns the Spatial Reference used in this Layer."
ptr = lgdal.OGR_L_GetSpatialRef(self._layer) ptr = get_layer_srs(self._ptr)
if ptr: if ptr:
return SpatialReference(lgdal.OSRClone(ptr), 'ogr') return SpatialReference(clone_srs(ptr))
else: else:
return None return None
@property @property
def fields(self): def fields(self):
"Returns a list of the fields available in this Layer." "Returns a list of the fields available in this Layer."
return [ string_at(lgdal.OGR_Fld_GetNameRef(lgdal.OGR_FD_GetFieldDefn(self._ldefn, i))) return [get_field_name(get_field_defn(self._ldefn, i))
for i in xrange(self.num_fields) ] for i in xrange(self.num_fields) ]
@property @property
def field_widths(self): def field_widths(self):
"Returns a list of the maximum field widths for the features." "Returns a list of the maximum field widths for the features."
return [ int(lgdal.OGR_Fld_GetWidth(lgdal.OGR_FD_GetFieldDefn(self._ldefn, i))) return [get_field_width(get_field_defn(self._ldefn, i))
for i in xrange(self.num_fields) ] for i in xrange(self.num_fields)]
@property @property
def field_precisions(self): def field_precisions(self):
"Returns the field precisions for the features." "Returns the field precisions for the features."
return [ int(lgdal.OGR_Fld_GetPrecision(lgdal.OGR_FD_GetFieldDefn(self._ldefn, i))) return [get_field_precision(get_field_defn(self._ldefn, i))
for i in xrange(self.num_fields) ] for i in xrange(self.num_fields)]
#### Layer Methods #### #### Layer Methods ####
def get_fields(self, field_name): def get_fields(self, field_name):
"""Returns a list containing the given field name for every Feature """
in the Layer.""" Returns a list containing the given field name for every Feature
in the Layer.
"""
if not field_name in self.fields: if not field_name in self.fields:
raise OGRException, 'invalid field name: %s' % field_name raise OGRException('invalid field name: %s' % field_name)
return [feat.get(field_name) for feat in self] return [feat.get(field_name) for feat in self]
def get_geoms(self, geos=False): def get_geoms(self, geos=False):
"""Returns a list containing the OGRGeometry for every Feature in """
the Layer.""" Returns a list containing the OGRGeometry for every Feature in
the Layer.
"""
if geos: if geos:
from django.contrib.gis.geos import fromstr from django.contrib.gis.geos import GEOSGeometry
return [fromstr(feat.geom.wkt) for feat in self] return [GEOSGeometry(feat.geom.wkb) for feat in self]
else: else:
return [feat.geom for feat in self] return [feat.geom for feat in self]

View File

@ -1,5 +1,6 @@
import os, sys import os, sys
from ctypes import CDLL, string_at from ctypes import CDLL, string_at
from ctypes.util import find_library
from django.contrib.gis.gdal.error import OGRException from django.contrib.gis.gdal.error import OGRException
if os.name == 'nt': if os.name == 'nt':
@ -7,16 +8,14 @@ if os.name == 'nt':
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]
if platform in ('Linux', 'SunOS'): if platform == 'Darwin':
# Linux or Solaris shared library
lib_name = 'libgdal.so'
elif platform == 'Darwin':
# Mac OSX shared library # Mac OSX shared library
lib_name = 'libgdal.dylib' lib_name = 'libgdal.dylib'
else: else:
raise OGRException, 'Unknown POSIX platform "%s"' % platform # Attempting to use .so extension for all other platforms.
lib_name = 'libgdal.so'
else: else:
raise OGRException, 'Unsupported OS "%s"' % os.name raise OGRException('Unsupported OS "%s"' % os.name)
# This loads the GDAL/OGR C library # This loads the GDAL/OGR C library
lgdal = CDLL(lib_name) lgdal = CDLL(lib_name)

View File

@ -0,0 +1,16 @@
"""
This routine provides shortuct functions to generate ctypes prototypes
for the GDAL routines.
"""
# OGR Geometry prototypes.
from django.contrib.gis.gdal.prototypes.geom import \
assign_srs, clone_geom, create_geom, destroy_geom, from_wkb, from_wkt, \
get_area, get_coord_dims, get_dims, get_envelope, get_geom_count, get_geom_name, get_geom_srs, get_geom_type, get_point_count, get_wkbsize, \
getx, get_geom_ref, gety, getz, to_gml, to_wkt
# Spatial Reference prototypes.
from django.contrib.gis.gdal.prototypes.srs import \
clone_srs
# TEMPORARY
from generation import double_output, string_output, void_output

View File

@ -0,0 +1,67 @@
"""
This module houses the ctypes function prototypes for OGR DataSource
related data structures. OGR_Dr_*, OGR_DS_*, OGR_L_*, OGR_F_*,
OGR_Fld_* routines are relevant here.
"""
from ctypes import c_char_p, c_int, c_long, c_void_p, POINTER
from django.contrib.gis.gdal.envelope import OGREnvelope
from django.contrib.gis.gdal.libgdal import lgdal
from django.contrib.gis.gdal.prototypes.generation import \
const_string_output, double_output, geom_output, int_output, \
srs_output, void_output, voidptr_output
c_int_p = POINTER(c_int) # shortcut type
### Driver Routines ###
register_all = void_output(lgdal.OGRRegisterAll, [], errcheck=False)
cleanup_all = void_output(lgdal.OGRCleanupAll, [], errcheck=False)
get_driver = voidptr_output(lgdal.OGRGetDriver, [c_int])
get_driver_by_name = voidptr_output(lgdal.OGRGetDriverByName, [c_char_p])
get_driver_count = int_output(lgdal.OGRGetDriverCount, [])
get_driver_name = const_string_output(lgdal.OGR_Dr_GetName, [c_void_p])
### DataSource ###
open_ds = voidptr_output(lgdal.OGROpen, [c_char_p, c_int, POINTER(c_void_p)])
destroy_ds = void_output(lgdal.OGR_DS_Destroy, [c_void_p], errcheck=False)
release_ds = void_output(lgdal.OGRReleaseDataSource, [c_void_p])
get_ds_name = const_string_output(lgdal.OGR_DS_GetName, [c_void_p])
get_layer = voidptr_output(lgdal.OGR_DS_GetLayer, [c_void_p, c_int])
get_layer_by_name = voidptr_output(lgdal.OGR_DS_GetLayerByName, [c_void_p, c_char_p])
get_layer_count = int_output(lgdal.OGR_DS_GetLayerCount, [c_void_p])
### Layer Routines ###
get_extent = void_output(lgdal.OGR_L_GetExtent, [c_void_p, POINTER(OGREnvelope), c_int])
get_feature = voidptr_output(lgdal.OGR_L_GetFeature, [c_void_p, c_long])
get_feature_count = int_output(lgdal.OGR_L_GetFeatureCount, [c_void_p, c_int])
get_layer_defn = voidptr_output(lgdal.OGR_L_GetLayerDefn, [c_void_p])
get_layer_srs = srs_output(lgdal.OGR_L_GetSpatialRef, [c_void_p])
get_next_feature = voidptr_output(lgdal.OGR_L_GetNextFeature, [c_void_p])
reset_reading = void_output(lgdal.OGR_L_ResetReading, [c_void_p], errcheck=False)
### Feature Definition Routines ###
get_fd_geom_type = int_output(lgdal.OGR_FD_GetGeomType, [c_void_p])
get_fd_name = const_string_output(lgdal.OGR_FD_GetName, [c_void_p])
get_feat_name = const_string_output(lgdal.OGR_FD_GetName, [c_void_p])
get_field_count = int_output(lgdal.OGR_FD_GetFieldCount, [c_void_p])
get_field_defn = voidptr_output(lgdal.OGR_FD_GetFieldDefn, [c_void_p, c_int])
### Feature Routines ###
clone_feature = voidptr_output(lgdal.OGR_F_Clone, [c_void_p])
destroy_feature = void_output(lgdal.OGR_F_Destroy, [c_void_p])
feature_equal = int_output(lgdal.OGR_F_Equal, [c_void_p, c_void_p])
get_feat_geom_ref = geom_output(lgdal.OGR_F_GetGeometryRef, [c_void_p])
get_feat_field_count = int_output(lgdal.OGR_F_GetFieldCount, [c_void_p])
get_feat_field_defn = voidptr_output(lgdal.OGR_F_GetFieldDefnRef, [c_void_p, c_int])
get_fid = int_output(lgdal.OGR_F_GetFID, [c_void_p])
get_field_as_datetime = int_output(lgdal.OGR_F_GetFieldAsDateTime, [c_void_p, c_int, c_int_p, c_int_p, c_int_p, c_int_p, c_int_p, c_int_p])
get_field_as_double = double_output(lgdal.OGR_F_GetFieldAsDouble, [c_void_p, c_int])
get_field_as_integer = int_output(lgdal.OGR_F_GetFieldAsInteger, [c_void_p, c_int])
get_field_as_string = const_string_output(lgdal.OGR_F_GetFieldAsString, [c_void_p, c_int])
get_field_index = int_output(lgdal.OGR_F_GetFieldIndex, [c_void_p, c_char_p])
### Field Routines ###
get_field_name = const_string_output(lgdal.OGR_Fld_GetNameRef, [c_void_p])
get_field_precision = int_output(lgdal.OGR_Fld_GetPrecision, [c_void_p])
get_field_type = int_output(lgdal.OGR_Fld_GetType, [c_void_p])
get_field_type_name = const_string_output(lgdal.OGR_GetFieldTypeName, [c_int])
get_field_width = int_output(lgdal.OGR_Fld_GetWidth, [c_void_p])

View File

@ -0,0 +1,125 @@
"""
This module houses the error-checking routines used by the GDAL
ctypes prototypes.
"""
from ctypes import c_void_p, string_at
from django.contrib.gis.gdal.error import check_err, OGRException, SRSException
from django.contrib.gis.gdal.libgdal import lgdal
# Helper routines for retrieving pointers and/or values from
# arguments passed in by reference.
def arg_byref(args, offset=-1):
"Returns the pointer argument's by-refernece value."
return args[offset]._obj.value
def ptr_byref(args, offset=-1):
"Returns the pointer argument passed in by-reference."
return args[offset]._obj
def check_bool(result, func, cargs):
"Returns the boolean evaluation of the value."
if bool(result): return True
else: return False
### String checking Routines ###
def check_const_string(result, func, cargs, offset=None):
"""
Similar functionality to `check_string`, but does not free the pointer.
"""
if offset:
check_err(result)
ptr = ptr_byref(cargs, offset)
return ptr.value
else:
return result
def check_string(result, func, cargs, offset=-1, str_result=False):
"""
Checks the string output returned from the given function, and frees
the string pointer allocated by OGR. The `str_result` keyword
may be used when the result is the string pointer, otherwise
the OGR error code is assumed. The `offset` keyword may be used
to extract the string pointer passed in by-reference at the given
slice offset in the function arguments.
"""
if str_result:
# For routines that return a string.
ptr = result
if not ptr: s = None
else: s = string_at(result)
else:
# Error-code return specified.
check_err(result)
ptr = ptr_byref(cargs, offset)
# Getting the string value
s = ptr.value
# Correctly freeing the allocated memory beind GDAL pointer
# w/the VSIFree routine.
if ptr: lgdal.VSIFree(ptr)
return s
### DataSource, Layer error-checking ###
### Envelope checking ###
def check_envelope(result, func, cargs, offset=-1):
"Checks a function that returns an OGR Envelope by reference."
env = ptr_byref(cargs, offset)
return env
### Geometry error-checking routines ###
def check_geom(result, func, cargs):
"Checks a function that returns a geometry."
# OGR_G_Clone may return an integer, even though the
# restype is set to c_void_p
if isinstance(result, int):
result = c_void_p(result)
if not result:
raise OGRException('Invalid geometry pointer returned from "%s".' % func.__name__)
return result
def check_geom_offset(result, func, cargs, offset=-1):
"Chcks the geometry at the given offset in the C parameter list."
check_err(result)
geom = ptr_byref(cargs, offset=offset)
return check_geom(geom, func, cargs)
### Spatial Reference error-checking routines ###
def check_srs(result, func, cargs):
if isinstance(result, int):
result = c_void_p(result)
if not result:
raise SRSException('Invalid spatial reference pointer returned from "%s".' % func.__name__)
return result
### Other error-checking routines ###
def check_arg_errcode(result, func, cargs):
"""
The error code is returned in the last argument, by reference.
Check its value with `check_err` before returning the result.
"""
check_err(arg_byref(cargs))
return result
def check_errcode(result, func, cargs):
"""
Check the error code returned (c_int).
"""
check_err(result)
return
def check_pointer(result, func, cargs):
"Makes sure the result pointer is valid."
if bool(result):
return result
else:
raise OGRException('Invalid pointer returned from "%s"' % func.__name__)
def check_str_arg(result, func, cargs):
"""
This is for the OSRGet[Angular|Linear]Units functions, which
require that the returned string pointer not be freed. This
returns both the double and tring values.
"""
dbl = result
ptr = cargs[-1]._obj
return dbl, ptr.value

View File

@ -0,0 +1,113 @@
"""
This module contains functions that generate ctypes prototypes for the
GDAL routines.
"""
from ctypes import c_char_p, c_double, c_int, c_void_p
from django.contrib.gis.gdal.prototypes.errcheck import \
check_arg_errcode, check_errcode, check_geom, check_geom_offset, \
check_pointer, check_srs, check_str_arg, check_string, check_const_string
def double_output(func, argtypes, errcheck=False, strarg=False):
"Generates a ctypes function that returns a double value."
func.argtypes = argtypes
func.restype = c_double
if errcheck: func.errcheck = check_arg_errcode
if strarg: func.errcheck = check_str_arg
return func
def geom_output(func, argtypes, offset=None):
"""
Generates a function that returns a Geometry either by reference
or directly (if the return_geom keyword is set to True).
"""
# Setting the argument types
func.argtypes = argtypes
if not offset:
# When a geometry pointer is directly returned.
func.restype = c_void_p
func.errcheck = check_geom
else:
# Error code returned, geometry is returned by-reference.
func.restype = c_int
def geomerrcheck(result, func, cargs):
return check_geom_offset(result, func, cargs, offset)
func.errcheck = geomerrcheck
return func
def int_output(func, argtypes):
"Generates a ctypes function that returns an integer value."
func.argtypes = argtypes
func.restype = c_int
return func
def srs_output(func, argtypes):
"""
Generates a ctypes prototype for the given function with
the given C arguments that returns a pointer to an OGR
Spatial Reference System.
"""
func.argtypes = argtypes
func.restype = c_void_p
func.errcheck = check_srs
return func
def const_string_output(func, argtypes, offset=None):
func.argtypes = argtypes
if offset:
func.restype = c_int
else:
func.restype = c_char_p
def _check_const(result, func, cargs):
return check_const_string(result, func, cargs, offset=offset)
func.errcheck = _check_const
return func
def string_output(func, argtypes, offset=-1, str_result=False):
"""
Generates a ctypes prototype for the given function with the
given argument types that returns a string from a GDAL pointer.
The `const` flag indicates whether the allocated pointer should
be freed via the GDAL library routine VSIFree -- but only applies
only when `str_result` is True.
"""
func.argtypes = argtypes
if str_result:
# String is the result, don't explicitly define
# the argument type so we can get the pointer.
pass
else:
# Error code is returned
func.restype = c_int
# Dynamically defining our error-checking function with the
# given offset.
def _check_str(result, func, cargs):
return check_string(result, func, cargs,
offset=offset, str_result=str_result)
func.errcheck = _check_str
return func
def void_output(func, argtypes, errcheck=True):
"""
For functions that don't only return an error code that needs to
be examined.
"""
if argtypes: func.argtypes = argtypes
if errcheck:
# `errcheck` keyword may be set to False for routines that
# return void, rather than a status code.
func.restype = c_int
func.errcheck = check_errcode
return func
def voidptr_output(func, argtypes):
"For functions that return c_void_p."
func.argtypes = argtypes
func.restype = c_void_p
func.errcheck = check_pointer
return func

View File

@ -0,0 +1,95 @@
from ctypes import c_char, c_char_p, c_double, c_int, c_ubyte, c_void_p, POINTER
from django.contrib.gis.gdal.envelope import OGREnvelope
from django.contrib.gis.gdal.libgdal import lgdal
from django.contrib.gis.gdal.prototypes.errcheck import check_bool, check_envelope
from django.contrib.gis.gdal.prototypes.generation import \
const_string_output, double_output, geom_output, int_output, \
srs_output, string_output, void_output
### Generation routines specific to this module ###
def env_func(f, argtypes):
"For getting OGREnvelopes."
f.argtypes = argtypes
f.restype = None
f.errcheck = check_envelope
return f
def pnt_func(f):
"For accessing point information."
return double_output(f, [c_void_p, c_int])
def topology_func(f):
f.argtypes = [c_void_p, c_void_p]
f.restype = c_int
f.errchck = check_bool
return f
### OGR_G ctypes function prototypes ###
# GetX, GetY, GetZ all return doubles.
getx = pnt_func(lgdal.OGR_G_GetX)
gety = pnt_func(lgdal.OGR_G_GetY)
getz = pnt_func(lgdal.OGR_G_GetZ)
# Geometry creation routines.
from_wkb = geom_output(lgdal.OGR_G_CreateFromWkb, [c_char_p, c_void_p, POINTER(c_void_p), c_int], offset=-2)
from_wkt = geom_output(lgdal.OGR_G_CreateFromWkt, [POINTER(c_char_p), c_void_p, POINTER(c_void_p)], offset=-1)
create_geom = geom_output(lgdal.OGR_G_CreateGeometry, [c_int])
clone_geom = geom_output(lgdal.OGR_G_Clone, [c_void_p])
get_geom_ref = geom_output(lgdal.OGR_G_GetGeometryRef, [c_void_p, c_int])
get_boundary = geom_output(lgdal.OGR_G_GetBoundary, [c_void_p])
geom_convex_hull = geom_output(lgdal.OGR_G_ConvexHull, [c_void_p])
geom_diff = geom_output(lgdal.OGR_G_Difference, [c_void_p, c_void_p])
geom_intersection = geom_output(lgdal.OGR_G_Intersection, [c_void_p, c_void_p])
geom_sym_diff = geom_output(lgdal.OGR_G_SymmetricDifference, [c_void_p, c_void_p])
geom_union = geom_output(lgdal.OGR_G_Union, [c_void_p, c_void_p])
# Geometry modification routines.
add_geom = void_output(lgdal.OGR_G_AddGeometry, [c_void_p, c_void_p])
import_wkt = void_output(lgdal.OGR_G_ImportFromWkt, [c_void_p, POINTER(c_char_p)])
# Destroys a geometry
destroy_geom = void_output(lgdal.OGR_G_DestroyGeometry, [c_void_p], errcheck=False)
# Geometry export routines.
to_wkb = void_output(lgdal.OGR_G_ExportToWkb, None, errcheck=True) # special handling for WKB.
to_wkt = string_output(lgdal.OGR_G_ExportToWkt, [c_void_p, POINTER(c_char_p)])
to_gml = string_output(lgdal.OGR_G_ExportToGML, [c_void_p], str_result=True)
get_wkbsize = int_output(lgdal.OGR_G_WkbSize, [c_void_p])
# Geometry spatial-reference related routines.
assign_srs = void_output(lgdal.OGR_G_AssignSpatialReference, [c_void_p, c_void_p], errcheck=False)
get_geom_srs = srs_output(lgdal.OGR_G_GetSpatialReference, [c_void_p])
# Geometry properties
get_area = double_output(lgdal.OGR_G_GetArea, [c_void_p])
get_centroid = void_output(lgdal.OGR_G_Centroid, [c_void_p, c_void_p])
get_dims = int_output(lgdal.OGR_G_GetDimension, [c_void_p])
get_coord_dims = int_output(lgdal.OGR_G_GetCoordinateDimension, [c_void_p])
get_geom_count = int_output(lgdal.OGR_G_GetGeometryCount, [c_void_p])
get_geom_name = const_string_output(lgdal.OGR_G_GetGeometryName, [c_void_p])
get_geom_type = int_output(lgdal.OGR_G_GetGeometryType, [c_void_p])
get_point_count = int_output(lgdal.OGR_G_GetPointCount, [c_void_p])
get_point = void_output(lgdal.OGR_G_GetPoint, [c_void_p, c_int, POINTER(c_double), POINTER(c_double), POINTER(c_double)], errcheck=False)
geom_close_rings = void_output(lgdal.OGR_G_CloseRings, [c_void_p])
# Topology routines.
ogr_contains = topology_func(lgdal.OGR_G_Contains)
ogr_crosses = topology_func(lgdal.OGR_G_Crosses)
ogr_disjoint = topology_func(lgdal.OGR_G_Disjoint)
ogr_equals = topology_func(lgdal.OGR_G_Equals)
ogr_intersects = topology_func(lgdal.OGR_G_Intersects)
ogr_overlaps = topology_func(lgdal.OGR_G_Overlaps)
ogr_touches = topology_func(lgdal.OGR_G_Touches)
ogr_within = topology_func(lgdal.OGR_G_Within)
# Transformation routines.
geom_transform = void_output(lgdal.OGR_G_Transform, [c_void_p, c_void_p], errcheck=True)
geom_transform_to = void_output(lgdal.OGR_G_TransformTo, [c_void_p, c_void_p], errcheck=True)
# For retrieving the envelope of the geometry.
get_envelope = lgdal.OGR_G_GetEnvelope
get_envelope.restype = None
get_envelope.argtypes = [c_void_p, POINTER(OGREnvelope)]
get_envelope.errcheck = check_envelope

View File

@ -0,0 +1,70 @@
from ctypes import c_char_p, c_int, c_void_p, POINTER
from django.contrib.gis.gdal.libgdal import lgdal
from django.contrib.gis.gdal.prototypes.generation import \
const_string_output, double_output, int_output, \
srs_output, string_output, void_output
## Shortcut generation for routines with known parameters.
def srs_double(f):
"""
Creates a function prototype for the OSR routines that take
the OSRSpatialReference object and
"""
return double_output(f, [c_void_p, POINTER(c_int)], errcheck=True)
def units_func(f):
"""
Creates a ctypes function prototype for OSR units functions, e.g.,
OSRGetAngularUnits, OSRGetLinearUnits.
"""
return double_output(f, [c_void_p, POINTER(c_char_p)], strarg=True)
# Creation & destruction.
clone_srs = srs_output(lgdal.OSRClone, [c_void_p])
new_srs = srs_output(lgdal.OSRNewSpatialReference, [c_char_p])
release_srs = void_output(lgdal.OSRRelease, [c_void_p])
srs_validate = void_output(lgdal.OSRValidate, [c_void_p], errcheck=True)
# Getting the semi_major, semi_minor, and flattening functions.
semi_major = srs_double(lgdal.OSRGetSemiMajor)
semi_minor = srs_double(lgdal.OSRGetSemiMinor)
invflattening = srs_double(lgdal.OSRGetInvFlattening)
# WKT, PROJ, EPSG, XML importation routines.
from_wkt = void_output(lgdal.OSRImportFromWkt, [c_void_p, POINTER(c_char_p)])
from_proj = void_output(lgdal.OSRImportFromProj4, [c_void_p, c_char_p])
from_epsg = void_output(lgdal.OSRImportFromEPSG, [c_void_p, c_int])
from_xml = void_output(lgdal.OSRImportFromXML, [c_void_p, c_char_p])
# Morphing to/from ESRI WKT.
morph_to_esri = void_output(lgdal.OSRMorphToESRI, [c_void_p])
morph_from_esri = void_output(lgdal.OSRMorphFromESRI, [c_void_p])
# Identifying the EPSG
identify_epsg = void_output(lgdal.OSRAutoIdentifyEPSG, [c_void_p])
# Getting the angular_units, linear_units functions
linear_units = units_func(lgdal.OSRGetLinearUnits)
angular_units = units_func(lgdal.OSRGetAngularUnits)
# For exporting to WKT, PROJ.4, "Pretty" WKT, and XML.
to_wkt = string_output(lgdal.OSRExportToWkt, [c_void_p, POINTER(c_char_p)])
to_proj = string_output(lgdal.OSRExportToProj4, [c_void_p, POINTER(c_char_p)])
to_pretty_wkt = string_output(lgdal.OSRExportToPrettyWkt, [c_void_p, POINTER(c_char_p), c_int], offset=-2)
# FIXME: This leaks memory, still don't know why.
to_xml = string_output(lgdal.OSRExportToXML, [c_void_p, POINTER(c_char_p), c_char_p], offset=-2)
# String attribute retrival routines.
get_attr_value = const_string_output(lgdal.OSRGetAttrValue, [c_void_p, c_char_p, c_int])
get_auth_name = const_string_output(lgdal.OSRGetAuthorityName, [c_void_p, c_char_p])
get_auth_code = const_string_output(lgdal.OSRGetAuthorityCode, [c_void_p, c_char_p])
# SRS Properties
isgeographic = int_output(lgdal.OSRIsGeographic, [c_void_p])
islocal = int_output(lgdal.OSRIsLocal, [c_void_p])
isprojected = int_output(lgdal.OSRIsProjected, [c_void_p])
# Coordinate transformation
new_ct= srs_output(lgdal.OCTNewCoordinateTransformation, [c_void_p, c_void_p])
destroy_ct = void_output(lgdal.OCTDestroyCoordinateTransformation, [c_void_p])

View File

@ -28,50 +28,18 @@
""" """
import re import re
from types import StringType, UnicodeType, TupleType from types import StringType, UnicodeType, TupleType
from ctypes import \ from ctypes import byref, c_char_p, c_int, c_void_p
c_char_p, c_int, c_double, c_void_p, POINTER, \
byref, string_at, create_string_buffer
# Getting the GDAL C Library
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.error import check_err, OGRException, SRSException from django.contrib.gis.gdal.error import OGRException, SRSException
from django.contrib.gis.gdal.prototypes.srs import *
#### ctypes function prototypes ####
def ellipsis_func(f):
"""
Creates a ctypes function prototype for OSR ellipsis property functions, e.g.,
OSRGetSemiMajor, OSRGetSemiMinor, OSRGetInvFlattening.
"""
f.restype = c_double
f.argtypes = [c_void_p, POINTER(c_int)]
return f
# Getting the semi_major, semi_minor, and flattening functions.
semi_major = ellipsis_func(lgdal.OSRGetSemiMajor)
semi_minor = ellipsis_func(lgdal.OSRGetSemiMinor)
invflattening = ellipsis_func(lgdal.OSRGetInvFlattening)
def units_func(f):
"""
Creates a ctypes function prototype for OSR units functions, e.g.,
OSRGetAngularUnits, OSRGetLinearUnits.
"""
f.restype = c_double
f.argtypes = [c_void_p, POINTER(c_char_p)]
return f
# Getting the angular_units, linear_units functions
linear_units = units_func(lgdal.OSRGetLinearUnits)
angular_units = units_func(lgdal.OSRGetAngularUnits)
#### Spatial Reference class. #### #### Spatial Reference class. ####
class SpatialReference(object): class SpatialReference(object):
""" """
A wrapper for the OGRSpatialReference object. According to the GDAL website, A wrapper for the OGRSpatialReference object. According to the GDAL website,
the SpatialReference object 'provide[s] services to represent coordinate the SpatialReference object "provide[s] services to represent coordinate
systems (projections and datums) and to transform between them.' systems (projections and datums) and to transform between them."
""" """
# Well-Known Geographical Coordinate System Name # Well-Known Geographical Coordinate System Name
@ -82,9 +50,8 @@ class SpatialReference(object):
def __init__(self, srs_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 = None # Initially NULL # Intializing pointer and string buffer.
self._ptr = None
# Creating an initial empty string buffer.
buf = c_char_p('') buf = c_char_p('')
# Encoding to ASCII if unicode passed in. # Encoding to ASCII if unicode passed in.
@ -92,37 +59,40 @@ class SpatialReference(object):
srs_input = srs_input.encode('ascii') srs_input = srs_input.encode('ascii')
if isinstance(srs_input, StringType): if isinstance(srs_input, StringType):
# Is this an EPSG well known name?
m = self._epsg_regex.match(srs_input) m = self._epsg_regex.match(srs_input)
if m: if m:
# Is this an EPSG well known name?
srs_type = 'epsg' srs_type = 'epsg'
srs_input = int(m.group('epsg')) srs_input = int(m.group('epsg'))
# Is this a short-hand well known name?
elif srs_input in self._well_known: elif srs_input in self._well_known:
# Is this a short-hand well known name?
srs_type = 'epsg' srs_type = 'epsg'
srs_input = self._well_known[srs_input] srs_input = self._well_known[srs_input]
elif srs_type == 'proj': elif srs_type == 'proj':
pass pass
else: else:
# Setting the buffer with WKT, PROJ.4 string, etc.
buf = c_char_p(srs_input) buf = c_char_p(srs_input)
elif isinstance(srs_input, int): elif isinstance(srs_input, int):
if srs_type == 'wkt': srs_type = 'epsg' # want to try epsg if only integer provided # EPSG integer code was input.
if srs_type not in ('epsg', 'ogr'): if srs_type != 'epsg': srs_type = 'epsg'
raise SRSException('Integer input requires SRS type of "ogr" or "epsg".') elif isinstance(srs_input, c_void_p):
srs_type = 'ogr'
else: else:
raise TypeError('Invalid SRS type "%s"' % srs_type) raise TypeError('Invalid SRS type "%s"' % srs_type)
# Calling OSRNewSpatialReference with the string buffer.
if srs_type == 'ogr': if srs_type == 'ogr':
srs = srs_input # SRS input is OGR pointer # SRS input is OGR pointer
srs = srs_input
else: else:
srs = lgdal.OSRNewSpatialReference(buf) # Creating a new pointer, using the string buffer.
srs = new_srs(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: %s' % srs_input) raise SRSException('Could not create spatial reference from: %s' % srs_input)
else: else:
self._srs = srs self._ptr = 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(srs_input) if srs_type == 'proj': self.import_proj(srs_input)
@ -130,7 +100,7 @@ class SpatialReference(object):
def __del__(self): def __del__(self):
"Destroys this spatial reference." "Destroys this spatial reference."
if self._srs: lgdal.OSRRelease(self._srs) if self._ptr: release_srs(self._ptr)
def __getitem__(self, target): def __getitem__(self, target):
""" """
@ -172,43 +142,48 @@ class SpatialReference(object):
"The string representation uses 'pretty' WKT." "The string representation uses 'pretty' WKT."
return self.pretty_wkt return self.pretty_wkt
def _string_ptr(self, ptr):
"""
Returns the string at the pointer if it is valid, None if the pointer
is NULL.
"""
if not ptr: return None
else: return string_at(ptr)
#### SpatialReference Methods #### #### SpatialReference Methods ####
def auth_name(self, target):
"Getting the authority name for the target node."
ptr = lgdal.OSRGetAuthorityName(self._srs, c_char_p(target))
return self._string_ptr(ptr)
def auth_code(self, target):
"Getting the authority code for the given target node."
ptr = lgdal.OSRGetAuthorityCode(self._srs, c_char_p(target))
return self._string_ptr(ptr)
def attr_value(self, target, index=0): def attr_value(self, target, index=0):
""" """
The attribute value for the given target node (e.g. 'PROJCS'). The index The attribute value for the given target node (e.g. 'PROJCS'). The index
keyword specifies an index of the child node to return. keyword specifies an index of the child node to return.
""" """
if not isinstance(target, str): if not isinstance(target, str) or not isinstance(index, int):
raise TypeError('Attribute target must be a string') raise TypeError
ptr = lgdal.OSRGetAttrValue(self._srs, c_char_p(target), c_int(index)) return get_attr_value(self._ptr, target, index)
return self._string_ptr(ptr)
def auth_name(self, target):
"Returns the authority name for the given string target node."
return get_auth_name(self._ptr, target)
def auth_code(self, target):
"Returns the authority code for the given string target node."
return get_auth_code(self._ptr, target)
def clone(self):
"Returns a clone of this SpatialReference object."
return SpatialReference(clone_srs(self._ptr))
def from_esri(self):
"Morphs this SpatialReference from ESRI's format to EPSG."
morph_from_esri(self._ptr)
def identify_epsg(self):
"""
This method inspects the WKT of this SpatialReference, and will
add EPSG authority nodes where an EPSG identifier is applicable.
"""
identify_epsg(self._ptr)
def to_esri(self):
"Morphs this SpatialReference to ESRI's format."
morph_to_esri(self._ptr)
def validate(self): def validate(self):
"Checks to see if the given spatial reference is valid." "Checks to see if the given spatial reference is valid."
check_err(lgdal.OSRValidate(self._srs)) srs_validate(self._ptr)
def clone(self):
"Returns a clone of this Spatial Reference."
return SpatialReference(lgdal.OSRClone(self._srs), 'ogr')
#### Name & SRID properties ####
@property @property
def name(self): def name(self):
"Returns the name of this Spatial Reference." "Returns the name of this Spatial Reference."
@ -226,43 +201,29 @@ class SpatialReference(object):
return None return None
#### Unit Properties #### #### Unit Properties ####
def _cache_linear(self):
"Caches the linear units value and name."
if not hasattr(self, '_linear_units') or not hasattr(self, '_linear_name'):
name_buf = c_char_p()
self._linear_units = linear_units(self._srs, byref(name_buf))
self._linear_name = string_at(name_buf)
@property @property
def linear_name(self): def linear_name(self):
"Returns the name of the linear units." "Returns the name of the linear units."
self._cache_linear() units, name = linear_units(self._ptr, byref(c_char_p()))
return self._linear_name return name
@property @property
def linear_units(self): def linear_units(self):
"Returns the value of the linear units." "Returns the value of the linear units."
self._cache_linear() units, name = linear_units(self._ptr, byref(c_char_p()))
return self._linear_units return units
def _cache_angular(self):
"Caches the angular units value and name."
name_buf = c_char_p()
if not hasattr(self, '_angular_units') or not hasattr(self, '_angular_name'):
self._angular_units = angular_units(self._srs, byref(name_buf))
self._angular_name = string_at(name_buf)
@property @property
def angular_name(self): def angular_name(self):
"Returns the name of the angular units." "Returns the name of the angular units."
self._cache_angular() units, name = angular_units(self._ptr, byref(c_char_p()))
return self._angular_name return name
@property @property
def angular_units(self): def angular_units(self):
"Returns the value of the angular units." "Returns the value of the angular units."
self._cache_angular() units, name = angular_units(self._ptr, byref(c_char_p()))
return self._angular_units return units
#### Spheroid/Ellipsoid Properties #### #### Spheroid/Ellipsoid Properties ####
@property @property
@ -276,26 +237,17 @@ class SpatialReference(object):
@property @property
def semi_major(self): def semi_major(self):
"Returns the Semi Major Axis for this Spatial Reference." "Returns the Semi Major Axis for this Spatial Reference."
err = c_int(0) return semi_major(self._ptr, byref(c_int()))
sm = semi_major(self._srs, byref(err))
check_err(err.value)
return sm
@property @property
def semi_minor(self): def semi_minor(self):
"Returns the Semi Minor Axis for this Spatial Reference." "Returns the Semi Minor Axis for this Spatial Reference."
err = c_int() return semi_minor(self._ptr, byref(c_int()))
sm = semi_minor(self._srs, byref(err))
check_err(err.value)
return sm
@property @property
def inverse_flattening(self): def inverse_flattening(self):
"Returns the Inverse Flattening for this Spatial Reference." "Returns the Inverse Flattening for this Spatial Reference."
err = c_int() return invflattening(self._ptr, byref(c_int()))
inv_flat = invflattening(self._srs, byref(err))
check_err(err.value)
return inv_flat
#### Boolean Properties #### #### Boolean Properties ####
@property @property
@ -304,14 +256,12 @@ class SpatialReference(object):
Returns True if this SpatialReference is geographic Returns True if this SpatialReference is geographic
(root node is GEOGCS). (root node is GEOGCS).
""" """
if lgdal.OSRIsGeographic(self._srs): return True return bool(isgeographic(self._ptr))
else: return False
@property @property
def local(self): def local(self):
"Returns True if this SpatialReference is local (root node is LOCAL_CS)." "Returns True if this SpatialReference is local (root node is LOCAL_CS)."
if lgdal.OSRIsLocal(self._srs): return True return bool(islocal(self._ptr))
else: return False
@property @property
def projected(self): def projected(self):
@ -319,48 +269,40 @@ class SpatialReference(object):
Returns True if this SpatialReference is a projected coordinate system Returns True if this SpatialReference is a projected coordinate system
(root node is PROJCS). (root node is PROJCS).
""" """
if lgdal.OSRIsProjected(self._srs): return True return bool(isprojected(self._ptr))
else: return False
#### Import Routines ##### #### Import Routines #####
def import_wkt(self, wkt): def import_wkt(self, wkt):
"Imports the Spatial Reference from OGC WKT (string)" "Imports the Spatial Reference from OGC WKT (string)"
buf = create_string_buffer(wkt) from_wkt(self._ptr, byref(c_char_p(wkt)))
check_err(lgdal.OSRImportFromWkt(self._srs, byref(buf)))
def import_proj(self, proj): def import_proj(self, proj):
"Imports the Spatial Reference from a PROJ.4 string." "Imports the Spatial Reference from a PROJ.4 string."
check_err(lgdal.OSRImportFromProj4(self._srs, create_string_buffer(proj))) from_proj(self._ptr, proj)
def import_epsg(self, epsg): def import_epsg(self, epsg):
"Imports the Spatial Reference from the EPSG code (an integer)." "Imports the Spatial Reference from the EPSG code (an integer)."
check_err(lgdal.OSRImportFromEPSG(self._srs, c_int(epsg))) from_epsg(self._ptr, epsg)
def import_xml(self, xml): def import_xml(self, xml):
"Imports the Spatial Reference from an XML string." "Imports the Spatial Reference from an XML string."
check_err(lgdal.OSRImportFromXML(self._srs, create_string_buffer(xml))) from_xml(self._ptr, xml)
#### Export Properties #### #### Export Properties ####
@property @property
def wkt(self): def wkt(self):
"Returns the WKT representation of this Spatial Reference." "Returns the WKT representation of this Spatial Reference."
w = c_char_p() return to_wkt(self._ptr, byref(c_char_p()))
check_err(lgdal.OSRExportToWkt(self._srs, byref(w)))
if w: return string_at(w)
@property @property
def pretty_wkt(self, simplify=0): def pretty_wkt(self, simplify=0):
"Returns the 'pretty' representation of the WKT." "Returns the 'pretty' representation of the WKT."
w = c_char_p() return to_pretty_wkt(self._ptr, byref(c_char_p()), simplify)
check_err(lgdal.OSRExportToPrettyWkt(self._srs, byref(w), c_int(simplify)))
if w: return string_at(w)
@property @property
def proj(self): def proj(self):
"Returns the PROJ.4 representation for this Spatial Reference." "Returns the PROJ.4 representation for this Spatial Reference."
w = c_char_p() return to_proj(self._ptr, byref(c_char_p()))
check_err(lgdal.OSRExportToProj4(self._srs, byref(w)))
if w: return string_at(w)
@property @property
def proj4(self): def proj4(self):
@ -370,28 +312,34 @@ class SpatialReference(object):
@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."
w = c_char_p() # FIXME: This leaks memory, have to figure out why.
check_err(lgdal.OSRExportToXML(self._srs, byref(w), create_string_buffer(dialect))) return to_xml(self._ptr, byref(c_char_p()), dialect)
return string_at(w)
def to_esri(self):
"Morphs this SpatialReference to ESRI's format."
morph_to_esri(self._ptr)
def from_esri(self):
"Morphs this SpatialReference from ESRI's format to EPSG."
morph_from_esri(self._ptr)
class CoordTransform(object): class CoordTransform(object):
"A coordinate system transformation object." "The coordinate system transformation object."
def __init__(self, source, target): def __init__(self, source, target):
"Initializes on a source and target SpatialReference objects." "Initializes on a source and target SpatialReference objects."
self._ct = 0 # Initially NULL self._ptr = None # 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) self._ptr = new_ct(source._ptr, target._ptr)
if not ct: if not self._ptr:
raise SRSException('could not intialize CoordTransform object') raise SRSException('could not intialize CoordTransform object')
self._ct = ct
self._srs1_name = source.name self._srs1_name = source.name
self._srs2_name = target.name self._srs2_name = target.name
def __del__(self): def __del__(self):
"Deletes this Coordinate Transformation object." "Deletes this Coordinate Transformation object."
if self._ct: lgdal.OCTDestroyCoordinateTransformation(self._ct) if self._ptr: destroy_ct(self._ptr)
def __str__(self): def __str__(self):
return 'Transform from "%s" to "%s"' % (str(self._srs1_name), str(self._srs2_name)) return 'Transform from "%s" to "%s"' % (self._srs1_name, self._srs2_name)

View File

@ -15,10 +15,10 @@ class TestSHP:
setattr(self, key, value) setattr(self, key, value)
# List of acceptable data sources. # List of acceptable data sources.
ds_list = (TestSHP('test_point', nfeat=5, nfld=3, geom='POINT', gtype=1, fields={'dbl' : OFTReal, 'int' : OFTReal, 'str' : OFTString,}, ds_list = (TestSHP('test_point', nfeat=5, nfld=3, geom='POINT', gtype=1, fields={'dbl' : OFTReal, 'int' : OFTInteger, 'str' : OFTString,},
extent=(-1.35011,0.166623,-0.524093,0.824508), # Got extent from QGIS extent=(-1.35011,0.166623,-0.524093,0.824508), # Got extent from QGIS
srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]'), srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]'),
TestSHP('test_poly', nfeat=3, nfld=3, geom='POLYGON', gtype=3, fields={'float' : OFTReal, 'int' : OFTReal, 'str' : OFTString,}, TestSHP('test_poly', nfeat=3, nfld=3, geom='POLYGON', gtype=3, fields={'float' : OFTReal, 'int' : OFTInteger, 'str' : OFTString,},
extent=(-1.01513,-0.558245,0.161876,0.839637), # Got extent from QGIS extent=(-1.01513,-0.558245,0.161876,0.839637), # Got extent from QGIS
srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]'), srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]'),
) )
@ -101,17 +101,12 @@ class DataSourceTest(unittest.TestCase):
# Making sure the fields match to an appropriate OFT type. # Making sure the fields match to an appropriate OFT type.
for k, v in source.fields.items(): for k, v in source.fields.items():
fld = feat[k] # Indexing with string value # Making sure we get the proper OGR Field instance, using
# a string value index for the feature.
# Asserting the string representation, and making sure we get self.assertEqual(True, isinstance(feat[k], v))
# the proper OGR Field instance.
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))
# Testing __iter__ on the Feature # Testing __iter__ on the Feature
for fld in feat: self.assertEqual(fld.name in source.fields.keys(), True) for fld in feat: self.assertEqual(True, fld.name in source.fields.keys())
def test05_geometries(self): def test05_geometries(self):
"Testing Geometries from Data Source Features." "Testing Geometries from Data Source Features."

View File

@ -1,5 +1,6 @@
import unittest import unittest
from django.contrib.gis.gdal import OGRGeometry, OGRGeomType, OGRException, SpatialReference from django.contrib.gis.gdal import OGRGeometry, OGRGeomType, \
OGRException, OGRIndexError, SpatialReference
from django.contrib.gis.tests.geometries import * from django.contrib.gis.tests.geometries import *
class OGRGeomTest(unittest.TestCase): class OGRGeomTest(unittest.TestCase):
@ -94,41 +95,78 @@ class OGRGeomTest(unittest.TestCase):
def test04_linestring(self): def test04_linestring(self):
"Testing LineString objects." "Testing LineString objects."
prev = OGRGeometry('POINT(0 0)')
for ls in linestrings: for ls in linestrings:
linestr = OGRGeometry(ls.wkt) linestr = OGRGeometry(ls.wkt)
self.assertEqual(2, linestr.geom_type) self.assertEqual(2, linestr.geom_type)
self.assertEqual('LINESTRING', linestr.geom_name) self.assertEqual('LINESTRING', linestr.geom_name)
self.assertEqual(ls.n_p, linestr.point_count) self.assertEqual(ls.n_p, linestr.point_count)
self.assertEqual(ls.tup, linestr.tuple) self.assertEqual(ls.tup, linestr.tuple)
self.assertEqual(True, linestr == OGRGeometry(ls.wkt))
self.assertEqual(True, linestr != prev)
self.assertRaises(OGRIndexError, linestr.__getitem__, len(linestr))
prev = linestr
def test05_multilinestring(self): def test05_multilinestring(self):
"Testing MultiLineString objects." "Testing MultiLineString objects."
prev = OGRGeometry('POINT(0 0)')
for mls in multilinestrings: for mls in multilinestrings:
mlinestr = OGRGeometry(mls.wkt) mlinestr = OGRGeometry(mls.wkt)
self.assertEqual(5, mlinestr.geom_type) self.assertEqual(5, mlinestr.geom_type)
self.assertEqual('MULTILINESTRING', mlinestr.geom_name) self.assertEqual('MULTILINESTRING', mlinestr.geom_name)
self.assertEqual(mls.n_p, mlinestr.point_count) self.assertEqual(mls.n_p, mlinestr.point_count)
self.assertEqual(mls.tup, mlinestr.tuple) self.assertEqual(mls.tup, mlinestr.tuple)
self.assertEqual(True, mlinestr == OGRGeometry(mls.wkt))
self.assertEqual(True, mlinestr != prev)
prev = mlinestr
for ls in mlinestr:
self.assertEqual(2, ls.geom_type)
self.assertEqual('LINESTRING', ls.geom_name)
self.assertRaises(OGRIndexError, mlinestr.__getitem__, len(mlinestr))
def test06_polygons(self): def test06_linearring(self):
"Testing LinearRing objects."
prev = OGRGeometry('POINT(0 0)')
for rr in linearrings:
lr = OGRGeometry(rr.wkt)
#self.assertEqual(101, lr.geom_type.num)
self.assertEqual('LINEARRING', lr.geom_name)
self.assertEqual(rr.n_p, len(lr))
self.assertEqual(True, lr == OGRGeometry(rr.wkt))
self.assertEqual(True, lr != prev)
prev = lr
def test07a_polygons(self):
"Testing Polygon objects." "Testing Polygon objects."
prev = OGRGeometry('POINT(0 0)')
for p in polygons: for p in polygons:
poly = OGRGeometry(p.wkt) poly = OGRGeometry(p.wkt)
self.assertEqual(3, poly.geom_type) self.assertEqual(3, poly.geom_type)
self.assertEqual('POLYGON', poly.geom_name) self.assertEqual('POLYGON', poly.geom_name)
self.assertEqual(p.n_p, poly.point_count) self.assertEqual(p.n_p, poly.point_count)
first = True self.assertEqual(p.n_i + 1, len(poly))
# Testing area & centroid.
self.assertAlmostEqual(p.area, poly.area, 9)
x, y = poly.centroid.tuple
self.assertAlmostEqual(p.centroid[0], x, 9)
self.assertAlmostEqual(p.centroid[1], y, 9)
# Testing equivalence
self.assertEqual(True, poly == OGRGeometry(p.wkt))
self.assertEqual(True, poly != prev)
if p.ext_ring_cs:
ring = poly[0]
self.assertEqual(p.ext_ring_cs, ring.tuple)
self.assertEqual(p.ext_ring_cs, poly[0].tuple)
self.assertEqual(len(p.ext_ring_cs), ring.point_count)
for r in poly: for r in poly:
if first and p.ext_ring_cs: self.assertEqual('LINEARRING', r.geom_name)
first = False
# Testing the equivilance of the exerior rings
# since the first iteration will be the exterior ring.
self.assertEqual(len(p.ext_ring_cs), r.point_count)
self.assertEqual(p.ext_ring_cs, r.tuple)
def test07_closepolygons(self): def test07b_closepolygons(self):
"Testing closing Polygon objects." "Testing closing Polygon objects."
# Both rings in this geometry are not closed. # 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))') 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) self.assertEqual(8, poly.point_count)
@ -149,6 +187,7 @@ class OGRGeomTest(unittest.TestCase):
def test08_multipolygons(self): def test08_multipolygons(self):
"Testing MultiPolygon objects." "Testing MultiPolygon objects."
prev = OGRGeometry('POINT(0 0)')
for mp in multipolygons: for mp in multipolygons:
mpoly = OGRGeometry(mp.wkt) mpoly = OGRGeometry(mp.wkt)
self.assertEqual(6, mpoly.geom_type) self.assertEqual(6, mpoly.geom_type)
@ -156,27 +195,53 @@ class OGRGeomTest(unittest.TestCase):
if mp.valid: if mp.valid:
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))
self.assertRaises(OGRIndexError, mpoly.__getitem__, len(mpoly))
for p in mpoly:
self.assertEqual('POLYGON', p.geom_name)
self.assertEqual(3, p.geom_type)
self.assertEqual(mpoly.wkt, OGRGeometry(mp.wkt).wkt)
def test09_srs(self): def test09_srs(self):
"Testing OGR Geometries with Spatial Reference objects." "Testing OGR Geometries with Spatial Reference objects."
for mp in multipolygons: for mp in multipolygons:
# Creating a geometry w/spatial reference
sr = SpatialReference('WGS84') sr = SpatialReference('WGS84')
mpoly = OGRGeometry(mp.wkt, sr) mpoly = OGRGeometry(mp.wkt, sr)
self.assertEqual(sr.wkt, mpoly.srs.wkt) self.assertEqual(sr.wkt, mpoly.srs.wkt)
# Ensuring that SRS is propagated to clones.
klone = mpoly.clone()
self.assertEqual(sr.wkt, klone.srs.wkt)
# Ensuring all children geometries (polygons and their rings) all
# return the assigned spatial reference as well.
for poly in mpoly: for poly in mpoly:
self.assertEqual(sr.wkt, poly.srs.wkt) self.assertEqual(sr.wkt, poly.srs.wkt)
for ring in poly: for ring in poly:
self.assertEqual(sr.wkt, ring.srs.wkt) self.assertEqual(sr.wkt, ring.srs.wkt)
# Ensuring SRS propagate in topological ops.
a, b = topology_geoms[0]
a, b = OGRGeometry(a.wkt, sr), OGRGeometry(b.wkt, sr)
diff = a.difference(b)
union = a.union(b)
self.assertEqual(sr.wkt, diff.srs.wkt)
self.assertEqual(sr.srid, union.srs.srid)
# Instantiating w/an integer SRID
mpoly = OGRGeometry(mp.wkt, 4326) mpoly = OGRGeometry(mp.wkt, 4326)
self.assertEqual(4326, mpoly.srid) self.assertEqual(4326, mpoly.srid)
mpoly.srs = SpatialReference(4269) mpoly.srs = SpatialReference(4269)
self.assertEqual(4269, mpoly.srid) self.assertEqual(4269, mpoly.srid)
self.assertEqual('NAD83', mpoly.srs.name) self.assertEqual('NAD83', mpoly.srs.name)
# Incrementing through the multipolyogn after the spatial reference
# has been re-assigned.
for poly in mpoly: for poly in mpoly:
self.assertEqual(mpoly.srs.wkt, poly.srs.wkt) self.assertEqual(mpoly.srs.wkt, poly.srs.wkt)
poly.srs = 32140 poly.srs = 32140
for ring in poly: for ring in poly:
# Changing each ring in the polygon
self.assertEqual(32140, ring.srs.srid) self.assertEqual(32140, ring.srs.srid)
self.assertEqual('NAD83 / Texas South Central', ring.srs.name) self.assertEqual('NAD83 / Texas South Central', ring.srs.name)
ring.srs = str(SpatialReference(4326)) # back to WGS84 ring.srs = str(SpatialReference(4326)) # back to WGS84
@ -187,7 +252,58 @@ class OGRGeomTest(unittest.TestCase):
self.assertEqual('WGS 72', ring.srs.name) self.assertEqual('WGS 72', ring.srs.name)
self.assertEqual(4322, ring.srid) self.assertEqual(4322, ring.srid)
def test10_difference(self):
"Testing difference()."
for i in xrange(len(topology_geoms)):
g_tup = topology_geoms[i]
a = OGRGeometry(g_tup[0].wkt)
b = OGRGeometry(g_tup[1].wkt)
d1 = OGRGeometry(diff_geoms[i].wkt)
d2 = a.difference(b)
self.assertEqual(d1, d2)
self.assertEqual(d1, a - b) # __sub__ is difference operator
a -= b # testing __isub__
self.assertEqual(d1, a)
def test11_intersection(self):
"Testing intersects() and intersection()."
for i in xrange(len(topology_geoms)):
g_tup = topology_geoms[i]
a = OGRGeometry(g_tup[0].wkt)
b = OGRGeometry(g_tup[1].wkt)
i1 = OGRGeometry(intersect_geoms[i].wkt)
self.assertEqual(True, a.intersects(b))
i2 = a.intersection(b)
self.assertEqual(i1, i2)
self.assertEqual(i1, a & b) # __and__ is intersection operator
a &= b # testing __iand__
self.assertEqual(i1, a)
def test12_symdifference(self):
"Testing sym_difference()."
for i in xrange(len(topology_geoms)):
g_tup = topology_geoms[i]
a = OGRGeometry(g_tup[0].wkt)
b = OGRGeometry(g_tup[1].wkt)
d1 = OGRGeometry(sdiff_geoms[i].wkt)
d2 = a.sym_difference(b)
self.assertEqual(d1, d2)
self.assertEqual(d1, a ^ b) # __xor__ is symmetric difference operator
a ^= b # testing __ixor__
self.assertEqual(d1, a)
def test13_union(self):
"Testing union()."
for i in xrange(len(topology_geoms)):
g_tup = topology_geoms[i]
a = OGRGeometry(g_tup[0].wkt)
b = OGRGeometry(g_tup[1].wkt)
u1 = OGRGeometry(union_geoms[i].wkt)
u2 = a.union(b)
self.assertEqual(u1, u2)
self.assertEqual(u1, a | b) # __or__ is union operator
a |= b # testing __ior__
self.assertEqual(u1, a)
def suite(): def suite():
s = unittest.TestSuite() s = unittest.TestSuite()