diff options
Diffstat (limited to 'lib/python2.7/site-packages/django/contrib/gis/gdal')
25 files changed, 3827 insertions, 0 deletions
diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/__init__.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/__init__.py new file mode 100644 index 0000000..2aa867b --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/__init__.py @@ -0,0 +1,55 @@ +""" + This module houses ctypes interfaces for GDAL objects. The following GDAL + objects are supported: + + CoordTransform: Used for coordinate transformations from one spatial + reference system to another. + + Driver: Wraps an OGR data source driver. + + DataSource: Wrapper for the OGR data source object, supports + OGR-supported data sources. + + Envelope: A ctypes structure for bounding boxes (GDAL library + not required). + + OGRGeometry: Object for accessing OGR Geometry functionality. + + OGRGeomType: A class for representing the different OGR Geometry + types (GDAL library not required). + + SpatialReference: Represents OSR Spatial Reference objects. + + The GDAL library will be imported from the system path using the default + library name for the current OS. The default library path may be overridden + by setting `GDAL_LIBRARY_PATH` in your settings with the path to the GDAL C + library on your system. + + GDAL links to a large number of external libraries that consume RAM when + loaded. Thus, it may desirable to disable GDAL on systems with limited + RAM resources -- this may be accomplished by setting `GDAL_LIBRARY_PATH` + to a non-existant file location (e.g., `GDAL_LIBRARY_PATH='/null/path'`; + setting to None/False/'' will not work as a string must be given). +""" +from django.contrib.gis.gdal.error import check_err, OGRException, OGRIndexError, SRSException +from django.contrib.gis.gdal.geomtype import OGRGeomType + +# Attempting to import objects that depend on the GDAL library. The +# HAS_GDAL flag will be set to True if the library is present on +# the system. +try: + from django.contrib.gis.gdal.driver import Driver + from django.contrib.gis.gdal.datasource import DataSource + from django.contrib.gis.gdal.libgdal import gdal_version, gdal_full_version, GDAL_VERSION + from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform + from django.contrib.gis.gdal.geometries import OGRGeometry + HAS_GDAL = True +except OGRException: + HAS_GDAL = False + +try: + from django.contrib.gis.gdal.envelope import Envelope +except ImportError: + # No ctypes, but don't raise an exception. + pass + diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/base.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/base.py new file mode 100644 index 0000000..e86277e --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/base.py @@ -0,0 +1,36 @@ +from ctypes import c_void_p + +from django.contrib.gis.gdal.error import GDALException +from django.utils import six + +class GDALBase(object): + """ + Base object for GDAL objects that has a pointer access property + that controls access to the underlying C pointer. + """ + # Initially the pointer is NULL. + _ptr = None + + # Default allowed pointer type. + ptr_type = c_void_p + + # Pointer access property. + def _get_ptr(self): + # Raise an exception if the pointer isn't valid don't + # want to be passing NULL pointers to routines -- + # that's very bad. + if self._ptr: return self._ptr + else: raise GDALException('GDAL %s pointer no longer valid.' % self.__class__.__name__) + + def _set_ptr(self, ptr): + # Only allow the pointer to be set with pointers of the + # compatible type or None (NULL). + if isinstance(ptr, six.integer_types): + self._ptr = self.ptr_type(ptr) + elif ptr is None or isinstance(ptr, self.ptr_type): + self._ptr = ptr + else: + raise TypeError('Incompatible pointer type') + + ptr = property(_get_ptr, _set_ptr) + diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/datasource.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/datasource.py new file mode 100644 index 0000000..c92b2e1 --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/datasource.py @@ -0,0 +1,135 @@ +""" + 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 +""" +# ctypes prerequisites. +from ctypes import byref + +# The GDAL C library, OGR exceptions, and the Layer object. +from django.contrib.gis.gdal.base import GDALBase +from django.contrib.gis.gdal.driver import Driver +from django.contrib.gis.gdal.error import OGRException, OGRIndexError +from django.contrib.gis.gdal.layer import Layer + +# Getting the ctypes prototypes for the DataSource. +from django.contrib.gis.gdal.prototypes import ds as capi + +from django.utils.encoding import force_bytes, force_text +from django.utils import six +from django.utils.six.moves import xrange + +# 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(GDALBase): + "Wraps an OGR Data Source object." + + #### Python 'magic' routines #### + def __init__(self, ds_input, ds_driver=False, write=False, encoding='utf-8'): + # The write flag. + if write: + self._write = 1 + else: + self._write = 0 + # See also http://trac.osgeo.org/gdal/wiki/rfc23_ogr_unicode + self.encoding = encoding + + # Registering all the drivers, this needs to be done + # _before_ we try to open up a data source. + if not capi.get_driver_count(): + capi.register_all() + + if isinstance(ds_input, six.string_types): + # The data source driver is a void pointer. + ds_driver = Driver.ptr_type() + try: + # OGROpen will auto-detect the data source type. + ds = capi.open_ds(force_bytes(ds_input), self._write, byref(ds_driver)) + except OGRException: + # Making the error message more clear rather than something + # like "Invalid pointer returned from OGROpen". + raise OGRException('Could not open the datasource at "%s"' % ds_input) + elif isinstance(ds_input, self.ptr_type) and isinstance(ds_driver, Driver.ptr_type): + ds = ds_input + else: + raise OGRException('Invalid data source input type: %s' % type(ds_input)) + + if bool(ds): + self.ptr = ds + self.driver = Driver(ds_driver) + else: + # Raise an exception if the returned pointer is NULL + raise OGRException('Invalid data source file "%s"' % ds_input) + + def __del__(self): + "Destroys this DataStructure object." + if self._ptr: capi.destroy_ds(self._ptr) + + def __iter__(self): + "Allows for iteration over the layers in a data source." + for i in xrange(self.layer_count): + yield self[i] + + def __getitem__(self, index): + "Allows use of the index [] operator to get a layer at the index." + if isinstance(index, six.string_types): + l = capi.get_layer_by_name(self.ptr, force_bytes(index)) + if not l: raise OGRIndexError('invalid OGR Layer name given: "%s"' % index) + elif isinstance(index, int): + if index < 0 or index >= self.layer_count: + raise OGRIndexError('index out of range') + l = capi.get_layer(self._ptr, index) + else: + raise TypeError('Invalid index type: %s' % type(index)) + return Layer(l, self) + + 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)) + + @property + def layer_count(self): + "Returns the number of layers in the data source." + return capi.get_layer_count(self._ptr) + + @property + def name(self): + "Returns the name of the data source." + name = capi.get_ds_name(self._ptr) + return force_text(name, self.encoding, strings_only=True) diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/driver.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/driver.py new file mode 100644 index 0000000..55a5d77 --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/driver.py @@ -0,0 +1,68 @@ +# prerequisites imports +from ctypes import c_void_p +from django.contrib.gis.gdal.base import GDALBase +from django.contrib.gis.gdal.error import OGRException +from django.contrib.gis.gdal.prototypes import ds as capi + +from django.utils import six +from django.utils.encoding import force_bytes + +# 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(GDALBase): + "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, dr_input): + "Initializes an OGR driver on either a string or integer input." + + if isinstance(dr_input, six.string_types): + # 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 dr_input.lower() in self._alias: + name = self._alias[dr_input.lower()] + else: + name = dr_input + + # Attempting to get the OGR driver by the string name. + dr = capi.get_driver_by_name(force_bytes(name)) + elif isinstance(dr_input, int): + self._register() + dr = capi.get_driver(dr_input) + elif isinstance(dr_input, c_void_p): + dr = dr_input + else: + raise OGRException('Unrecognized input type for OGR Driver: %s' % str(type(dr_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(dr_input)) + self.ptr = dr + + def __str__(self): + "Returns the string name of the OGR Driver." + return capi.get_driver_name(self.ptr) + + 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: capi.register_all() + + # Driver properties + @property + def driver_count(self): + "Returns the number of OGR data source drivers registered." + return capi.get_driver_count() diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/envelope.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/envelope.py new file mode 100644 index 0000000..f145526 --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/envelope.py @@ -0,0 +1,175 @@ +""" + 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----------+ +""" +from ctypes import Structure, c_double +from django.contrib.gis.gdal.error import OGRException + +# 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): + """ + The Envelope object is a C structure that contains the minimum and + maximum X, Y coordinates for a rectangle bounding box. The naming + of the variables is compatible with the OGR Envelope structure. + """ + + def __init__(self, *args): + """ + The initialization function may take an OGREnvelope structure, 4-element + tuple or list, or 4 individual arguments. + """ + + if len(args) == 1: + if isinstance(args[0], OGREnvelope): + # OGREnvelope (a ctypes Structure) was passed in. + self._envelope = args[0] + elif isinstance(args[0], (tuple, list)): + # A tuple was passed in. + if len(args[0]) != 4: + raise OGRException('Incorrect number of tuple elements (%d).' % len(args[0])) + else: + self._from_sequence(args[0]) + else: + raise TypeError('Incorrect type of argument: %s' % str(type(args[0]))) + elif len(args) == 4: + # Individual parameters passed in. + # Thanks to ww for the help + self._from_sequence([float(a) for a in args]) + else: + raise OGRException('Incorrect number (%d) of arguments.' % len(args)) + + # Checking the x,y coordinates + if self.min_x > self.max_x: + raise OGRException('Envelope minimum X > maximum X.') + if self.min_y > self.max_y: + raise OGRException('Envelope minimum Y > maximum Y.') + + def __eq__(self, other): + """ + 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, tuple) 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_sequence(self, seq): + "Initializes the C OGR Envelope structure from the given sequence." + self._envelope = OGREnvelope() + self._envelope.MinX = seq[0] + self._envelope.MinY = seq[1] + self._envelope.MaxX = seq[2] + self._envelope.MaxY = seq[3] + + def expand_to_include(self, *args): + """ + Modifies the envelope to expand to include the boundaries of + the passed-in 2-tuple (a point), 4-tuple (an extent) or + envelope. + """ + # We provide a number of different signatures for this method, + # and the logic here is all about converting them into a + # 4-tuple single parameter which does the actual work of + # expanding the envelope. + if len(args) == 1: + if isinstance(args[0], Envelope): + return self.expand_to_include(args[0].tuple) + elif hasattr(args[0], 'x') and hasattr(args[0], 'y'): + return self.expand_to_include(args[0].x, args[0].y, args[0].x, args[0].y) + elif isinstance(args[0], (tuple, list)): + # A tuple was passed in. + if len(args[0]) == 2: + return self.expand_to_include((args[0][0], args[0][1], args[0][0], args[0][1])) + elif len(args[0]) == 4: + (minx, miny, maxx, maxy) = args[0] + if minx < self._envelope.MinX: + self._envelope.MinX = minx + if miny < self._envelope.MinY: + self._envelope.MinY = miny + if maxx > self._envelope.MaxX: + self._envelope.MaxX = maxx + if maxy > self._envelope.MaxY: + self._envelope.MaxY = maxy + else: + raise OGRException('Incorrect number of tuple elements (%d).' % len(args[0])) + else: + raise TypeError('Incorrect type of argument: %s' % str(type(args[0]))) + elif len(args) == 2: + # An x and an y parameter were passed in + return self.expand_to_include((args[0], args[1], args[0], args[1])) + elif len(args) == 4: + # Individual parameters passed in. + return self.expand_to_include(args) + else: + raise OGRException('Incorrect number (%d) of arguments.' % len(args[0])) + + @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/lib/python2.7/site-packages/django/contrib/gis/gdal/error.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/error.py new file mode 100644 index 0000000..1d89ad1 --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/error.py @@ -0,0 +1,42 @@ +""" + This module houses the OGR & SRS Exception objects, and the + check_err() routine which checks the status code returned by + OGR methods. +""" +#### OGR & SRS Exceptions #### +class GDALException(Exception): pass +class OGRException(Exception): pass +class SRSException(Exception): pass +class OGRIndexError(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 + +#### OGR error checking codes and routine #### + +# OGR Error Codes +OGRERR_DICT = { 1 : (OGRException, 'Not enough data.'), + 2 : (OGRException, 'Not enough memory.'), + 3 : (OGRException, 'Unsupported geometry type.'), + 4 : (OGRException, 'Unsupported operation.'), + 5 : (OGRException, 'Corrupt data.'), + 6 : (OGRException, 'OGR failure.'), + 7 : (SRSException, 'Unsupported SRS.'), + 8 : (OGRException, 'Invalid handle.'), + } +OGRERR_NONE = 0 + +def check_err(code): + "Checks the given OGRERR, and raises an exception where appropriate." + + if code == OGRERR_NONE: + return + elif code in OGRERR_DICT: + e, msg = OGRERR_DICT[code] + raise e(msg) + else: + raise OGRException('Unknown error code: "%s"' % code) diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/feature.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/feature.py new file mode 100644 index 0000000..a11a687 --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/feature.py @@ -0,0 +1,124 @@ +# The GDAL C library, OGR exception, and the Field object +from django.contrib.gis.gdal.base import GDALBase +from django.contrib.gis.gdal.error import OGRException, OGRIndexError +from django.contrib.gis.gdal.field import Field +from django.contrib.gis.gdal.geometries import OGRGeometry, OGRGeomType + +# ctypes function prototypes +from django.contrib.gis.gdal.prototypes import ds as capi, geom as geom_api + +from django.utils.encoding import force_bytes, force_text +from django.utils import six +from django.utils.six.moves import xrange + +# 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(GDALBase): + """ + This class that wraps an OGR Feature, needs to be instantiated + from a Layer object. + """ + + #### Python 'magic' routines #### + def __init__(self, feat, layer): + """ + Initializes Feature from a pointer and its Layer object. + """ + if not feat: + raise OGRException('Cannot create OGR Feature, invalid pointer given.') + self.ptr = feat + self._layer = layer + + def __del__(self): + "Releases a reference to this object." + if self._ptr: capi.destroy_feature(self._ptr) + + def __getitem__(self, index): + """ + Gets the Field object at the specified index, which may be either + an integer or the Field's string label. Note that the Field object + is not the field's _value_ -- use the `get` method instead to + retrieve the value (e.g. an integer) instead of a Field instance. + """ + if isinstance(index, six.string_types): + i = self.index(index) + else: + if index < 0 or index > self.num_fields: + raise OGRIndexError('index out of range') + i = index + return Field(self, i) + + def __iter__(self): + "Iterates over each field in the Feature." + for i in xrange(self.num_fields): + yield self[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." + return bool(capi.feature_equal(self.ptr, other._ptr)) + + #### Feature Properties #### + @property + def encoding(self): + return self._layer._ds.encoding + + @property + def fid(self): + "Returns the feature identifier." + return capi.get_fid(self.ptr) + + @property + def layer_name(self): + "Returns the name of the layer for the feature." + name = capi.get_feat_name(self._layer._ldefn) + return force_text(name, self.encoding, strings_only=True) + + @property + def num_fields(self): + "Returns the number of fields in the Feature." + return capi.get_feat_field_count(self.ptr) + + @property + def fields(self): + "Returns a list of fields in the Feature." + return [capi.get_field_name(capi.get_field_defn(self._layer._ldefn, i)) + for i in xrange(self.num_fields)] + + @property + def geom(self): + "Returns the OGR Geometry for this Feature." + # Retrieving the geometry pointer for the feature. + geom_ptr = capi.get_feat_geom_ref(self.ptr) + return OGRGeometry(geom_api.clone_geom(geom_ptr)) + + @property + def geom_type(self): + "Returns the OGR Geometry Type for this Feture." + return OGRGeomType(capi.get_fd_geom_type(self._layer._ldefn)) + + #### Feature Methods #### + def get(self, field): + """ + Returns the value of the field, instead of an instance of the Field + object. May take a string of the field name or a Field object as + parameters. + """ + field_name = getattr(field, 'name', field) + return self[field_name].value + + def index(self, field_name): + "Returns the index of the given field name." + i = capi.get_field_index(self.ptr, force_bytes(field_name)) + if i < 0: + raise OGRIndexError('invalid OFT field name given: "%s"' % field_name) + return i diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/field.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/field.py new file mode 100644 index 0000000..2415f32 --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/field.py @@ -0,0 +1,194 @@ +from ctypes import byref, c_int +from datetime import date, datetime, time +from django.contrib.gis.gdal.base import GDALBase +from django.contrib.gis.gdal.error import OGRException +from django.contrib.gis.gdal.prototypes import ds as capi +from django.utils.encoding import force_text + + +# 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(GDALBase): + """ + This class wraps an OGR Field, and needs to be instantiated + from a Feature object. + """ + + #### Python 'magic' routines #### + def __init__(self, feat, index): + """ + Initializes on the feature object and the integer index of + the field within the feature. + """ + # Setting the feature pointer and index. + self._feat = feat + self._index = index + + # Getting the pointer for this field. + fld_ptr = capi.get_feat_field_defn(feat.ptr, index) + if not fld_ptr: + raise OGRException('Cannot create OGR Field, invalid pointer given.') + self.ptr = fld_ptr + + # Setting the class depending upon the OGR Field Type (OFT) + self.__class__ = OGRFieldTypes[self.type] + + # OFTReal with no precision should be an OFTInteger. + if isinstance(self, OFTReal) and self.precision == 0: + self.__class__ = OFTInteger + self._double = True + + def __str__(self): + "Returns the string representation of the Field." + return str(self.value).strip() + + #### Field Methods #### + def as_double(self): + "Retrieves the Field's value as a double (float)." + return capi.get_field_as_double(self._feat.ptr, self._index) + + def as_int(self): + "Retrieves the Field's value as an integer." + return capi.get_field_as_integer(self._feat.ptr, self._index) + + def as_string(self): + "Retrieves the Field's value as a string." + string = capi.get_field_as_string(self._feat.ptr, self._index) + return force_text(string, encoding=self._feat.encoding, strings_only=True) + + def as_datetime(self): + "Retrieves the Field's value as a tuple of date & time components." + yy, mm, dd, hh, mn, ss, tz = [c_int() for i in range(7)] + status = capi.get_field_as_datetime( + self._feat.ptr, self._index, byref(yy), byref(mm), byref(dd), + byref(hh), byref(mn), byref(ss), byref(tz)) + if status: + return (yy, mm, dd, hh, mn, ss, tz) + else: + raise OGRException('Unable to retrieve date & time information from the field.') + + #### Field Properties #### + @property + def name(self): + "Returns the name of this Field." + name = capi.get_field_name(self.ptr) + return force_text(name, encoding=self._feat.encoding, strings_only=True) + + @property + def precision(self): + "Returns the precision of this Field." + return capi.get_field_precision(self.ptr) + + @property + def type(self): + "Returns the OGR type of this Field." + return capi.get_field_type(self.ptr) + + @property + def type_name(self): + "Return the OGR field type name for this Field." + return capi.get_field_type_name(self.type) + + @property + def value(self): + "Returns the value of this Field." + # Default is to get the field as a string. + return self.as_string() + + @property + def width(self): + "Returns the width of this Field." + return capi.get_field_width(self.ptr) + +### The Field sub-classes for each OGR Field type. ### +class OFTInteger(Field): + _double = False + + @property + def value(self): + "Returns an integer contained in this field." + if self._double: + # If this is really from an OFTReal field with no precision, + # read as a double and cast as Python int (to prevent overflow). + return int(self.as_double()) + else: + return self.as_int() + + @property + def type(self): + """ + GDAL uses OFTReals to represent OFTIntegers in created + shapefiles -- forcing the type here since the underlying field + type may actually be OFTReal. + """ + return 0 + +class OFTReal(Field): + @property + def value(self): + "Returns a float contained in this field." + return self.as_double() + +# String & Binary fields, just subclasses +class OFTString(Field): pass +class OFTWideString(Field): pass +class OFTBinary(Field): pass + +# OFTDate, OFTTime, OFTDateTime fields. +class OFTDate(Field): + @property + def value(self): + "Returns a Python `date` object for the OFTDate field." + try: + yy, mm, dd, hh, mn, ss, tz = self.as_datetime() + return date(yy.value, mm.value, dd.value) + except (ValueError, OGRException): + return None + +class OFTDateTime(Field): + @property + def value(self): + "Returns a Python `datetime` object for this OFTDateTime field." + # TODO: Adapt timezone information. + # See http://lists.osgeo.org/pipermail/gdal-dev/2006-February/007990.html + # The `tz` variable has values of: 0=unknown, 1=localtime (ambiguous), + # 100=GMT, 104=GMT+1, 80=GMT-5, etc. + try: + yy, mm, dd, hh, mn, ss, tz = self.as_datetime() + return datetime(yy.value, mm.value, dd.value, hh.value, mn.value, ss.value) + except (ValueError, OGRException): + return None + +class OFTTime(Field): + @property + def value(self): + "Returns a Python `time` object for this OFTTime field." + try: + yy, mm, dd, hh, mn, ss, tz = self.as_datetime() + return time(hh.value, mn.value, ss.value) + except (ValueError, OGRException): + return None + +# List fields are also just subclasses +class OFTIntegerList(Field): pass +class OFTRealList(Field): pass +class OFTStringList(Field): pass +class OFTWideStringList(Field): pass + +# Class mapping dictionary for OFT Types and reverse mapping. +OGRFieldTypes = { 0 : OFTInteger, + 1 : OFTIntegerList, + 2 : OFTReal, + 3 : OFTRealList, + 4 : OFTString, + 5 : OFTStringList, + 6 : OFTWideString, + 7 : OFTWideStringList, + 8 : OFTBinary, + 9 : OFTDate, + 10 : OFTTime, + 11 : OFTDateTime, + } +ROGRFieldTypes = dict([(cls, num) for num, cls in OGRFieldTypes.items()]) diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/geometries.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/geometries.py new file mode 100644 index 0000000..0d75620 --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/geometries.py @@ -0,0 +1,726 @@ +""" + 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 True +""" +# Python library requisites. +import sys +from binascii import a2b_hex, b2a_hex +from ctypes import byref, string_at, c_char_p, c_double, c_ubyte, c_void_p + +from django.contrib.gis import memoryview + +# Getting GDAL prerequisites +from django.contrib.gis.gdal.base import GDALBase +from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope +from django.contrib.gis.gdal.error import OGRException, OGRIndexError, SRSException +from django.contrib.gis.gdal.geomtype import OGRGeomType +from django.contrib.gis.gdal.libgdal import GDAL_VERSION +from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform + +# Getting the ctypes prototype functions that interface w/the GDAL C library. +from django.contrib.gis.gdal.prototypes import geom as capi, srs as srs_api + +# For recognizing geometry input. +from django.contrib.gis.geometry.regex import hex_regex, wkt_regex, json_regex + +from django.utils import six +from django.utils.six.moves import xrange + +# 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. + +#### OGRGeometry Class #### +class OGRGeometry(GDALBase): + "Generally encapsulates an OGR geometry." + + def __init__(self, geom_input, srs=None): + "Initializes Geometry on either WKT or an OGR pointer as input." + + str_instance = isinstance(geom_input, six.string_types) + + # If HEX, unpack input to a binary buffer. + if str_instance and hex_regex.match(geom_input): + geom_input = memoryview(a2b_hex(geom_input.upper().encode())) + str_instance = False + + # Constructing the geometry, + if str_instance: + wkt_m = wkt_regex.match(geom_input) + json_m = json_regex.match(geom_input) + if wkt_m: + if wkt_m.group('srid'): + # If there's EWKT, set the SRS w/value of the SRID. + srs = int(wkt_m.group('srid')) + if wkt_m.group('type').upper() == 'LINEARRING': + # OGR_G_CreateFromWkt doesn't work with LINEARRING WKT. + # See http://trac.osgeo.org/gdal/ticket/1992. + g = capi.create_geom(OGRGeomType(wkt_m.group('type')).num) + capi.import_wkt(g, byref(c_char_p(wkt_m.group('wkt').encode()))) + else: + g = capi.from_wkt(byref(c_char_p(wkt_m.group('wkt').encode())), None, byref(c_void_p())) + elif json_m: + g = capi.from_json(geom_input.encode()) + else: + # Seeing if the input is a valid short-hand string + # (e.g., 'Point', 'POLYGON'). + ogr_t = OGRGeomType(geom_input) + g = capi.create_geom(OGRGeomType(geom_input).num) + elif isinstance(geom_input, memoryview): + # WKB was passed in + g = capi.from_wkb(bytes(geom_input), None, byref(c_void_p()), len(geom_input)) + elif isinstance(geom_input, OGRGeomType): + # OGRGeomType was passed in, an empty geometry will be created. + g = capi.create_geom(geom_input.num) + elif isinstance(geom_input, self.ptr_type): + # OGR pointer (c_void_p) was the input. + g = geom_input + else: + raise OGRException('Invalid input type for OGR Geometry construction: %s' % type(geom_input)) + + # Now checking the Geometry pointer before finishing initialization + # by setting the pointer for the object. + if not g: + raise OGRException('Cannot create OGR Geometry from input: %s' % str(geom_input)) + self.ptr = g + + # Assigning the SpatialReference object to the geometry, if valid. + if bool(srs): self.srs = srs + + # Setting the class depending upon the OGR Geometry Type + self.__class__ = GEO_CLASSES[self.geom_type.num] + + def __del__(self): + "Deletes this Geometry." + if self._ptr: capi.destroy_geom(self._ptr) + + # Pickle routines + def __getstate__(self): + srs = self.srs + if srs: + srs = srs.wkt + else: + srs = None + return bytes(self.wkb), srs + + def __setstate__(self, state): + wkb, srs = state + ptr = capi.from_wkb(wkb, None, byref(c_void_p()), len(wkb)) + if not ptr: raise OGRException('Invalid OGRGeometry loaded from pickled state.') + self.ptr = ptr + self.srs = srs + + @classmethod + def from_bbox(cls, bbox): + "Constructs a Polygon from a bounding box (4-tuple)." + x0, y0, x1, y1 = bbox + return OGRGeometry( 'POLYGON((%s %s, %s %s, %s %s, %s %s, %s %s))' % ( + x0, y0, x0, y1, x1, y1, x1, y0, x0, y0) ) + + ### 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?" + if isinstance(other, OGRGeometry): + return self.equals(other) + else: + return False + + def __ne__(self, other): + "Tests for inequality." + return not (self == 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 capi.get_dims(self.ptr) + + def _get_coord_dim(self): + "Returns the coordinate dimension of the Geometry." + if isinstance(self, GeometryCollection) and GDAL_VERSION < (1, 5, 2): + # On GDAL versions prior to 1.5.2, there exists a bug in which + # the coordinate dimension of geometry collections is always 2: + # http://trac.osgeo.org/gdal/ticket/2334 + # Here we workaround by returning the coordinate dimension of the + # first geometry in the collection instead. + if len(self): + return capi.get_coord_dim(capi.get_geom_ref(self.ptr, 0)) + return capi.get_coord_dim(self.ptr) + + def _set_coord_dim(self, dim): + "Sets the coordinate dimension of this Geometry." + if not dim in (2, 3): + raise ValueError('Geometry dimension must be either 2 or 3') + capi.set_coord_dim(self.ptr, dim) + + coord_dim = property(_get_coord_dim, _set_coord_dim) + + @property + def geom_count(self): + "The number of elements in this Geometry." + return capi.get_geom_count(self.ptr) + + @property + def point_count(self): + "Returns the number of Points in this Geometry." + return capi.get_point_count(self.ptr) + + @property + def num_points(self): + "Alias for `point_count` (same name method in GEOS API.)" + return self.point_count + + @property + def num_coords(self): + "Alais for `point_count`." + return self.point_count + + @property + def geom_type(self): + "Returns the Type for this Geometry." + return OGRGeomType(capi.get_geom_type(self.ptr)) + + @property + def geom_name(self): + "Returns the Name of this Geometry." + return capi.get_geom_name(self.ptr) + + @property + def area(self): + "Returns the area for a LinearRing, Polygon, or MultiPolygon; 0 otherwise." + return capi.get_area(self.ptr) + + @property + def envelope(self): + "Returns the envelope for this Geometry." + # TODO: Fix Envelope() for Point geometries. + return Envelope(capi.get_envelope(self.ptr, byref(OGREnvelope()))) + + @property + def extent(self): + "Returns the envelope as a 4-tuple, instead of as an Envelope object." + return self.envelope.tuple + + #### SpatialReference-related Properties #### + + # The SRS property + def _get_srs(self): + "Returns the Spatial Reference for this Geometry." + try: + srs_ptr = capi.get_geom_srs(self.ptr) + return SpatialReference(srs_api.clone_srs(srs_ptr)) + except SRSException: + return None + + def _set_srs(self, srs): + "Sets the SpatialReference for this geometry." + # Do not have to clone the `SpatialReference` object pointer because + # when it is assigned to this `OGRGeometry` it's internal OGR + # reference count is incremented, and will likewise be released + # (decremented) when this geometry's destructor is called. + if isinstance(srs, SpatialReference): + srs_ptr = srs.ptr + elif isinstance(srs, six.integer_types + six.string_types): + sr = SpatialReference(srs) + srs_ptr = sr.ptr + else: + raise TypeError('Cannot assign spatial reference with object of type: %s' % type(srs)) + capi.assign_srs(self.ptr, srs_ptr) + + srs = property(_get_srs, _set_srs) + + # The SRID property + def _get_srid(self): + srs = self.srs + if srs: return srs.srid + return None + + def _set_srid(self, srid): + if isinstance(srid, six.integer_types): + self.srs = srid + else: + raise TypeError('SRID must be set with an integer.') + + srid = property(_get_srid, _set_srid) + + #### Output Methods #### + @property + def geos(self): + "Returns a GEOSGeometry object from this OGRGeometry." + from django.contrib.gis.geos import GEOSGeometry + return GEOSGeometry(self.wkb, self.srid) + + @property + def gml(self): + "Returns the GML representation of the Geometry." + return capi.to_gml(self.ptr) + + @property + def hex(self): + "Returns the hexadecimal representation of the WKB (a string)." + return b2a_hex(self.wkb).upper() + + @property + def json(self): + """ + Returns the GeoJSON representation of this Geometry. + """ + return capi.to_json(self.ptr) + geojson = json + + @property + def kml(self): + "Returns the KML representation of the Geometry." + return capi.to_kml(self.ptr, None) + + @property + def wkb_size(self): + "Returns the size of the WKB buffer." + return capi.get_wkbsize(self.ptr) + + @property + def wkb(self): + "Returns the WKB representation of the Geometry." + if sys.byteorder == 'little': + byteorder = 1 # wkbNDR (from ogr_core.h) + else: + byteorder = 0 # wkbXDR + sz = self.wkb_size + # Creating the unsigned character buffer, and passing it in by reference. + buf = (c_ubyte * sz)() + wkb = capi.to_wkb(self.ptr, byteorder, byref(buf)) + # Returning a buffer of the string at the pointer. + return memoryview(string_at(buf, sz)) + + @property + def wkt(self): + "Returns the WKT representation of the Geometry." + return capi.to_wkt(self.ptr, byref(c_char_p())) + + @property + def ewkt(self): + "Returns the EWKT representation of the Geometry." + srs = self.srs + if srs and srs.srid: + return 'SRID=%s;%s' % (srs.srid, self.wkt) + else: + return self.wkt + + #### Geometry Methods #### + def clone(self): + "Clones this OGR Geometry." + return OGRGeometry(capi.clone_geom(self.ptr), self.srs) + + 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. + capi.geom_close_rings(self.ptr) + + def transform(self, coord_trans, clone=False): + """ + Transforms this geometry to a different spatial reference system. + May take a CoordTransform object, a SpatialReference object, string + WKT or PROJ.4, and/or an integer SRID. By default nothing is returned + and the geometry is transformed in-place. However, if the `clone` + keyword is set, then a transformed clone of this geometry will be + returned. + """ + if clone: + klone = self.clone() + klone.transform(coord_trans) + return klone + + # Have to get the coordinate dimension of the original geometry + # so it can be used to reset the transformed geometry's dimension + # afterwards. This is done because of GDAL bug (in versions prior + # to 1.7) that turns geometries 3D after transformation, see: + # http://trac.osgeo.org/gdal/changeset/17792 + if GDAL_VERSION < (1, 7): + orig_dim = self.coord_dim + + # Depending on the input type, use the appropriate OGR routine + # to perform the transformation. + if isinstance(coord_trans, CoordTransform): + capi.geom_transform(self.ptr, coord_trans.ptr) + elif isinstance(coord_trans, SpatialReference): + capi.geom_transform_to(self.ptr, coord_trans.ptr) + elif isinstance(coord_trans, six.integer_types + six.string_types): + sr = SpatialReference(coord_trans) + capi.geom_transform_to(self.ptr, sr.ptr) + else: + raise TypeError('Transform only accepts CoordTransform, ' + 'SpatialReference, string, and integer objects.') + + # Setting with original dimension, see comment above. + if GDAL_VERSION < (1, 7): + if isinstance(self, GeometryCollection): + # With geometry collections have to set dimension on + # each internal geometry reference, as the collection + # dimension isn't affected. + for i in xrange(len(self)): + internal_ptr = capi.get_geom_ref(self.ptr, i) + if orig_dim != capi.get_coord_dim(internal_ptr): + capi.set_coord_dim(internal_ptr, orig_dim) + else: + if self.coord_dim != orig_dim: + self.coord_dim = orig_dim + + def transform_to(self, srs): + "For backwards-compatibility." + self.transform(srs) + + #### Topology Methods #### + def _topology(self, 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 TypeError('Must use another OGRGeometry object for topology operations!') + + # Returning the output of the given function with the other geometry's + # pointer. + return func(self.ptr, other.ptr) + + def intersects(self, other): + "Returns True if this geometry intersects with the other." + return self._topology(capi.ogr_intersects, other) + + def equals(self, other): + "Returns True if this geometry is equivalent to the other." + return self._topology(capi.ogr_equals, other) + + def disjoint(self, other): + "Returns True if this geometry and the other are spatially disjoint." + return self._topology(capi.ogr_disjoint, other) + + def touches(self, other): + "Returns True if this geometry touches the other." + return self._topology(capi.ogr_touches, other) + + def crosses(self, other): + "Returns True if this geometry crosses the other." + return self._topology(capi.ogr_crosses, other) + + def within(self, other): + "Returns True if this geometry is within the other." + return self._topology(capi.ogr_within, other) + + def contains(self, other): + "Returns True if this geometry contains the other." + return self._topology(capi.ogr_contains, other) + + def overlaps(self, other): + "Returns True if this geometry overlaps the other." + return self._topology(capi.ogr_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.ptr, other.ptr), self.srs) + else: + return OGRGeometry(gen_func(self.ptr), self.srs) + + @property + def boundary(self): + "Returns the boundary of this geometry." + return self._geomgen(capi.get_boundary) + + @property + def convex_hull(self): + """ + Returns the smallest convex Polygon that contains all the points in + this Geometry. + """ + return self._geomgen(capi.geom_convex_hull) + + 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(capi.geom_diff, other) + + def intersection(self, other): + """ + Returns a new geometry consisting of the region of intersection of this + geometry and the other. + """ + return self._geomgen(capi.geom_intersection, other) + + def sym_difference(self, other): + """ + Returns a new geometry which is the symmetric difference of this + geometry and the other. + """ + return self._geomgen(capi.geom_sym_diff, other) + + def union(self, other): + """ + Returns a new geometry consisting of the region which is the union of + this geometry and the other. + """ + return self._geomgen(capi.geom_union, other) + +# The subclasses for OGR Geometry. +class Point(OGRGeometry): + + @property + def x(self): + "Returns the X coordinate for this Point." + return capi.getx(self.ptr, 0) + + @property + def y(self): + "Returns the Y coordinate for this Point." + return capi.gety(self.ptr, 0) + + @property + def z(self): + "Returns the Z coordinate for this Point." + if self.coord_dim == 3: + return capi.getz(self.ptr, 0) + + @property + def tuple(self): + "Returns the tuple of this point." + if self.coord_dim == 2: + return (self.x, self.y) + elif self.coord_dim == 3: + return (self.x, self.y, self.z) + coords = tuple + +class LineString(OGRGeometry): + + def __getitem__(self, index): + "Returns the Point at the given index." + if index >= 0 and index < self.point_count: + x, y, z = c_double(), c_double(), c_double() + capi.get_point(self.ptr, index, byref(x), byref(y), byref(z)) + dim = self.coord_dim + if dim == 1: + return (x.value,) + elif dim == 2: + return (x.value, y.value) + elif dim == 3: + return (x.value, y.value, z.value) + else: + raise OGRIndexError('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[i] + + def __len__(self): + "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[i] for i in xrange(len(self))]) + coords = tuple + + def _listarr(self, func): + """ + Internal routine that returns a sequence (list) corresponding with + the given function. + """ + return [func(self.ptr, i) for i in xrange(len(self))] + + @property + def x(self): + "Returns the X coordinates in a list." + return self._listarr(capi.getx) + + @property + def y(self): + "Returns the Y coordinates in a list." + return self._listarr(capi.gety) + + @property + def z(self): + "Returns the Z coordinates in a list." + if self.coord_dim == 3: + return self._listarr(capi.getz) + +# 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[i] + + def __getitem__(self, index): + "Gets the ring at the specified index." + if index < 0 or index >= self.geom_count: + raise OGRIndexError('index out of range: %s' % index) + else: + return OGRGeometry(capi.clone_geom(capi.get_geom_ref(self.ptr, index)), self.srs) + + # Polygon Properties + @property + def shell(self): + "Returns the shell of this Polygon." + return self[0] # First ring is the shell + exterior_ring = shell + + @property + def tuple(self): + "Returns a tuple of LinearRing coordinate tuples." + return tuple([self[i].tuple for i in xrange(self.geom_count)]) + coords = tuple + + @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[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')) + capi.get_centroid(self.ptr, p.ptr) + 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 OGRIndexError('index out of range: %s' % index) + else: + return OGRGeometry(capi.clone_geom(capi.get_geom_ref(self.ptr, index)), self.srs) + + def __iter__(self): + "Iterates over each Geometry." + for i in xrange(self.geom_count): + yield self[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): + if isinstance(geom, self.__class__): + for g in geom: capi.add_geom(self.ptr, g.ptr) + else: + capi.add_geom(self.ptr, geom.ptr) + elif isinstance(geom, six.string_types): + tmp = OGRGeometry(geom) + capi.add_geom(self.ptr, tmp.ptr) + else: + raise OGRException('Must add an OGRGeometry.') + + @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[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[i].tuple for i in xrange(self.geom_count)]) + coords = tuple + +# 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, + 101: LinearRing, + 1 + OGRGeomType.wkb25bit : Point, + 2 + OGRGeomType.wkb25bit : LineString, + 3 + OGRGeomType.wkb25bit : Polygon, + 4 + OGRGeomType.wkb25bit : MultiPoint, + 5 + OGRGeomType.wkb25bit : MultiLineString, + 6 + OGRGeomType.wkb25bit : MultiPolygon, + 7 + OGRGeomType.wkb25bit : GeometryCollection, + } diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/geomtype.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/geomtype.py new file mode 100644 index 0000000..fe4b89a --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/geomtype.py @@ -0,0 +1,87 @@ +from django.contrib.gis.gdal.error import OGRException + +from django.utils import six + +#### OGRGeomType #### +class OGRGeomType(object): + "Encapulates OGR Geometry Types." + + wkb25bit = -2147483648 + + # Dictionary of acceptable OGRwkbGeometryType s and their string names. + _types = {0 : 'Unknown', + 1 : 'Point', + 2 : 'LineString', + 3 : 'Polygon', + 4 : 'MultiPoint', + 5 : 'MultiLineString', + 6 : 'MultiPolygon', + 7 : 'GeometryCollection', + 100 : 'None', + 101 : 'LinearRing', + 1 + wkb25bit: 'Point25D', + 2 + wkb25bit: 'LineString25D', + 3 + wkb25bit: 'Polygon25D', + 4 + wkb25bit: 'MultiPoint25D', + 5 + wkb25bit : 'MultiLineString25D', + 6 + wkb25bit : 'MultiPolygon25D', + 7 + wkb25bit : 'GeometryCollection25D', + } + # Reverse type dictionary, keyed by lower-case of the name. + _str_types = dict([(v.lower(), k) for k, v in _types.items()]) + + def __init__(self, type_input): + "Figures out the correct OGR Type based upon the input." + if isinstance(type_input, OGRGeomType): + num = type_input.num + elif isinstance(type_input, six.string_types): + type_input = type_input.lower() + if type_input == 'geometry': type_input='unknown' + num = self._str_types.get(type_input, None) + if num is None: + raise OGRException('Invalid OGR String Type "%s"' % type_input) + elif isinstance(type_input, int): + if not type_input in self._types: + raise OGRException('Invalid OGR Integer Type: %d' % type_input) + num = type_input + else: + raise TypeError('Invalid OGR input type given.') + + # Setting the OGR geometry type number. + self.num = num + + def __str__(self): + "Returns the value of the name property." + return self.name + + 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.num == other.num + elif isinstance(other, six.string_types): + return self.name.lower() == other.lower() + elif isinstance(other, int): + return self.num == other + else: + return False + + def __ne__(self, other): + return not (self == other) + + @property + def name(self): + "Returns a short-hand string form of the OGR Geometry type." + return self._types[self.num] + + @property + def django(self): + "Returns the Django GeometryField for this OGR Type." + s = self.name.replace('25D', '') + if s in ('LinearRing', 'None'): + return None + elif s == 'Unknown': + s = 'Geometry' + return s + 'Field' diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/layer.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/layer.py new file mode 100644 index 0000000..7f935cd --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/layer.py @@ -0,0 +1,218 @@ +# Needed ctypes routines +from ctypes import c_double, byref + +# Other GDAL imports. +from django.contrib.gis.gdal.base import GDALBase +from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope +from django.contrib.gis.gdal.error import OGRException, OGRIndexError, SRSException +from django.contrib.gis.gdal.feature import Feature +from django.contrib.gis.gdal.field import OGRFieldTypes +from django.contrib.gis.gdal.geomtype import OGRGeomType +from django.contrib.gis.gdal.geometries import OGRGeometry +from django.contrib.gis.gdal.srs import SpatialReference + +# GDAL ctypes function prototypes. +from django.contrib.gis.gdal.prototypes import ds as capi, geom as geom_api, srs as srs_api + +from django.utils.encoding import force_bytes, force_text +from django.utils import six +from django.utils.six.moves import xrange + +# 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. +class Layer(GDALBase): + "A class that wraps an OGR Layer, needs to be instantiated from a DataSource object." + + #### Python 'magic' routines #### + def __init__(self, layer_ptr, ds): + """ + Initializes on an OGR C pointer to the Layer and the `DataSource` object + that owns this layer. The `DataSource` object is required so that a + reference to it is kept with this Layer. This prevents garbage + collection of the `DataSource` while this Layer is still active. + """ + if not layer_ptr: + raise OGRException('Cannot create Layer, invalid pointer given') + self.ptr = layer_ptr + self._ds = ds + self._ldefn = capi.get_layer_defn(self._ptr) + # Does the Layer support random reading? + self._random_read = self.test_capability(b'RandomRead') + + def __getitem__(self, index): + "Gets the Feature at the specified index." + if isinstance(index, six.integer_types): + # An integer index was given -- we cannot do a check based on the + # number of features because the beginning and ending feature IDs + # are not guaranteed to be 0 and len(layer)-1, respectively. + if index < 0: raise OGRIndexError('Negative indices are not allowed on OGR Layers.') + return self._make_feature(index) + elif isinstance(index, slice): + # A slice was given + start, stop, stride = index.indices(self.num_feat) + return [self._make_feature(fid) for fid in xrange(start, stop, stride)] + else: + raise TypeError('Integers and slices may only be used when indexing OGR Layers.') + + def __iter__(self): + "Iterates over each Feature in the Layer." + # ResetReading() must be called before iteration is to begin. + capi.reset_reading(self._ptr) + for i in xrange(self.num_feat): + yield Feature(capi.get_next_feature(self._ptr), self) + + 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 + + def _make_feature(self, feat_id): + """ + Helper routine for __getitem__ that constructs a Feature from the given + Feature ID. If the OGR Layer does not support random-access reading, + then each feature of the layer will be incremented through until the + a Feature is found matching the given feature ID. + """ + if self._random_read: + # If the Layer supports random reading, return. + try: + return Feature(capi.get_feature(self.ptr, feat_id), self) + except OGRException: + pass + else: + # Random access isn't supported, have to increment through + # each feature until the given feature ID is encountered. + for feat in self: + if feat.fid == feat_id: return feat + # Should have returned a Feature, raise an OGRIndexError. + raise OGRIndexError('Invalid feature id: %s.' % feat_id) + + #### Layer properties #### + @property + def extent(self): + "Returns the extent (an Envelope) of this layer." + env = OGREnvelope() + capi.get_extent(self.ptr, byref(env), 1) + return Envelope(env) + + @property + def name(self): + "Returns the name of this layer in the Data Source." + name = capi.get_fd_name(self._ldefn) + return force_text(name, self._ds.encoding, strings_only=True) + + @property + def num_feat(self, force=1): + "Returns the number of features in the Layer." + return capi.get_feature_count(self.ptr, force) + + @property + def num_fields(self): + "Returns the number of fields in the Layer." + return capi.get_field_count(self._ldefn) + + @property + def geom_type(self): + "Returns the geometry type (OGRGeomType) of the Layer." + return OGRGeomType(capi.get_fd_geom_type(self._ldefn)) + + @property + def srs(self): + "Returns the Spatial Reference used in this Layer." + try: + ptr = capi.get_layer_srs(self.ptr) + return SpatialReference(srs_api.clone_srs(ptr)) + except SRSException: + return None + + @property + def fields(self): + """ + Returns a list of string names corresponding to each of the Fields + available in this Layer. + """ + return [force_text(capi.get_field_name(capi.get_field_defn(self._ldefn, i)), + self._ds.encoding, strings_only=True) + for i in xrange(self.num_fields)] + + @property + def field_types(self): + """ + Returns a list of the types of fields in this Layer. For example, + the list [OFTInteger, OFTReal, OFTString] would be returned for + an OGR layer that had an integer, a floating-point, and string + fields. + """ + return [OGRFieldTypes[capi.get_field_type(capi.get_field_defn(self._ldefn, i))] + for i in xrange(self.num_fields)] + + @property + def field_widths(self): + "Returns a list of the maximum field widths for the features." + return [capi.get_field_width(capi.get_field_defn(self._ldefn, i)) + for i in xrange(self.num_fields)] + + @property + def field_precisions(self): + "Returns the field precisions for the features." + return [capi.get_field_precision(capi.get_field_defn(self._ldefn, i)) + for i in xrange(self.num_fields)] + + def _get_spatial_filter(self): + try: + return OGRGeometry(geom_api.clone_geom(capi.get_spatial_filter(self.ptr))) + except OGRException: + return None + + def _set_spatial_filter(self, filter): + if isinstance(filter, OGRGeometry): + capi.set_spatial_filter(self.ptr, filter.ptr) + elif isinstance(filter, (tuple, list)): + if not len(filter) == 4: + raise ValueError('Spatial filter list/tuple must have 4 elements.') + # Map c_double onto params -- if a bad type is passed in it + # will be caught here. + xmin, ymin, xmax, ymax = map(c_double, filter) + capi.set_spatial_filter_rect(self.ptr, xmin, ymin, xmax, ymax) + elif filter is None: + capi.set_spatial_filter(self.ptr, None) + else: + raise TypeError('Spatial filter must be either an OGRGeometry instance, a 4-tuple, or None.') + + spatial_filter = property(_get_spatial_filter, _set_spatial_filter) + + #### Layer Methods #### + def get_fields(self, field_name): + """ + Returns a list containing the given field name for every Feature + in the Layer. + """ + if not field_name in self.fields: + raise OGRException('invalid field name: %s' % field_name) + return [feat.get(field_name) for feat in self] + + def get_geoms(self, geos=False): + """ + Returns a list containing the OGRGeometry for every Feature in + the Layer. + """ + if geos: + from django.contrib.gis.geos import GEOSGeometry + return [GEOSGeometry(feat.geom.wkb) for feat in self] + else: + return [feat.geom for feat in self] + + def test_capability(self, capability): + """ + Returns a bool indicating whether the this Layer supports the given + capability (a string). Valid capability strings include: + 'RandomRead', 'SequentialWrite', 'RandomWrite', 'FastSpatialFilter', + 'FastFeatureCount', 'FastGetExtent', 'CreateField', 'Transactions', + 'DeleteFeature', and 'FastSetNextByIndex'. + """ + return bool(capi.test_capability(self.ptr, force_bytes(capability))) diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/libgdal.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/libgdal.py new file mode 100644 index 0000000..91f8d61 --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/libgdal.py @@ -0,0 +1,109 @@ +from __future__ import unicode_literals + +import logging +import os +import re +from ctypes import c_char_p, c_int, CDLL, CFUNCTYPE +from ctypes.util import find_library + +from django.contrib.gis.gdal.error import OGRException +from django.core.exceptions import ImproperlyConfigured + +logger = logging.getLogger('django.contrib.gis') + +# Custom library path set? +try: + from django.conf import settings + lib_path = settings.GDAL_LIBRARY_PATH +except (AttributeError, EnvironmentError, + ImportError, ImproperlyConfigured): + lib_path = None + +if lib_path: + lib_names = None +elif os.name == 'nt': + # Windows NT shared libraries + lib_names = ['gdal19', 'gdal18', 'gdal17', 'gdal16', 'gdal15'] +elif os.name == 'posix': + # *NIX library names. + lib_names = ['gdal', 'GDAL', 'gdal1.9.0', 'gdal1.8.0', 'gdal1.7.0', + 'gdal1.6.0', 'gdal1.5.0'] +else: + raise OGRException('Unsupported OS "%s"' % os.name) + +# Using the ctypes `find_library` utility to find the +# path to the GDAL library from the list of library names. +if lib_names: + for lib_name in lib_names: + lib_path = find_library(lib_name) + if not lib_path is None: break + +if lib_path is None: + raise OGRException('Could not find the GDAL library (tried "%s"). ' + 'Try setting GDAL_LIBRARY_PATH in your settings.' % + '", "'.join(lib_names)) + +# This loads the GDAL/OGR C library +lgdal = CDLL(lib_path) + +# On Windows, the GDAL binaries have some OSR routines exported with +# STDCALL, while others are not. Thus, the library will also need to +# be loaded up as WinDLL for said OSR functions that require the +# different calling convention. +if os.name == 'nt': + from ctypes import WinDLL + lwingdal = WinDLL(lib_path) + +def std_call(func): + """ + Returns the correct STDCALL function for certain OSR routines on Win32 + platforms. + """ + if os.name == 'nt': + return lwingdal[func] + else: + return lgdal[func] + +#### Version-information functions. #### + +# Returns GDAL library version information with the given key. +_version_info = std_call('GDALVersionInfo') +_version_info.argtypes = [c_char_p] +_version_info.restype = c_char_p + +def gdal_version(): + "Returns only the GDAL version number information." + return _version_info(b'RELEASE_NAME') + +def gdal_full_version(): + "Returns the full GDAL version information." + return _version_info('') + +version_regex = re.compile(r'^(?P<major>\d+)\.(?P<minor>\d+)(\.(?P<subminor>\d+))?') +def gdal_version_info(): + ver = gdal_version().decode() + m = version_regex.match(ver) + if not m: raise OGRException('Could not parse GDAL version string "%s"' % ver) + return dict([(key, m.group(key)) for key in ('major', 'minor', 'subminor')]) + +_verinfo = gdal_version_info() +GDAL_MAJOR_VERSION = int(_verinfo['major']) +GDAL_MINOR_VERSION = int(_verinfo['minor']) +GDAL_SUBMINOR_VERSION = _verinfo['subminor'] and int(_verinfo['subminor']) +GDAL_VERSION = (GDAL_MAJOR_VERSION, GDAL_MINOR_VERSION, GDAL_SUBMINOR_VERSION) +del _verinfo + +# Set library error handling so as errors are logged +CPLErrorHandler = CFUNCTYPE(None, c_int, c_int, c_char_p) +def err_handler(error_class, error_number, message): + logger.error('GDAL_ERROR %d: %s' % (error_number, message)) +err_handler = CPLErrorHandler(err_handler) + +def function(name, args, restype): + func = std_call(name) + func.argtypes = args + func.restype = restype + return func + +set_error_handler = function('CPLSetErrorHandler', [CPLErrorHandler], CPLErrorHandler) +set_error_handler(err_handler) diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/prototypes/__init__.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/prototypes/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/prototypes/__init__.py diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/prototypes/ds.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/prototypes/ds.py new file mode 100644 index 0000000..f798069 --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/prototypes/ds.py @@ -0,0 +1,70 @@ +""" + This module houses the ctypes function prototypes for OGR DataSource + related data structures. OGR_Dr_*, OGR_DS_*, OGR_L_*, OGR_F_*, + OGR_Fld_* routines are relevant here. +""" +from ctypes import c_char_p, c_double, c_int, c_long, c_void_p, POINTER +from django.contrib.gis.gdal.envelope import OGREnvelope +from django.contrib.gis.gdal.libgdal import lgdal +from django.contrib.gis.gdal.prototypes.generation import (const_string_output, + double_output, geom_output, int_output, srs_output, void_output, voidptr_output) + +c_int_p = POINTER(c_int) # shortcut type + +### Driver Routines ### +register_all = void_output(lgdal.OGRRegisterAll, [], errcheck=False) +cleanup_all = void_output(lgdal.OGRCleanupAll, [], errcheck=False) +get_driver = voidptr_output(lgdal.OGRGetDriver, [c_int]) +get_driver_by_name = voidptr_output(lgdal.OGRGetDriverByName, [c_char_p]) +get_driver_count = int_output(lgdal.OGRGetDriverCount, []) +get_driver_name = const_string_output(lgdal.OGR_Dr_GetName, [c_void_p], decoding='ascii') + +### DataSource ### +open_ds = voidptr_output(lgdal.OGROpen, [c_char_p, c_int, POINTER(c_void_p)]) +destroy_ds = void_output(lgdal.OGR_DS_Destroy, [c_void_p], errcheck=False) +release_ds = void_output(lgdal.OGRReleaseDataSource, [c_void_p]) +get_ds_name = const_string_output(lgdal.OGR_DS_GetName, [c_void_p]) +get_layer = voidptr_output(lgdal.OGR_DS_GetLayer, [c_void_p, c_int]) +get_layer_by_name = voidptr_output(lgdal.OGR_DS_GetLayerByName, [c_void_p, c_char_p]) +get_layer_count = int_output(lgdal.OGR_DS_GetLayerCount, [c_void_p]) + +### Layer Routines ### +get_extent = void_output(lgdal.OGR_L_GetExtent, [c_void_p, POINTER(OGREnvelope), c_int]) +get_feature = voidptr_output(lgdal.OGR_L_GetFeature, [c_void_p, c_long]) +get_feature_count = int_output(lgdal.OGR_L_GetFeatureCount, [c_void_p, c_int]) +get_layer_defn = voidptr_output(lgdal.OGR_L_GetLayerDefn, [c_void_p]) +get_layer_srs = srs_output(lgdal.OGR_L_GetSpatialRef, [c_void_p]) +get_next_feature = voidptr_output(lgdal.OGR_L_GetNextFeature, [c_void_p]) +reset_reading = void_output(lgdal.OGR_L_ResetReading, [c_void_p], errcheck=False) +test_capability = int_output(lgdal.OGR_L_TestCapability, [c_void_p, c_char_p]) +get_spatial_filter = geom_output(lgdal.OGR_L_GetSpatialFilter, [c_void_p]) +set_spatial_filter = void_output(lgdal.OGR_L_SetSpatialFilter, [c_void_p, c_void_p], errcheck=False) +set_spatial_filter_rect = void_output(lgdal.OGR_L_SetSpatialFilterRect, [c_void_p, c_double, c_double, c_double, c_double], errcheck=False) + +### Feature Definition Routines ### +get_fd_geom_type = int_output(lgdal.OGR_FD_GetGeomType, [c_void_p]) +get_fd_name = const_string_output(lgdal.OGR_FD_GetName, [c_void_p]) +get_feat_name = const_string_output(lgdal.OGR_FD_GetName, [c_void_p]) +get_field_count = int_output(lgdal.OGR_FD_GetFieldCount, [c_void_p]) +get_field_defn = voidptr_output(lgdal.OGR_FD_GetFieldDefn, [c_void_p, c_int]) + +### Feature Routines ### +clone_feature = voidptr_output(lgdal.OGR_F_Clone, [c_void_p]) +destroy_feature = void_output(lgdal.OGR_F_Destroy, [c_void_p], errcheck=False) +feature_equal = int_output(lgdal.OGR_F_Equal, [c_void_p, c_void_p]) +get_feat_geom_ref = geom_output(lgdal.OGR_F_GetGeometryRef, [c_void_p]) +get_feat_field_count = int_output(lgdal.OGR_F_GetFieldCount, [c_void_p]) +get_feat_field_defn = voidptr_output(lgdal.OGR_F_GetFieldDefnRef, [c_void_p, c_int]) +get_fid = int_output(lgdal.OGR_F_GetFID, [c_void_p]) +get_field_as_datetime = int_output(lgdal.OGR_F_GetFieldAsDateTime, [c_void_p, c_int, c_int_p, c_int_p, c_int_p, c_int_p, c_int_p, c_int_p]) +get_field_as_double = double_output(lgdal.OGR_F_GetFieldAsDouble, [c_void_p, c_int]) +get_field_as_integer = int_output(lgdal.OGR_F_GetFieldAsInteger, [c_void_p, c_int]) +get_field_as_string = const_string_output(lgdal.OGR_F_GetFieldAsString, [c_void_p, c_int]) +get_field_index = int_output(lgdal.OGR_F_GetFieldIndex, [c_void_p, c_char_p]) + +### Field Routines ### +get_field_name = const_string_output(lgdal.OGR_Fld_GetNameRef, [c_void_p]) +get_field_precision = int_output(lgdal.OGR_Fld_GetPrecision, [c_void_p]) +get_field_type = int_output(lgdal.OGR_Fld_GetType, [c_void_p]) +get_field_type_name = const_string_output(lgdal.OGR_GetFieldTypeName, [c_int]) +get_field_width = int_output(lgdal.OGR_Fld_GetWidth, [c_void_p]) diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/prototypes/errcheck.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/prototypes/errcheck.py new file mode 100644 index 0000000..2d27911 --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/prototypes/errcheck.py @@ -0,0 +1,128 @@ +""" + This module houses the error-checking routines used by the GDAL + ctypes prototypes. +""" +from ctypes import c_void_p, string_at +from django.contrib.gis.gdal.error import check_err, OGRException, SRSException +from django.contrib.gis.gdal.libgdal import lgdal +from django.utils import six + +# Helper routines for retrieving pointers and/or values from +# arguments passed in by reference. +def arg_byref(args, offset=-1): + "Returns the pointer argument's by-refernece value." + return args[offset]._obj.value + +def ptr_byref(args, offset=-1): + "Returns the pointer argument passed in by-reference." + return args[offset]._obj + +def check_bool(result, func, cargs): + "Returns the boolean evaluation of the value." + if bool(result): return True + else: return False + +### String checking Routines ### +def check_const_string(result, func, cargs, offset=None): + """ + Similar functionality to `check_string`, but does not free the pointer. + """ + if offset: + check_err(result) + ptr = ptr_byref(cargs, offset) + return ptr.value + else: + return result + +def check_string(result, func, cargs, offset=-1, str_result=False): + """ + Checks the string output returned from the given function, and frees + the string pointer allocated by OGR. The `str_result` keyword + may be used when the result is the string pointer, otherwise + the OGR error code is assumed. The `offset` keyword may be used + to extract the string pointer passed in by-reference at the given + slice offset in the function arguments. + """ + if str_result: + # For routines that return a string. + ptr = result + if not ptr: s = None + else: s = string_at(result) + else: + # Error-code return specified. + check_err(result) + ptr = ptr_byref(cargs, offset) + # Getting the string value + s = ptr.value + # Correctly freeing the allocated memory beind GDAL pointer + # w/the VSIFree routine. + if ptr: lgdal.VSIFree(ptr) + return s + +### DataSource, Layer error-checking ### + +### Envelope checking ### +def check_envelope(result, func, cargs, offset=-1): + "Checks a function that returns an OGR Envelope by reference." + env = ptr_byref(cargs, offset) + return env + +### Geometry error-checking routines ### +def check_geom(result, func, cargs): + "Checks a function that returns a geometry." + # OGR_G_Clone may return an integer, even though the + # restype is set to c_void_p + if isinstance(result, six.integer_types): + result = c_void_p(result) + if not result: + raise OGRException('Invalid geometry pointer returned from "%s".' % func.__name__) + return result + +def check_geom_offset(result, func, cargs, offset=-1): + "Chcks the geometry at the given offset in the C parameter list." + check_err(result) + geom = ptr_byref(cargs, offset=offset) + return check_geom(geom, func, cargs) + +### Spatial Reference error-checking routines ### +def check_srs(result, func, cargs): + if isinstance(result, six.integer_types): + result = c_void_p(result) + if not result: + raise SRSException('Invalid spatial reference pointer returned from "%s".' % func.__name__) + return result + +### Other error-checking routines ### +def check_arg_errcode(result, func, cargs): + """ + The error code is returned in the last argument, by reference. + Check its value with `check_err` before returning the result. + """ + check_err(arg_byref(cargs)) + return result + +def check_errcode(result, func, cargs): + """ + Check the error code returned (c_int). + """ + check_err(result) + return + +def check_pointer(result, func, cargs): + "Makes sure the result pointer is valid." + if isinstance(result, six.integer_types): + result = c_void_p(result) + if bool(result): + return result + else: + raise OGRException('Invalid pointer returned from "%s"' % func.__name__) + +def check_str_arg(result, func, cargs): + """ + This is for the OSRGet[Angular|Linear]Units functions, which + require that the returned string pointer not be freed. This + returns both the double and tring values. + """ + dbl = result + ptr = cargs[-1]._obj + return dbl, ptr.value.decode() diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/prototypes/generation.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/prototypes/generation.py new file mode 100644 index 0000000..577d29b --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/prototypes/generation.py @@ -0,0 +1,125 @@ +""" + This module contains functions that generate ctypes prototypes for the + GDAL routines. +""" + +from ctypes import c_char_p, c_double, c_int, c_void_p +from django.contrib.gis.gdal.prototypes.errcheck import ( + check_arg_errcode, check_errcode, check_geom, check_geom_offset, + check_pointer, check_srs, check_str_arg, check_string, check_const_string) + +class gdal_char_p(c_char_p): + pass + +def double_output(func, argtypes, errcheck=False, strarg=False): + "Generates a ctypes function that returns a double value." + func.argtypes = argtypes + func.restype = c_double + if errcheck: func.errcheck = check_arg_errcode + if strarg: func.errcheck = check_str_arg + return func + +def geom_output(func, argtypes, offset=None): + """ + Generates a function that returns a Geometry either by reference + or directly (if the return_geom keyword is set to True). + """ + # Setting the argument types + func.argtypes = argtypes + + if not offset: + # When a geometry pointer is directly returned. + func.restype = c_void_p + func.errcheck = check_geom + else: + # Error code returned, geometry is returned by-reference. + func.restype = c_int + def geomerrcheck(result, func, cargs): + return check_geom_offset(result, func, cargs, offset) + func.errcheck = geomerrcheck + + return func + +def int_output(func, argtypes): + "Generates a ctypes function that returns an integer value." + func.argtypes = argtypes + func.restype = c_int + return func + +def srs_output(func, argtypes): + """ + Generates a ctypes prototype for the given function with + the given C arguments that returns a pointer to an OGR + Spatial Reference System. + """ + func.argtypes = argtypes + func.restype = c_void_p + func.errcheck = check_srs + return func + +def const_string_output(func, argtypes, offset=None, decoding=None): + func.argtypes = argtypes + if offset: + func.restype = c_int + else: + func.restype = c_char_p + + def _check_const(result, func, cargs): + res = check_const_string(result, func, cargs, offset=offset) + if res and decoding: + res = res.decode(decoding) + return res + func.errcheck = _check_const + + return func + +def string_output(func, argtypes, offset=-1, str_result=False, decoding=None): + """ + Generates a ctypes prototype for the given function with the + given argument types that returns a string from a GDAL pointer. + The `const` flag indicates whether the allocated pointer should + be freed via the GDAL library routine VSIFree -- but only applies + only when `str_result` is True. + """ + func.argtypes = argtypes + if str_result: + # Use subclass of c_char_p so the error checking routine + # can free the memory at the pointer's address. + func.restype = gdal_char_p + else: + # Error code is returned + func.restype = c_int + + # Dynamically defining our error-checking function with the + # given offset. + def _check_str(result, func, cargs): + res = check_string(result, func, cargs, + offset=offset, str_result=str_result) + if res and decoding: + res = res.decode(decoding) + return res + func.errcheck = _check_str + return func + +def void_output(func, argtypes, errcheck=True): + """ + For functions that don't only return an error code that needs to + be examined. + """ + if argtypes: func.argtypes = argtypes + if errcheck: + # `errcheck` keyword may be set to False for routines that + # return void, rather than a status code. + func.restype = c_int + func.errcheck = check_errcode + else: + func.restype = None + + return func + +def voidptr_output(func, argtypes): + "For functions that return c_void_p." + func.argtypes = argtypes + func.restype = c_void_p + func.errcheck = check_pointer + return func diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/prototypes/geom.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/prototypes/geom.py new file mode 100644 index 0000000..fa0b503 --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/prototypes/geom.py @@ -0,0 +1,98 @@ +from ctypes import c_char_p, c_double, c_int, c_void_p, POINTER +from django.contrib.gis.gdal.envelope import OGREnvelope +from django.contrib.gis.gdal.libgdal import lgdal +from django.contrib.gis.gdal.prototypes.errcheck import check_bool, check_envelope +from django.contrib.gis.gdal.prototypes.generation import (const_string_output, + double_output, geom_output, int_output, srs_output, string_output, void_output) + +### Generation routines specific to this module ### +def env_func(f, argtypes): + "For getting OGREnvelopes." + f.argtypes = argtypes + f.restype = None + f.errcheck = check_envelope + return f + +def pnt_func(f): + "For accessing point information." + return double_output(f, [c_void_p, c_int]) + +def topology_func(f): + f.argtypes = [c_void_p, c_void_p] + f.restype = c_int + f.errchck = check_bool + return f + +### OGR_G ctypes function prototypes ### + +# GeoJSON routines. +from_json = geom_output(lgdal.OGR_G_CreateGeometryFromJson, [c_char_p]) +to_json = string_output(lgdal.OGR_G_ExportToJson, [c_void_p], str_result=True, decoding='ascii') +to_kml = string_output(lgdal.OGR_G_ExportToKML, [c_void_p, c_char_p], str_result=True, decoding='ascii') + +# GetX, GetY, GetZ all return doubles. +getx = pnt_func(lgdal.OGR_G_GetX) +gety = pnt_func(lgdal.OGR_G_GetY) +getz = pnt_func(lgdal.OGR_G_GetZ) + +# Geometry creation routines. +from_wkb = geom_output(lgdal.OGR_G_CreateFromWkb, [c_char_p, c_void_p, POINTER(c_void_p), c_int], offset=-2) +from_wkt = geom_output(lgdal.OGR_G_CreateFromWkt, [POINTER(c_char_p), c_void_p, POINTER(c_void_p)], offset=-1) +create_geom = geom_output(lgdal.OGR_G_CreateGeometry, [c_int]) +clone_geom = geom_output(lgdal.OGR_G_Clone, [c_void_p]) +get_geom_ref = geom_output(lgdal.OGR_G_GetGeometryRef, [c_void_p, c_int]) +get_boundary = geom_output(lgdal.OGR_G_GetBoundary, [c_void_p]) +geom_convex_hull = geom_output(lgdal.OGR_G_ConvexHull, [c_void_p]) +geom_diff = geom_output(lgdal.OGR_G_Difference, [c_void_p, c_void_p]) +geom_intersection = geom_output(lgdal.OGR_G_Intersection, [c_void_p, c_void_p]) +geom_sym_diff = geom_output(lgdal.OGR_G_SymmetricDifference, [c_void_p, c_void_p]) +geom_union = geom_output(lgdal.OGR_G_Union, [c_void_p, c_void_p]) + +# Geometry modification routines. +add_geom = void_output(lgdal.OGR_G_AddGeometry, [c_void_p, c_void_p]) +import_wkt = void_output(lgdal.OGR_G_ImportFromWkt, [c_void_p, POINTER(c_char_p)]) + +# Destroys a geometry +destroy_geom = void_output(lgdal.OGR_G_DestroyGeometry, [c_void_p], errcheck=False) + +# Geometry export routines. +to_wkb = void_output(lgdal.OGR_G_ExportToWkb, None, errcheck=True) # special handling for WKB. +to_wkt = string_output(lgdal.OGR_G_ExportToWkt, [c_void_p, POINTER(c_char_p)], decoding='ascii') +to_gml = string_output(lgdal.OGR_G_ExportToGML, [c_void_p], str_result=True, decoding='ascii') +get_wkbsize = int_output(lgdal.OGR_G_WkbSize, [c_void_p]) + +# Geometry spatial-reference related routines. +assign_srs = void_output(lgdal.OGR_G_AssignSpatialReference, [c_void_p, c_void_p], errcheck=False) +get_geom_srs = srs_output(lgdal.OGR_G_GetSpatialReference, [c_void_p]) + +# Geometry properties +get_area = double_output(lgdal.OGR_G_GetArea, [c_void_p]) +get_centroid = void_output(lgdal.OGR_G_Centroid, [c_void_p, c_void_p]) +get_dims = int_output(lgdal.OGR_G_GetDimension, [c_void_p]) +get_coord_dim = int_output(lgdal.OGR_G_GetCoordinateDimension, [c_void_p]) +set_coord_dim = void_output(lgdal.OGR_G_SetCoordinateDimension, [c_void_p, c_int], errcheck=False) + +get_geom_count = int_output(lgdal.OGR_G_GetGeometryCount, [c_void_p]) +get_geom_name = const_string_output(lgdal.OGR_G_GetGeometryName, [c_void_p], decoding='ascii') +get_geom_type = int_output(lgdal.OGR_G_GetGeometryType, [c_void_p]) +get_point_count = int_output(lgdal.OGR_G_GetPointCount, [c_void_p]) +get_point = void_output(lgdal.OGR_G_GetPoint, [c_void_p, c_int, POINTER(c_double), POINTER(c_double), POINTER(c_double)], errcheck=False) +geom_close_rings = void_output(lgdal.OGR_G_CloseRings, [c_void_p], errcheck=False) + +# Topology routines. +ogr_contains = topology_func(lgdal.OGR_G_Contains) +ogr_crosses = topology_func(lgdal.OGR_G_Crosses) +ogr_disjoint = topology_func(lgdal.OGR_G_Disjoint) +ogr_equals = topology_func(lgdal.OGR_G_Equals) +ogr_intersects = topology_func(lgdal.OGR_G_Intersects) +ogr_overlaps = topology_func(lgdal.OGR_G_Overlaps) +ogr_touches = topology_func(lgdal.OGR_G_Touches) +ogr_within = topology_func(lgdal.OGR_G_Within) + +# Transformation routines. +geom_transform = void_output(lgdal.OGR_G_Transform, [c_void_p, c_void_p]) +geom_transform_to = void_output(lgdal.OGR_G_TransformTo, [c_void_p, c_void_p]) + +# For retrieving the envelope of the geometry. +get_envelope = env_func(lgdal.OGR_G_GetEnvelope, [c_void_p, POINTER(OGREnvelope)]) + diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/prototypes/srs.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/prototypes/srs.py new file mode 100644 index 0000000..58ceb75 --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/prototypes/srs.py @@ -0,0 +1,71 @@ +from ctypes import c_char_p, c_int, c_void_p, POINTER +from django.contrib.gis.gdal.libgdal import lgdal, std_call +from django.contrib.gis.gdal.prototypes.generation import (const_string_output, + double_output, int_output, srs_output, string_output, void_output) + +## Shortcut generation for routines with known parameters. +def srs_double(f): + """ + Creates a function prototype for the OSR routines that take + the OSRSpatialReference object and + """ + return double_output(f, [c_void_p, POINTER(c_int)], errcheck=True) + +def units_func(f): + """ + Creates a ctypes function prototype for OSR units functions, e.g., + OSRGetAngularUnits, OSRGetLinearUnits. + """ + return double_output(f, [c_void_p, POINTER(c_char_p)], strarg=True) + +# Creation & destruction. +clone_srs = srs_output(std_call('OSRClone'), [c_void_p]) +new_srs = srs_output(std_call('OSRNewSpatialReference'), [c_char_p]) +release_srs = void_output(lgdal.OSRRelease, [c_void_p], errcheck=False) +destroy_srs = void_output(std_call('OSRDestroySpatialReference'), [c_void_p], errcheck=False) +srs_validate = void_output(lgdal.OSRValidate, [c_void_p]) + +# Getting the semi_major, semi_minor, and flattening functions. +semi_major = srs_double(lgdal.OSRGetSemiMajor) +semi_minor = srs_double(lgdal.OSRGetSemiMinor) +invflattening = srs_double(lgdal.OSRGetInvFlattening) + +# WKT, PROJ, EPSG, XML importation routines. +from_wkt = void_output(lgdal.OSRImportFromWkt, [c_void_p, POINTER(c_char_p)]) +from_proj = void_output(lgdal.OSRImportFromProj4, [c_void_p, c_char_p]) +from_epsg = void_output(std_call('OSRImportFromEPSG'), [c_void_p, c_int]) +from_xml = void_output(lgdal.OSRImportFromXML, [c_void_p, c_char_p]) +from_user_input = void_output(std_call('OSRSetFromUserInput'), [c_void_p, c_char_p]) + +# Morphing to/from ESRI WKT. +morph_to_esri = void_output(lgdal.OSRMorphToESRI, [c_void_p]) +morph_from_esri = void_output(lgdal.OSRMorphFromESRI, [c_void_p]) + +# Identifying the EPSG +identify_epsg = void_output(lgdal.OSRAutoIdentifyEPSG, [c_void_p]) + +# Getting the angular_units, linear_units functions +linear_units = units_func(lgdal.OSRGetLinearUnits) +angular_units = units_func(lgdal.OSRGetAngularUnits) + +# For exporting to WKT, PROJ.4, "Pretty" WKT, and XML. +to_wkt = string_output(std_call('OSRExportToWkt'), [c_void_p, POINTER(c_char_p)], decoding='ascii') +to_proj = string_output(std_call('OSRExportToProj4'), [c_void_p, POINTER(c_char_p)], decoding='ascii') +to_pretty_wkt = string_output(std_call('OSRExportToPrettyWkt'), [c_void_p, POINTER(c_char_p), c_int], offset=-2, decoding='ascii') + +# Memory leak fixed in GDAL 1.5; still exists in 1.4. +to_xml = string_output(lgdal.OSRExportToXML, [c_void_p, POINTER(c_char_p), c_char_p], offset=-2, decoding='ascii') + +# String attribute retrival routines. +get_attr_value = const_string_output(std_call('OSRGetAttrValue'), [c_void_p, c_char_p, c_int], decoding='ascii') +get_auth_name = const_string_output(lgdal.OSRGetAuthorityName, [c_void_p, c_char_p], decoding='ascii') +get_auth_code = const_string_output(lgdal.OSRGetAuthorityCode, [c_void_p, c_char_p], decoding='ascii') + +# SRS Properties +isgeographic = int_output(lgdal.OSRIsGeographic, [c_void_p]) +islocal = int_output(lgdal.OSRIsLocal, [c_void_p]) +isprojected = int_output(lgdal.OSRIsProjected, [c_void_p]) + +# Coordinate transformation +new_ct= srs_output(std_call('OCTNewCoordinateTransformation'), [c_void_p, c_void_p]) +destroy_ct = void_output(std_call('OCTDestroyCoordinateTransformation'), [c_void_p], errcheck=False) diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/srs.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/srs.py new file mode 100644 index 0000000..66a8d4e --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/srs.py @@ -0,0 +1,342 @@ +""" + 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 +""" +from ctypes import byref, c_char_p, c_int + +# Getting the error checking routine and exceptions +from django.contrib.gis.gdal.base import GDALBase +from django.contrib.gis.gdal.error import SRSException +from django.contrib.gis.gdal.prototypes import srs as capi + +from django.utils import six +from django.utils.encoding import force_bytes + + +#### Spatial Reference class. #### +class SpatialReference(GDALBase): + """ + A wrapper for the OGRSpatialReference object. According to the GDAL Web site, + the SpatialReference object "provide[s] services to represent coordinate + systems (projections and datums) and to transform between them." + """ + + #### Python 'magic' routines #### + def __init__(self, srs_input=''): + """ + Creates a GDAL OSR Spatial Reference object from the given input. + The input may be string of OGC Well Known Text (WKT), an integer + EPSG code, a PROJ.4 string, and/or a projection "well known" shorthand + string (one of 'WGS84', 'WGS72', 'NAD27', 'NAD83'). + """ + srs_type = 'user' + + if isinstance(srs_input, six.string_types): + # Encoding to ASCII if unicode passed in. + if isinstance(srs_input, six.text_type): + srs_input = srs_input.encode('ascii') + try: + # If SRID is a string, e.g., '4326', then make acceptable + # as user input. + srid = int(srs_input) + srs_input = 'EPSG:%d' % srid + except ValueError: + pass + elif isinstance(srs_input, six.integer_types): + # EPSG integer code was input. + srs_type = 'epsg' + elif isinstance(srs_input, self.ptr_type): + srs = srs_input + srs_type = 'ogr' + else: + raise TypeError('Invalid SRS type "%s"' % srs_type) + + if srs_type == 'ogr': + # Input is already an SRS pointer. + srs = srs_input + else: + # Creating a new SRS pointer, using the string buffer. + buf = c_char_p(b'') + srs = capi.new_srs(buf) + + # If the pointer is NULL, throw an exception. + if not srs: + raise SRSException('Could not create spatial reference from: %s' % srs_input) + else: + self.ptr = srs + + # Importing from either the user input string or an integer SRID. + if srs_type == 'user': + self.import_user_input(srs_input) + elif srs_type == 'epsg': + self.import_epsg(srs_input) + + def __del__(self): + "Destroys this spatial reference." + if self._ptr: capi.release_srs(self._ptr) + + 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 index of the attribute in the WKT. For example: + + >>> wkt = 'GEOGCS["WGS 84", DATUM["WGS_1984, ... AUTHORITY["EPSG","4326"]]') + >>> srs = SpatialReference(wkt) # could also use 'WGS84', or 4326 + >>> print(srs['GEOGCS']) + WGS 84 + >>> print(srs['DATUM']) + WGS_1984 + >>> print(srs['AUTHORITY']) + EPSG + >>> print(srs['AUTHORITY', 1]) # The authority value + 4326 + >>> print(srs['TOWGS84', 4]) # the fourth value in this wkt + 0 + >>> print(srs['UNIT|AUTHORITY']) # For the units authority, have to use the pipe symbole. + EPSG + >>> print(srs['UNIT|AUTHORITY', 1]) # The authority value for the untis + 9122 + """ + if isinstance(target, tuple): + return self.attr_value(*target) + else: + return self.attr_value(target) + + def __str__(self): + "The string representation uses 'pretty' WKT." + return self.pretty_wkt + + #### SpatialReference Methods #### + 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. + """ + if not isinstance(target, six.string_types) or not isinstance(index, int): + raise TypeError + return capi.get_attr_value(self.ptr, force_bytes(target), index) + + def auth_name(self, target): + "Returns the authority name for the given string target node." + return capi.get_auth_name(self.ptr, force_bytes(target)) + + def auth_code(self, target): + "Returns the authority code for the given string target node." + return capi.get_auth_code(self.ptr, force_bytes(target)) + + def clone(self): + "Returns a clone of this SpatialReference object." + return SpatialReference(capi.clone_srs(self.ptr)) + + def from_esri(self): + "Morphs this SpatialReference from ESRI's format to EPSG." + capi.morph_from_esri(self.ptr) + + def identify_epsg(self): + """ + This method inspects the WKT of this SpatialReference, and will + add EPSG authority nodes where an EPSG identifier is applicable. + """ + capi.identify_epsg(self.ptr) + + def to_esri(self): + "Morphs this SpatialReference to ESRI's format." + capi.morph_to_esri(self.ptr) + + def validate(self): + "Checks to see if the given spatial reference is valid." + capi.srs_validate(self.ptr) + + #### Name & SRID properties #### + @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 + + @property + def srid(self): + "Returns the SRID of top-level authority, or None if undefined." + try: + return int(self.attr_value('AUTHORITY', 1)) + except (TypeError, ValueError): + return None + + #### Unit Properties #### + @property + def linear_name(self): + "Returns the name of the linear units." + units, name = capi.linear_units(self.ptr, byref(c_char_p())) + return name + + @property + def linear_units(self): + "Returns the value of the linear units." + units, name = capi.linear_units(self.ptr, byref(c_char_p())) + return units + + @property + def angular_name(self): + "Returns the name of the angular units." + units, name = capi.angular_units(self.ptr, byref(c_char_p())) + return name + + @property + def angular_units(self): + "Returns the value of the angular units." + units, name = capi.angular_units(self.ptr, byref(c_char_p())) + return units + + @property + def units(self): + """ + Returns a 2-tuple of the units value and the units name, + and will automatically determines whether to return the linear + or angular units. + """ + units, name = None, None + if self.projected or self.local: + units, name = capi.linear_units(self.ptr, byref(c_char_p())) + elif self.geographic: + units, name = capi.angular_units(self.ptr, byref(c_char_p())) + if name is not None: + name.decode() + return (units, name) + + #### 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): + "Returns the Semi Major Axis for this Spatial Reference." + return capi.semi_major(self.ptr, byref(c_int())) + + @property + def semi_minor(self): + "Returns the Semi Minor Axis for this Spatial Reference." + return capi.semi_minor(self.ptr, byref(c_int())) + + @property + def inverse_flattening(self): + "Returns the Inverse Flattening for this Spatial Reference." + return capi.invflattening(self.ptr, byref(c_int())) + + #### Boolean Properties #### + @property + def geographic(self): + """ + Returns True if this SpatialReference is geographic + (root node is GEOGCS). + """ + return bool(capi.isgeographic(self.ptr)) + + @property + def local(self): + "Returns True if this SpatialReference is local (root node is LOCAL_CS)." + return bool(capi.islocal(self.ptr)) + + @property + def projected(self): + """ + Returns True if this SpatialReference is a projected coordinate system + (root node is PROJCS). + """ + return bool(capi.isprojected(self.ptr)) + + #### Import Routines ##### + def import_epsg(self, epsg): + "Imports the Spatial Reference from the EPSG code (an integer)." + capi.from_epsg(self.ptr, epsg) + + def import_proj(self, proj): + "Imports the Spatial Reference from a PROJ.4 string." + capi.from_proj(self.ptr, proj) + + def import_user_input(self, user_input): + "Imports the Spatial Reference from the given user input string." + capi.from_user_input(self.ptr, force_bytes(user_input)) + + def import_wkt(self, wkt): + "Imports the Spatial Reference from OGC WKT (string)" + capi.from_wkt(self.ptr, byref(c_char_p(wkt))) + + def import_xml(self, xml): + "Imports the Spatial Reference from an XML string." + capi.from_xml(self.ptr, xml) + + #### Export Properties #### + @property + def wkt(self): + "Returns the WKT representation of this Spatial Reference." + return capi.to_wkt(self.ptr, byref(c_char_p())) + + @property + def pretty_wkt(self, simplify=0): + "Returns the 'pretty' representation of the WKT." + return capi.to_pretty_wkt(self.ptr, byref(c_char_p()), simplify) + + @property + def proj(self): + "Returns the PROJ.4 representation for this Spatial Reference." + return capi.to_proj(self.ptr, byref(c_char_p())) + + @property + def proj4(self): + "Alias for proj()." + return self.proj + + @property + def xml(self, dialect=''): + "Returns the XML representation of this Spatial Reference." + return capi.to_xml(self.ptr, byref(c_char_p()), dialect) + +class CoordTransform(GDALBase): + "The coordinate system transformation object." + + def __init__(self, source, target): + "Initializes on a source and target SpatialReference objects." + if not isinstance(source, SpatialReference) or not isinstance(target, SpatialReference): + raise TypeError('source and target must be of type SpatialReference') + self.ptr = capi.new_ct(source._ptr, target._ptr) + self._srs1_name = source.name + self._srs2_name = target.name + + def __del__(self): + "Deletes this Coordinate Transformation object." + if self._ptr: capi.destroy_ct(self._ptr) + + def __str__(self): + return 'Transform from "%s" to "%s"' % (self._srs1_name, self._srs2_name) diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/tests/__init__.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/tests/__init__.py diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/tests/test_driver.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/tests/test_driver.py new file mode 100644 index 0000000..c27302d --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/tests/test_driver.py @@ -0,0 +1,39 @@ +from django.contrib.gis.gdal import HAS_GDAL +from django.utils import unittest +from django.utils.unittest import skipUnless + +if HAS_GDAL: + from django.contrib.gis.gdal import Driver, OGRException + + +valid_drivers = ('ESRI Shapefile', 'MapInfo File', 'TIGER', 'S57', 'DGN', + 'Memory', 'CSV', 'GML', 'KML') + +invalid_drivers = ('Foo baz', 'clucka', 'ESRI Shp') + +aliases = {'eSrI' : 'ESRI Shapefile', + 'TigER/linE' : 'TIGER', + 'SHAPE' : 'ESRI Shapefile', + 'sHp' : 'ESRI Shapefile', + } + + +@skipUnless(HAS_GDAL, "GDAL is required") +class DriverTest(unittest.TestCase): + + def test01_valid_driver(self): + "Testing valid OGR Data Source Drivers." + for d in valid_drivers: + dr = Driver(d) + self.assertEqual(d, str(dr)) + + def test02_invalid_driver(self): + "Testing invalid OGR Data Source Drivers." + for i in invalid_drivers: + self.assertRaises(OGRException, Driver, i) + + def test03_aliases(self): + "Testing driver aliases." + for alias, full_name in aliases.items(): + dr = Driver(alias) + self.assertEqual(full_name, str(dr)) diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/tests/test_ds.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/tests/test_ds.py new file mode 100644 index 0000000..3664f05 --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/tests/test_ds.py @@ -0,0 +1,244 @@ +import os + +from django.contrib.gis.gdal import HAS_GDAL +from django.contrib.gis.geometry.test_data import get_ds_file, TestDS, TEST_DATA +from django.utils import unittest +from django.utils.unittest import skipUnless + +if HAS_GDAL: + from django.contrib.gis.gdal import DataSource, Envelope, OGRGeometry, OGRException, OGRIndexError, GDAL_VERSION + from django.contrib.gis.gdal.field import OFTReal, OFTInteger, OFTString + + # List of acceptable data sources. + ds_list = ( + TestDS('test_point', nfeat=5, nfld=3, geom='POINT', gtype=1, driver='ESRI Shapefile', + fields={'dbl' : OFTReal, 'int' : OFTInteger, 'str' : OFTString,}, + extent=(-1.35011,0.166623,-0.524093,0.824508), # Got extent from QGIS + srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]', + field_values={'dbl' : [float(i) for i in range(1, 6)], 'int' : list(range(1, 6)), 'str' : [str(i) for i in range(1, 6)]}, + fids=range(5)), + TestDS('test_vrt', ext='vrt', nfeat=3, nfld=3, geom='POINT', gtype='Point25D', driver='VRT', + fields={'POINT_X' : OFTString, 'POINT_Y' : OFTString, 'NUM' : OFTString}, # VRT uses CSV, which all types are OFTString. + extent=(1.0, 2.0, 100.0, 523.5), # Min/Max from CSV + field_values={'POINT_X' : ['1.0', '5.0', '100.0'], 'POINT_Y' : ['2.0', '23.0', '523.5'], 'NUM' : ['5', '17', '23']}, + fids=range(1,4)), + TestDS('test_poly', nfeat=3, nfld=3, geom='POLYGON', gtype=3, + driver='ESRI Shapefile', + fields={'float' : OFTReal, 'int' : OFTInteger, 'str' : OFTString,}, + extent=(-1.01513,-0.558245,0.161876,0.839637), # Got extent from QGIS + srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]'), + ) + +bad_ds = (TestDS('foo'),) + + +@skipUnless(HAS_GDAL, "GDAL is required") +class DataSourceTest(unittest.TestCase): + + def test01_valid_shp(self): + "Testing valid SHP Data Source files." + + for source in ds_list: + # Loading up the data source + ds = DataSource(source.ds) + + # Making sure the layer count is what's expected (only 1 layer in a SHP file) + self.assertEqual(1, len(ds)) + + # Making sure GetName works + self.assertEqual(source.ds, ds.name) + + # Making sure the driver name matches up + self.assertEqual(source.driver, str(ds.driver)) + + # Making sure indexing works + try: + ds[len(ds)] + except OGRIndexError: + pass + else: + self.fail('Expected an IndexError!') + + def test02_invalid_shp(self): + "Testing invalid SHP files for the Data Source." + for source in bad_ds: + self.assertRaises(OGRException, DataSource, source.ds) + + def test03a_layers(self): + "Testing Data Source Layers." + for source in ds_list: + ds = DataSource(source.ds) + + # Incrementing through each layer, this tests DataSource.__iter__ + for layer in ds: + # Making sure we get the number of features we expect + self.assertEqual(len(layer), source.nfeat) + + # Making sure we get the number of fields we expect + self.assertEqual(source.nfld, layer.num_fields) + self.assertEqual(source.nfld, len(layer.fields)) + + # Testing the layer's extent (an Envelope), and it's properties + if source.driver == 'VRT' and (GDAL_VERSION >= (1, 7, 0) and GDAL_VERSION < (1, 7, 3)): + # There's a known GDAL regression with retrieving the extent + # of a VRT layer in versions 1.7.0-1.7.2: + # http://trac.osgeo.org/gdal/ticket/3783 + pass + else: + self.assertEqual(True, isinstance(layer.extent, Envelope)) + self.assertAlmostEqual(source.extent[0], layer.extent.min_x, 5) + self.assertAlmostEqual(source.extent[1], layer.extent.min_y, 5) + self.assertAlmostEqual(source.extent[2], layer.extent.max_x, 5) + self.assertAlmostEqual(source.extent[3], layer.extent.max_y, 5) + + # Now checking the field names. + flds = layer.fields + for f in flds: self.assertEqual(True, f in source.fields) + + # Negative FIDs are not allowed. + self.assertRaises(OGRIndexError, layer.__getitem__, -1) + self.assertRaises(OGRIndexError, layer.__getitem__, 50000) + + if hasattr(source, 'field_values'): + fld_names = source.field_values.keys() + + # Testing `Layer.get_fields` (which uses Layer.__iter__) + for fld_name in fld_names: + self.assertEqual(source.field_values[fld_name], layer.get_fields(fld_name)) + + # Testing `Layer.__getitem__`. + for i, fid in enumerate(source.fids): + feat = layer[fid] + self.assertEqual(fid, feat.fid) + # Maybe this should be in the test below, but we might as well test + # the feature values here while in this loop. + for fld_name in fld_names: + self.assertEqual(source.field_values[fld_name][i], feat.get(fld_name)) + + def test03b_layer_slice(self): + "Test indexing and slicing on Layers." + # Using the first data-source because the same slice + # can be used for both the layer and the control values. + source = ds_list[0] + ds = DataSource(source.ds) + + sl = slice(1, 3) + feats = ds[0][sl] + + for fld_name in ds[0].fields: + test_vals = [feat.get(fld_name) for feat in feats] + control_vals = source.field_values[fld_name][sl] + self.assertEqual(control_vals, test_vals) + + def test03c_layer_references(self): + """ + Ensure OGR objects keep references to the objects they belong to. + """ + source = ds_list[0] + + # See ticket #9448. + def get_layer(): + # This DataSource object is not accessible outside this + # scope. However, a reference should still be kept alive + # on the `Layer` returned. + ds = DataSource(source.ds) + return ds[0] + + # Making sure we can call OGR routines on the Layer returned. + lyr = get_layer() + self.assertEqual(source.nfeat, len(lyr)) + self.assertEqual(source.gtype, lyr.geom_type.num) + + # Same issue for Feature/Field objects, see #18640 + self.assertEqual(str(lyr[0]['str']), "1") + + def test04_features(self): + "Testing Data Source Features." + for source in ds_list: + ds = DataSource(source.ds) + + # Incrementing through each layer + for layer in ds: + # Incrementing through each feature in the layer + for feat in layer: + # Making sure the number of fields, and the geometry type + # are what's expected. + self.assertEqual(source.nfld, len(list(feat))) + self.assertEqual(source.gtype, feat.geom_type) + + # Making sure the fields match to an appropriate OFT type. + for k, v in source.fields.items(): + # Making sure we get the proper OGR Field instance, using + # a string value index for the feature. + self.assertEqual(True, isinstance(feat[k], v)) + + # Testing Feature.__iter__ + for fld in feat: + self.assertEqual(True, fld.name in source.fields.keys()) + + def test05_geometries(self): + "Testing Geometries from Data Source Features." + for source in ds_list: + ds = DataSource(source.ds) + + # Incrementing through each layer and feature. + for layer in ds: + for feat in layer: + g = feat.geom + + # Making sure we get the right Geometry name & type + self.assertEqual(source.geom, g.geom_name) + self.assertEqual(source.gtype, g.geom_type) + + # Making sure the SpatialReference is as expected. + if hasattr(source, 'srs_wkt'): + self.assertEqual( + source.srs_wkt, + # Depending on lib versions, WGS_84 might be WGS_1984 + g.srs.wkt.replace('SPHEROID["WGS_84"', 'SPHEROID["WGS_1984"') + ) + + def test06_spatial_filter(self): + "Testing the Layer.spatial_filter property." + ds = DataSource(get_ds_file('cities', 'shp')) + lyr = ds[0] + + # When not set, it should be None. + self.assertEqual(None, lyr.spatial_filter) + + # Must be set a/an OGRGeometry or 4-tuple. + self.assertRaises(TypeError, lyr._set_spatial_filter, 'foo') + + # Setting the spatial filter with a tuple/list with the extent of + # a buffer centering around Pueblo. + self.assertRaises(ValueError, lyr._set_spatial_filter, list(range(5))) + filter_extent = (-105.609252, 37.255001, -103.609252, 39.255001) + lyr.spatial_filter = (-105.609252, 37.255001, -103.609252, 39.255001) + self.assertEqual(OGRGeometry.from_bbox(filter_extent), lyr.spatial_filter) + feats = [feat for feat in lyr] + self.assertEqual(1, len(feats)) + self.assertEqual('Pueblo', feats[0].get('Name')) + + # Setting the spatial filter with an OGRGeometry for buffer centering + # around Houston. + filter_geom = OGRGeometry('POLYGON((-96.363151 28.763374,-94.363151 28.763374,-94.363151 30.763374,-96.363151 30.763374,-96.363151 28.763374))') + lyr.spatial_filter = filter_geom + self.assertEqual(filter_geom, lyr.spatial_filter) + feats = [feat for feat in lyr] + self.assertEqual(1, len(feats)) + self.assertEqual('Houston', feats[0].get('Name')) + + # Clearing the spatial filter by setting it to None. Now + # should indicate that there are 3 features in the Layer. + lyr.spatial_filter = None + self.assertEqual(3, len(lyr)) + + def test07_integer_overflow(self): + "Testing that OFTReal fields, treated as OFTInteger, do not overflow." + # Using *.dbf from Census 2010 TIGER Shapefile for Texas, + # which has land area ('ALAND10') stored in a Real field + # with no precision. + ds = DataSource(os.path.join(TEST_DATA, 'texas.dbf')) + feat = ds[0][0] + # Reference value obtained using `ogrinfo`. + self.assertEqual(676586997978, feat.get('ALAND10')) diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/tests/test_envelope.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/tests/test_envelope.py new file mode 100644 index 0000000..7518dc6 --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/tests/test_envelope.py @@ -0,0 +1,93 @@ +from django.contrib.gis.gdal import HAS_GDAL +from django.utils import unittest +from django.utils.unittest import skipUnless + +if HAS_GDAL: + from django.contrib.gis.gdal import Envelope, OGRException + + +class TestPoint(object): + def __init__(self, x, y): + self.x = x + self.y = y + + +@skipUnless(HAS_GDAL, "GDAL is required") +class EnvelopeTest(unittest.TestCase): + + def setUp(self): + self.e = Envelope(0, 0, 5, 5) + + def test01_init(self): + "Testing Envelope initilization." + e1 = Envelope((0, 0, 5, 5)) + e2 = Envelope(0, 0, 5, 5) + e3 = Envelope(0, '0', '5', 5) # Thanks to ww for this + e4 = Envelope(e1._envelope) + self.assertRaises(OGRException, Envelope, (5, 5, 0, 0)) + self.assertRaises(OGRException, Envelope, 5, 5, 0, 0) + self.assertRaises(OGRException, Envelope, (0, 0, 5, 5, 3)) + self.assertRaises(OGRException, Envelope, ()) + self.assertRaises(ValueError, Envelope, 0, 'a', 5, 5) + self.assertRaises(TypeError, Envelope, 'foo') + self.assertRaises(OGRException, Envelope, (1, 1, 0, 0)) + try: + Envelope(0, 0, 0, 0) + except OGRException: + self.fail("shouldn't raise an exception for min_x == max_x or min_y == max_y") + + def test02_properties(self): + "Testing Envelope properties." + e = Envelope(0, 0, 2, 3) + self.assertEqual(0, e.min_x) + self.assertEqual(0, e.min_y) + self.assertEqual(2, e.max_x) + self.assertEqual(3, e.max_y) + self.assertEqual((0, 0), e.ll) + self.assertEqual((2, 3), e.ur) + self.assertEqual((0, 0, 2, 3), e.tuple) + self.assertEqual('POLYGON((0.0 0.0,0.0 3.0,2.0 3.0,2.0 0.0,0.0 0.0))', e.wkt) + self.assertEqual('(0.0, 0.0, 2.0, 3.0)', str(e)) + + def test03_equivalence(self): + "Testing Envelope equivalence." + e1 = Envelope(0.523, 0.217, 253.23, 523.69) + e2 = Envelope((0.523, 0.217, 253.23, 523.69)) + self.assertEqual(e1, e2) + self.assertEqual((0.523, 0.217, 253.23, 523.69), e1) + + def test04_expand_to_include_pt_2_params(self): + "Testing Envelope expand_to_include -- point as two parameters." + self.e.expand_to_include(2, 6) + self.assertEqual((0, 0, 5, 6), self.e) + self.e.expand_to_include(-1, -1) + self.assertEqual((-1, -1, 5, 6), self.e) + + def test05_expand_to_include_pt_2_tuple(self): + "Testing Envelope expand_to_include -- point as a single 2-tuple parameter." + self.e.expand_to_include((10, 10)) + self.assertEqual((0, 0, 10, 10), self.e) + self.e.expand_to_include((-10, -10)) + self.assertEqual((-10, -10, 10, 10), self.e) + + def test06_expand_to_include_extent_4_params(self): + "Testing Envelope expand_to_include -- extent as 4 parameters." + self.e.expand_to_include(-1, 1, 3, 7) + self.assertEqual((-1, 0, 5, 7), self.e) + + def test06_expand_to_include_extent_4_tuple(self): + "Testing Envelope expand_to_include -- extent as a single 4-tuple parameter." + self.e.expand_to_include((-1, 1, 3, 7)) + self.assertEqual((-1, 0, 5, 7), self.e) + + def test07_expand_to_include_envelope(self): + "Testing Envelope expand_to_include with Envelope as parameter." + self.e.expand_to_include(Envelope(-1, 1, 3, 7)) + self.assertEqual((-1, 0, 5, 7), self.e) + + def test08_expand_to_include_point(self): + "Testing Envelope expand_to_include with Point as parameter." + self.e.expand_to_include(TestPoint(-1, 1)) + self.assertEqual((-1, 0, 5, 5), self.e) + self.e.expand_to_include(TestPoint(10, 10)) + self.assertEqual((-1, 0, 10, 10), self.e) diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/tests/test_geom.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/tests/test_geom.py new file mode 100644 index 0000000..c048d2b --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/tests/test_geom.py @@ -0,0 +1,485 @@ +import json +from binascii import b2a_hex +try: + from django.utils.six.moves import cPickle as pickle +except ImportError: + import pickle + +from django.contrib.gis.gdal import HAS_GDAL +from django.contrib.gis.geometry.test_data import TestDataMixin +from django.utils.six.moves import xrange +from django.utils import unittest +from django.utils.unittest import skipUnless + +if HAS_GDAL: + from django.contrib.gis.gdal import (OGRGeometry, OGRGeomType, + OGRException, OGRIndexError, SpatialReference, CoordTransform, + GDAL_VERSION) + + +@skipUnless(HAS_GDAL, "GDAL is required") +class OGRGeomTest(unittest.TestCase, TestDataMixin): + "This tests the OGR Geometry." + + def test00a_geomtype(self): + "Testing OGRGeomType object." + + # OGRGeomType should initialize on all these inputs. + try: + g = OGRGeomType(1) + g = OGRGeomType(7) + g = OGRGeomType('point') + g = OGRGeomType('GeometrycollectioN') + g = OGRGeomType('LINearrING') + g = OGRGeomType('Unknown') + except: + self.fail('Could not create an OGRGeomType object!') + + # Should throw TypeError on this input + self.assertRaises(OGRException, OGRGeomType, 23) + self.assertRaises(OGRException, OGRGeomType, 'fooD') + self.assertRaises(OGRException, OGRGeomType, 9) + + # Equivalence can take strings, ints, and other OGRGeomTypes + self.assertEqual(True, OGRGeomType(1) == OGRGeomType(1)) + self.assertEqual(True, OGRGeomType(7) == 'GeometryCollection') + self.assertEqual(True, OGRGeomType('point') == 'POINT') + self.assertEqual(False, OGRGeomType('point') == 2) + self.assertEqual(True, OGRGeomType('unknown') == 0) + self.assertEqual(True, OGRGeomType(6) == 'MULtiPolyGON') + self.assertEqual(False, OGRGeomType(1) != OGRGeomType('point')) + self.assertEqual(True, OGRGeomType('POINT') != OGRGeomType(6)) + + # Testing the Django field name equivalent property. + self.assertEqual('PointField', OGRGeomType('Point').django) + self.assertEqual('GeometryField', OGRGeomType('Unknown').django) + self.assertEqual(None, OGRGeomType('none').django) + + # 'Geometry' initialization implies an unknown geometry type. + gt = OGRGeomType('Geometry') + self.assertEqual(0, gt.num) + self.assertEqual('Unknown', gt.name) + + def test00b_geomtype_25d(self): + "Testing OGRGeomType object with 25D types." + wkb25bit = OGRGeomType.wkb25bit + self.assertTrue(OGRGeomType(wkb25bit + 1) == 'Point25D') + self.assertTrue(OGRGeomType('MultiLineString25D') == (5 + wkb25bit)) + self.assertEqual('GeometryCollectionField', OGRGeomType('GeometryCollection25D').django) + + def test01a_wkt(self): + "Testing WKT output." + for g in self.geometries.wkt_out: + geom = OGRGeometry(g.wkt) + self.assertEqual(g.wkt, geom.wkt) + + def test01a_ewkt(self): + "Testing EWKT input/output." + for ewkt_val in ('POINT (1 2 3)', 'LINEARRING (0 0,1 1,2 1,0 0)'): + # First with ewkt output when no SRID in EWKT + self.assertEqual(ewkt_val, OGRGeometry(ewkt_val).ewkt) + # No test consumption with an SRID specified. + ewkt_val = 'SRID=4326;%s' % ewkt_val + geom = OGRGeometry(ewkt_val) + self.assertEqual(ewkt_val, geom.ewkt) + self.assertEqual(4326, geom.srs.srid) + + def test01b_gml(self): + "Testing GML output." + for g in self.geometries.wkt_out: + geom = OGRGeometry(g.wkt) + exp_gml = g.gml + if GDAL_VERSION >= (1, 8): + # In GDAL 1.8, the non-conformant GML tag <gml:GeometryCollection> was + # replaced with <gml:MultiGeometry>. + exp_gml = exp_gml.replace('GeometryCollection', 'MultiGeometry') + self.assertEqual(exp_gml, geom.gml) + + def test01c_hex(self): + "Testing HEX input/output." + for g in self.geometries.hex_wkt: + geom1 = OGRGeometry(g.wkt) + self.assertEqual(g.hex.encode(), geom1.hex) + # Constructing w/HEX + geom2 = OGRGeometry(g.hex) + self.assertEqual(geom1, geom2) + + def test01d_wkb(self): + "Testing WKB input/output." + for g in self.geometries.hex_wkt: + geom1 = OGRGeometry(g.wkt) + wkb = geom1.wkb + self.assertEqual(b2a_hex(wkb).upper(), g.hex.encode()) + # Constructing w/WKB. + geom2 = OGRGeometry(wkb) + self.assertEqual(geom1, geom2) + + def test01e_json(self): + "Testing GeoJSON input/output." + for g in self.geometries.json_geoms: + geom = OGRGeometry(g.wkt) + if not hasattr(g, 'not_equal'): + # Loading jsons to prevent decimal differences + self.assertEqual(json.loads(g.json), json.loads(geom.json)) + self.assertEqual(json.loads(g.json), json.loads(geom.geojson)) + self.assertEqual(OGRGeometry(g.wkt), OGRGeometry(geom.json)) + + def test02_points(self): + "Testing Point objects." + + prev = OGRGeometry('POINT(0 0)') + for p in self.geometries.points: + if not hasattr(p, 'z'): # No 3D + pnt = OGRGeometry(p.wkt) + self.assertEqual(1, pnt.geom_type) + self.assertEqual('POINT', pnt.geom_name) + self.assertEqual(p.x, pnt.x) + self.assertEqual(p.y, pnt.y) + self.assertEqual((p.x, p.y), pnt.tuple) + + def test03_multipoints(self): + "Testing MultiPoint objects." + for mp in self.geometries.multipoints: + mgeom1 = OGRGeometry(mp.wkt) # First one from WKT + self.assertEqual(4, mgeom1.geom_type) + self.assertEqual('MULTIPOINT', mgeom1.geom_name) + mgeom2 = OGRGeometry('MULTIPOINT') # Creating empty multipoint + mgeom3 = OGRGeometry('MULTIPOINT') + for g in mgeom1: + mgeom2.add(g) # adding each point from the multipoints + mgeom3.add(g.wkt) # should take WKT as well + self.assertEqual(mgeom1, mgeom2) # they should equal + self.assertEqual(mgeom1, mgeom3) + self.assertEqual(mp.coords, mgeom2.coords) + self.assertEqual(mp.n_p, mgeom2.point_count) + + def test04_linestring(self): + "Testing LineString objects." + prev = OGRGeometry('POINT(0 0)') + for ls in self.geometries.linestrings: + linestr = OGRGeometry(ls.wkt) + self.assertEqual(2, linestr.geom_type) + self.assertEqual('LINESTRING', linestr.geom_name) + self.assertEqual(ls.n_p, linestr.point_count) + self.assertEqual(ls.coords, linestr.tuple) + self.assertEqual(True, linestr == OGRGeometry(ls.wkt)) + self.assertEqual(True, linestr != prev) + self.assertRaises(OGRIndexError, linestr.__getitem__, len(linestr)) + prev = linestr + + # Testing the x, y properties. + x = [tmpx for tmpx, tmpy in ls.coords] + y = [tmpy for tmpx, tmpy in ls.coords] + self.assertEqual(x, linestr.x) + self.assertEqual(y, linestr.y) + + def test05_multilinestring(self): + "Testing MultiLineString objects." + prev = OGRGeometry('POINT(0 0)') + for mls in self.geometries.multilinestrings: + mlinestr = OGRGeometry(mls.wkt) + self.assertEqual(5, mlinestr.geom_type) + self.assertEqual('MULTILINESTRING', mlinestr.geom_name) + self.assertEqual(mls.n_p, mlinestr.point_count) + self.assertEqual(mls.coords, mlinestr.tuple) + self.assertEqual(True, mlinestr == OGRGeometry(mls.wkt)) + self.assertEqual(True, mlinestr != prev) + prev = mlinestr + for ls in mlinestr: + self.assertEqual(2, ls.geom_type) + self.assertEqual('LINESTRING', ls.geom_name) + self.assertRaises(OGRIndexError, mlinestr.__getitem__, len(mlinestr)) + + def test06_linearring(self): + "Testing LinearRing objects." + prev = OGRGeometry('POINT(0 0)') + for rr in self.geometries.linearrings: + lr = OGRGeometry(rr.wkt) + #self.assertEqual(101, lr.geom_type.num) + self.assertEqual('LINEARRING', lr.geom_name) + self.assertEqual(rr.n_p, len(lr)) + self.assertEqual(True, lr == OGRGeometry(rr.wkt)) + self.assertEqual(True, lr != prev) + prev = lr + + def test07a_polygons(self): + "Testing Polygon objects." + + # Testing `from_bbox` class method + bbox = (-180,-90,180,90) + p = OGRGeometry.from_bbox( bbox ) + self.assertEqual(bbox, p.extent) + + prev = OGRGeometry('POINT(0 0)') + for p in self.geometries.polygons: + poly = OGRGeometry(p.wkt) + self.assertEqual(3, poly.geom_type) + self.assertEqual('POLYGON', poly.geom_name) + self.assertEqual(p.n_p, poly.point_count) + self.assertEqual(p.n_i + 1, len(poly)) + + # Testing area & centroid. + self.assertAlmostEqual(p.area, poly.area, 9) + x, y = poly.centroid.tuple + self.assertAlmostEqual(p.centroid[0], x, 9) + self.assertAlmostEqual(p.centroid[1], y, 9) + + # Testing equivalence + self.assertEqual(True, poly == OGRGeometry(p.wkt)) + self.assertEqual(True, poly != prev) + + if p.ext_ring_cs: + ring = poly[0] + self.assertEqual(p.ext_ring_cs, ring.tuple) + self.assertEqual(p.ext_ring_cs, poly[0].tuple) + self.assertEqual(len(p.ext_ring_cs), ring.point_count) + + for r in poly: + self.assertEqual('LINEARRING', r.geom_name) + + def test07b_closepolygons(self): + "Testing closing Polygon objects." + # Both rings in this geometry are not closed. + poly = OGRGeometry('POLYGON((0 0, 5 0, 5 5, 0 5), (1 1, 2 1, 2 2, 2 1))') + self.assertEqual(8, poly.point_count) + with self.assertRaises(OGRException): + _ = poly.centroid + + poly.close_rings() + self.assertEqual(10, poly.point_count) # Two closing points should've been added + self.assertEqual(OGRGeometry('POINT(2.5 2.5)'), poly.centroid) + + def test08_multipolygons(self): + "Testing MultiPolygon objects." + prev = OGRGeometry('POINT(0 0)') + for mp in self.geometries.multipolygons: + mpoly = OGRGeometry(mp.wkt) + self.assertEqual(6, mpoly.geom_type) + self.assertEqual('MULTIPOLYGON', mpoly.geom_name) + if mp.valid: + self.assertEqual(mp.n_p, mpoly.point_count) + self.assertEqual(mp.num_geom, len(mpoly)) + self.assertRaises(OGRIndexError, mpoly.__getitem__, len(mpoly)) + for p in mpoly: + self.assertEqual('POLYGON', p.geom_name) + self.assertEqual(3, p.geom_type) + self.assertEqual(mpoly.wkt, OGRGeometry(mp.wkt).wkt) + + def test09a_srs(self): + "Testing OGR Geometries with Spatial Reference objects." + for mp in self.geometries.multipolygons: + # Creating a geometry w/spatial reference + sr = SpatialReference('WGS84') + mpoly = OGRGeometry(mp.wkt, sr) + self.assertEqual(sr.wkt, mpoly.srs.wkt) + + # Ensuring that SRS is propagated to clones. + klone = mpoly.clone() + self.assertEqual(sr.wkt, klone.srs.wkt) + + # Ensuring all children geometries (polygons and their rings) all + # return the assigned spatial reference as well. + for poly in mpoly: + self.assertEqual(sr.wkt, poly.srs.wkt) + for ring in poly: + self.assertEqual(sr.wkt, ring.srs.wkt) + + # Ensuring SRS propagate in topological ops. + a = OGRGeometry(self.geometries.topology_geoms[0].wkt_a, sr) + b = OGRGeometry(self.geometries.topology_geoms[0].wkt_b, sr) + diff = a.difference(b) + union = a.union(b) + self.assertEqual(sr.wkt, diff.srs.wkt) + self.assertEqual(sr.srid, union.srs.srid) + + # Instantiating w/an integer SRID + mpoly = OGRGeometry(mp.wkt, 4326) + self.assertEqual(4326, mpoly.srid) + mpoly.srs = SpatialReference(4269) + self.assertEqual(4269, mpoly.srid) + self.assertEqual('NAD83', mpoly.srs.name) + + # Incrementing through the multipolyogn after the spatial reference + # has been re-assigned. + for poly in mpoly: + self.assertEqual(mpoly.srs.wkt, poly.srs.wkt) + poly.srs = 32140 + for ring in poly: + # Changing each ring in the polygon + self.assertEqual(32140, ring.srs.srid) + self.assertEqual('NAD83 / Texas South Central', ring.srs.name) + ring.srs = str(SpatialReference(4326)) # back to WGS84 + self.assertEqual(4326, ring.srs.srid) + + # Using the `srid` property. + ring.srid = 4322 + self.assertEqual('WGS 72', ring.srs.name) + self.assertEqual(4322, ring.srid) + + def test09b_srs_transform(self): + "Testing transform()." + orig = OGRGeometry('POINT (-104.609 38.255)', 4326) + trans = OGRGeometry('POINT (992385.4472045 481455.4944650)', 2774) + + # Using an srid, a SpatialReference object, and a CoordTransform object + # or transformations. + t1, t2, t3 = orig.clone(), orig.clone(), orig.clone() + t1.transform(trans.srid) + t2.transform(SpatialReference('EPSG:2774')) + ct = CoordTransform(SpatialReference('WGS84'), SpatialReference(2774)) + t3.transform(ct) + + # Testing use of the `clone` keyword. + k1 = orig.clone() + k2 = k1.transform(trans.srid, clone=True) + self.assertEqual(k1, orig) + self.assertNotEqual(k1, k2) + + prec = 3 + for p in (t1, t2, t3, k2): + self.assertAlmostEqual(trans.x, p.x, prec) + self.assertAlmostEqual(trans.y, p.y, prec) + + def test09c_transform_dim(self): + "Testing coordinate dimension is the same on transformed geometries." + ls_orig = OGRGeometry('LINESTRING(-104.609 38.255)', 4326) + ls_trans = OGRGeometry('LINESTRING(992385.4472045 481455.4944650)', 2774) + + prec = 3 + ls_orig.transform(ls_trans.srs) + # Making sure the coordinate dimension is still 2D. + self.assertEqual(2, ls_orig.coord_dim) + self.assertAlmostEqual(ls_trans.x[0], ls_orig.x[0], prec) + self.assertAlmostEqual(ls_trans.y[0], ls_orig.y[0], prec) + + def test10_difference(self): + "Testing difference()." + for i in xrange(len(self.geometries.topology_geoms)): + a = OGRGeometry(self.geometries.topology_geoms[i].wkt_a) + b = OGRGeometry(self.geometries.topology_geoms[i].wkt_b) + d1 = OGRGeometry(self.geometries.diff_geoms[i].wkt) + d2 = a.difference(b) + self.assertEqual(d1, d2) + self.assertEqual(d1, a - b) # __sub__ is difference operator + a -= b # testing __isub__ + self.assertEqual(d1, a) + + def test11_intersection(self): + "Testing intersects() and intersection()." + for i in xrange(len(self.geometries.topology_geoms)): + a = OGRGeometry(self.geometries.topology_geoms[i].wkt_a) + b = OGRGeometry(self.geometries.topology_geoms[i].wkt_b) + i1 = OGRGeometry(self.geometries.intersect_geoms[i].wkt) + self.assertEqual(True, a.intersects(b)) + i2 = a.intersection(b) + self.assertEqual(i1, i2) + self.assertEqual(i1, a & b) # __and__ is intersection operator + a &= b # testing __iand__ + self.assertEqual(i1, a) + + def test12_symdifference(self): + "Testing sym_difference()." + for i in xrange(len(self.geometries.topology_geoms)): + a = OGRGeometry(self.geometries.topology_geoms[i].wkt_a) + b = OGRGeometry(self.geometries.topology_geoms[i].wkt_b) + d1 = OGRGeometry(self.geometries.sdiff_geoms[i].wkt) + d2 = a.sym_difference(b) + self.assertEqual(d1, d2) + self.assertEqual(d1, a ^ b) # __xor__ is symmetric difference operator + a ^= b # testing __ixor__ + self.assertEqual(d1, a) + + def test13_union(self): + "Testing union()." + for i in xrange(len(self.geometries.topology_geoms)): + a = OGRGeometry(self.geometries.topology_geoms[i].wkt_a) + b = OGRGeometry(self.geometries.topology_geoms[i].wkt_b) + u1 = OGRGeometry(self.geometries.union_geoms[i].wkt) + u2 = a.union(b) + self.assertEqual(u1, u2) + self.assertEqual(u1, a | b) # __or__ is union operator + a |= b # testing __ior__ + self.assertEqual(u1, a) + + def test14_add(self): + "Testing GeometryCollection.add()." + # Can't insert a Point into a MultiPolygon. + mp = OGRGeometry('MultiPolygon') + pnt = OGRGeometry('POINT(5 23)') + self.assertRaises(OGRException, mp.add, pnt) + + # GeometryCollection.add may take an OGRGeometry (if another collection + # of the same type all child geoms will be added individually) or WKT. + for mp in self.geometries.multipolygons: + mpoly = OGRGeometry(mp.wkt) + mp1 = OGRGeometry('MultiPolygon') + mp2 = OGRGeometry('MultiPolygon') + mp3 = OGRGeometry('MultiPolygon') + + for poly in mpoly: + mp1.add(poly) # Adding a geometry at a time + mp2.add(poly.wkt) # Adding WKT + mp3.add(mpoly) # Adding a MultiPolygon's entire contents at once. + for tmp in (mp1, mp2, mp3): self.assertEqual(mpoly, tmp) + + def test15_extent(self): + "Testing `extent` property." + # The xmin, ymin, xmax, ymax of the MultiPoint should be returned. + mp = OGRGeometry('MULTIPOINT(5 23, 0 0, 10 50)') + self.assertEqual((0.0, 0.0, 10.0, 50.0), mp.extent) + # Testing on the 'real world' Polygon. + poly = OGRGeometry(self.geometries.polygons[3].wkt) + ring = poly.shell + x, y = ring.x, ring.y + xmin, ymin = min(x), min(y) + xmax, ymax = max(x), max(y) + self.assertEqual((xmin, ymin, xmax, ymax), poly.extent) + + def test16_25D(self): + "Testing 2.5D geometries." + pnt_25d = OGRGeometry('POINT(1 2 3)') + self.assertEqual('Point25D', pnt_25d.geom_type.name) + self.assertEqual(3.0, pnt_25d.z) + self.assertEqual(3, pnt_25d.coord_dim) + ls_25d = OGRGeometry('LINESTRING(1 1 1,2 2 2,3 3 3)') + self.assertEqual('LineString25D', ls_25d.geom_type.name) + self.assertEqual([1.0, 2.0, 3.0], ls_25d.z) + self.assertEqual(3, ls_25d.coord_dim) + + def test17_pickle(self): + "Testing pickle support." + g1 = OGRGeometry('LINESTRING(1 1 1,2 2 2,3 3 3)', 'WGS84') + g2 = pickle.loads(pickle.dumps(g1)) + self.assertEqual(g1, g2) + self.assertEqual(4326, g2.srs.srid) + self.assertEqual(g1.srs.wkt, g2.srs.wkt) + + def test18_ogrgeometry_transform_workaround(self): + "Testing coordinate dimensions on geometries after transformation." + # A bug in GDAL versions prior to 1.7 changes the coordinate + # dimension of a geometry after it has been transformed. + # This test ensures that the bug workarounds employed within + # `OGRGeometry.transform` indeed work. + wkt_2d = "MULTILINESTRING ((0 0,1 1,2 2))" + wkt_3d = "MULTILINESTRING ((0 0 0,1 1 1,2 2 2))" + srid = 4326 + + # For both the 2D and 3D MultiLineString, ensure _both_ the dimension + # of the collection and the component LineString have the expected + # coordinate dimension after transform. + geom = OGRGeometry(wkt_2d, srid) + geom.transform(srid) + self.assertEqual(2, geom.coord_dim) + self.assertEqual(2, geom[0].coord_dim) + self.assertEqual(wkt_2d, geom.wkt) + + geom = OGRGeometry(wkt_3d, srid) + geom.transform(srid) + self.assertEqual(3, geom.coord_dim) + self.assertEqual(3, geom[0].coord_dim) + self.assertEqual(wkt_3d, geom.wkt) + + def test19_equivalence_regression(self): + "Testing equivalence methods with non-OGRGeometry instances." + self.assertNotEqual(None, OGRGeometry('POINT(0 0)')) + self.assertEqual(False, OGRGeometry('LINESTRING(0 0, 1 1)') == 3) diff --git a/lib/python2.7/site-packages/django/contrib/gis/gdal/tests/test_srs.py b/lib/python2.7/site-packages/django/contrib/gis/gdal/tests/test_srs.py new file mode 100644 index 0000000..363b597 --- /dev/null +++ b/lib/python2.7/site-packages/django/contrib/gis/gdal/tests/test_srs.py @@ -0,0 +1,163 @@ +from django.contrib.gis.gdal import HAS_GDAL +from django.utils import unittest +from django.utils.unittest import skipUnless + +if HAS_GDAL: + from django.contrib.gis.gdal import SpatialReference, CoordTransform, OGRException, SRSException + + +class TestSRS: + def __init__(self, wkt, **kwargs): + self.wkt = wkt + for key, value in kwargs.items(): + setattr(self, key, value) + +# Some Spatial Reference examples +srlist = (TestSRS('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"]]', + proj='+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ', + epsg=4326, projected=False, geographic=True, local=False, + lin_name='unknown', ang_name='degree', lin_units=1.0, ang_units=0.0174532925199, + auth={'GEOGCS' : ('EPSG', '4326'), 'spheroid' : ('EPSG', '7030')}, + attr=(('DATUM', 'WGS_1984'), (('SPHEROID', 1), '6378137'),('primem|authority', 'EPSG'),), + ), + TestSRS('PROJCS["NAD83 / Texas South Central",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",30.28333333333333],PARAMETER["standard_parallel_2",28.38333333333333],PARAMETER["latitude_of_origin",27.83333333333333],PARAMETER["central_meridian",-99],PARAMETER["false_easting",600000],PARAMETER["false_northing",4000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","32140"]]', + proj=None, epsg=32140, projected=True, geographic=False, local=False, + lin_name='metre', ang_name='degree', lin_units=1.0, ang_units=0.0174532925199, + auth={'PROJCS' : ('EPSG', '32140'), 'spheroid' : ('EPSG', '7019'), 'unit' : ('EPSG', '9001'),}, + attr=(('DATUM', 'North_American_Datum_1983'),(('SPHEROID', 2), '298.257222101'),('PROJECTION','Lambert_Conformal_Conic_2SP'),), + ), + TestSRS('PROJCS["NAD_1983_StatePlane_Texas_South_Central_FIPS_4204_Feet",GEOGCS["GCS_North_American_1983",DATUM["North_American_Datum_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["False_Easting",1968500.0],PARAMETER["False_Northing",13123333.33333333],PARAMETER["Central_Meridian",-99.0],PARAMETER["Standard_Parallel_1",28.38333333333333],PARAMETER["Standard_Parallel_2",30.28333333333334],PARAMETER["Latitude_Of_Origin",27.83333333333333],UNIT["Foot_US",0.3048006096012192]]', + proj=None, epsg=None, projected=True, geographic=False, local=False, + lin_name='Foot_US', ang_name='Degree', lin_units=0.3048006096012192, ang_units=0.0174532925199, + auth={'PROJCS' : (None, None),}, + attr=(('PROJCS|GeOgCs|spheroid', 'GRS_1980'),(('projcs', 9), 'UNIT'), (('projcs', 11), None),), + ), + # This is really ESRI format, not WKT -- but the import should work the same + TestSRS('LOCAL_CS["Non-Earth (Meter)",LOCAL_DATUM["Local Datum",0],UNIT["Meter",1.0],AXIS["X",EAST],AXIS["Y",NORTH]]', + esri=True, proj=None, epsg=None, projected=False, geographic=False, local=True, + lin_name='Meter', ang_name='degree', lin_units=1.0, ang_units=0.0174532925199, + attr=(('LOCAL_DATUM', 'Local Datum'), ('unit', 'Meter')), + ), + ) + +# Well-Known Names +well_known = (TestSRS('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"]]', wk='WGS84', name='WGS 84', attrs=(('GEOGCS|AUTHORITY', 1, '4326'), ('SPHEROID', 'WGS 84'))), + TestSRS('GEOGCS["WGS 72",DATUM["WGS_1972",SPHEROID["WGS 72",6378135,298.26,AUTHORITY["EPSG","7043"]],AUTHORITY["EPSG","6322"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4322"]]', wk='WGS72', name='WGS 72', attrs=(('GEOGCS|AUTHORITY', 1, '4322'), ('SPHEROID', 'WGS 72'))), + TestSRS('GEOGCS["NAD27",DATUM["North_American_Datum_1927",SPHEROID["Clarke 1866",6378206.4,294.9786982138982,AUTHORITY["EPSG","7008"]],AUTHORITY["EPSG","6267"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4267"]]', wk='NAD27', name='NAD27', attrs=(('GEOGCS|AUTHORITY', 1, '4267'), ('SPHEROID', 'Clarke 1866'))), + TestSRS('GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]]', wk='NAD83', name='NAD83', attrs=(('GEOGCS|AUTHORITY', 1, '4269'), ('SPHEROID', 'GRS 1980'))), + TestSRS('PROJCS["NZGD49 / Karamea Circuit",GEOGCS["NZGD49",DATUM["New_Zealand_Geodetic_Datum_1949",SPHEROID["International 1924",6378388,297,AUTHORITY["EPSG","7022"]],TOWGS84[59.47,-5.04,187.44,0.47,-0.1,1.024,-4.5993],AUTHORITY["EPSG","6272"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4272"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",-41.28991152777778],PARAMETER["central_meridian",172.1090281944444],PARAMETER["scale_factor",1],PARAMETER["false_easting",300000],PARAMETER["false_northing",700000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","27216"]]', wk='EPSG:27216', name='NZGD49 / Karamea Circuit', attrs=(('PROJECTION','Transverse_Mercator'), ('SPHEROID', 'International 1924'))), + ) + +bad_srlist = ('Foobar', 'OOJCS["NAD83 / Texas South Central",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",30.28333333333333],PARAMETER["standard_parallel_2",28.38333333333333],PARAMETER["latitude_of_origin",27.83333333333333],PARAMETER["central_meridian",-99],PARAMETER["false_easting",600000],PARAMETER["false_northing",4000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","32140"]]',) + + +@skipUnless(HAS_GDAL, "GDAL is required") +class SpatialRefTest(unittest.TestCase): + + def test01_wkt(self): + "Testing initialization on valid OGC WKT." + for s in srlist: + srs = SpatialReference(s.wkt) + + def test02_bad_wkt(self): + "Testing initialization on invalid WKT." + for bad in bad_srlist: + try: + srs = SpatialReference(bad) + srs.validate() + except (SRSException, OGRException): + pass + else: + self.fail('Should not have initialized on bad WKT "%s"!') + + def test03_get_wkt(self): + "Testing getting the WKT." + for s in srlist: + srs = SpatialReference(s.wkt) + self.assertEqual(s.wkt, srs.wkt) + + def test04_proj(self): + "Test PROJ.4 import and export." + for s in srlist: + if s.proj: + srs1 = SpatialReference(s.wkt) + srs2 = SpatialReference(s.proj) + self.assertEqual(srs1.proj, srs2.proj) + + def test05_epsg(self): + "Test EPSG import." + for s in srlist: + if s.epsg: + srs1 = SpatialReference(s.wkt) + srs2 = SpatialReference(s.epsg) + srs3 = SpatialReference(str(s.epsg)) + srs4 = SpatialReference('EPSG:%d' % s.epsg) + for srs in (srs1, srs2, srs3, srs4): + for attr, expected in s.attr: + self.assertEqual(expected, srs[attr]) + + def test07_boolean_props(self): + "Testing the boolean properties." + for s in srlist: + srs = SpatialReference(s.wkt) + self.assertEqual(s.projected, srs.projected) + self.assertEqual(s.geographic, srs.geographic) + + def test08_angular_linear(self): + "Testing the linear and angular units routines." + for s in srlist: + srs = SpatialReference(s.wkt) + self.assertEqual(s.ang_name, srs.angular_name) + self.assertEqual(s.lin_name, srs.linear_name) + self.assertAlmostEqual(s.ang_units, srs.angular_units, 9) + self.assertAlmostEqual(s.lin_units, srs.linear_units, 9) + + def test09_authority(self): + "Testing the authority name & code routines." + for s in srlist: + if hasattr(s, 'auth'): + srs = SpatialReference(s.wkt) + for target, tup in s.auth.items(): + self.assertEqual(tup[0], srs.auth_name(target)) + self.assertEqual(tup[1], srs.auth_code(target)) + + def test10_attributes(self): + "Testing the attribute retrieval routines." + for s in srlist: + srs = SpatialReference(s.wkt) + for tup in s.attr: + att = tup[0] # Attribute to test + exp = tup[1] # Expected result + self.assertEqual(exp, srs[att]) + + def test11_wellknown(self): + "Testing Well Known Names of Spatial References." + for s in well_known: + srs = SpatialReference(s.wk) + self.assertEqual(s.name, srs.name) + for tup in s.attrs: + if len(tup) == 2: + key = tup[0] + exp = tup[1] + elif len(tup) == 3: + key = tup[:2] + exp = tup[2] + self.assertEqual(srs[key], exp) + + def test12_coordtransform(self): + "Testing initialization of a CoordTransform." + target = SpatialReference('WGS84') + for s in srlist: + if s.proj: + ct = CoordTransform(SpatialReference(s.wkt), target) + + def test13_attr_value(self): + "Testing the attr_value() method." + s1 = SpatialReference('WGS84') + self.assertRaises(TypeError, s1.__getitem__, 0) + self.assertRaises(TypeError, s1.__getitem__, ('GEOGCS', 'foo')) + self.assertEqual('WGS 84', s1['GEOGCS']) + self.assertEqual('WGS_1984', s1['DATUM']) + self.assertEqual('EPSG', s1['AUTHORITY']) + self.assertEqual(4326, int(s1['AUTHORITY', 1])) + self.assertEqual(None, s1['FOOBAR']) |