From 0be7b58460f6a9367c6158c7be39de3d3e9d24c1 Mon Sep 17 00:00:00 2001 From: Justin Bronn Date: Sun, 22 Jul 2007 05:13:22 +0000 Subject: gis: got rid of CamelCase module names and made minor improvements in gdal; made srid a mutable property in geos geometries git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@5749 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/contrib/gis/gdal/DataSource.py | 128 ------- django/contrib/gis/gdal/Driver.py | 86 ----- django/contrib/gis/gdal/Envelope.py | 112 ------ django/contrib/gis/gdal/Feature.py | 109 ------ django/contrib/gis/gdal/Field.py | 93 ----- django/contrib/gis/gdal/Layer.py | 113 ------ django/contrib/gis/gdal/OGRError.py | 35 -- django/contrib/gis/gdal/OGRGeometry.py | 540 ---------------------------- django/contrib/gis/gdal/SpatialReference.py | 339 ----------------- django/contrib/gis/gdal/__init__.py | 13 +- django/contrib/gis/gdal/datasource.py | 128 +++++++ django/contrib/gis/gdal/driver.py | 85 +++++ django/contrib/gis/gdal/envelope.py | 112 ++++++ django/contrib/gis/gdal/error.py | 35 ++ django/contrib/gis/gdal/feature.py | 109 ++++++ django/contrib/gis/gdal/field.py | 93 +++++ django/contrib/gis/gdal/geometries.py | 496 +++++++++++++++++++++++++ django/contrib/gis/gdal/geomtype.py | 67 ++++ django/contrib/gis/gdal/layer.py | 114 ++++++ django/contrib/gis/gdal/libgdal.py | 4 +- django/contrib/gis/gdal/srs.py | 339 +++++++++++++++++ django/contrib/gis/geos/base.py | 6 +- django/contrib/gis/tests/test_gdal_ds.py | 4 +- 23 files changed, 1592 insertions(+), 1568 deletions(-) delete mode 100644 django/contrib/gis/gdal/DataSource.py delete mode 100644 django/contrib/gis/gdal/Driver.py delete mode 100644 django/contrib/gis/gdal/Envelope.py delete mode 100644 django/contrib/gis/gdal/Feature.py delete mode 100644 django/contrib/gis/gdal/Field.py delete mode 100644 django/contrib/gis/gdal/Layer.py delete mode 100644 django/contrib/gis/gdal/OGRError.py delete mode 100644 django/contrib/gis/gdal/OGRGeometry.py delete mode 100644 django/contrib/gis/gdal/SpatialReference.py create mode 100644 django/contrib/gis/gdal/datasource.py create mode 100644 django/contrib/gis/gdal/driver.py create mode 100644 django/contrib/gis/gdal/envelope.py create mode 100644 django/contrib/gis/gdal/error.py create mode 100644 django/contrib/gis/gdal/feature.py create mode 100644 django/contrib/gis/gdal/field.py create mode 100644 django/contrib/gis/gdal/geometries.py create mode 100644 django/contrib/gis/gdal/geomtype.py create mode 100644 django/contrib/gis/gdal/layer.py create mode 100644 django/contrib/gis/gdal/srs.py diff --git a/django/contrib/gis/gdal/DataSource.py b/django/contrib/gis/gdal/DataSource.py deleted file mode 100644 index 5ad434348b..0000000000 --- a/django/contrib/gis/gdal/DataSource.py +++ /dev/null @@ -1,128 +0,0 @@ -# types and ctypes -from types import StringType -from ctypes import c_char_p, c_int, c_void_p, byref, string_at - -# The GDAL C library, OGR exceptions, and the Layer object. -from django.contrib.gis.gdal.libgdal import lgdal -from django.contrib.gis.gdal.OGRError import OGRException, check_err -from django.contrib.gis.gdal.Layer import Layer -from django.contrib.gis.gdal.Driver import Driver - -""" - DataSource is a wrapper for the OGR Data Source object, which provides - an interface for reading vector geometry data from many different file - formats (including ESRI shapefiles). - - When instantiating a DataSource object, use the filename of a - GDAL-supported data source. For example, a SHP file or a - TIGER/Line file from the government. - - The ds_driver keyword is used internally when a ctypes pointer - is passed in directly. - - Example: - ds = DataSource('/home/foo/bar.shp') - for layer in ds: - 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: -# http://www.gdal.org/ogr/ogr__api_8h.html -# -# The OGR_DS_* routines are relevant here. - -class DataSource(object): - "Wraps an OGR Data Source object." - - _ds = 0 # Initially NULL - - #### Python 'magic' routines #### - def __init__(self, ds_input, ds_driver=False): - - # Registering all the drivers, this needs to be done - # _before_ we try to open up a data source. - if not lgdal.OGRGetDriverCount() and not lgdal.OGRRegisterAll(): - raise OGRException, 'Could not register all the OGR data source drivers!' - - if isinstance(ds_input, StringType): - - # The data source driver is a void pointer. - ds_driver = c_void_p() - - # OGROpen will auto-detect the data source type. - ds = lgdal.OGROpen(c_char_p(ds_input), c_int(0), byref(ds_driver)) - elif isinstance(ds_input, c_void_p) and isinstance(ds_driver, c_void_p): - ds = ds_input - else: - raise OGRException, 'Invalid data source input type: %s' % str(type(ds_input)) - - # Raise an exception if the returned pointer is NULL - if not ds: - self._ds = False - raise OGRException, 'Invalid data source file "%s"' % ds_input - else: - self._ds = ds - self._driver = Driver(ds_driver) - - def __del__(self): - "This releases the reference to the data source (destroying it if it's the only one)." - if self._ds: lgdal.OGRReleaseDataSource(self._ds) - - def __iter__(self): - "Allows for iteration over the layers in a data source." - for i in xrange(self.layer_count): - yield self.__getitem__(i) - - def __getitem__(self, index): - "Allows use of the index [] operator to get a layer at the index." - if isinstance(index, StringType): - l = lgdal.OGR_DS_GetLayerByName(self._ds, c_char_p(index)) - if not l: raise IndexError, 'invalid OGR Layer name given: "%s"' % index - else: - if index < 0 or index >= self.layer_count: - raise IndexError, 'index out of range' - l = lgdal.OGR_DS_GetLayer(self._ds, c_int(index)) - return Layer(l) - - def __len__(self): - "Returns the number of layers within the data source." - return self.layer_count - - def __str__(self): - "Returns OGR GetName and Driver for the Data Source." - return '%s (%s)' % (self.name, str(self.driver)) - - #### DataSource Properties #### - @property - def driver(self): - "Returns the Driver object for this Data Source." - return self._driver - - @property - def layer_count(self): - "Returns the number of layers in the data source." - return lgdal.OGR_DS_GetLayerCount(self._ds) - - @property - def name(self): - "Returns the name of the data source." - return string_at(lgdal.OGR_DS_GetName(self._ds)) - diff --git a/django/contrib/gis/gdal/Driver.py b/django/contrib/gis/gdal/Driver.py deleted file mode 100644 index 977dbe5358..0000000000 --- a/django/contrib/gis/gdal/Driver.py +++ /dev/null @@ -1,86 +0,0 @@ -# types and ctypes -from types import StringType -from ctypes import c_char_p, c_int, c_void_p, byref, string_at - -# The GDAL C library, OGR exceptions, and the Layer object. -from django.contrib.gis.gdal.libgdal import lgdal -from django.contrib.gis.gdal.OGRError import OGRException - -# For more information, see the OGR C API source code: -# http://www.gdal.org/ogr/ogr__api_8h.html -# -# The OGR_Dr_* routines are relevant here. - -class Driver(object): - "Wraps an OGR Data Source Driver." - - _dr = 0 # Initially NULL - - # Case-insensitive aliases for OGR Drivers. - _alias = {'esri' : 'ESRI Shapefile', - 'shp' : 'ESRI Shapefile', - 'shape' : 'ESRI Shapefile', - 'tiger' : 'TIGER', - 'tiger/line' : 'TIGER', - } - - def __init__(self, input, ptr=False): - "Initializes an OGR driver on either a string or integer input." - - if isinstance(input, StringType): - # If a string name of the driver was passed in - self._register() - - # Checking the alias dictionary (case-insensitive) to see if an alias - # exists for the given driver. - if input.lower() in self._alias: - name = c_char_p(self._alias[input.lower()]) - else: - name = c_char_p(input) - - # Attempting to get the OGR driver by the string name. - dr = lgdal.OGRGetDriverByName(name) - elif isinstance(input, int): - self._register() - dr = lgdal.OGRGetDriver(c_int(input)) - elif isinstance(input, c_void_p): - dr = input - else: - raise OGRException, 'Unrecognized input type for OGR Driver: %s' % str(type(input)) - - # Making sure we get a valid pointer to the OGR Driver - if not dr: - raise OGRException, 'Could not initialize OGR Driver on input: %s' % str(input) - self._dr = dr - - def __str__(self): - "Returns the string name of the OGR Driver." - return string_at(lgdal.OGR_Dr_GetName(self._dr)) - - def _register(self): - "Attempts to register all the data source drivers." - # Only register all if the driver count is 0 (or else all drivers - # will be registered over and over again) - if not self.driver_count and not lgdal.OGRRegisterAll(): - raise OGRException, 'Could not register all the OGR data source drivers!' - - # Driver properties - @property - def driver_count(self): - "Returns the number of OGR data source drivers registered." - return lgdal.OGRGetDriverCount() - - def create_ds(self, **kwargs): - "Creates a data source using the keyword args as name value options." - raise NotImplementedError - # Getting the options string - #options = '' - #n_opts = len(kwargs) - #for i in xrange(n_opts): - # options += '%s=%s' % (str(k), str(v)) - # if i < n_opts-1: options += ',' - #opts = c_char_p(options) - - - - diff --git a/django/contrib/gis/gdal/Envelope.py b/django/contrib/gis/gdal/Envelope.py deleted file mode 100644 index 1a7ff80529..0000000000 --- a/django/contrib/gis/gdal/Envelope.py +++ /dev/null @@ -1,112 +0,0 @@ -from ctypes import Structure, c_double -from types import TupleType - -""" - The GDAL/OGR library uses an Envelope structure to hold the bounding - box information for a geometry. The envelope (bounding box) contains - two pairs of coordinates, one for the lower left coordinate and one - for the upper right coordinate: - - +----------o Upper right; (max_x, max_y) - | | - | | - | | - Lower left (min_x, min_y) o----------+ - -""" - -# The OGR definition of an Envelope is a C structure containing four doubles. -# See the 'ogr_core.h' source file for more information: -# http://www.gdal.org/ogr/ogr__core_8h-source.html -class OGREnvelope(Structure): - "Represents the OGREnvelope C Structure." - _fields_ = [("MinX", c_double), - ("MaxX", c_double), - ("MinY", c_double), - ("MaxY", c_double), - ] - -class Envelope(object): - "A class that will wrap an OGR Envelope structure." - - def __init__(self, *args): - if len(args) == 1: - if isinstance(args[0], OGREnvelope): - # OGREnvelope (a ctypes Structure) was passed in. - self._envelope = args[0] - elif isinstance(args[0], TupleType) and len(args[0]) == 4: - # A Tuple was passed in - self._from_tuple(args[0]) - else: - raise OGRException, 'Incorrect type of argument: %s' % str(type(args[0])) - elif len(args) == 4: - self._from_tuple(args) - else: - raise OGRException, 'Incorrect number of arguments!' - - def __eq__(self, other): - "Returns true if the envelopes are equivalent; can compare against other Envelopes and 4-tuples." - if isinstance(other, Envelope): - return (self.min_x == other.min_x) and (self.min_y == other.min_y) and \ - (self.max_x == other.max_x) and (self.max_y == other.max_y) - elif isinstance(other, TupleType) and len(other) == 4: - return (self.min_x == other[0]) and (self.min_y == other[1]) and \ - (self.max_x == other[2]) and (self.max_y == other[3]) - else: - raise OGRException, 'Equivalence testing only works with other Envelopes.' - - def __str__(self): - "Returns a string representation of the tuple." - return str(self.tuple) - - def _from_tuple(self, tup): - "Initializes the C OGR Envelope structure from the given tuple." - self._envelope = OGREnvelope() - self._envelope.MinX = tup[0] - self._envelope.MinY = tup[1] - self._envelope.MaxX = tup[2] - self._envelope.MaxY = tup[3] - - @property - def min_x(self): - "Returns the value of the minimum X coordinate." - return self._envelope.MinX - - @property - def min_y(self): - "Returns the value of the minimum Y coordinate." - return self._envelope.MinY - - @property - def max_x(self): - "Returns the value of the maximum X coordinate." - return self._envelope.MaxX - - @property - def max_y(self): - "Returns the value of the maximum Y coordinate." - return self._envelope.MaxY - - @property - def ur(self): - "Returns the upper-right coordinate." - return (self.max_x, self.max_y) - - @property - def ll(self): - "Returns the lower-left coordinate." - return (self.min_x, self.min_y) - - @property - def tuple(self): - "Returns a tuple representing the envelope." - return (self.min_x, self.min_y, self.max_x, self.max_y) - - @property - def wkt(self): - "Returns WKT representing a Polygon for this envelope." - # TODO: Fix significant figures. - return 'POLYGON((%f %f,%f %f,%f %f,%f %f,%f %f))' % (self.min_x, self.min_y, self.min_x, self.max_y, - self.max_x, self.max_y, self.max_x, self.min_y, - self.min_x, self.min_y) - diff --git a/django/contrib/gis/gdal/Feature.py b/django/contrib/gis/gdal/Feature.py deleted file mode 100644 index 1c37feac8a..0000000000 --- a/django/contrib/gis/gdal/Feature.py +++ /dev/null @@ -1,109 +0,0 @@ -# types and ctypes -from types import StringType -from ctypes import c_char_p, c_int, string_at - -# The GDAL C library, OGR exception, and the Field object -from django.contrib.gis.gdal.libgdal import lgdal -from django.contrib.gis.gdal.OGRError import OGRException -from django.contrib.gis.gdal.Field import Field -from django.contrib.gis.gdal.OGRGeometry import OGRGeometry, OGRGeomType - -# For more information, see the OGR C API source code: -# http://www.gdal.org/ogr/ogr__api_8h.html -# -# The OGR_F_* routines are relevant here. -class Feature(object): - "A class that wraps an OGR Feature, needs to be instantiated from a Layer object." - - _feat = 0 # Initially NULL - - #### Python 'magic' routines #### - def __init__(self, f): - "Needs a C pointer (Python integer in ctypes) in order to initialize." - if not f: - raise OGRException, 'Cannot create OGR Feature, invalid pointer given.' - self._feat = f - self._fdefn = lgdal.OGR_F_GetDefnRef(f) - - def __del__(self): - "Releases a reference to this object." - if self._fdefn: lgdal.OGR_FD_Release(self._fdefn) - - def __getitem__(self, index): - "Gets the Field at the specified index." - if isinstance(index, StringType): - i = self.index(index) - else: - if index < 0 or index > self.num_fields: - raise IndexError, 'index out of range' - i = index - return Field(lgdal.OGR_F_GetFieldDefnRef(self._feat, c_int(i)), - string_at(lgdal.OGR_F_GetFieldAsString(self._feat, c_int(i)))) - - def __iter__(self): - "Iterates over each field in the Feature." - for i in xrange(self.num_fields): - yield self.__getitem__(i) - - def __len__(self): - "Returns the count of fields in this feature." - return self.num_fields - - def __str__(self): - "The string name of the feature." - return 'Feature FID %d in Layer<%s>' % (self.fid, self.layer_name) - - def __eq__(self, other): - "Does equivalence testing on the features." - if lgdal.OGR_F_Equal(self._feat, other._feat): - return True - else: - return False - - #### Feature Properties #### - @property - def fid(self): - "Returns the feature identifier." - return lgdal.OGR_F_GetFID(self._feat) - - @property - def layer_name(self): - "Returns the name of the layer for the feature." - return string_at(lgdal.OGR_FD_GetName(self._fdefn)) - - @property - def num_fields(self): - "Returns the number of fields in the Feature." - return lgdal.OGR_F_GetFieldCount(self._feat) - - @property - def fields(self): - "Returns a list of fields in the Feature." - return [ string_at(lgdal.OGR_Fld_GetNameRef(lgdal.OGR_FD_GetFieldDefn(self._fdefn, i))) - for i in xrange(self.num_fields) ] - @property - def geom(self): - "Returns the OGR Geometry for this Feature." - # A clone is used, so destruction of the Geometry won't bork the Feature. - return OGRGeometry(lgdal.OGR_G_Clone(lgdal.OGR_F_GetGeometryRef(self._feat))) - - @property - def geom_type(self): - "Returns the OGR Geometry Type for this Feture." - return OGRGeomType(lgdal.OGR_FD_GetGeomType(self._fdefn)) - - #### Feature Methods #### - def get(self, field): - "Returns the value of the field, instead of an instance of the Field object." - field_name = getattr(field, 'name', field) - return self.__getitem__(field_name).value - - def index(self, field_name): - "Returns the index of the given field name." - i = lgdal.OGR_F_GetFieldIndex(self._feat, c_char_p(field_name)) - if i < 0: raise IndexError, 'invalid OFT field name given: "%s"' % field_name - return i - - def clone(self): - "Clones this Feature." - return Feature(lgdal.OGR_F_Clone(self._feat)) diff --git a/django/contrib/gis/gdal/Field.py b/django/contrib/gis/gdal/Field.py deleted file mode 100644 index 858d7512ff..0000000000 --- a/django/contrib/gis/gdal/Field.py +++ /dev/null @@ -1,93 +0,0 @@ -from ctypes import string_at - -from django.contrib.gis.gdal.libgdal import lgdal -from django.contrib.gis.gdal.OGRError import OGRException - -# For more information, see the OGR C API source code: -# http://www.gdal.org/ogr/ogr__api_8h.html -# -# The OGR_Fld_* routines are relevant here. -class Field(object): - "A class that wraps an OGR Field, needs to be instantiated from a Feature object." - - _fld = 0 # Initially NULL - - #### Python 'magic' routines #### - def __init__(self, fld, val=''): - "Needs a C pointer (Python integer in ctypes) in order to initialize." - if not fld: - raise OGRException, 'Cannot create OGR Field, invalid pointer given.' - self._fld = fld - self._val = val - - # Setting the class depending upon the OGR Field Type (OFT) - self.__class__ = FIELD_CLASSES[self.type] - - def __str__(self): - "Returns the string representation of the Field." - return '%s (%s)' % (self.name, self.value) - - #### Field Properties #### - @property - def name(self): - "Returns the name of the field." - return string_at(lgdal.OGR_Fld_GetNameRef(self._fld)) - - @property - def type(self): - "Returns the type of this field." - return lgdal.OGR_Fld_GetType(self._fld) - - @property - def value(self): - "Returns the value of this type of field." - return self._val - -# The Field sub-classes for each OGR Field type. -class OFTInteger(Field): - @property - def value(self): - "Returns an integer contained in this field." - try: - return int(self._val) - except ValueError: - return 0 - -class OFTIntegerList(Field): pass -class OFTReal(Field): - @property - def value(self): - "Returns a float contained in this field." - - try: - return float(self._val) - except ValueError: - #FIXME: 0? None? - return 0 - - - -class OFTRealList(Field): pass -class OFTString(Field): pass -class OFTStringList(Field): pass -class OFTWideString(Field): pass -class OFTWideStringList(Field): pass -class OFTBinary(Field): pass -class OFTDate(Field): pass -class OFTTime(Field): pass -class OFTDateTime(Field): pass - -# Class mapping dictionary for OFT Types -FIELD_CLASSES = { 0 : OFTInteger, - 1 : OFTIntegerList, - 2 : OFTReal, - 3 : OFTRealList, - 4 : OFTString, - 5 : OFTStringList, - 6 : OFTWideString, - 7 : OFTWideStringList, - 8 : OFTBinary, - 9 : OFTDate, - 10 : OFTTime, - 11 : OFTDateTime, - } diff --git a/django/contrib/gis/gdal/Layer.py b/django/contrib/gis/gdal/Layer.py deleted file mode 100644 index 77bc9191d8..0000000000 --- a/django/contrib/gis/gdal/Layer.py +++ /dev/null @@ -1,113 +0,0 @@ -# Needed ctypes routines -from ctypes import c_int, c_long, c_void_p, byref, string_at - -# The GDAL C Library -from django.contrib.gis.gdal.libgdal import lgdal - -# Other GDAL imports. -from django.contrib.gis.gdal.Envelope import Envelope, OGREnvelope -from django.contrib.gis.gdal.Feature import Feature -from django.contrib.gis.gdal.OGRGeometry import OGRGeomType -from django.contrib.gis.gdal.OGRError import OGRException, check_err -from django.contrib.gis.gdal.SpatialReference import SpatialReference - -# For more information, see the OGR C API source code: -# http://www.gdal.org/ogr/ogr__api_8h.html -# -# The OGR_L_* routines are relevant here. - -get_srs = lgdal.OGR_L_GetSpatialRef -get_srs.restype = c_void_p -get_srs.argtypes = [c_void_p] - -class Layer(object): - "A class that wraps an OGR Layer, needs to be instantiated from a DataSource object." - - _layer = 0 # Initially NULL - - #### Python 'magic' routines #### - def __init__(self, l): - "Needs a C pointer (Python/ctypes integer) in order to initialize." - if not l: - raise OGRException, 'Cannot create Layer, invalid pointer given' - self._layer = l - self._ldefn = lgdal.OGR_L_GetLayerDefn(l) - - def __getitem__(self, index): - "Gets the Feature at the specified index." - def make_feature(offset): - return Feature(lgdal.OGR_L_GetFeature(self._layer, - c_long(offset))) - end = self.num_feat - if not isinstance(index, (slice, int)): - raise TypeError - - if isinstance(index,int): - # An integer index was given - if index < 0: - index = end - index - if index < 0 or index >= self.num_feat: - raise IndexError, 'index out of range' - return make_feature(index) - else: - # A slice was given - start, stop, stride = index.indices(end) - return [make_feature(offset) for offset in range(start,stop,stride)] - - def __iter__(self): - "Iterates over each Feature in the Layer." - #TODO: is OGR's GetNextFeature faster here? - for i in range(self.num_feat): - yield self.__getitem__(i) - - def __len__(self): - "The length is the number of features." - return self.num_feat - - def __str__(self): - "The string name of the layer." - return self.name - - #### Layer properties #### - @property - def extent(self): - "Returns the extent (an Envelope) of this layer." - env = OGREnvelope() - check_err(lgdal.OGR_L_GetExtent(self._layer, byref(env), c_int(1))) - return Envelope(env) - - @property - def name(self): - "Returns the name of this layer in the Data Source." - return string_at(lgdal.OGR_FD_GetName(self._ldefn)) - - @property - def num_feat(self, force=1): - "Returns the number of features in the Layer." - return lgdal.OGR_L_GetFeatureCount(self._layer, c_int(force)) - - @property - def num_fields(self): - "Returns the number of fields in the Layer." - return lgdal.OGR_FD_GetFieldCount(self._ldefn) - - @property - def geom_type(self): - "Returns the geometry type (OGRGeomType) of the Layer." - return OGRGeomType(lgdal.OGR_FD_GetGeomType(self._ldefn)) - - @property - def srs(self): - "Returns the Spatial Reference used in this Layer." - ptr = lgdal.OGR_L_GetSpatialRef(self._layer) - if ptr: - return SpatialReference(lgdal.OSRClone(ptr), 'ogr') - else: - return None - - @property - def fields(self): - "Returns a list of the fields available in this Layer." - return [ string_at(lgdal.OGR_Fld_GetNameRef(lgdal.OGR_FD_GetFieldDefn(self._ldefn, i))) - for i in xrange(self.num_fields) ] - diff --git a/django/contrib/gis/gdal/OGRError.py b/django/contrib/gis/gdal/OGRError.py deleted file mode 100644 index 7162e2eb18..0000000000 --- a/django/contrib/gis/gdal/OGRError.py +++ /dev/null @@ -1,35 +0,0 @@ -# OGR Error Codes -OGRERR_NONE = 0 -OGRERR_NOT_ENOUGH_DATA = 1 -OGRERR_NOT_ENOUGH_MEMORY = 2 -OGRERR_UNSUPPORTED_GEOMETRY_TYPE = 3 -OGRERR_UNSUPPORTED_OPERATION = 4 -OGRERR_CORRUPT_DATA = 5 -OGRERR_FAILURE = 6 -OGRERR_UNSUPPORTED_SRS = 7 - -# OGR & SRS Exceptions -class OGRException(Exception): pass -class SRSException(Exception): pass - -def check_err(code, msg=False): - "Checks the given OGRERR, and raises an exception where appropriate." - - if code == OGRERR_NONE: - return - elif code == OGRERR_NOT_ENOUGH_DATA: - raise OGRException, 'Not enough data!' - elif code == OGRERR_NOT_ENOUGH_MEMORY: - raise OGRException, 'Not enough memory!' - elif code == OGRERR_UNSUPPORTED_GEOMETRY_TYPE: - raise OGRException, 'Unsupported Geometry Type!' - elif code == OGRERR_UNSUPPORTED_OPERATION: - raise OGRException, 'Unsupported Operation!' - elif code == OGRERR_CORRUPT_DATA: - raise OGRException, 'Corrupt Data!' - elif code == OGRERR_FAILURE: - raise OGRException, 'OGR Failure!' - elif code == OGRERR_UNSUPPORTED_SRS: - raise SRSException, 'Unsupported SRS!' - else: - raise OGRException, 'Unknown error code: "%s"' % str(code) diff --git a/django/contrib/gis/gdal/OGRGeometry.py b/django/contrib/gis/gdal/OGRGeometry.py deleted file mode 100644 index 5a7b0b2480..0000000000 --- a/django/contrib/gis/gdal/OGRGeometry.py +++ /dev/null @@ -1,540 +0,0 @@ -# types & ctypes -from types import IntType, StringType -from ctypes import byref, string_at, c_char_p, c_double, c_int, c_void_p - -# Getting the GDAL C library and error checking facilities -from django.contrib.gis.gdal.libgdal import lgdal -from django.contrib.gis.gdal.Envelope import Envelope, OGREnvelope -from django.contrib.gis.gdal.OGRError import check_err, OGRException -from django.contrib.gis.gdal.SpatialReference import SpatialReference, CoordTransform - -""" - The OGRGeometry is a wrapper for using the OGR Geometry class - (see http://www.gdal.org/ogr/classOGRGeometry.html). OGRGeometry - may be instantiated when reading geometries from OGR Data Sources - (e.g. SHP files), or when given OGC WKT (a string). - - While the 'full' API is not present yet, the API is "pythonic" unlike - the traditional and "next-generation" OGR Python bindings. One major - advantage OGR Geometries have over their GEOS counterparts is support - for spatial reference systems and their transformation. - - Example: - >>> from django.contrib.gis.gdal import OGRGeometry, OGRGeomType, SpatialReference - >>> wkt1, wkt2 = 'POINT(-90 30)', 'POLYGON((0 0, 5 0, 5 5, 0 5)' - >>> pnt = OGRGeometry(wkt1) - >>> print pnt - POINT (-90 30) - >>> mpnt = OGRGeometry(OGRGeomType('MultiPoint'), SpatialReference('WGS84')) - >>> mpnt.add(wkt1) - >>> mpnt.add(wkt1) - >>> print mpnt - MULTIPOINT (-90 30,-90 30) - >>> print mpnt.srs.name - WGS 84 - >>> print mpnt.srs.proj - +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs - >>> mpnt.transform_to(SpatialReference('NAD27')) - >>> print mpnt.proj - +proj=longlat +ellps=clrk66 +datum=NAD27 +no_defs - >>> print mpnt - MULTIPOINT (-89.999930378602485 29.999797886557641,-89.999930378602485 29.999797886557641) - - The OGRGeomType class is to make it easy to specify an OGR geometry type: - >>> from django.contrib.gis.gdal import OGRGeomType - >>> gt1 = OGRGeomType(3) # Using an integer for the type - >>> gt2 = OGRGeomType('Polygon') # Using a string - >>> gt3 = OGRGeomType('POLYGON') # It's case-insensitive - >>> print gt1 == 3, gt1 == 'Polygon' # Equivalence works w/non-OGRGeomType objects - True -""" - -# For more information, see the OGR C API source code: -# http://www.gdal.org/ogr/ogr__api_8h.html -# -# The OGR_G_* routines are relevant here. - -#### ctypes prototypes #### -def pnt_func(f): - "For accessing point information." - f.restype = c_double - f.argtypes = [c_void_p, c_int] - return f -getx = pnt_func(lgdal.OGR_G_GetX) -gety = pnt_func(lgdal.OGR_G_GetY) -getz = pnt_func(lgdal.OGR_G_GetZ) - -#### OGRGeomType #### -class OGRGeomType(object): - "Encapulates OGR Geometry Types." - - # Ordered array of acceptable strings and their corresponding OGRwkbGeometryType - __ogr_str = ['Point', 'LineString', 'Polygon', 'MultiPoint', - 'MultiLineString', 'MultiPolygon', 'GeometryCollection', - 'LinearRing'] - __ogr_int = [1, 2, 3, 4, 5, 6, 7, 101] - - def __init__(self, input): - "Figures out the correct OGR Type based upon the input." - if isinstance(input, OGRGeomType): - self._index = input._index - elif isinstance(input, StringType): - idx = self._has_str(self.__ogr_str, input) - if idx == None: - raise OGRException, 'Invalid OGR String Type "%s"' % input - self._index = idx - elif isinstance(input, int): - if not input in self.__ogr_int: - raise OGRException, 'Invalid OGR Integer Type: %d' % input - self._index = self.__ogr_int.index(input) - else: - raise TypeError, 'Invalid OGR Input type given!' - - def __str__(self): - "Returns a short-hand string form of the OGR Geometry type." - return self.__ogr_str[self._index] - - def __eq__(self, other): - """Does an equivalence test on the OGR type with the given - other OGRGeomType, the short-hand string, or the integer.""" - if isinstance(other, OGRGeomType): - return self._index == other._index - elif isinstance(other, StringType): - idx = self._has_str(self.__ogr_str, other) - if not (idx == None): return self._index == idx - return False - elif isinstance(other, int): - if not other in self.__ogr_int: return False - return self.__ogr_int.index(other) == self._index - else: - raise TypeError, 'Cannot compare with type: %s' % str(type(other)) - - def _has_str(self, arr, s): - "Case-insensitive search of the string array for the given pattern." - s_low = s.lower() - for i in xrange(len(arr)): - if s_low == arr[i].lower(): return i - return None - - @property - def django(self): - "Returns the Django GeometryField for this OGR Type." - s = self.__ogr_str[self._index] - if s in ('Unknown', 'LinearRing'): - return None - else: - return s + 'Field' - - @property - def num(self): - "Returns the OGRwkbGeometryType number for the OGR Type." - return self.__ogr_int[self._index] - -#### OGRGeometry Class #### -class OGRGeometryIndexError(OGRException, KeyError): - """This exception is raised when an invalid index is encountered, and has - the 'silent_variable_feature' attribute set to true. This ensures that - django's templates proceed to use the next lookup type gracefully when - an Exception is raised. Fixes ticket #4740. - """ - silent_variable_failure = True - -class OGRGeometry(object): - "Generally encapsulates an OGR geometry." - - _g = 0 # Initially NULL - - def __init__(self, input, srs=False): - "Initializes Geometry on either WKT or an OGR pointer as input." - - if isinstance(input, StringType): - # Getting the spatial reference - self._init_srs(srs) - - # First, trying the input as WKT - buf = c_char_p(input) - g = c_void_p() - - try: - check_err(lgdal.OGR_G_CreateFromWkt(byref(buf), self._s._srs, byref(g))) - except OGRException, msg: - try: - ogr_t = OGRGeomType(input) # Seeing if the input is a valid short-hand string - g = lgdal.OGR_G_CreateGeometry(ogr_t.num) - except: - raise OGRException, 'Could not initialize on WKT "%s"' % input - elif isinstance(input, OGRGeomType): - self._init_srs(srs) - g = lgdal.OGR_G_CreateGeometry(input.num) - lgdal.OGR_G_AssignSpatialReference(g, self._s._srs) - elif isinstance(input, IntType): - # OGR Pointer (integer) was the input - g = input - else: - raise OGRException, 'Type of input cannot be determined!' - - # Now checking the Geometry pointer before finishing initialization - if not g: - raise OGRException, 'Cannot create OGR Geometry from input: %s' % str(input) - self._g = g - - # Setting the class depending upon the OGR Geometry Type - self.__class__ = GEO_CLASSES[self.geom_type.num] - - def _init_srs(self, srs): - # Getting the spatial - if not isinstance(srs, SpatialReference): - self._s = SpatialReference() # creating an empty spatial reference - else: - self._s = srs.clone() # cloning the given spatial reference - - def __add__(self, other): - "Returns the union of the two geometries." - return self.union(other) - - def __del__(self): - "Deletes this Geometry." - if self._g: lgdal.OGR_G_DestroyGeometry(self._g) - - def __eq__(self, other): - "Is this Geometry equal to the other?" - return self.equals(other) - - def __str__(self): - "WKT is used for the string representation." - return self.wkt - - #### Geometry Properties #### - @property - def dimension(self): - "Returns 0 for points, 1 for lines, and 2 for surfaces." - return lgdal.OGR_G_GetDimension(self._g) - - @property - def coord_dim(self): - "Returns the coordinate dimension of the Geometry." - return lgdal.OGR_G_GetCoordinateDimension(self._g) - - @property - def geom_count(self): - "The number of elements in this Geometry." - return lgdal.OGR_G_GetGeometryCount(self._g) - - @property - def point_count(self): - "Returns the number of Points in this Geometry." - return lgdal.OGR_G_GetPointCount(self._g) - - @property - def num_coords(self): - "Returns the number of Points in this Geometry." - return self.point_count - - @property - def srs(self): - "Returns the Spatial Reference for this Geometry." - return SpatialReference(lgdal.OSRClone(lgdal.OGR_G_GetSpatialReference(self._g)), 'ogr') - - @property - def geom_type(self): - "Returns the Type for this Geometry." - return OGRGeomType(lgdal.OGR_G_GetGeometryType(self._g)) - - @property - def geom_name(self): - "Returns the Name of this Geometry." - return string_at(lgdal.OGR_G_GetGeometryName(self._g)) - - @property - def wkt(self): - "Returns the WKT form of the Geometry." - buf = c_char_p() - check_err(lgdal.OGR_G_ExportToWkt(self._g, byref(buf))) - return string_at(buf) - - @property - def area(self): - "Returns the area for a LinearRing, Polygon, or MultiPolygon; 0 otherwise." - a = lgdal.OGR_G_GetArea(self._g) - return a.value - - @property - def envelope(self): - "Returns the envelope for this Geometry." - env = OGREnvelope() - lgdal.OGR_G_GetEnvelope(self._g, byref(env)) - return Envelope(env) - - #### Geometry Methods #### - def clone(self): - "Clones this OGR Geometry." - return OGRGeometry(lgdal.OGR_G_Clone(self._g)) - - def close_rings(self): - """If there are any rings within this geometry that have not been - closed, this routine will do so by adding the starting point at the - end.""" - # Closing the open rings. - lgdal.OGR_G_CloseRings(self._g) - # This "fixes" a GDAL bug. See http://trac.osgeo.org/gdal/ticket/1673 - foo = self.wkt - - def transform(self, coord_trans): - "Transforms this Geometry with the given CoordTransform object." - if not isinstance(coord_trans, CoordTransform): - raise OGRException, 'CoordTransform object required for transform.' - check_err(lgdal.OGR_G_Transform(self._g, coord_trans._ct)) - - def transform_to(self, srs): - "Transforms this Geometry with the given SpatialReference." - if not isinstance(srs, SpatialReference): - raise OGRException, 'SpatialReference object required for transform_to.' - check_err(lgdal.OGR_G_TransformTo(self._g, srs._srs)) - - #### Topology Methods #### - def _topology(self, topo_func, other): - """A generalized function for topology operations, takes a GDAL function and - the other geometry to perform the operation on.""" - if not isinstance(other, OGRGeometry): - raise OGRException, 'Must use another OGRGeometry object for topology operations!' - - # Calling the passed-in topology function with the other geometry - status = topo_func(self._g, other._g) - - # Returning based on the status code (an integer) - if status: return True - else: return False - - def intersects(self, other): - "Returns True if this geometry intersects with the other." - return self._topology(lgdal.OGR_G_Intersects, other) - - def equals(self, other): - "Returns True if this geometry is equivalent to the other." - return self._topology(lgdal.OGR_G_Equals, other) - - def disjoint(self, other): - "Returns True if this geometry and the other are spatially disjoint." - return self._topology(lgdal.OGR_G_Disjoint, other) - - def touches(self, other): - "Returns True if this geometry touches the other." - return self._topology(lgdal.OGR_G_Touches, other) - - def crosses(self, other): - "Returns True if this geometry crosses the other." - return self._topology(lgdal.OGR_G_Crosses, other) - - def within(self, other): - "Returns True if this geometry is within the other." - return self._topology(lgdal.OGR_G_Within, other) - - def contains(self, other): - "Returns True if this geometry contains the other." - return self._topology(lgdal.OGR_G_Contains, other) - - def overlaps(self, other): - "Returns True if this geometry overlaps the other." - return self._topology(lgdal.OGR_G_Overlaps, other) - - #### Geometry-generation Methods #### - def _geomgen(self, gen_func, other=None): - "A helper routine for the OGR routines that generate geometries." - if isinstance(other, OGRGeometry): - return OGRGeometry(gen_func(self._g, other._g)) - else: - return OGRGeometry(gen_func(self._g)) - - @property - def boundary(self): - "Returns the boundary of this geometry." - return self._geomgen(lgdal.OGR_G_GetBoundary) - - @property - def convex_hull(self): - "Returns the smallest convex Polygon that contains all the points in the Geometry." - return self._geomgen(lgdal.OGR_G_ConvexHull) - - def union(self, other): - """Returns a new geometry consisting of the region which is the union of - this geometry and the other.""" - return self._geomgen(lgdal.OGR_G_Union, other) - - def difference(self, other): - """Returns a new geometry consisting of the region which is the difference - of this geometry and the other.""" - return self._geomgen(lgdal.OGR_G_Difference, other) - - def sym_difference(self, other): - """Returns a new geometry which is the symmetric difference of this - geometry and the other.""" - return self._geomgen(lgdal.OGR_G_SymmetricDifference, other) - - def intersection(self, other): - """Returns a new geometry consisting of the region of intersection of this - geometry and the other.""" - return self._geomgen(lgdal.OGR_G_Intersection, other) - -# The subclasses for OGR Geometry. -class Point(OGRGeometry): - - @property - def x(self): - "Returns the X coordinate for this Point." - return getx(self._g, c_int(0)) - - @property - def y(self): - "Returns the Y coordinate for this Point." - return gety(self._g, c_int(0)) - - @property - def z(self): - "Returns the Z coordinate for this Point." - return getz(self._g, c_int(0)) - - @property - def tuple(self): - "Returns the tuple of this point." - if self.coord_dim == 1: - return (self.x,) - elif self.coord_dim == 2: - return (self.x, self.y) - elif self.coord_dim == 3: - return (self.x, self.y, self.z) - -class LineString(OGRGeometry): - - def __getitem__(self, index): - "Returns the Point at the given index." - if index > 0 or index < self.point_count: - x = c_double() - y = c_double() - z = c_double() - lgdal.OGR_G_GetPoint(self._g, c_int(index), - byref(x), byref(y), byref(z)) - if self.coord_dim == 1: - return (x.value,) - elif self.coord_dim == 2: - return (x.value, y.value) - elif self.coord_dim == 3: - return (x.value, y.value, z.value) - else: - raise OGRGeometryIndexError, 'index out of range: %s' % str(index) - - def __iter__(self): - "Iterates over each point in the LineString." - for i in xrange(self.point_count): - yield self.__getitem__(i) - - def __len__(self, index): - "The length returns the number of points in the LineString." - return self.point_count - - @property - def tuple(self): - "Returns the tuple representation of this LineString." - return tuple(self.__getitem__(i) for i in xrange(self.point_count)) - -# LinearRings are used in Polygons. -class LinearRing(LineString): pass - -class Polygon(OGRGeometry): - - def __len__(self): - "The number of interior rings in this Polygon." - return self.geom_count - - def __iter__(self): - "Iterates through each ring in the Polygon." - for i in xrange(self.geom_count): - yield self.__getitem__(i) - - def __getitem__(self, index): - "Gets the ring at the specified index." - if index < 0 or index >= self.geom_count: - raise OGRGeometryIndexError, 'index out of range: %s' % str(index) - else: - return OGRGeometry(lgdal.OGR_G_Clone(lgdal.OGR_G_GetGeometryRef(self._g, c_int(index)))) - - # Polygon Properties - @property - def shell(self): - "Returns the shell of this Polygon." - return self.__getitem__(0) # First ring is the shell - - @property - def tuple(self): - "Returns a tuple of LinearRing coordinate tuples." - return tuple(self.__getitem__(i).tuple for i in xrange(self.geom_count)) - - @property - def point_count(self): - "The number of Points in this Polygon." - # Summing up the number of points in each ring of the Polygon. - return sum([self.__getitem__(i).point_count for i in xrange(self.geom_count)]) - - @property - def centroid(self): - "Returns the centroid (a Point) of this Polygon." - # The centroid is a Point, create a geometry for this. - p = OGRGeometry(OGRGeomType('Point')) - check_err(lgdal.OGR_G_Centroid(self._g, p._g)) - return p - -# Geometry Collection base class. -class GeometryCollection(OGRGeometry): - "The Geometry Collection class." - - def __getitem__(self, index): - "Gets the Geometry at the specified index." - if index < 0 or index >= self.geom_count: - raise OGRGeometryIndexError, 'index out of range: %s' % str(index) - else: - return OGRGeometry(lgdal.OGR_G_Clone(lgdal.OGR_G_GetGeometryRef(self._g, c_int(index)))) - - def __iter__(self): - "Iterates over each Geometry." - for i in xrange(self.geom_count): - yield self.__getitem__(i) - - def __len__(self): - "The number of geometries in this Geometry Collection." - return self.geom_count - - def add(self, geom): - "Add the geometry to this Geometry Collection." - if isinstance(geom, OGRGeometry): - ptr = geom._g - elif isinstance(geom, StringType): - tmp = OGRGeometry(geom) - ptr = tmp._g - else: - raise OGRException, 'Must add an OGRGeometry.' - lgdal.OGR_G_AddGeometry(self._g, ptr) - - @property - def point_count(self): - "The number of Points in this Geometry Collection." - # Summing up the number of points in each geometry in this collection - return sum([self.__getitem__(i).point_count for i in xrange(self.geom_count)]) - - @property - def tuple(self): - "Returns a tuple representation of this Geometry Collection." - return tuple(self.__getitem__(i).tuple for i in xrange(self.geom_count)) - -# Multiple Geometry types. -class MultiPoint(GeometryCollection): pass -class MultiLineString(GeometryCollection): pass -class MultiPolygon(GeometryCollection): pass - -# Class mapping dictionary (using the OGRwkbGeometryType as the key) -GEO_CLASSES = {1 : Point, - 2 : LineString, - 3 : Polygon, - 4 : MultiPoint, - 5 : MultiLineString, - 6 : MultiPolygon, - 7 : GeometryCollection, - } diff --git a/django/contrib/gis/gdal/SpatialReference.py b/django/contrib/gis/gdal/SpatialReference.py deleted file mode 100644 index 54e338e854..0000000000 --- a/django/contrib/gis/gdal/SpatialReference.py +++ /dev/null @@ -1,339 +0,0 @@ -# Getting what we need from ctypes -import re -from types import StringType, TupleType -from ctypes import \ - 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 -from django.contrib.gis.gdal.OGRError import check_err, OGRException, SRSException - -""" - The Spatial Reference class, represensents OGR Spatial Reference objects. - - Example: - >>> from django.contrib.gis.gdal import SpatialReference - >>> srs = SpatialReference('WGS84') - >>> print srs - GEOGCS["WGS 84", - DATUM["WGS_1984", - SPHEROID["WGS 84",6378137,298.257223563, - AUTHORITY["EPSG","7030"]], - TOWGS84[0,0,0,0,0,0,0], - AUTHORITY["EPSG","6326"]], - PRIMEM["Greenwich",0, - AUTHORITY["EPSG","8901"]], - UNIT["degree",0.01745329251994328, - AUTHORITY["EPSG","9122"]], - AUTHORITY["EPSG","4326"]] - >>> print srs.proj - +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs - >>> print srs.ellipsoid - (6378137.0, 6356752.3142451793, 298.25722356300003) - >>> print srs.projected, srs.geographic - False True - >>> srs.import_epsg(32140) - >>> print srs.name - NAD83 / Texas South Central -""" - - -#### ctypes function prototypes #### -def ellipsis_func(f): - """Creates a ctypes function prototype for OSR ellipsis property functions, - 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. #### -class SpatialReference(object): - """A wrapper for the OGRSpatialReference object. According to the GDAL website, - the SpatialReference object 'provide[s] services to represent coordinate systems - (projections and datums) and to transform between them.'""" - - _srs = 0 # Initially NULL - - # Well-Known Geographical Coordinate System Name - _well_known = {'WGS84':4326, 'WGS72':4322, 'NAD27':4267, 'NAD83':4269} - _epsg_regex = re.compile('^EPSG:(?P\d+)$', re.I) - - #### Python 'magic' routines #### - def __init__(self, input='', srs_type='wkt'): - "Creates a spatial reference object from the given OGC Well Known Text (WKT)." - - # Creating an initial empty string buffer. - buf = c_char_p('') - - if isinstance(input, StringType): - # Is this an EPSG well known name? - m = self._epsg_regex.match(input) - if m: - srs_type = 'epsg' - input = int(m.group('epsg')) - # Is this a short-hand well known name? - elif input in self._well_known: - srs_type = 'epsg' - input = self._well_known[input] - elif srs_type == 'proj': - pass - else: - buf = c_char_p(input) - elif isinstance(input, int): - if srs_type not in ('epsg', 'ogr'): - raise SRSException, 'Integer input requires SRS type of "ogr" or "epsg".' - else: - raise TypeError, 'Invalid SRS type "%s"' % srs_type - - # Calling OSRNewSpatialReference with the string buffer. - if srs_type == 'ogr': - srs = input # Input is OGR pointer - else: - srs = lgdal.OSRNewSpatialReference(buf) - - # If the pointer is NULL, throw an exception. - if not srs: - raise SRSException, 'Could not create spatial reference from WKT!' - else: - self._srs = srs - - # Post-processing if in PROJ.4 or EPSG formats. - if srs_type == 'proj': self.import_proj(input) - elif srs_type == 'epsg': self.import_epsg(input) - - def __del__(self): - "Destroys this spatial reference." - if self._srs: lgdal.OSRRelease(self._srs) - - def __getitem__(self, target): - """Returns the value of the given string attribute node, None if the node doesn't exist. - Can also take a tuple as a parameter, (target, child), where child is the child index to get.""" - if isinstance(target, TupleType): - return self.attr_value(*target) - else: - return self.attr_value(target) - - def __str__(self): - "The string representation uses '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 #### - 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): - """The attribute value for the given target node (e.g. 'PROJCS'). The index keyword - specifies an index of the child node to return.""" - ptr = lgdal.OSRGetAttrValue(self._srs, c_char_p(target), c_int(index)) - return self._string_ptr(ptr) - - def validate(self): - "Checks to see if the given spatial reference is valid." - check_err(lgdal.OSRValidate(self._srs)) - - def clone(self): - "Returns a clone of this Spatial Reference." - return SpatialReference(lgdal.OSRClone(self._srs), 'ogr') - - @property - def name(self): - "Returns the name of this Spatial Reference." - if self.projected: return self.attr_value('PROJCS') - elif self.geographic: return self.attr_value('GEOGCS') - elif self.local: return self.attr_value('LOCAL_CS') - else: return None - - #### 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 - def linear_name(self): - "Returns the name of the linear units." - self._cache_linear() - return self._linear_name - - @property - def linear_units(self): - "Returns the value of the linear units." - self._cache_linear() - return self._linear_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 - def angular_name(self): - "Returns the name of the angular units." - self._cache_angular() - return self._angular_name - - @property - def angular_units(self): - "Returns the value of the angular units." - self._cache_angular() - return self._angular_units - - #### Spheroid/Ellipsoid Properties #### - @property - def ellipsoid(self): - """Returns a tuple of the ellipsoid parameters: - (semimajor axis, semiminor axis, and inverse flattening).""" - return (self.semi_major, self.semi_minor, self.inverse_flattening) - - @property - def semi_major(self): - "Gets the Semi Major Axis for this Spatial Reference." - err = c_int(0) - sm = semi_major(self._srs, byref(err)) - check_err(err.value) - return sm - - @property - def semi_minor(self): - "Gets the Semi Minor Axis for this Spatial Reference." - err = c_int() - sm = semi_minor(self._srs, byref(err)) - check_err(err.value) - return sm - - @property - def inverse_flattening(self): - "Gets the Inverse Flattening for this Spatial Reference." - err = c_int() - inv_flat = invflattening(self._srs, byref(err)) - check_err(err.value) - return inv_flat - - #### Boolean Properties #### - @property - def geographic(self): - "Returns True if this SpatialReference is geographic (root node is GEOGCS)." - if lgdal.OSRIsGeographic(self._srs): return True - else: return False - - @property - def local(self): - "Returns True if this SpatialReference is local (root node is LOCAL_CS)." - if lgdal.OSRIsLocal(self._srs): return True - else: return False - - @property - def projected(self): - "Returns True if this SpatialReference is a projected coordinate system (root node is PROJCS)." - if lgdal.OSRIsProjected(self._srs): return True - else: return False - - #### Import Routines ##### - def import_wkt(self, wkt): - "Imports the Spatial Reference from OGC WKT (string)" - buf = create_string_buffer(wkt) - check_err(lgdal.OSRImportFromWkt(self._srs, byref(buf))) - - def import_proj(self, proj): - "Imports the Spatial Reference from a PROJ.4 string." - check_err(lgdal.OSRImportFromProj4(self._srs, create_string_buffer(proj))) - - def import_epsg(self, epsg): - "Imports the Spatial Reference from the EPSG code (an integer)." - check_err(lgdal.OSRImportFromEPSG(self._srs, c_int(epsg))) - - def import_xml(self, xml): - "Imports the Spatial Reference from an XML string." - check_err(lgdal.OSRImportFromXML(self._srs, create_string_buffer(xml))) - - #### Export Properties #### - @property - def wkt(self): - "Returns the WKT representation of this Spatial Reference." - w = c_char_p() - check_err(lgdal.OSRExportToWkt(self._srs, byref(w))) - return string_at(w) - - @property - def pretty_wkt(self, simplify=0): - "Returns the 'pretty' representation of the WKT." - w = c_char_p() - check_err(lgdal.OSRExportToPrettyWkt(self._srs, byref(w), c_int(simplify))) - return string_at(w) - - @property - def proj(self): - "Returns the PROJ.4 representation for this Spatial Reference." - w = c_char_p() - check_err(lgdal.OSRExportToProj4(self._srs, byref(w))) - return string_at(w) - - def proj4(self): - "Alias for proj()." - return self.proj - - @property - def xml(self, dialect=''): - "Returns the XML representation of this Spatial Reference." - w = c_char_p() - check_err(lgdal.OSRExportToXML(self._srs, byref(w), create_string_buffer(dialect))) - return string_at(w) - -class CoordTransform(object): - "A coordinate system transformation object." - - _ct = 0 # Initially NULL - - def __init__(self, source, target): - "Initializes on a source and target SpatialReference objects." - if not isinstance(source, SpatialReference) or not isinstance(target, SpatialReference): - raise SRSException, 'source and target must be of type SpatialReference' - ct = lgdal.OCTNewCoordinateTransformation(source._srs, target._srs) - if not ct: - raise SRSException, 'could not intialize CoordTransform object' - self._ct = ct - self._srs1_name = source.name - self._srs2_name = target.name - - def __del__(self): - "Deletes this Coordinate Transformation object." - if self._ct: lgdal.OCTDestroyCoordinateTransformation(self._ct) - - def __str__(self): - return 'Transform from "%s" to "%s"' % (str(self._srs1_name), str(self._srs2_name)) - diff --git a/django/contrib/gis/gdal/__init__.py b/django/contrib/gis/gdal/__init__.py index 180dc558ac..255f781ddb 100644 --- a/django/contrib/gis/gdal/__init__.py +++ b/django/contrib/gis/gdal/__init__.py @@ -1,7 +1,8 @@ -from Driver import Driver -from Envelope import Envelope -from DataSource import DataSource -from SpatialReference import SpatialReference, CoordTransform -from OGRGeometry import OGRGeometry, OGRGeomType -from OGRError import check_err, OGRException, SRSException +from driver import Driver +from envelope import Envelope +from datasource import DataSource +from srs import SpatialReference, CoordTransform +from geometries import OGRGeometry +from geomtype import OGRGeomType +from error import check_err, OGRException, SRSException diff --git a/django/contrib/gis/gdal/datasource.py b/django/contrib/gis/gdal/datasource.py new file mode 100644 index 0000000000..1776b34185 --- /dev/null +++ b/django/contrib/gis/gdal/datasource.py @@ -0,0 +1,128 @@ +# types and ctypes +from types import StringType +from ctypes import c_char_p, c_int, c_void_p, byref, string_at + +# The GDAL C library, OGR exceptions, and the Layer object. +from django.contrib.gis.gdal.libgdal import lgdal +from django.contrib.gis.gdal.error import OGRException, check_err +from django.contrib.gis.gdal.layer import Layer +from django.contrib.gis.gdal.driver import Driver + +""" + DataSource is a wrapper for the OGR Data Source object, which provides + an interface for reading vector geometry data from many different file + formats (including ESRI shapefiles). + + When instantiating a DataSource object, use the filename of a + GDAL-supported data source. For example, a SHP file or a + TIGER/Line file from the government. + + The ds_driver keyword is used internally when a ctypes pointer + is passed in directly. + + Example: + ds = DataSource('/home/foo/bar.shp') + for layer in ds: + 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: +# http://www.gdal.org/ogr/ogr__api_8h.html +# +# The OGR_DS_* routines are relevant here. + +class DataSource(object): + "Wraps an OGR Data Source object." + + #### Python 'magic' routines #### + def __init__(self, ds_input, ds_driver=False): + + self._ds = 0 # Initially NULL + + # Registering all the drivers, this needs to be done + # _before_ we try to open up a data source. + if not lgdal.OGRGetDriverCount() and not lgdal.OGRRegisterAll(): + raise OGRException, 'Could not register all the OGR data source drivers!' + + if isinstance(ds_input, StringType): + + # The data source driver is a void pointer. + ds_driver = c_void_p() + + # OGROpen will auto-detect the data source type. + ds = lgdal.OGROpen(c_char_p(ds_input), c_int(0), byref(ds_driver)) + elif isinstance(ds_input, c_void_p) and isinstance(ds_driver, c_void_p): + ds = ds_input + else: + raise OGRException, 'Invalid data source input type: %s' % str(type(ds_input)) + + # Raise an exception if the returned pointer is NULL + if not ds: + self._ds = False + raise OGRException, 'Invalid data source file "%s"' % ds_input + else: + self._ds = ds + self._driver = Driver(ds_driver) + + def __del__(self): + "This releases the reference to the data source (destroying it if it's the only one)." + if self._ds: lgdal.OGRReleaseDataSource(self._ds) + + def __iter__(self): + "Allows for iteration over the layers in a data source." + for i in xrange(self.layer_count): + yield self.__getitem__(i) + + def __getitem__(self, index): + "Allows use of the index [] operator to get a layer at the index." + if isinstance(index, StringType): + l = lgdal.OGR_DS_GetLayerByName(self._ds, c_char_p(index)) + if not l: raise IndexError, 'invalid OGR Layer name given: "%s"' % index + else: + if index < 0 or index >= self.layer_count: + raise IndexError, 'index out of range' + l = lgdal.OGR_DS_GetLayer(self._ds, c_int(index)) + return Layer(l) + + def __len__(self): + "Returns the number of layers within the data source." + return self.layer_count + + def __str__(self): + "Returns OGR GetName and Driver for the Data Source." + return '%s (%s)' % (self.name, str(self.driver)) + + #### DataSource Properties #### + @property + def driver(self): + "Returns the Driver object for this Data Source." + return self._driver + + @property + def layer_count(self): + "Returns the number of layers in the data source." + return lgdal.OGR_DS_GetLayerCount(self._ds) + + @property + def name(self): + "Returns the name of the data source." + return string_at(lgdal.OGR_DS_GetName(self._ds)) + diff --git a/django/contrib/gis/gdal/driver.py b/django/contrib/gis/gdal/driver.py new file mode 100644 index 0000000000..404bf7facf --- /dev/null +++ b/django/contrib/gis/gdal/driver.py @@ -0,0 +1,85 @@ +# types and ctypes +from types import StringType +from ctypes import c_char_p, c_int, c_void_p, byref, string_at + +# The GDAL C library, OGR exceptions, and the Layer object. +from django.contrib.gis.gdal.libgdal import lgdal +from django.contrib.gis.gdal.error import OGRException + +# For more information, see the OGR C API source code: +# http://www.gdal.org/ogr/ogr__api_8h.html +# +# The OGR_Dr_* routines are relevant here. + +class Driver(object): + "Wraps an OGR Data Source Driver." + + # Case-insensitive aliases for OGR Drivers. + _alias = {'esri' : 'ESRI Shapefile', + 'shp' : 'ESRI Shapefile', + 'shape' : 'ESRI Shapefile', + 'tiger' : 'TIGER', + 'tiger/line' : 'TIGER', + } + + def __init__(self, input, ptr=False): + "Initializes an OGR driver on either a string or integer input." + + if isinstance(input, StringType): + # If a string name of the driver was passed in + self._dr = 0 # Initially NULL + self._register() + + # Checking the alias dictionary (case-insensitive) to see if an alias + # exists for the given driver. + if input.lower() in self._alias: + name = c_char_p(self._alias[input.lower()]) + else: + name = c_char_p(input) + + # Attempting to get the OGR driver by the string name. + dr = lgdal.OGRGetDriverByName(name) + elif isinstance(input, int): + self._register() + dr = lgdal.OGRGetDriver(c_int(input)) + elif isinstance(input, c_void_p): + dr = input + else: + raise OGRException, 'Unrecognized input type for OGR Driver: %s' % str(type(input)) + + # Making sure we get a valid pointer to the OGR Driver + if not dr: + raise OGRException, 'Could not initialize OGR Driver on input: %s' % str(input) + self._dr = dr + + def __str__(self): + "Returns the string name of the OGR Driver." + return string_at(lgdal.OGR_Dr_GetName(self._dr)) + + def _register(self): + "Attempts to register all the data source drivers." + # Only register all if the driver count is 0 (or else all drivers + # will be registered over and over again) + if not self.driver_count and not lgdal.OGRRegisterAll(): + raise OGRException, 'Could not register all the OGR data source drivers!' + + # Driver properties + @property + def driver_count(self): + "Returns the number of OGR data source drivers registered." + return lgdal.OGRGetDriverCount() + + def create_ds(self, **kwargs): + "Creates a data source using the keyword args as name value options." + raise NotImplementedError + # Getting the options string + #options = '' + #n_opts = len(kwargs) + #for i in xrange(n_opts): + # options += '%s=%s' % (str(k), str(v)) + # if i < n_opts-1: options += ',' + #opts = c_char_p(options) + + + + diff --git a/django/contrib/gis/gdal/envelope.py b/django/contrib/gis/gdal/envelope.py new file mode 100644 index 0000000000..88d9895dbe --- /dev/null +++ b/django/contrib/gis/gdal/envelope.py @@ -0,0 +1,112 @@ +from ctypes import Structure, c_double +from types import TupleType + +""" + The GDAL/OGR library uses an Envelope structure to hold the bounding + box information for a geometry. The envelope (bounding box) contains + two pairs of coordinates, one for the lower left coordinate and one + for the upper right coordinate: + + +----------o Upper right; (max_x, max_y) + | | + | | + | | + Lower left (min_x, min_y) o----------+ + +""" + +# The OGR definition of an Envelope is a C structure containing four doubles. +# See the 'ogr_core.h' source file for more information: +# http://www.gdal.org/ogr/ogr__core_8h-source.html +class OGREnvelope(Structure): + "Represents the OGREnvelope C Structure." + _fields_ = [("MinX", c_double), + ("MaxX", c_double), + ("MinY", c_double), + ("MaxY", c_double), + ] + +class Envelope(object): + "A class that will wrap an OGR Envelope structure." + + def __init__(self, *args): + if len(args) == 1: + if isinstance(args[0], OGREnvelope): + # OGREnvelope (a ctypes Structure) was passed in. + self._envelope = args[0] + elif isinstance(args[0], TupleType) and len(args[0]) == 4: + # A Tuple was passed in + self._from_tuple(args[0]) + else: + raise OGRException, 'Incorrect type of argument: %s' % str(type(args[0])) + elif len(args) == 4: + self._from_tuple(args) + else: + raise OGRException, 'Incorrect number of arguments!' + + def __eq__(self, other): + "Returns true if the envelopes are equivalent; can compare against other Envelopes and 4-tuples." + if isinstance(other, Envelope): + return (self.min_x == other.min_x) and (self.min_y == other.min_y) and \ + (self.max_x == other.max_x) and (self.max_y == other.max_y) + elif isinstance(other, TupleType) and len(other) == 4: + return (self.min_x == other[0]) and (self.min_y == other[1]) and \ + (self.max_x == other[2]) and (self.max_y == other[3]) + else: + raise OGRException, 'Equivalence testing only works with other Envelopes.' + + def __str__(self): + "Returns a string representation of the tuple." + return str(self.tuple) + + def _from_tuple(self, tup): + "Initializes the C OGR Envelope structure from the given tuple." + self._envelope = OGREnvelope() + self._envelope.MinX = tup[0] + self._envelope.MinY = tup[1] + self._envelope.MaxX = tup[2] + self._envelope.MaxY = tup[3] + + @property + def min_x(self): + "Returns the value of the minimum X coordinate." + return self._envelope.MinX + + @property + def min_y(self): + "Returns the value of the minimum Y coordinate." + return self._envelope.MinY + + @property + def max_x(self): + "Returns the value of the maximum X coordinate." + return self._envelope.MaxX + + @property + def max_y(self): + "Returns the value of the maximum Y coordinate." + return self._envelope.MaxY + + @property + def ur(self): + "Returns the upper-right coordinate." + return (self.max_x, self.max_y) + + @property + def ll(self): + "Returns the lower-left coordinate." + return (self.min_x, self.min_y) + + @property + def tuple(self): + "Returns a tuple representing the envelope." + return (self.min_x, self.min_y, self.max_x, self.max_y) + + @property + def wkt(self): + "Returns WKT representing a Polygon for this envelope." + # TODO: Fix significant figures. + return 'POLYGON((%s %s,%s %s,%s %s,%s %s,%s %s))' % (self.min_x, self.min_y, self.min_x, self.max_y, + self.max_x, self.max_y, self.max_x, self.min_y, + self.min_x, self.min_y) + diff --git a/django/contrib/gis/gdal/error.py b/django/contrib/gis/gdal/error.py new file mode 100644 index 0000000000..7162e2eb18 --- /dev/null +++ b/django/contrib/gis/gdal/error.py @@ -0,0 +1,35 @@ +# OGR Error Codes +OGRERR_NONE = 0 +OGRERR_NOT_ENOUGH_DATA = 1 +OGRERR_NOT_ENOUGH_MEMORY = 2 +OGRERR_UNSUPPORTED_GEOMETRY_TYPE = 3 +OGRERR_UNSUPPORTED_OPERATION = 4 +OGRERR_CORRUPT_DATA = 5 +OGRERR_FAILURE = 6 +OGRERR_UNSUPPORTED_SRS = 7 + +# OGR & SRS Exceptions +class OGRException(Exception): pass +class SRSException(Exception): pass + +def check_err(code, msg=False): + "Checks the given OGRERR, and raises an exception where appropriate." + + if code == OGRERR_NONE: + return + elif code == OGRERR_NOT_ENOUGH_DATA: + raise OGRException, 'Not enough data!' + elif code == OGRERR_NOT_ENOUGH_MEMORY: + raise OGRException, 'Not enough memory!' + elif code == OGRERR_UNSUPPORTED_GEOMETRY_TYPE: + raise OGRException, 'Unsupported Geometry Type!' + elif code == OGRERR_UNSUPPORTED_OPERATION: + raise OGRException, 'Unsupported Operation!' + elif code == OGRERR_CORRUPT_DATA: + raise OGRException, 'Corrupt Data!' + elif code == OGRERR_FAILURE: + raise OGRException, 'OGR Failure!' + elif code == OGRERR_UNSUPPORTED_SRS: + raise SRSException, 'Unsupported SRS!' + else: + raise OGRException, 'Unknown error code: "%s"' % str(code) diff --git a/django/contrib/gis/gdal/feature.py b/django/contrib/gis/gdal/feature.py new file mode 100644 index 0000000000..e487565519 --- /dev/null +++ b/django/contrib/gis/gdal/feature.py @@ -0,0 +1,109 @@ +# types and ctypes +from types import StringType +from ctypes import c_char_p, c_int, string_at + +# 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 +from django.contrib.gis.gdal.field import Field +from django.contrib.gis.gdal.geometries import OGRGeometry, OGRGeomType + +# For more information, see the OGR C API source code: +# http://www.gdal.org/ogr/ogr__api_8h.html +# +# The OGR_F_* routines are relevant here. +class Feature(object): + "A class that wraps an OGR Feature, needs to be instantiated from a Layer object." + + #### Python 'magic' routines #### + def __init__(self, f): + "Needs a C pointer (Python integer in ctypes) in order to initialize." + self._feat = 0 # Initially NULL + self._fdefn = 0 + if not f: + raise OGRException, 'Cannot create OGR Feature, invalid pointer given.' + self._feat = f + self._fdefn = lgdal.OGR_F_GetDefnRef(f) + + def __del__(self): + "Releases a reference to this object." + if self._fdefn: lgdal.OGR_FD_Release(self._fdefn) + + def __getitem__(self, index): + "Gets the Field at the specified index." + if isinstance(index, StringType): + i = self.index(index) + else: + if index < 0 or index > self.num_fields: + raise IndexError, 'index out of range' + i = index + return Field(lgdal.OGR_F_GetFieldDefnRef(self._feat, c_int(i)), + string_at(lgdal.OGR_F_GetFieldAsString(self._feat, c_int(i)))) + + def __iter__(self): + "Iterates over each field in the Feature." + for i in xrange(self.num_fields): + yield self.__getitem__(i) + + def __len__(self): + "Returns the count of fields in this feature." + return self.num_fields + + def __str__(self): + "The string name of the feature." + return 'Feature FID %d in Layer<%s>' % (self.fid, self.layer_name) + + def __eq__(self, other): + "Does equivalence testing on the features." + if lgdal.OGR_F_Equal(self._feat, other._feat): + return True + else: + return False + + #### Feature Properties #### + @property + def fid(self): + "Returns the feature identifier." + return lgdal.OGR_F_GetFID(self._feat) + + @property + def layer_name(self): + "Returns the name of the layer for the feature." + return string_at(lgdal.OGR_FD_GetName(self._fdefn)) + + @property + def num_fields(self): + "Returns the number of fields in the Feature." + return lgdal.OGR_F_GetFieldCount(self._feat) + + @property + def fields(self): + "Returns a list of fields in the Feature." + return [ string_at(lgdal.OGR_Fld_GetNameRef(lgdal.OGR_FD_GetFieldDefn(self._fdefn, i))) + for i in xrange(self.num_fields) ] + @property + def geom(self): + "Returns the OGR Geometry for this Feature." + # A clone is used, so destruction of the Geometry won't bork the Feature. + return OGRGeometry(lgdal.OGR_G_Clone(lgdal.OGR_F_GetGeometryRef(self._feat))) + + @property + def geom_type(self): + "Returns the OGR Geometry Type for this Feture." + return OGRGeomType(lgdal.OGR_FD_GetGeomType(self._fdefn)) + + #### Feature Methods #### + def get(self, field): + "Returns the value of the field, instead of an instance of the Field object." + field_name = getattr(field, 'name', field) + return self.__getitem__(field_name).value + + def index(self, field_name): + "Returns the index of the given field name." + i = lgdal.OGR_F_GetFieldIndex(self._feat, c_char_p(field_name)) + if i < 0: raise IndexError, 'invalid OFT field name given: "%s"' % field_name + return i + + def clone(self): + "Clones this Feature." + return Feature(lgdal.OGR_F_Clone(self._feat)) diff --git a/django/contrib/gis/gdal/field.py b/django/contrib/gis/gdal/field.py new file mode 100644 index 0000000000..338a886b03 --- /dev/null +++ b/django/contrib/gis/gdal/field.py @@ -0,0 +1,93 @@ +from ctypes import string_at + +from django.contrib.gis.gdal.libgdal import lgdal +from django.contrib.gis.gdal.error import OGRException + +# For more information, see the OGR C API source code: +# http://www.gdal.org/ogr/ogr__api_8h.html +# +# The OGR_Fld_* routines are relevant here. +class Field(object): + "A class that wraps an OGR Field, needs to be instantiated from a Feature object." + + _fld = 0 # Initially NULL + + #### Python 'magic' routines #### + def __init__(self, fld, val=''): + "Needs a C pointer (Python integer in ctypes) in order to initialize." + if not fld: + raise OGRException, 'Cannot create OGR Field, invalid pointer given.' + self._fld = fld + self._val = val + + # Setting the class depending upon the OGR Field Type (OFT) + self.__class__ = FIELD_CLASSES[self.type] + + def __str__(self): + "Returns the string representation of the Field." + return '%s (%s)' % (self.name, self.value) + + #### Field Properties #### + @property + def name(self): + "Returns the name of the field." + return string_at(lgdal.OGR_Fld_GetNameRef(self._fld)) + + @property + def type(self): + "Returns the type of this field." + return lgdal.OGR_Fld_GetType(self._fld) + + @property + def value(self): + "Returns the value of this type of field." + return self._val + +# The Field sub-classes for each OGR Field type. +class OFTInteger(Field): + @property + def value(self): + "Returns an integer contained in this field." + try: + return int(self._val) + except ValueError: + return 0 + +class OFTIntegerList(Field): pass +class OFTReal(Field): + @property + def value(self): + "Returns a float contained in this field." + + try: + return float(self._val) + except ValueError: + #FIXME: 0? None? + return 0 + + + +class OFTRealList(Field): pass +class OFTString(Field): pass +class OFTStringList(Field): pass +class OFTWideString(Field): pass +class OFTWideStringList(Field): pass +class OFTBinary(Field): pass +class OFTDate(Field): pass +class OFTTime(Field): pass +class OFTDateTime(Field): pass + +# Class mapping dictionary for OFT Types +FIELD_CLASSES = { 0 : OFTInteger, + 1 : OFTIntegerList, + 2 : OFTReal, + 3 : OFTRealList, + 4 : OFTString, + 5 : OFTStringList, + 6 : OFTWideString, + 7 : OFTWideStringList, + 8 : OFTBinary, + 9 : OFTDate, + 10 : OFTTime, + 11 : OFTDateTime, + } diff --git a/django/contrib/gis/gdal/geometries.py b/django/contrib/gis/gdal/geometries.py new file mode 100644 index 0000000000..355ba4580a --- /dev/null +++ b/django/contrib/gis/gdal/geometries.py @@ -0,0 +1,496 @@ +# types & ctypes +from types import IntType, StringType +from ctypes import byref, string_at, c_char_p, c_double, c_int, c_void_p + +# Getting geodjango gdal prerequisites +from django.contrib.gis.gdal.libgdal import lgdal +from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope +from django.contrib.gis.gdal.error import check_err, OGRException +from django.contrib.gis.gdal.geomtype import OGRGeomType +from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform + +""" + The OGRGeometry is a wrapper for using the OGR Geometry class + (see http://www.gdal.org/ogr/classOGRGeometry.html). OGRGeometry + may be instantiated when reading geometries from OGR Data Sources + (e.g. SHP files), or when given OGC WKT (a string). + + While the 'full' API is not present yet, the API is "pythonic" unlike + the traditional and "next-generation" OGR Python bindings. One major + advantage OGR Geometries have over their GEOS counterparts is support + for spatial reference systems and their transformation. + + Example: + >>> from django.contrib.gis.gdal import OGRGeometry, OGRGeomType, SpatialReference + >>> wkt1, wkt2 = 'POINT(-90 30)', 'POLYGON((0 0, 5 0, 5 5, 0 5)' + >>> pnt = OGRGeometry(wkt1) + >>> print pnt + POINT (-90 30) + >>> mpnt = OGRGeometry(OGRGeomType('MultiPoint'), SpatialReference('WGS84')) + >>> mpnt.add(wkt1) + >>> mpnt.add(wkt1) + >>> print mpnt + MULTIPOINT (-90 30,-90 30) + >>> print mpnt.srs.name + WGS 84 + >>> print mpnt.srs.proj + +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs + >>> mpnt.transform_to(SpatialReference('NAD27')) + >>> print mpnt.proj + +proj=longlat +ellps=clrk66 +datum=NAD27 +no_defs + >>> print mpnt + MULTIPOINT (-89.999930378602485 29.999797886557641,-89.999930378602485 29.999797886557641) + + The OGRGeomType class is to make it easy to specify an OGR geometry type: + >>> from django.contrib.gis.gdal import OGRGeomType + >>> gt1 = OGRGeomType(3) # Using an integer for the type + >>> gt2 = OGRGeomType('Polygon') # Using a string + >>> gt3 = OGRGeomType('POLYGON') # It's case-insensitive + >>> print gt1 == 3, gt1 == 'Polygon' # Equivalence works w/non-OGRGeomType objects + True +""" + +# For more information, see the OGR C API source code: +# http://www.gdal.org/ogr/ogr__api_8h.html +# +# The OGR_G_* routines are relevant here. + +#### ctypes prototypes #### +def pnt_func(f): + "For accessing point information." + f.restype = c_double + f.argtypes = [c_void_p, c_int] + return f +getx = pnt_func(lgdal.OGR_G_GetX) +gety = pnt_func(lgdal.OGR_G_GetY) +getz = pnt_func(lgdal.OGR_G_GetZ) + +#### OGRGeometry Class #### +class OGRGeometryIndexError(OGRException, KeyError): + """This exception is raised when an invalid index is encountered, and has + the 'silent_variable_feature' attribute set to true. This ensures that + django's templates proceed to use the next lookup type gracefully when + an Exception is raised. Fixes ticket #4740. + """ + silent_variable_failure = True + +class OGRGeometry(object): + "Generally encapsulates an OGR geometry." + + def __init__(self, input, srs=False): + "Initializes Geometry on either WKT or an OGR pointer as input." + + self._g = 0 # Initially NULL + + if isinstance(input, StringType): + # Getting the spatial reference + self._init_srs(srs) + + # First, trying the input as WKT + buf = c_char_p(input) + g = c_void_p() + + try: + check_err(lgdal.OGR_G_CreateFromWkt(byref(buf), self._s._srs, byref(g))) + except OGRException, msg: + try: + ogr_t = OGRGeomType(input) # Seeing if the input is a valid short-hand string + g = lgdal.OGR_G_CreateGeometry(ogr_t.num) + except: + raise OGRException, 'Could not initialize on WKT "%s"' % input + elif isinstance(input, OGRGeomType): + self._init_srs(srs) + g = lgdal.OGR_G_CreateGeometry(input.num) + lgdal.OGR_G_AssignSpatialReference(g, self._s._srs) + elif isinstance(input, IntType): + # OGR Pointer (integer) was the input + g = input + else: + raise OGRException, 'Type of input cannot be determined!' + + # Now checking the Geometry pointer before finishing initialization + if not g: + raise OGRException, 'Cannot create OGR Geometry from input: %s' % str(input) + self._g = g + + # Setting the class depending upon the OGR Geometry Type + self.__class__ = GEO_CLASSES[self.geom_type.num] + + def __del__(self): + "Deletes this Geometry." + if self._g: lgdal.OGR_G_DestroyGeometry(self._g) + + def _init_srs(self, srs): + # Getting the spatial + if not isinstance(srs, SpatialReference): + self._s = SpatialReference() # creating an empty spatial reference + else: + self._s = srs.clone() # cloning the given spatial reference + + ### Geometry set-like operations ### + # g = g1 | g2 + def __or__(self, other): + "Returns the union of the two geometries." + return self.union(other) + + # g = g1 & g2 + def __and__(self, other): + "Returns the intersection of this Geometry and the other." + return self.intersection(other) + + # g = g1 - g2 + def __sub__(self, other): + "Return the difference this Geometry and the other." + return self.difference(other) + + # g = g1 ^ g2 + def __xor__(self, other): + "Return the symmetric difference of this Geometry and the other." + return self.sym_difference(other) + + def __eq__(self, other): + "Is this Geometry equal to the other?" + return self.equals(other) + + def __ne__(self, other): + "Tests for inequality." + return not self.equals(other) + + def __str__(self): + "WKT is used for the string representation." + return self.wkt + + #### Geometry Properties #### + @property + def dimension(self): + "Returns 0 for points, 1 for lines, and 2 for surfaces." + return lgdal.OGR_G_GetDimension(self._g) + + @property + def coord_dim(self): + "Returns the coordinate dimension of the Geometry." + return lgdal.OGR_G_GetCoordinateDimension(self._g) + + @property + def geom_count(self): + "The number of elements in this Geometry." + return lgdal.OGR_G_GetGeometryCount(self._g) + + @property + def point_count(self): + "Returns the number of Points in this Geometry." + return lgdal.OGR_G_GetPointCount(self._g) + + @property + def num_coords(self): + "Returns the number of Points in this Geometry." + return self.point_count + + @property + def srs(self): + "Returns the Spatial Reference for this Geometry." + return SpatialReference(lgdal.OSRClone(lgdal.OGR_G_GetSpatialReference(self._g)), 'ogr') + + @property + def geom_type(self): + "Returns the Type for this Geometry." + return OGRGeomType(lgdal.OGR_G_GetGeometryType(self._g)) + + @property + def geom_name(self): + "Returns the Name of this Geometry." + return string_at(lgdal.OGR_G_GetGeometryName(self._g)) + + @property + def wkt(self): + "Returns the WKT form of the Geometry." + buf = c_char_p() + check_err(lgdal.OGR_G_ExportToWkt(self._g, byref(buf))) + return string_at(buf) + + @property + def area(self): + "Returns the area for a LinearRing, Polygon, or MultiPolygon; 0 otherwise." + a = lgdal.OGR_G_GetArea(self._g) + return a.value + + @property + def envelope(self): + "Returns the envelope for this Geometry." + env = OGREnvelope() + lgdal.OGR_G_GetEnvelope(self._g, byref(env)) + return Envelope(env) + + #### Geometry Methods #### + def clone(self): + "Clones this OGR Geometry." + return OGRGeometry(lgdal.OGR_G_Clone(self._g)) + + def close_rings(self): + """If there are any rings within this geometry that have not been + closed, this routine will do so by adding the starting point at the + end.""" + # Closing the open rings. + lgdal.OGR_G_CloseRings(self._g) + # This "fixes" a GDAL bug. See http://trac.osgeo.org/gdal/ticket/1673 + foo = self.wkt + + def transform(self, coord_trans): + "Transforms this Geometry with the given CoordTransform object." + if not isinstance(coord_trans, CoordTransform): + raise OGRException, 'CoordTransform object required for transform.' + check_err(lgdal.OGR_G_Transform(self._g, coord_trans._ct)) + + def transform_to(self, srs): + "Transforms this Geometry with the given SpatialReference." + if not isinstance(srs, SpatialReference): + raise OGRException, 'SpatialReference object required for transform_to.' + check_err(lgdal.OGR_G_TransformTo(self._g, srs._srs)) + + #### Topology Methods #### + def _topology(self, topo_func, other): + """A generalized function for topology operations, takes a GDAL function and + the other geometry to perform the operation on.""" + if not isinstance(other, OGRGeometry): + raise OGRException, 'Must use another OGRGeometry object for topology operations!' + + # Calling the passed-in topology function with the other geometry + status = topo_func(self._g, other._g) + + # Returning based on the status code (an integer) + if status: return True + else: return False + + def intersects(self, other): + "Returns True if this geometry intersects with the other." + return self._topology(lgdal.OGR_G_Intersects, other) + + def equals(self, other): + "Returns True if this geometry is equivalent to the other." + return self._topology(lgdal.OGR_G_Equals, other) + + def disjoint(self, other): + "Returns True if this geometry and the other are spatially disjoint." + return self._topology(lgdal.OGR_G_Disjoint, other) + + def touches(self, other): + "Returns True if this geometry touches the other." + return self._topology(lgdal.OGR_G_Touches, other) + + def crosses(self, other): + "Returns True if this geometry crosses the other." + return self._topology(lgdal.OGR_G_Crosses, other) + + def within(self, other): + "Returns True if this geometry is within the other." + return self._topology(lgdal.OGR_G_Within, other) + + def contains(self, other): + "Returns True if this geometry contains the other." + return self._topology(lgdal.OGR_G_Contains, other) + + def overlaps(self, other): + "Returns True if this geometry overlaps the other." + return self._topology(lgdal.OGR_G_Overlaps, other) + + #### Geometry-generation Methods #### + def _geomgen(self, gen_func, other=None): + "A helper routine for the OGR routines that generate geometries." + if isinstance(other, OGRGeometry): + return OGRGeometry(gen_func(self._g, other._g)) + else: + return OGRGeometry(gen_func(self._g)) + + @property + def boundary(self): + "Returns the boundary of this geometry." + return self._geomgen(lgdal.OGR_G_GetBoundary) + + @property + def convex_hull(self): + "Returns the smallest convex Polygon that contains all the points in the Geometry." + return self._geomgen(lgdal.OGR_G_ConvexHull) + + def union(self, other): + """Returns a new geometry consisting of the region which is the union of + this geometry and the other.""" + return self._geomgen(lgdal.OGR_G_Union, other) + + def difference(self, other): + """Returns a new geometry consisting of the region which is the difference + of this geometry and the other.""" + return self._geomgen(lgdal.OGR_G_Difference, other) + + def sym_difference(self, other): + """Returns a new geometry which is the symmetric difference of this + geometry and the other.""" + return self._geomgen(lgdal.OGR_G_SymmetricDifference, other) + + def intersection(self, other): + """Returns a new geometry consisting of the region of intersection of this + geometry and the other.""" + return self._geomgen(lgdal.OGR_G_Intersection, other) + +# The subclasses for OGR Geometry. +class Point(OGRGeometry): + + @property + def x(self): + "Returns the X coordinate for this Point." + return getx(self._g, c_int(0)) + + @property + def y(self): + "Returns the Y coordinate for this Point." + return gety(self._g, c_int(0)) + + @property + def z(self): + "Returns the Z coordinate for this Point." + return getz(self._g, c_int(0)) + + @property + def tuple(self): + "Returns the tuple of this point." + if self.coord_dim == 1: + return (self.x,) + elif self.coord_dim == 2: + return (self.x, self.y) + elif self.coord_dim == 3: + return (self.x, self.y, self.z) + +class LineString(OGRGeometry): + + def __getitem__(self, index): + "Returns the Point at the given index." + if index > 0 or index < self.point_count: + x = c_double() + y = c_double() + z = c_double() + lgdal.OGR_G_GetPoint(self._g, c_int(index), + byref(x), byref(y), byref(z)) + if self.coord_dim == 1: + return (x.value,) + elif self.coord_dim == 2: + return (x.value, y.value) + elif self.coord_dim == 3: + return (x.value, y.value, z.value) + else: + raise OGRGeometryIndexError, 'index out of range: %s' % str(index) + + def __iter__(self): + "Iterates over each point in the LineString." + for i in xrange(self.point_count): + yield self.__getitem__(i) + + def __len__(self, index): + "The length returns the number of points in the LineString." + return self.point_count + + @property + def tuple(self): + "Returns the tuple representation of this LineString." + return tuple(self.__getitem__(i) for i in xrange(self.point_count)) + +# LinearRings are used in Polygons. +class LinearRing(LineString): pass + +class Polygon(OGRGeometry): + + def __len__(self): + "The number of interior rings in this Polygon." + return self.geom_count + + def __iter__(self): + "Iterates through each ring in the Polygon." + for i in xrange(self.geom_count): + yield self.__getitem__(i) + + def __getitem__(self, index): + "Gets the ring at the specified index." + if index < 0 or index >= self.geom_count: + raise OGRGeometryIndexError, 'index out of range: %s' % str(index) + else: + return OGRGeometry(lgdal.OGR_G_Clone(lgdal.OGR_G_GetGeometryRef(self._g, c_int(index)))) + + # Polygon Properties + @property + def shell(self): + "Returns the shell of this Polygon." + return self.__getitem__(0) # First ring is the shell + + @property + def tuple(self): + "Returns a tuple of LinearRing coordinate tuples." + return tuple(self.__getitem__(i).tuple for i in xrange(self.geom_count)) + + @property + def point_count(self): + "The number of Points in this Polygon." + # Summing up the number of points in each ring of the Polygon. + return sum([self.__getitem__(i).point_count for i in xrange(self.geom_count)]) + + @property + def centroid(self): + "Returns the centroid (a Point) of this Polygon." + # The centroid is a Point, create a geometry for this. + p = OGRGeometry(OGRGeomType('Point')) + check_err(lgdal.OGR_G_Centroid(self._g, p._g)) + return p + +# Geometry Collection base class. +class GeometryCollection(OGRGeometry): + "The Geometry Collection class." + + def __getitem__(self, index): + "Gets the Geometry at the specified index." + if index < 0 or index >= self.geom_count: + raise OGRGeometryIndexError, 'index out of range: %s' % str(index) + else: + return OGRGeometry(lgdal.OGR_G_Clone(lgdal.OGR_G_GetGeometryRef(self._g, c_int(index)))) + + def __iter__(self): + "Iterates over each Geometry." + for i in xrange(self.geom_count): + yield self.__getitem__(i) + + def __len__(self): + "The number of geometries in this Geometry Collection." + return self.geom_count + + def add(self, geom): + "Add the geometry to this Geometry Collection." + if isinstance(geom, OGRGeometry): + ptr = geom._g + elif isinstance(geom, StringType): + tmp = OGRGeometry(geom) + ptr = tmp._g + else: + raise OGRException, 'Must add an OGRGeometry.' + lgdal.OGR_G_AddGeometry(self._g, ptr) + + @property + def point_count(self): + "The number of Points in this Geometry Collection." + # Summing up the number of points in each geometry in this collection + return sum([self.__getitem__(i).point_count for i in xrange(self.geom_count)]) + + @property + def tuple(self): + "Returns a tuple representation of this Geometry Collection." + return tuple(self.__getitem__(i).tuple for i in xrange(self.geom_count)) + +# Multiple Geometry types. +class MultiPoint(GeometryCollection): pass +class MultiLineString(GeometryCollection): pass +class MultiPolygon(GeometryCollection): pass + +# Class mapping dictionary (using the OGRwkbGeometryType as the key) +GEO_CLASSES = {1 : Point, + 2 : LineString, + 3 : Polygon, + 4 : MultiPoint, + 5 : MultiLineString, + 6 : MultiPolygon, + 7 : GeometryCollection, + } diff --git a/django/contrib/gis/gdal/geomtype.py b/django/contrib/gis/gdal/geomtype.py new file mode 100644 index 0000000000..1f2fb28c28 --- /dev/null +++ b/django/contrib/gis/gdal/geomtype.py @@ -0,0 +1,67 @@ +from types import StringType + +#### OGRGeomType #### +class OGRGeomType(object): + "Encapulates OGR Geometry Types." + + # Ordered array of acceptable strings and their corresponding OGRwkbGeometryType + __ogr_str = ['Point', 'LineString', 'Polygon', 'MultiPoint', + 'MultiLineString', 'MultiPolygon', 'GeometryCollection', + 'LinearRing'] + __ogr_int = [1, 2, 3, 4, 5, 6, 7, 101] + + def __init__(self, input): + "Figures out the correct OGR Type based upon the input." + if isinstance(input, OGRGeomType): + self._index = input._index + elif isinstance(input, StringType): + idx = self._has_str(self.__ogr_str, input) + if idx == None: + raise OGRException, 'Invalid OGR String Type "%s"' % input + self._index = idx + elif isinstance(input, int): + if not input in self.__ogr_int: + raise OGRException, 'Invalid OGR Integer Type: %d' % input + self._index = self.__ogr_int.index(input) + else: + raise TypeError, 'Invalid OGR Input type given!' + + def __str__(self): + "Returns a short-hand string form of the OGR Geometry type." + return self.__ogr_str[self._index] + + def __eq__(self, other): + """Does an equivalence test on the OGR type with the given + other OGRGeomType, the short-hand string, or the integer.""" + if isinstance(other, OGRGeomType): + return self._index == other._index + elif isinstance(other, StringType): + idx = self._has_str(self.__ogr_str, other) + if not (idx == None): return self._index == idx + return False + elif isinstance(other, int): + if not other in self.__ogr_int: return False + return self.__ogr_int.index(other) == self._index + else: + raise TypeError, 'Cannot compare with type: %s' % str(type(other)) + + def _has_str(self, arr, s): + "Case-insensitive search of the string array for the given pattern." + s_low = s.lower() + for i in xrange(len(arr)): + if s_low == arr[i].lower(): return i + return None + + @property + def django(self): + "Returns the Django GeometryField for this OGR Type." + s = self.__ogr_str[self._index] + if s in ('Unknown', 'LinearRing'): + return None + else: + return s + 'Field' + + @property + def num(self): + "Returns the OGRwkbGeometryType number for the OGR Type." + return self.__ogr_int[self._index] diff --git a/django/contrib/gis/gdal/layer.py b/django/contrib/gis/gdal/layer.py new file mode 100644 index 0000000000..1d343ef363 --- /dev/null +++ b/django/contrib/gis/gdal/layer.py @@ -0,0 +1,114 @@ +# Needed ctypes routines +from ctypes import c_int, c_long, c_void_p, byref, string_at + +# The GDAL C Library +from django.contrib.gis.gdal.libgdal import lgdal + +# Other GDAL imports. +from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope +from django.contrib.gis.gdal.feature import Feature +from django.contrib.gis.gdal.geometries import OGRGeomType +from django.contrib.gis.gdal.error import OGRException, check_err +from django.contrib.gis.gdal.srs import SpatialReference + +# For more information, see the OGR C API source code: +# http://www.gdal.org/ogr/ogr__api_8h.html +# +# 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): + "A class that wraps an OGR Layer, needs to be instantiated from a DataSource object." + + #### Python 'magic' routines #### + def __init__(self, l): + "Needs a C pointer (Python/ctypes integer) in order to initialize." + self._layer = 0 # Initially NULL + self._ldefn = 0 + if not l: + raise OGRException, 'Cannot create Layer, invalid pointer given' + self._layer = l + self._ldefn = lgdal.OGR_L_GetLayerDefn(l) + + def __getitem__(self, index): + "Gets the Feature at the specified index." + def make_feature(offset): + return Feature(lgdal.OGR_L_GetFeature(self._layer, + c_long(offset))) + end = self.num_feat + if not isinstance(index, (slice, int)): + raise TypeError + + if isinstance(index,int): + # An integer index was given + if index < 0: + index = end - index + if index < 0 or index >= self.num_feat: + raise IndexError, 'index out of range' + return make_feature(index) + else: + # A slice was given + start, stop, stride = index.indices(end) + return [make_feature(offset) for offset in range(start,stop,stride)] + + def __iter__(self): + "Iterates over each Feature in the Layer." + #TODO: is OGR's GetNextFeature faster here? + for i in range(self.num_feat): + yield self.__getitem__(i) + + def __len__(self): + "The length is the number of features." + return self.num_feat + + def __str__(self): + "The string name of the layer." + return self.name + + #### Layer properties #### + @property + def extent(self): + "Returns the extent (an Envelope) of this layer." + env = OGREnvelope() + check_err(lgdal.OGR_L_GetExtent(self._layer, byref(env), c_int(1))) + return Envelope(env) + + @property + def name(self): + "Returns the name of this layer in the Data Source." + return string_at(lgdal.OGR_FD_GetName(self._ldefn)) + + @property + def num_feat(self, force=1): + "Returns the number of features in the Layer." + return lgdal.OGR_L_GetFeatureCount(self._layer, c_int(force)) + + @property + def num_fields(self): + "Returns the number of fields in the Layer." + return lgdal.OGR_FD_GetFieldCount(self._ldefn) + + @property + def geom_type(self): + "Returns the geometry type (OGRGeomType) of the Layer." + return OGRGeomType(lgdal.OGR_FD_GetGeomType(self._ldefn)) + + @property + def srs(self): + "Returns the Spatial Reference used in this Layer." + ptr = lgdal.OGR_L_GetSpatialRef(self._layer) + if ptr: + return SpatialReference(lgdal.OSRClone(ptr), 'ogr') + else: + return None + + @property + def fields(self): + "Returns a list of the fields available in this Layer." + return [ string_at(lgdal.OGR_Fld_GetNameRef(lgdal.OGR_FD_GetFieldDefn(self._ldefn, i))) + for i in xrange(self.num_fields) ] + diff --git a/django/contrib/gis/gdal/libgdal.py b/django/contrib/gis/gdal/libgdal.py index 76cca40de9..e40f9138d1 100644 --- a/django/contrib/gis/gdal/libgdal.py +++ b/django/contrib/gis/gdal/libgdal.py @@ -1,6 +1,6 @@ import os, sys from ctypes import CDLL -from django.contrib.gis.gdal.OGRError import OGRException +from django.contrib.gis.gdal.error import OGRException if os.name == 'nt': # Windows NT shared library @@ -20,4 +20,4 @@ else: # This loads the GDAL/OGR C library lgdal = CDLL(lib_name) - + diff --git a/django/contrib/gis/gdal/srs.py b/django/contrib/gis/gdal/srs.py new file mode 100644 index 0000000000..d5639f1b0a --- /dev/null +++ b/django/contrib/gis/gdal/srs.py @@ -0,0 +1,339 @@ +# Getting what we need from ctypes +import re +from types import StringType, TupleType +from ctypes import \ + 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 +from django.contrib.gis.gdal.error import check_err, OGRException, SRSException + +""" + The Spatial Reference class, represensents OGR Spatial Reference objects. + + Example: + >>> from django.contrib.gis.gdal import SpatialReference + >>> srs = SpatialReference('WGS84') + >>> print srs + GEOGCS["WGS 84", + DATUM["WGS_1984", + SPHEROID["WGS 84",6378137,298.257223563, + AUTHORITY["EPSG","7030"]], + TOWGS84[0,0,0,0,0,0,0], + AUTHORITY["EPSG","6326"]], + PRIMEM["Greenwich",0, + AUTHORITY["EPSG","8901"]], + UNIT["degree",0.01745329251994328, + AUTHORITY["EPSG","9122"]], + AUTHORITY["EPSG","4326"]] + >>> print srs.proj + +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs + >>> print srs.ellipsoid + (6378137.0, 6356752.3142451793, 298.25722356300003) + >>> print srs.projected, srs.geographic + False True + >>> srs.import_epsg(32140) + >>> print srs.name + NAD83 / Texas South Central +""" + + +#### ctypes function prototypes #### +def ellipsis_func(f): + """Creates a ctypes function prototype for OSR ellipsis property functions, + 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. #### +class SpatialReference(object): + """A wrapper for the OGRSpatialReference object. According to the GDAL website, + the SpatialReference object 'provide[s] services to represent coordinate systems + (projections and datums) and to transform between them.'""" + + # Well-Known Geographical Coordinate System Name + _well_known = {'WGS84':4326, 'WGS72':4322, 'NAD27':4267, 'NAD83':4269} + _epsg_regex = re.compile('^EPSG:(?P\d+)$', re.I) + + #### Python 'magic' routines #### + def __init__(self, input='', srs_type='wkt'): + "Creates a spatial reference object from the given OGC Well Known Text (WKT)." + + self._srs = 0 # Initially NULL + + # Creating an initial empty string buffer. + buf = c_char_p('') + + if isinstance(input, StringType): + # Is this an EPSG well known name? + m = self._epsg_regex.match(input) + if m: + srs_type = 'epsg' + input = int(m.group('epsg')) + # Is this a short-hand well known name? + elif input in self._well_known: + srs_type = 'epsg' + input = self._well_known[input] + elif srs_type == 'proj': + pass + else: + buf = c_char_p(input) + elif isinstance(input, int): + if srs_type == 'wkt': srs_type = 'epsg' # want to try epsg if only integer provided + if srs_type not in ('epsg', 'ogr'): + raise SRSException, 'Integer input requires SRS type of "ogr" or "epsg".' + else: + raise TypeError, 'Invalid SRS type "%s"' % srs_type + + # Calling OSRNewSpatialReference with the string buffer. + if srs_type == 'ogr': + srs = input # Input is OGR pointer + else: + srs = lgdal.OSRNewSpatialReference(buf) + + # If the pointer is NULL, throw an exception. + if not srs: + raise SRSException, 'Could not create spatial reference from WKT!' + else: + self._srs = srs + + # Post-processing if in PROJ.4 or EPSG formats. + if srs_type == 'proj': self.import_proj(input) + elif srs_type == 'epsg': self.import_epsg(input) + + def __del__(self): + "Destroys this spatial reference." + if self._srs: lgdal.OSRRelease(self._srs) + + def __getitem__(self, target): + """Returns the value of the given string attribute node, None if the node doesn't exist. + Can also take a tuple as a parameter, (target, child), where child is the child index to get.""" + if isinstance(target, TupleType): + return self.attr_value(*target) + else: + return self.attr_value(target) + + def __str__(self): + "The string representation uses '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 #### + 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): + """The attribute value for the given target node (e.g. 'PROJCS'). The index keyword + specifies an index of the child node to return.""" + ptr = lgdal.OSRGetAttrValue(self._srs, c_char_p(target), c_int(index)) + return self._string_ptr(ptr) + + def validate(self): + "Checks to see if the given spatial reference is valid." + check_err(lgdal.OSRValidate(self._srs)) + + def clone(self): + "Returns a clone of this Spatial Reference." + return SpatialReference(lgdal.OSRClone(self._srs), 'ogr') + + @property + def name(self): + "Returns the name of this Spatial Reference." + if self.projected: return self.attr_value('PROJCS') + elif self.geographic: return self.attr_value('GEOGCS') + elif self.local: return self.attr_value('LOCAL_CS') + else: return None + + #### 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 + def linear_name(self): + "Returns the name of the linear units." + self._cache_linear() + return self._linear_name + + @property + def linear_units(self): + "Returns the value of the linear units." + self._cache_linear() + return self._linear_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 + def angular_name(self): + "Returns the name of the angular units." + self._cache_angular() + return self._angular_name + + @property + def angular_units(self): + "Returns the value of the angular units." + self._cache_angular() + return self._angular_units + + #### Spheroid/Ellipsoid Properties #### + @property + def ellipsoid(self): + """Returns a tuple of the ellipsoid parameters: + (semimajor axis, semiminor axis, and inverse flattening).""" + return (self.semi_major, self.semi_minor, self.inverse_flattening) + + @property + def semi_major(self): + "Gets the Semi Major Axis for this Spatial Reference." + err = c_int(0) + sm = semi_major(self._srs, byref(err)) + check_err(err.value) + return sm + + @property + def semi_minor(self): + "Gets the Semi Minor Axis for this Spatial Reference." + err = c_int() + sm = semi_minor(self._srs, byref(err)) + check_err(err.value) + return sm + + @property + def inverse_flattening(self): + "Gets the Inverse Flattening for this Spatial Reference." + err = c_int() + inv_flat = invflattening(self._srs, byref(err)) + check_err(err.value) + return inv_flat + + #### Boolean Properties #### + @property + def geographic(self): + "Returns True if this SpatialReference is geographic (root node is GEOGCS)." + if lgdal.OSRIsGeographic(self._srs): return True + else: return False + + @property + def local(self): + "Returns True if this SpatialReference is local (root node is LOCAL_CS)." + if lgdal.OSRIsLocal(self._srs): return True + else: return False + + @property + def projected(self): + "Returns True if this SpatialReference is a projected coordinate system (root node is PROJCS)." + if lgdal.OSRIsProjected(self._srs): return True + else: return False + + #### Import Routines ##### + def import_wkt(self, wkt): + "Imports the Spatial Reference from OGC WKT (string)" + buf = create_string_buffer(wkt) + check_err(lgdal.OSRImportFromWkt(self._srs, byref(buf))) + + def import_proj(self, proj): + "Imports the Spatial Reference from a PROJ.4 string." + check_err(lgdal.OSRImportFromProj4(self._srs, create_string_buffer(proj))) + + def import_epsg(self, epsg): + "Imports the Spatial Reference from the EPSG code (an integer)." + check_err(lgdal.OSRImportFromEPSG(self._srs, c_int(epsg))) + + def import_xml(self, xml): + "Imports the Spatial Reference from an XML string." + check_err(lgdal.OSRImportFromXML(self._srs, create_string_buffer(xml))) + + #### Export Properties #### + @property + def wkt(self): + "Returns the WKT representation of this Spatial Reference." + w = c_char_p() + check_err(lgdal.OSRExportToWkt(self._srs, byref(w))) + return string_at(w) + + @property + def pretty_wkt(self, simplify=0): + "Returns the 'pretty' representation of the WKT." + w = c_char_p() + check_err(lgdal.OSRExportToPrettyWkt(self._srs, byref(w), c_int(simplify))) + return string_at(w) + + @property + def proj(self): + "Returns the PROJ.4 representation for this Spatial Reference." + w = c_char_p() + check_err(lgdal.OSRExportToProj4(self._srs, byref(w))) + return string_at(w) + + def proj4(self): + "Alias for proj()." + return self.proj + + @property + def xml(self, dialect=''): + "Returns the XML representation of this Spatial Reference." + w = c_char_p() + check_err(lgdal.OSRExportToXML(self._srs, byref(w), create_string_buffer(dialect))) + return string_at(w) + +class CoordTransform(object): + "A coordinate system transformation object." + + def __init__(self, source, target): + "Initializes on a source and target SpatialReference objects." + self._ct = 0 # Initially NULL + if not isinstance(source, SpatialReference) or not isinstance(target, SpatialReference): + raise SRSException, 'source and target must be of type SpatialReference' + ct = lgdal.OCTNewCoordinateTransformation(source._srs, target._srs) + if not ct: + raise SRSException, 'could not intialize CoordTransform object' + self._ct = ct + self._srs1_name = source.name + self._srs2_name = target.name + + def __del__(self): + "Deletes this Coordinate Transformation object." + if self._ct: lgdal.OCTDestroyCoordinateTransformation(self._ct) + + def __str__(self): + return 'Transform from "%s" to "%s"' % (str(self._srs1_name), str(self._srs2_name)) + diff --git a/django/contrib/gis/geos/base.py b/django/contrib/gis/geos/base.py index 10213a63c0..274d4c146b 100644 --- a/django/contrib/gis/geos/base.py +++ b/django/contrib/gis/geos/base.py @@ -281,8 +281,7 @@ class GEOSGeometry(object): return self._binary_predicate(lgeos.GEOSEqualsExact, other, tol) #### SRID Routines #### - @property - def srid(self): + def get_srid(self): "Gets the SRID for the geometry, returns None if no SRID is set." s = lgeos.GEOSGetSRID(self._ptr()) if s == 0: @@ -293,7 +292,8 @@ class GEOSGeometry(object): def set_srid(self, srid): "Sets the SRID for the geometry." lgeos.GEOSSetSRID(self._ptr(), c_int(srid)) - + srid = property(get_srid, set_srid) + #### Output Routines #### @property def wkt(self): diff --git a/django/contrib/gis/tests/test_gdal_ds.py b/django/contrib/gis/tests/test_gdal_ds.py index 19dc74534a..799442cf70 100644 --- a/django/contrib/gis/tests/test_gdal_ds.py +++ b/django/contrib/gis/tests/test_gdal_ds.py @@ -1,7 +1,7 @@ import os, os.path, unittest from django.contrib.gis.gdal import DataSource, OGRException -from django.contrib.gis.gdal.Envelope import Envelope -from django.contrib.gis.gdal.Field import OFTReal, OFTInteger, OFTString +from django.contrib.gis.gdal.envelope import Envelope +from django.contrib.gis.gdal.field import OFTReal, OFTInteger, OFTString # Path for SHP files shp_path = os.path.dirname(__file__) -- cgit v1.3