From f1ada99997de72fdd3f765ffa11fa4d1d9f438f9 Mon Sep 17 00:00:00 2001 From: Justin Bronn Date: Sat, 7 Mar 2009 23:02:48 +0000 Subject: Maintenance refactor of the GDAL (OGR) ctypes interface. Changes include: * All C API method explictly called from their prototype module, no longer imported via *. * Applied DRY to C pointer management, classes that do so subclass from `GDALBase`. * `OGRGeometry`: Added `from_bbox` class method (patch from Christopher Schmidt) and `kml` property. * `SpatialReference`: Now initialize with `SetFromUserInput` (initialization is now more simple and flexible); removed duplicate methods. * `Envelope`: Added `expand_to_include` method and now allow same coordinates for lower left and upper right points. Thanks to Paul Smith for tickets and patches. * `OGRGeomType`: Now treat OGC 'Geometry' type as 'Unknown'. Fixed #9855, #10368, #10380. Refs #9806. git-svn-id: http://code.djangoproject.com/svn/django/trunk@9985 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/contrib/gis/gdal/LICENSE | 2 +- django/contrib/gis/gdal/base.py | 35 +++ django/contrib/gis/gdal/datasource.py | 40 +-- django/contrib/gis/gdal/driver.py | 19 +- django/contrib/gis/gdal/envelope.py | 55 +++- django/contrib/gis/gdal/error.py | 1 + django/contrib/gis/gdal/feature.py | 35 +-- django/contrib/gis/gdal/field.py | 41 ++- django/contrib/gis/gdal/geometries.py | 174 ++++++----- django/contrib/gis/gdal/geomtype.py | 11 +- django/contrib/gis/gdal/layer.py | 47 ++- django/contrib/gis/gdal/prototypes/geom.py | 2 + django/contrib/gis/gdal/prototypes/srs.py | 1 + django/contrib/gis/gdal/srs.py | 159 ++++------ django/contrib/gis/gdal/tests/__init__.py | 25 ++ django/contrib/gis/gdal/tests/test_driver.py | 40 +++ django/contrib/gis/gdal/tests/test_ds.py | 201 ++++++++++++ django/contrib/gis/gdal/tests/test_envelope.py | 94 ++++++ django/contrib/gis/gdal/tests/test_geom.py | 415 +++++++++++++++++++++++++ django/contrib/gis/gdal/tests/test_srs.py | 169 ++++++++++ django/contrib/gis/tests/__init__.py | 87 ++++-- django/contrib/gis/tests/test_gdal.py | 28 -- django/contrib/gis/tests/test_gdal_driver.py | 40 --- django/contrib/gis/tests/test_gdal_ds.py | 198 ------------ django/contrib/gis/tests/test_gdal_envelope.py | 45 --- django/contrib/gis/tests/test_gdal_geom.py | 404 ------------------------ django/contrib/gis/tests/test_gdal_srs.py | 169 ---------- 27 files changed, 1340 insertions(+), 1197 deletions(-) create mode 100644 django/contrib/gis/gdal/base.py create mode 100644 django/contrib/gis/gdal/tests/__init__.py create mode 100644 django/contrib/gis/gdal/tests/test_driver.py create mode 100644 django/contrib/gis/gdal/tests/test_ds.py create mode 100644 django/contrib/gis/gdal/tests/test_envelope.py create mode 100644 django/contrib/gis/gdal/tests/test_geom.py create mode 100644 django/contrib/gis/gdal/tests/test_srs.py delete mode 100644 django/contrib/gis/tests/test_gdal.py delete mode 100644 django/contrib/gis/tests/test_gdal_driver.py delete mode 100644 django/contrib/gis/tests/test_gdal_ds.py delete mode 100644 django/contrib/gis/tests/test_gdal_envelope.py delete mode 100644 django/contrib/gis/tests/test_gdal_geom.py delete mode 100644 django/contrib/gis/tests/test_gdal_srs.py diff --git a/django/contrib/gis/gdal/LICENSE b/django/contrib/gis/gdal/LICENSE index 4bd0bed44b..30d410ecfe 100644 --- a/django/contrib/gis/gdal/LICENSE +++ b/django/contrib/gis/gdal/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2007, Justin Bronn +Copyright (c) 2007-2009, Justin Bronn All rights reserved. Redistribution and use in source and binary forms, with or without modification, diff --git a/django/contrib/gis/gdal/base.py b/django/contrib/gis/gdal/base.py new file mode 100644 index 0000000000..e19b748e6f --- /dev/null +++ b/django/contrib/gis/gdal/base.py @@ -0,0 +1,35 @@ +from ctypes import c_void_p +from types import NoneType +from django.contrib.gis.gdal.error import GDALException + +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, int): + self._ptr = self.ptr_type(ptr) + elif isinstance(ptr, (self.ptr_type, NoneType)): + self._ptr = ptr + else: + raise TypeError('Incompatible pointer type') + + ptr = property(_get_ptr, _set_ptr) + diff --git a/django/contrib/gis/gdal/datasource.py b/django/contrib/gis/gdal/datasource.py index 1eace25bef..7db5fd9ce3 100644 --- a/django/contrib/gis/gdal/datasource.py +++ b/django/contrib/gis/gdal/datasource.py @@ -37,28 +37,23 @@ from ctypes import byref, c_void_p # 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.ds import \ - destroy_ds, get_driver_count, register_all, open_ds, release_ds, \ - get_ds_name, get_layer, get_layer_count, get_layer_by_name +from django.contrib.gis.gdal.prototypes import ds as capi # For more information, see the OGR C API source code: # http://www.gdal.org/ogr/ogr__api_8h.html # # The OGR_DS_* routines are relevant here. -class DataSource(object): +class DataSource(GDALBase): "Wraps an OGR Data Source object." #### Python 'magic' routines #### def __init__(self, ds_input, ds_driver=False, write=False): - - # DataSource pointer is initially NULL. - self._ptr = None - # The write flag. if write: self._write = 1 @@ -67,33 +62,34 @@ class DataSource(object): # Registering all the drivers, this needs to be done # _before_ we try to open up a data source. - if not get_driver_count(): register_all() + if not capi.get_driver_count(): + capi.register_all() if isinstance(ds_input, basestring): # The data source driver is a void pointer. - ds_driver = c_void_p() + ds_driver = Driver.ptr_type() try: # OGROpen will auto-detect the data source type. - ds = open_ds(ds_input, self._write, byref(ds_driver)) + ds = capi.open_ds(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, c_void_p) and isinstance(ds_driver, c_void_p): + 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) + 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: destroy_ds(self._ptr) + if self._ptr: capi.destroy_ds(self._ptr) def __iter__(self): "Allows for iteration over the layers in a data source." @@ -103,12 +99,12 @@ class DataSource(object): def __getitem__(self, index): "Allows use of the index [] operator to get a layer at the index." if isinstance(index, basestring): - l = get_layer_by_name(self._ptr, index) + l = capi.get_layer_by_name(self.ptr, 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 = get_layer(self._ptr, index) + l = capi.get_layer(self._ptr, index) else: raise TypeError('Invalid index type: %s' % type(index)) return Layer(l, self) @@ -121,18 +117,12 @@ class DataSource(object): "Returns OGR GetName and Driver for the Data Source." return '%s (%s)' % (self.name, str(self.driver)) - #### DataSource Properties #### - @property - def driver(self): - "Returns the Driver object for this Data Source." - return self._driver - @property def layer_count(self): "Returns the number of layers in the data source." - return get_layer_count(self._ptr) + return capi.get_layer_count(self._ptr) @property def name(self): "Returns the name of the data source." - return get_ds_name(self._ptr) + return capi.get_ds_name(self._ptr) diff --git a/django/contrib/gis/gdal/driver.py b/django/contrib/gis/gdal/driver.py index 2d5b3df807..1753db2b2b 100644 --- a/django/contrib/gis/gdal/driver.py +++ b/django/contrib/gis/gdal/driver.py @@ -1,14 +1,14 @@ # 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.ds import \ - get_driver, get_driver_by_name, get_driver_count, get_driver_name, register_all +from django.contrib.gis.gdal.prototypes import ds as capi # For more information, see the OGR C API source code: # http://www.gdal.org/ogr/ogr__api_8h.html # # The OGR_Dr_* routines are relevant here. -class Driver(object): +class Driver(GDALBase): "Wraps an OGR Data Source Driver." # Case-insensitive aliases for OGR Drivers. @@ -24,7 +24,6 @@ class Driver(object): if isinstance(dr_input, basestring): # If a string name of the driver was passed in - self._ptr = None # Initially NULL self._register() # Checking the alias dictionary (case-insensitive) to see if an alias @@ -35,10 +34,10 @@ class Driver(object): name = dr_input # Attempting to get the OGR driver by the string name. - dr = get_driver_by_name(name) + dr = capi.get_driver_by_name(name) elif isinstance(dr_input, int): self._register() - dr = get_driver(dr_input) + dr = capi.get_driver(dr_input) elif isinstance(dr_input, c_void_p): dr = dr_input else: @@ -47,20 +46,20 @@ class Driver(object): # 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 + self.ptr = dr def __str__(self): "Returns the string name of the OGR Driver." - return get_driver_name(self._ptr) + 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: register_all() + 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 get_driver_count() + return capi.get_driver_count() diff --git a/django/contrib/gis/gdal/envelope.py b/django/contrib/gis/gdal/envelope.py index 971f06da90..0e6aa0ece6 100644 --- a/django/contrib/gis/gdal/envelope.py +++ b/django/contrib/gis/gdal/envelope.py @@ -11,7 +11,6 @@ Lower left (min_x, min_y) o----------+ """ from ctypes import Structure, c_double -from types import TupleType, ListType from django.contrib.gis.gdal.error import OGRException # The OGR definition of an Envelope is a C structure containing four doubles. @@ -42,7 +41,7 @@ class Envelope(object): if isinstance(args[0], OGREnvelope): # OGREnvelope (a ctypes Structure) was passed in. self._envelope = args[0] - elif isinstance(args[0], (TupleType, ListType)): + 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])) @@ -58,10 +57,10 @@ class Envelope(object): 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.') + 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): """ @@ -71,7 +70,7 @@ class Envelope(object): if isinstance(other, Envelope): return (self.min_x == other.min_x) and (self.min_y == other.min_y) and \ (self.max_x == other.max_x) and (self.max_y == other.max_y) - elif isinstance(other, TupleType) and len(other) == 4: + 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: @@ -89,6 +88,48 @@ class Envelope(object): 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: + # Individiual 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." diff --git a/django/contrib/gis/gdal/error.py b/django/contrib/gis/gdal/error.py index 4ec3f7e735..58ca891672 100644 --- a/django/contrib/gis/gdal/error.py +++ b/django/contrib/gis/gdal/error.py @@ -4,6 +4,7 @@ OGR methods. """ #### OGR & SRS Exceptions #### +class GDALException(Exception): pass class OGRException(Exception): pass class SRSException(Exception): pass class OGRIndexError(OGRException, KeyError): diff --git a/django/contrib/gis/gdal/feature.py b/django/contrib/gis/gdal/feature.py index bc3857f606..b5c173a240 100644 --- a/django/contrib/gis/gdal/feature.py +++ b/django/contrib/gis/gdal/feature.py @@ -1,36 +1,31 @@ # 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 from django.contrib.gis.gdal.srs import SpatialReference # ctypes function prototypes -from django.contrib.gis.gdal.prototypes.ds import \ - destroy_feature, feature_equal, get_fd_geom_type, get_feat_geom_ref, \ - get_feat_name, get_feat_field_count, get_fid, get_field_defn, \ - get_field_index, get_field_name -from django.contrib.gis.gdal.prototypes.geom import clone_geom, get_geom_srs -from django.contrib.gis.gdal.prototypes.srs import clone_srs +from django.contrib.gis.gdal.prototypes import ds as capi, geom as geom_api # For more information, see the OGR C API source code: # http://www.gdal.org/ogr/ogr__api_8h.html # # The OGR_F_* routines are relevant here. -class Feature(object): +class Feature(GDALBase): "A class that wraps an OGR Feature, needs to be instantiated from a Layer object." #### Python 'magic' routines #### def __init__(self, feat, fdefn): "Initializes on the pointers for the feature and the layer definition." - self._ptr = None # Initially NULL if not feat or not fdefn: raise OGRException('Cannot create OGR Feature, invalid pointer given.') - self._ptr = feat + self.ptr = feat self._fdefn = fdefn def __del__(self): "Releases a reference to this object." - if self._ptr: destroy_feature(self._ptr) + if self._ptr: capi.destroy_feature(self._ptr) def __getitem__(self, index): """ @@ -45,7 +40,7 @@ class Feature(object): if index < 0 or index > self.num_fields: raise OGRIndexError('index out of range') i = index - return Field(self._ptr, i) + return Field(self.ptr, i) def __iter__(self): "Iterates over each field in the Feature." @@ -62,41 +57,41 @@ class Feature(object): def __eq__(self, other): "Does equivalence testing on the features." - return bool(feature_equal(self._ptr, other._ptr)) + return bool(capi.feature_equal(self.ptr, other._ptr)) #### Feature Properties #### @property def fid(self): "Returns the feature identifier." - return get_fid(self._ptr) + return capi.get_fid(self.ptr) @property def layer_name(self): "Returns the name of the layer for the feature." - return get_feat_name(self._fdefn) + return capi.get_feat_name(self._fdefn) @property def num_fields(self): "Returns the number of fields in the Feature." - return get_feat_field_count(self._ptr) + return capi.get_feat_field_count(self.ptr) @property def fields(self): "Returns a list of fields in the Feature." - return [get_field_name(get_field_defn(self._fdefn, i)) + return [capi.get_field_name(capi.get_field_defn(self._fdefn, 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 = get_feat_geom_ref(self._ptr) - return OGRGeometry(clone_geom(geom_ptr)) + 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(get_fd_geom_type(self._fdefn)) + return OGRGeomType(capi.get_fd_geom_type(self._fdefn)) #### Feature Methods #### def get(self, field): @@ -110,6 +105,6 @@ class Feature(object): def index(self, field_name): "Returns the index of the given field name." - i = get_field_index(self._ptr, field_name) + i = capi.get_field_index(self.ptr, field_name) if i < 0: raise OGRIndexError('invalid OFT field name given: "%s"' % field_name) return i diff --git a/django/contrib/gis/gdal/field.py b/django/contrib/gis/gdal/field.py index 6374bc7d80..46dbc869b7 100644 --- a/django/contrib/gis/gdal/field.py +++ b/django/contrib/gis/gdal/field.py @@ -1,16 +1,14 @@ 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.ds import \ - get_feat_field_defn, get_field_as_datetime, get_field_as_double, \ - get_field_as_integer, get_field_as_string, get_field_name, get_field_precision, \ - get_field_type, get_field_type_name, get_field_width +from django.contrib.gis.gdal.prototypes import ds as capi # For more information, see the OGR C API source code: # http://www.gdal.org/ogr/ogr__api_8h.html # # The OGR_Fld_* routines are relevant here. -class Field(object): +class Field(GDALBase): "A class that wraps an OGR Field, needs to be instantiated from a Feature object." #### Python 'magic' routines #### @@ -24,13 +22,13 @@ class Field(object): self._index = index # Getting the pointer for this field. - fld = get_feat_field_defn(feat, index) - if not fld: + fld_ptr = capi.get_feat_field_defn(feat, index) + if not fld_ptr: raise OGRException('Cannot create OGR Field, invalid pointer given.') - self._ptr = fld + self.ptr = fld_ptr # Setting the class depending upon the OGR Field Type (OFT) - self.__class__ = FIELD_CLASSES[self.type] + self.__class__ = OGRFieldTypes[self.type] # OFTReal with no precision should be an OFTInteger. if isinstance(self, OFTReal) and self.precision == 0: @@ -43,21 +41,21 @@ class Field(object): #### Field Methods #### def as_double(self): "Retrieves the Field's value as a double (float)." - return get_field_as_double(self._feat, self._index) + return capi.get_field_as_double(self._feat, self._index) def as_int(self): "Retrieves the Field's value as an integer." - return get_field_as_integer(self._feat, self._index) + return capi.get_field_as_integer(self._feat, self._index) def as_string(self): "Retrieves the Field's value as a string." - return get_field_as_string(self._feat, self._index) + return capi.get_field_as_string(self._feat, self._index) def as_datetime(self): "Retrieves the Field's value as a tuple of date & time components." yy, mm, dd, hh, mn, ss, tz = [c_int() for i in range(7)] - status = get_field_as_datetime(self._feat, self._index, byref(yy), byref(mm), byref(dd), - byref(hh), byref(mn), byref(ss), byref(tz)) + status = capi.get_field_as_datetime(self._feat, self._index, byref(yy), byref(mm), byref(dd), + byref(hh), byref(mn), byref(ss), byref(tz)) if status: return (yy, mm, dd, hh, mn, ss, tz) else: @@ -67,22 +65,22 @@ class Field(object): @property def name(self): "Returns the name of this Field." - return get_field_name(self._ptr) + return capi.get_field_name(self.ptr) @property def precision(self): "Returns the precision of this Field." - return get_field_precision(self._ptr) + return capi.get_field_precision(self.ptr) @property def type(self): "Returns the OGR type of this Field." - return get_field_type(self._ptr) + return capi.get_field_type(self.ptr) @property def type_name(self): "Return the OGR field type name for this Field." - return get_field_type_name(self.type) + return capi.get_field_type_name(self.type) @property def value(self): @@ -93,7 +91,7 @@ class Field(object): @property def width(self): "Returns the width of this Field." - return get_field_width(self._ptr) + return capi.get_field_width(self.ptr) ### The Field sub-classes for each OGR Field type. ### class OFTInteger(Field): @@ -163,8 +161,8 @@ class OFTRealList(Field): pass class OFTStringList(Field): pass class OFTWideStringList(Field): pass -# Class mapping dictionary for OFT Types -FIELD_CLASSES = { 0 : OFTInteger, +# Class mapping dictionary for OFT Types and reverse mapping. +OGRFieldTypes = { 0 : OFTInteger, 1 : OFTIntegerList, 2 : OFTReal, 3 : OFTRealList, @@ -177,3 +175,4 @@ FIELD_CLASSES = { 0 : OFTInteger, 10 : OFTTime, 11 : OFTDateTime, } +ROGRFieldTypes = dict([(cls, num) for num, cls in OGRFieldTypes.items()]) diff --git a/django/contrib/gis/gdal/geometries.py b/django/contrib/gis/gdal/geometries.py index a880a75d13..05b824b95d 100644 --- a/django/contrib/gis/gdal/geometries.py +++ b/django/contrib/gis/gdal/geometries.py @@ -44,14 +44,15 @@ from binascii import a2b_hex from ctypes import byref, string_at, c_char_p, c_double, c_ubyte, c_void_p # 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.srs import SpatialReference, CoordTransform # Getting the ctypes prototype functions that interface w/the GDAL C library. -from django.contrib.gis.gdal.prototypes.geom import * -from django.contrib.gis.gdal.prototypes.srs import clone_srs +from django.contrib.gis.gdal.prototypes import geom as capi, srs as srs_api +GEOJSON = capi.GEOJSON # For more information, see the OGR C API source code: # http://www.gdal.org/ogr/ogr__api_8h.html @@ -64,13 +65,12 @@ wkt_regex = re.compile(r'^(?PPOINT|LINESTRING|LINEARRING|POLYGON|MULTIPOIN json_regex = re.compile(r'^(\s+)?\{[\s\w,\[\]\{\}\-\."\':]+\}(\s+)?$') #### OGRGeometry Class #### -class OGRGeometry(object): +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." - self._ptr = c_void_p(None) # Initially NULL str_instance = isinstance(geom_input, basestring) # If HEX, unpack input to to a binary buffer. @@ -91,27 +91,27 @@ class OGRGeometry(object): 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 = create_geom(OGRGeomType(wkt_m.group('type')).num) - import_wkt(g, byref(c_char_p(geom_input))) + g = capi.create_geom(OGRGeomType(wkt_m.group('type')).num) + capi.import_wkt(g, byref(c_char_p(geom_input))) else: - g = from_wkt(byref(c_char_p(geom_input)), None, byref(c_void_p())) + g = capi.from_wkt(byref(c_char_p(geom_input)), None, byref(c_void_p())) elif json_m: if GEOJSON: - g = from_json(geom_input) + g = capi.from_json(geom_input) else: raise NotImplementedError('GeoJSON input only supported on GDAL 1.5+.') else: # Seeing if the input is a valid short-hand string # (e.g., 'Point', 'POLYGON'). ogr_t = OGRGeomType(geom_input) - g = create_geom(OGRGeomType(geom_input).num) + g = capi.create_geom(OGRGeomType(geom_input).num) elif isinstance(geom_input, buffer): # WKB was passed in - g = from_wkb(str(geom_input), None, byref(c_void_p()), len(geom_input)) + g = capi.from_wkb(str(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 = create_geom(geom_input.num) - elif isinstance(geom_input, c_void_p): + 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: @@ -121,7 +121,7 @@ class OGRGeometry(object): # 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 + self.ptr = g # Assigning the SpatialReference object to the geometry, if valid. if bool(srs): self.srs = srs @@ -129,9 +129,16 @@ class OGRGeometry(object): # Setting the class depending upon the OGR Geometry Type self.__class__ = GEO_CLASSES[self.geom_type.num] + @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) ) + def __del__(self): "Deletes this Geometry." - if self._ptr: destroy_geom(self._ptr) + if self._ptr: capi.destroy_geom(self._ptr) ### Geometry set-like operations ### # g = g1 | g2 @@ -170,22 +177,22 @@ class OGRGeometry(object): @property def dimension(self): "Returns 0 for points, 1 for lines, and 2 for surfaces." - return get_dims(self._ptr) + return capi.get_dims(self.ptr) @property def coord_dim(self): "Returns the coordinate dimension of the Geometry." - return get_coord_dims(self._ptr) + return capi.get_coord_dims(self.ptr) @property def geom_count(self): "The number of elements in this Geometry." - return get_geom_count(self._ptr) + return capi.get_geom_count(self.ptr) @property def point_count(self): "Returns the number of Points in this Geometry." - return get_point_count(self._ptr) + return capi.get_point_count(self.ptr) @property def num_points(self): @@ -201,28 +208,28 @@ class OGRGeometry(object): def geom_type(self): "Returns the Type for this Geometry." try: - return OGRGeomType(get_geom_type(self._ptr)) + return OGRGeomType(capi.get_geom_type(self.ptr)) except OGRException: # VRT datasources return an invalid geometry type # number, but a valid name -- we'll try that instead. # See: http://trac.osgeo.org/gdal/ticket/2491 - return OGRGeomType(get_geom_name(self._ptr)) + return OGRGeomType(capi.get_geom_name(self.ptr)) @property def geom_name(self): "Returns the Name of this Geometry." - return get_geom_name(self._ptr) + return capi.get_geom_name(self.ptr) @property def area(self): "Returns the area for a LinearRing, Polygon, or MultiPolygon; 0 otherwise." - return get_area(self._ptr) + return capi.get_area(self.ptr) @property def envelope(self): "Returns the envelope for this Geometry." # TODO: Fix Envelope() for Point geometries. - return Envelope(get_envelope(self._ptr, byref(OGREnvelope()))) + return Envelope(capi.get_envelope(self.ptr, byref(OGREnvelope()))) @property def extent(self): @@ -232,39 +239,40 @@ class OGRGeometry(object): #### SpatialReference-related Properties #### # The SRS property - def get_srs(self): + def _get_srs(self): "Returns the Spatial Reference for this Geometry." try: - srs_ptr = get_geom_srs(self._ptr) - return SpatialReference(clone_srs(srs_ptr)) + 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): + def _set_srs(self, srs): "Sets the SpatialReference for this geometry." if isinstance(srs, SpatialReference): - srs_ptr = clone_srs(srs._ptr) + srs_ptr = srs_api.clone_srs(srs.ptr) elif isinstance(srs, (int, long, basestring)): sr = SpatialReference(srs) - srs_ptr = clone_srs(sr._ptr) + srs_ptr = srs_api.clone_srs(sr.ptr) else: raise TypeError('Cannot assign spatial reference with object of type: %s' % type(srs)) - assign_srs(self._ptr, srs_ptr) + capi.assign_srs(self.ptr, srs_ptr) - srs = property(get_srs, set_srs) + srs = property(_get_srs, _set_srs) # The SRID property - def get_srid(self): - if self.srs: return self.srs.srid - else: return None + def _get_srid(self): + srs = self.srs + if srs: return srs.srid + return None - def set_srid(self, srid): + def _set_srid(self, srid): if isinstance(srid, (int, long)): self.srs = srid else: raise TypeError('SRID must be set with an integer.') - srid = property(get_srid, set_srid) + srid = property(_get_srid, _set_srid) #### Output Methods #### @property @@ -276,7 +284,7 @@ class OGRGeometry(object): @property def gml(self): "Returns the GML representation of the Geometry." - return to_gml(self._ptr) + return capi.to_gml(self.ptr) @property def hex(self): @@ -286,16 +294,28 @@ class OGRGeometry(object): @property def json(self): + """ + Returns the GeoJSON representation of this Geometry (requires + GDAL 1.5+). + """ if GEOJSON: - return to_json(self._ptr) + return capi.to_json(self.ptr) else: raise NotImplementedError('GeoJSON output only supported on GDAL 1.5+.') geojson = json + @property + def kml(self): + "Returns the KML representation of the Geometry." + if GEOJSON: + return capi.to_kml(self.ptr, None) + else: + raise NotImplementedError('KML output only supported on GDAL 1.5+.') + @property def wkb_size(self): "Returns the size of the WKB buffer." - return get_wkbsize(self._ptr) + return capi.get_wkbsize(self.ptr) @property def wkb(self): @@ -307,19 +327,19 @@ class OGRGeometry(object): sz = self.wkb_size # Creating the unsigned character buffer, and passing it in by reference. buf = (c_ubyte * sz)() - wkb = to_wkb(self._ptr, byteorder, byref(buf)) + wkb = capi.to_wkb(self.ptr, byteorder, byref(buf)) # Returning a buffer of the string at the pointer. return buffer(string_at(buf, sz)) @property def wkt(self): "Returns the WKT representation of the Geometry." - return to_wkt(self._ptr, byref(c_char_p())) + return capi.to_wkt(self.ptr, byref(c_char_p())) #### Geometry Methods #### def clone(self): "Clones this OGR Geometry." - return OGRGeometry(clone_geom(self._ptr), self.srs) + return OGRGeometry(capi.clone_geom(self.ptr), self.srs) def close_rings(self): """ @@ -328,7 +348,7 @@ class OGRGeometry(object): end. """ # Closing the open rings. - geom_close_rings(self._ptr) + capi.geom_close_rings(self.ptr) def transform(self, coord_trans, clone=False): """ @@ -344,12 +364,12 @@ class OGRGeometry(object): klone.transform(coord_trans) return klone if isinstance(coord_trans, CoordTransform): - geom_transform(self._ptr, coord_trans._ptr) + capi.geom_transform(self.ptr, coord_trans.ptr) elif isinstance(coord_trans, SpatialReference): - geom_transform_to(self._ptr, coord_trans._ptr) + capi.geom_transform_to(self.ptr, coord_trans.ptr) elif isinstance(coord_trans, (int, long, basestring)): sr = SpatialReference(coord_trans) - geom_transform_to(self._ptr, sr._ptr) + capi.geom_transform_to(self.ptr, sr.ptr) else: raise TypeError('Transform only accepts CoordTransform, SpatialReference, string, and integer objects.') @@ -366,52 +386,52 @@ class OGRGeometry(object): # Returning the output of the given function with the other geometry's # pointer. - return func(self._ptr, other._ptr) + return func(self.ptr, other.ptr) def intersects(self, other): "Returns True if this geometry intersects with the other." - return self._topology(ogr_intersects, 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(ogr_equals, 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(ogr_disjoint, other) + return self._topology(capi.ogr_disjoint, other) def touches(self, other): "Returns True if this geometry touches the other." - return self._topology(ogr_touches, other) + return self._topology(capi.ogr_touches, other) def crosses(self, other): "Returns True if this geometry crosses the other." - return self._topology(ogr_crosses, other) + return self._topology(capi.ogr_crosses, other) def within(self, other): "Returns True if this geometry is within the other." - return self._topology(ogr_within, other) + return self._topology(capi.ogr_within, other) def contains(self, other): "Returns True if this geometry contains the other." - return self._topology(ogr_contains, other) + return self._topology(capi.ogr_contains, other) def overlaps(self, other): "Returns True if this geometry overlaps the other." - return self._topology(ogr_overlaps, 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) + return OGRGeometry(gen_func(self.ptr, other.ptr), self.srs) else: - return OGRGeometry(gen_func(self._ptr), self.srs) + return OGRGeometry(gen_func(self.ptr), self.srs) @property def boundary(self): "Returns the boundary of this geometry." - return self._geomgen(get_boundary) + return self._geomgen(capi.get_boundary) @property def convex_hull(self): @@ -419,35 +439,35 @@ class OGRGeometry(object): Returns the smallest convex Polygon that contains all the points in this Geometry. """ - return self._geomgen(geom_convex_hull) + 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(geom_diff, 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(geom_intersection, 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(geom_sym_diff, 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(geom_union, other) + return self._geomgen(capi.geom_union, other) # The subclasses for OGR Geometry. class Point(OGRGeometry): @@ -455,18 +475,18 @@ class Point(OGRGeometry): @property def x(self): "Returns the X coordinate for this Point." - return getx(self._ptr, 0) + return capi.getx(self.ptr, 0) @property def y(self): "Returns the Y coordinate for this Point." - return gety(self._ptr, 0) + return capi.gety(self.ptr, 0) @property def z(self): "Returns the Z coordinate for this Point." if self.coord_dim == 3: - return getz(self._ptr, 0) + return capi.getz(self.ptr, 0) @property def tuple(self): @@ -483,7 +503,7 @@ class LineString(OGRGeometry): "Returns the Point at the given index." if index >= 0 and index < self.point_count: x, y, z = c_double(), c_double(), c_double() - get_point(self._ptr, index, byref(x), byref(y), byref(z)) + capi.get_point(self.ptr, index, byref(x), byref(y), byref(z)) dim = self.coord_dim if dim == 1: return (x.value,) @@ -514,23 +534,23 @@ class LineString(OGRGeometry): Internal routine that returns a sequence (list) corresponding with the given function. """ - return [func(self._ptr, i) for i in xrange(len(self))] + 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(getx) + return self._listarr(capi.getx) @property def y(self): "Returns the Y coordinates in a list." - return self._listarr(gety) + return self._listarr(capi.gety) @property def z(self): "Returns the Z coordinates in a list." if self.coord_dim == 3: - return self._listarr(getz) + return self._listarr(capi.getz) # LinearRings are used in Polygons. class LinearRing(LineString): pass @@ -551,7 +571,7 @@ class Polygon(OGRGeometry): if index < 0 or index >= self.geom_count: raise OGRIndexError('index out of range: %s' % index) else: - return OGRGeometry(clone_geom(get_geom_ref(self._ptr, index)), self.srs) + return OGRGeometry(capi.clone_geom(capi.get_geom_ref(self.ptr, index)), self.srs) # Polygon Properties @property @@ -577,7 +597,7 @@ class Polygon(OGRGeometry): "Returns the centroid (a Point) of this Polygon." # The centroid is a Point, create a geometry for this. p = OGRGeometry(OGRGeomType('Point')) - get_centroid(self._ptr, p._ptr) + capi.get_centroid(self.ptr, p.ptr) return p # Geometry Collection base class. @@ -589,7 +609,7 @@ class GeometryCollection(OGRGeometry): if index < 0 or index >= self.geom_count: raise OGRIndexError('index out of range: %s' % index) else: - return OGRGeometry(clone_geom(get_geom_ref(self._ptr, index)), self.srs) + return OGRGeometry(capi.clone_geom(capi.get_geom_ref(self.ptr, index)), self.srs) def __iter__(self): "Iterates over each Geometry." @@ -604,12 +624,12 @@ class GeometryCollection(OGRGeometry): "Add the geometry to this Geometry Collection." if isinstance(geom, OGRGeometry): if isinstance(geom, self.__class__): - for g in geom: add_geom(self._ptr, g._ptr) + for g in geom: capi.add_geom(self.ptr, g.ptr) else: - add_geom(self._ptr, geom._ptr) + capi.add_geom(self.ptr, geom.ptr) elif isinstance(geom, basestring): tmp = OGRGeometry(geom) - add_geom(self._ptr, tmp._ptr) + capi.add_geom(self.ptr, tmp.ptr) else: raise OGRException('Must add an OGRGeometry.') diff --git a/django/contrib/gis/gdal/geomtype.py b/django/contrib/gis/gdal/geomtype.py index 565326f5a8..b3309531c0 100644 --- a/django/contrib/gis/gdal/geomtype.py +++ b/django/contrib/gis/gdal/geomtype.py @@ -24,7 +24,9 @@ class OGRGeomType(object): if isinstance(type_input, OGRGeomType): num = type_input.num elif isinstance(type_input, basestring): - num = self._str_types.get(type_input.lower(), None) + 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): @@ -67,7 +69,8 @@ class OGRGeomType(object): def django(self): "Returns the Django GeometryField for this OGR Type." s = self.name - if s in ('Unknown', 'LinearRing', 'None'): + if s in ('LinearRing', 'None'): return None - else: - return s + 'Field' + elif s == 'Unknown': + s = 'Geometry' + return s + 'Field' diff --git a/django/contrib/gis/gdal/layer.py b/django/contrib/gis/gdal/layer.py index 45e823dedc..cf5e57866e 100644 --- a/django/contrib/gis/gdal/layer.py +++ b/django/contrib/gis/gdal/layer.py @@ -2,26 +2,22 @@ from ctypes import 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 FIELD_CLASSES +from django.contrib.gis.gdal.field import OGRFieldTypes from django.contrib.gis.gdal.geometries import OGRGeomType from django.contrib.gis.gdal.srs import SpatialReference # GDAL ctypes function prototypes. -from django.contrib.gis.gdal.prototypes.ds import \ - get_extent, get_fd_geom_type, get_fd_name, get_feature, get_feature_count, \ - get_field_count, get_field_defn, get_field_name, get_field_precision, \ - get_field_width, get_field_type, get_layer_defn, get_layer_srs, \ - get_next_feature, reset_reading, test_capability -from django.contrib.gis.gdal.prototypes.srs import clone_srs +from django.contrib.gis.gdal.prototypes import ds as capi, srs as srs_api # 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(object): +class Layer(GDALBase): "A class that wraps an OGR Layer, needs to be instantiated from a DataSource object." #### Python 'magic' routines #### @@ -32,12 +28,11 @@ class Layer(object): reference to it is kept with this Layer. This prevents garbage collection of the `DataSource` while this Layer is still active. """ - self._ptr = None # Initially NULL if not layer_ptr: raise OGRException('Cannot create Layer, invalid pointer given') - self._ptr = layer_ptr + self.ptr = layer_ptr self._ds = ds - self._ldefn = get_layer_defn(self._ptr) + self._ldefn = capi.get_layer_defn(self._ptr) # Does the Layer support random reading? self._random_read = self.test_capability('RandomRead') @@ -59,9 +54,9 @@ class Layer(object): def __iter__(self): "Iterates over each Feature in the Layer." # ResetReading() must be called before iteration is to begin. - reset_reading(self._ptr) + capi.reset_reading(self._ptr) for i in xrange(self.num_feat): - yield Feature(get_next_feature(self._ptr), self._ldefn) + yield Feature(capi.get_next_feature(self._ptr), self._ldefn) def __len__(self): "The length is the number of features." @@ -81,7 +76,7 @@ class Layer(object): if self._random_read: # If the Layer supports random reading, return. try: - return Feature(get_feature(self._ptr, feat_id), self._ldefn) + return Feature(capi.get_feature(self.ptr, feat_id), self._ldefn) except OGRException: pass else: @@ -97,35 +92,35 @@ class Layer(object): def extent(self): "Returns the extent (an Envelope) of this layer." env = OGREnvelope() - get_extent(self._ptr, byref(env), 1) + 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." - return get_fd_name(self._ldefn) + return capi.get_fd_name(self._ldefn) @property def num_feat(self, force=1): "Returns the number of features in the Layer." - return get_feature_count(self._ptr, force) + return capi.get_feature_count(self.ptr, force) @property def num_fields(self): "Returns the number of fields in the Layer." - return get_field_count(self._ldefn) + return capi.get_field_count(self._ldefn) @property def geom_type(self): "Returns the geometry type (OGRGeomType) of the Layer." - return OGRGeomType(get_fd_geom_type(self._ldefn)) + return OGRGeomType(capi.get_fd_geom_type(self._ldefn)) @property def srs(self): "Returns the Spatial Reference used in this Layer." try: - ptr = get_layer_srs(self._ptr) - return SpatialReference(clone_srs(ptr)) + ptr = capi.get_layer_srs(self.ptr) + return SpatialReference(srs_api.clone_srs(ptr)) except SRSException: return None @@ -135,7 +130,7 @@ class Layer(object): Returns a list of string names corresponding to each of the Fields available in this Layer. """ - return [get_field_name(get_field_defn(self._ldefn, i)) + return [capi.get_field_name(capi.get_field_defn(self._ldefn, i)) for i in xrange(self.num_fields) ] @property @@ -146,19 +141,19 @@ class Layer(object): an OGR layer that had an integer, a floating-point, and string fields. """ - return [FIELD_CLASSES[get_field_type(get_field_defn(self._ldefn, i))] + 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 [get_field_width(get_field_defn(self._ldefn, i)) + 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 [get_field_precision(get_field_defn(self._ldefn, i)) + return [capi.get_field_precision(capi.get_field_defn(self._ldefn, i)) for i in xrange(self.num_fields)] #### Layer Methods #### @@ -190,4 +185,4 @@ class Layer(object): 'FastFeatureCount', 'FastGetExtent', 'CreateField', 'Transactions', 'DeleteFeature', and 'FastSetNextByIndex'. """ - return bool(test_capability(self._ptr, capability)) + return bool(capi.test_capability(self.ptr, capability)) diff --git a/django/contrib/gis/gdal/prototypes/geom.py b/django/contrib/gis/gdal/prototypes/geom.py index 7757f557ad..2898198d0d 100644 --- a/django/contrib/gis/gdal/prototypes/geom.py +++ b/django/contrib/gis/gdal/prototypes/geom.py @@ -38,9 +38,11 @@ def topology_func(f): if GEOJSON: 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) + to_kml = string_output(lgdal.OGR_G_ExportToKML, [c_void_p, c_char_p], str_result=True) else: from_json = False to_json = False + to_kml = False # GetX, GetY, GetZ all return doubles. getx = pnt_func(lgdal.OGR_G_GetX) diff --git a/django/contrib/gis/gdal/prototypes/srs.py b/django/contrib/gis/gdal/prototypes/srs.py index ff4d0add95..411cec903c 100644 --- a/django/contrib/gis/gdal/prototypes/srs.py +++ b/django/contrib/gis/gdal/prototypes/srs.py @@ -36,6 +36,7 @@ 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]) diff --git a/django/contrib/gis/gdal/srs.py b/django/contrib/gis/gdal/srs.py index d70e71ebc7..93bd8416ff 100644 --- a/django/contrib/gis/gdal/srs.py +++ b/django/contrib/gis/gdal/srs.py @@ -27,89 +27,74 @@ NAD83 / Texas South Central """ import re -from types import UnicodeType, TupleType from ctypes import byref, c_char_p, c_int, c_void_p # Getting the error checking routine and exceptions +from django.contrib.gis.gdal.base import GDALBase from django.contrib.gis.gdal.error import OGRException, SRSException -from django.contrib.gis.gdal.prototypes.srs import * +from django.contrib.gis.gdal.prototypes import srs as capi #### Spatial Reference class. #### -class SpatialReference(object): +class SpatialReference(GDALBase): """ A wrapper for the OGRSpatialReference object. According to the GDAL website, the SpatialReference object "provide[s] services to represent coordinate systems (projections and datums) and to transform between them." """ - # Well-Known Geographical Coordinate System Name - _well_known = {'WGS84':4326, 'WGS72':4322, 'NAD27':4267, 'NAD83':4269} - _epsg_regex = re.compile('^(EPSG:)?(?P\d+)$', re.I) - _proj_regex = re.compile(r'^\+proj') - #### Python 'magic' routines #### - def __init__(self, srs_input='', srs_type='wkt'): + 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'). """ - # Intializing pointer and string buffer. - self._ptr = None buf = c_char_p('') + srs_type = 'user' if isinstance(srs_input, basestring): # Encoding to ASCII if unicode passed in. - if isinstance(srs_input, UnicodeType): + if isinstance(srs_input, unicode): srs_input = srs_input.encode('ascii') - - epsg_m = self._epsg_regex.match(srs_input) - proj_m = self._proj_regex.match(srs_input) - if epsg_m: - # Is this an EPSG well known name? - srs_type = 'epsg' - srs_input = int(epsg_m.group('epsg')) - elif proj_m: - # Is the string a PROJ.4 string? - srs_type = 'proj' - elif srs_input in self._well_known: - # Is this a short-hand well known name? - srs_type = 'epsg' - srs_input = self._well_known[srs_input] - elif srs_type == 'proj': + 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 - else: - # Setting the buffer with WKT, PROJ.4 string, etc. - buf = c_char_p(srs_input) - elif isinstance(srs_input, int): + elif isinstance(srs_input, (int, long)): # EPSG integer code was input. - if srs_type != 'epsg': srs_type = 'epsg' - elif isinstance(srs_input, c_void_p): + 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': - # SRS input is OGR pointer + # Input is already an SRS pointer. srs = srs_input else: - # Creating a new pointer, using the string buffer. - srs = new_srs(buf) + # Creating a new SRS pointer, using the string buffer. + 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 + self.ptr = srs - # Post-processing if in PROJ.4 or EPSG formats. - if srs_type == 'proj': self.import_proj(srs_input) - elif srs_type == 'epsg': self.import_epsg(srs_input) + # 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: release_srs(self._ptr) + if self._ptr: capi.release_srs(self._ptr) def __getitem__(self, target): """ @@ -134,7 +119,7 @@ class SpatialReference(object): >>> print srs['UNIT|AUTHORITY', 1] # The authority value for the untis 9122 """ - if isinstance(target, TupleType): + if isinstance(target, tuple): return self.attr_value(*target) else: return self.attr_value(target) @@ -149,40 +134,40 @@ class SpatialReference(object): 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, str) or not isinstance(index, int): + if not isinstance(target, basestring) or not isinstance(index, int): raise TypeError - return get_attr_value(self._ptr, target, index) + return capi.get_attr_value(self.ptr, target, index) def auth_name(self, target): "Returns the authority name for the given string target node." - return get_auth_name(self._ptr, target) + return capi.get_auth_name(self.ptr, target) def auth_code(self, target): "Returns the authority code for the given string target node." - return get_auth_code(self._ptr, target) + return capi.get_auth_code(self.ptr, target) def clone(self): "Returns a clone of this SpatialReference object." - return SpatialReference(clone_srs(self._ptr)) + return SpatialReference(capi.clone_srs(self.ptr)) def from_esri(self): "Morphs this SpatialReference from ESRI's format to EPSG." - morph_from_esri(self._ptr) + 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. """ - identify_epsg(self._ptr) + capi.identify_epsg(self.ptr) def to_esri(self): "Morphs this SpatialReference to ESRI's format." - morph_to_esri(self._ptr) + capi.morph_to_esri(self.ptr) def validate(self): "Checks to see if the given spatial reference is valid." - srs_validate(self._ptr) + capi.srs_validate(self.ptr) #### Name & SRID properties #### @property @@ -205,25 +190,25 @@ class SpatialReference(object): @property def linear_name(self): "Returns the name of the linear units." - units, name = linear_units(self._ptr, byref(c_char_p())) + 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 = linear_units(self._ptr, byref(c_char_p())) + 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 = angular_units(self._ptr, byref(c_char_p())) + 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 = angular_units(self._ptr, byref(c_char_p())) + units, name = capi.angular_units(self.ptr, byref(c_char_p())) return units @property @@ -234,9 +219,9 @@ class SpatialReference(object): or angular units. """ if self.projected or self.local: - return linear_units(self._ptr, byref(c_char_p())) + return capi.linear_units(self.ptr, byref(c_char_p())) elif self.geographic: - return angular_units(self._ptr, byref(c_char_p())) + return capi.angular_units(self.ptr, byref(c_char_p())) else: return (None, None) @@ -252,17 +237,17 @@ class SpatialReference(object): @property def semi_major(self): "Returns the Semi Major Axis for this Spatial Reference." - return semi_major(self._ptr, byref(c_int())) + return capi.semi_major(self.ptr, byref(c_int())) @property def semi_minor(self): "Returns the Semi Minor Axis for this Spatial Reference." - return semi_minor(self._ptr, byref(c_int())) + return capi.semi_minor(self.ptr, byref(c_int())) @property def inverse_flattening(self): "Returns the Inverse Flattening for this Spatial Reference." - return invflattening(self._ptr, byref(c_int())) + return capi.invflattening(self.ptr, byref(c_int())) #### Boolean Properties #### @property @@ -271,12 +256,12 @@ class SpatialReference(object): Returns True if this SpatialReference is geographic (root node is GEOGCS). """ - return bool(isgeographic(self._ptr)) + return bool(capi.isgeographic(self.ptr)) @property def local(self): "Returns True if this SpatialReference is local (root node is LOCAL_CS)." - return bool(islocal(self._ptr)) + return bool(capi.islocal(self.ptr)) @property def projected(self): @@ -284,40 +269,44 @@ class SpatialReference(object): Returns True if this SpatialReference is a projected coordinate system (root node is PROJCS). """ - return bool(isprojected(self._ptr)) + return bool(capi.isprojected(self.ptr)) #### Import Routines ##### - def import_wkt(self, wkt): - "Imports the Spatial Reference from OGC WKT (string)" - from_wkt(self._ptr, byref(c_char_p(wkt))) + 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." - from_proj(self._ptr, proj) + capi.from_proj(self.ptr, proj) - def import_epsg(self, epsg): - "Imports the Spatial Reference from the EPSG code (an integer)." - from_epsg(self._ptr, epsg) + def import_user_input(self, user_input): + "Imports the Spatial Reference from the given user input string." + capi.from_user_input(self.ptr, 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." - from_xml(self._ptr, xml) + capi.from_xml(self.ptr, xml) #### Export Properties #### @property def wkt(self): "Returns the WKT representation of this Spatial Reference." - return to_wkt(self._ptr, byref(c_char_p())) + 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 to_pretty_wkt(self._ptr, byref(c_char_p()), simplify) + 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 to_proj(self._ptr, byref(c_char_p())) + return capi.to_proj(self.ptr, byref(c_char_p())) @property def proj4(self): @@ -327,34 +316,22 @@ class SpatialReference(object): @property def xml(self, dialect=''): "Returns the XML representation of this Spatial Reference." - # FIXME: This leaks memory, have to figure out why. - return to_xml(self._ptr, byref(c_char_p()), dialect) - - def to_esri(self): - "Morphs this SpatialReference to ESRI's format." - morph_to_esri(self._ptr) - - def from_esri(self): - "Morphs this SpatialReference from ESRI's format to EPSG." - morph_from_esri(self._ptr) + return capi.to_xml(self.ptr, byref(c_char_p()), dialect) -class CoordTransform(object): +class CoordTransform(GDALBase): "The coordinate system transformation object." def __init__(self, source, target): "Initializes on a source and target SpatialReference objects." - self._ptr = None # Initially NULL if not isinstance(source, SpatialReference) or not isinstance(target, SpatialReference): - raise SRSException('source and target must be of type SpatialReference') - self._ptr = new_ct(source._ptr, target._ptr) - if not self._ptr: - raise SRSException('could not intialize CoordTransform object') + 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: destroy_ct(self._ptr) + 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/django/contrib/gis/gdal/tests/__init__.py b/django/contrib/gis/gdal/tests/__init__.py new file mode 100644 index 0000000000..aada5f40fc --- /dev/null +++ b/django/contrib/gis/gdal/tests/__init__.py @@ -0,0 +1,25 @@ +""" +Module for executing all of the GDAL tests. None +of these tests require the use of the database. +""" +from unittest import TestSuite, TextTestRunner + +# Importing the GDAL test modules. +import test_driver, test_ds, test_envelope, test_geom, test_srs + +test_suites = [test_driver.suite(), + test_ds.suite(), + test_envelope.suite(), + test_geom.suite(), + test_srs.suite(), + ] + +def suite(): + "Builds a test suite for the GDAL tests." + s = TestSuite() + map(s.addTest, test_suites) + return s + +def run(verbosity=1): + "Runs the GDAL tests." + TextTestRunner(verbosity=verbosity).run(suite()) diff --git a/django/contrib/gis/gdal/tests/test_driver.py b/django/contrib/gis/gdal/tests/test_driver.py new file mode 100644 index 0000000000..1ff65ac31b --- /dev/null +++ b/django/contrib/gis/gdal/tests/test_driver.py @@ -0,0 +1,40 @@ +import os, os.path, unittest +from django.contrib.gis.gdal import Driver, OGRException + +valid_drivers = ('ESRI Shapefile', 'MapInfo File', 'TIGER', 'S57', 'DGN', + 'Memory', 'CSV', 'GML', 'KML') + +invalid_drivers = ('Foo baz', 'clucka', 'ESRI Shp') + +aliases = {'eSrI' : 'ESRI Shapefile', + 'TigER/linE' : 'TIGER', + 'SHAPE' : 'ESRI Shapefile', + 'sHp' : 'ESRI Shapefile', + } + +class DriverTest(unittest.TestCase): + + def test01_valid_driver(self): + "Testing valid OGR Data Source Drivers." + for d in valid_drivers: + dr = Driver(d) + self.assertEqual(d, str(dr)) + + def test02_invalid_driver(self): + "Testing invalid OGR Data Source Drivers." + for i in invalid_drivers: + self.assertRaises(OGRException, Driver, i) + + def test03_aliases(self): + "Testing driver aliases." + for alias, full_name in aliases.items(): + dr = Driver(alias) + self.assertEqual(full_name, str(dr)) + +def suite(): + s = unittest.TestSuite() + s.addTest(unittest.makeSuite(DriverTest)) + return s + +def run(verbosity=2): + unittest.TextTestRunner(verbosity=verbosity).run(suite()) diff --git a/django/contrib/gis/gdal/tests/test_ds.py b/django/contrib/gis/gdal/tests/test_ds.py new file mode 100644 index 0000000000..30ce462475 --- /dev/null +++ b/django/contrib/gis/gdal/tests/test_ds.py @@ -0,0 +1,201 @@ +import os, os.path, unittest +from django.contrib.gis.gdal import DataSource, Envelope, OGRException, OGRIndexError +from django.contrib.gis.gdal.field import OFTReal, OFTInteger, OFTString +from django.contrib import gis + +# Path for SHP files +data_path = os.path.join(os.path.dirname(gis.__file__), 'tests' + os.sep + 'data') +def get_ds_file(name, ext): + + + return os.sep.join([data_path, name, name + '.%s' % ext]) + +# Test SHP data source object +class TestDS: + def __init__(self, name, **kwargs): + ext = kwargs.pop('ext', 'shp') + self.ds = get_ds_file(name, ext) + for key, value in kwargs.items(): + setattr(self, key, value) + +# 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' : 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=1, 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'), + ) + +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." + print "\nBEGIN - expecting out of range feature id error; safe to ignore.\n" + 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 + 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)) + print "\nEND - expecting out of range feature id error; safe to ignore." + + 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): + "Test to make sure Layer access is still available without the DataSource." + 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) + + 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, g.srs.wkt) + + +def suite(): + s = unittest.TestSuite() + s.addTest(unittest.makeSuite(DataSourceTest)) + return s + +def run(verbosity=2): + unittest.TextTestRunner(verbosity=verbosity).run(suite()) diff --git a/django/contrib/gis/gdal/tests/test_envelope.py b/django/contrib/gis/gdal/tests/test_envelope.py new file mode 100644 index 0000000000..6c0c13724c --- /dev/null +++ b/django/contrib/gis/gdal/tests/test_envelope.py @@ -0,0 +1,94 @@ +import unittest +from django.contrib.gis.gdal import Envelope, OGRException + +class TestPoint(object): + def __init__(self, x, y): + self.x = x + self.y = y + +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, u'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) + +def suite(): + s = unittest.TestSuite() + s.addTest(unittest.makeSuite(EnvelopeTest)) + return s + +def run(verbosity=2): + unittest.TextTestRunner(verbosity=verbosity).run(suite()) diff --git a/django/contrib/gis/gdal/tests/test_geom.py b/django/contrib/gis/gdal/tests/test_geom.py new file mode 100644 index 0000000000..c920adc6c0 --- /dev/null +++ b/django/contrib/gis/gdal/tests/test_geom.py @@ -0,0 +1,415 @@ +import unittest +from django.contrib.gis.gdal import OGRGeometry, OGRGeomType, \ + OGRException, OGRIndexError, SpatialReference, CoordTransform, \ + gdal_version +from django.contrib.gis.tests.geometries import * + +class OGRGeomTest(unittest.TestCase): + "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 test01a_wkt(self): + "Testing WKT output." + for g in wkt_out: + geom = OGRGeometry(g.wkt) + self.assertEqual(g.wkt, geom.wkt) + + def test01b_gml(self): + "Testing GML output." + for g in wkt_out: + geom = OGRGeometry(g.wkt) + self.assertEqual(g.gml, geom.gml) + + def test01c_hex(self): + "Testing HEX input/output." + for g in hex_wkt: + geom1 = OGRGeometry(g.wkt) + self.assertEqual(g.hex, geom1.hex) + # Constructing w/HEX + geom2 = OGRGeometry(g.hex) + self.assertEqual(geom1, geom2) + + def test01d_wkb(self): + "Testing WKB input/output." + from binascii import b2a_hex + for g in hex_wkt: + geom1 = OGRGeometry(g.wkt) + wkb = geom1.wkb + self.assertEqual(b2a_hex(wkb).upper(), g.hex) + # Constructing w/WKB. + geom2 = OGRGeometry(wkb) + self.assertEqual(geom1, geom2) + + def test01e_json(self): + "Testing GeoJSON input/output." + from django.contrib.gis.gdal.prototypes.geom import GEOJSON + if not GEOJSON: return + for g in json_geoms: + geom = OGRGeometry(g.wkt) + if not hasattr(g, 'not_equal'): + self.assertEqual(g.json, geom.json) + self.assertEqual(g.json, 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 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 multipoints: + mgeom1 = OGRGeometry(mp.wkt) # First one from WKT + self.assertEqual(4, mgeom1.geom_type) + self.assertEqual('MULTIPOINT', mgeom1.geom_name) + mgeom2 = OGRGeometry('MULTIPOINT') # Creating empty multipoint + mgeom3 = OGRGeometry('MULTIPOINT') + for g in mgeom1: + mgeom2.add(g) # adding each point from the multipoints + mgeom3.add(g.wkt) # should take WKT as well + self.assertEqual(mgeom1, mgeom2) # they should equal + self.assertEqual(mgeom1, mgeom3) + self.assertEqual(mp.points, mgeom2.tuple) + self.assertEqual(mp.n_p, mgeom2.point_count) + + def test04_linestring(self): + "Testing LineString objects." + prev = OGRGeometry('POINT(0 0)') + for ls in linestrings: + linestr = OGRGeometry(ls.wkt) + self.assertEqual(2, linestr.geom_type) + self.assertEqual('LINESTRING', linestr.geom_name) + self.assertEqual(ls.n_p, linestr.point_count) + self.assertEqual(ls.tup, linestr.tuple) + 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.tup] + y = [tmpy for tmpx, tmpy in ls.tup] + 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 multilinestrings: + mlinestr = OGRGeometry(mls.wkt) + self.assertEqual(5, mlinestr.geom_type) + self.assertEqual('MULTILINESTRING', mlinestr.geom_name) + self.assertEqual(mls.n_p, mlinestr.point_count) + self.assertEqual(mls.tup, mlinestr.tuple) + 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 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 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) + print "\nBEGIN - expecting IllegalArgumentException; safe to ignore.\n" + try: + c = poly.centroid + except OGRException: + # Should raise an OGR exception, rings are not closed + pass + else: + self.fail('Should have raised an OGRException!') + print "\nEND - expecting IllegalArgumentException; safe to ignore.\n" + + # Closing the rings -- doesn't work on GDAL versions 1.4.1 and below: + # http://trac.osgeo.org/gdal/ticket/1673 + major, minor1, minor2 = gdal_version().split('.') + if major == '1': + iminor1 = int(minor1) + if iminor1 < 4 or (iminor1 == 4 and minor2.startswith('1')): return + 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 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 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, b = topology_geoms[0] + a, b = OGRGeometry(a.wkt, sr), OGRGeometry(b.wkt, sr) + diff = a.difference(b) + union = a.union(b) + self.assertEqual(sr.wkt, diff.srs.wkt) + self.assertEqual(sr.srid, union.srs.srid) + + # Instantiating w/an integer SRID + mpoly = OGRGeometry(mp.wkt, 4326) + 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 test10_difference(self): + "Testing difference()." + for i in xrange(len(topology_geoms)): + g_tup = topology_geoms[i] + a = OGRGeometry(g_tup[0].wkt) + b = OGRGeometry(g_tup[1].wkt) + d1 = OGRGeometry(diff_geoms[i].wkt) + d2 = a.difference(b) + self.assertEqual(d1, d2) + self.assertEqual(d1, a - b) # __sub__ is difference operator + a -= b # testing __isub__ + self.assertEqual(d1, a) + + def test11_intersection(self): + "Testing intersects() and intersection()." + for i in xrange(len(topology_geoms)): + g_tup = topology_geoms[i] + a = OGRGeometry(g_tup[0].wkt) + b = OGRGeometry(g_tup[1].wkt) + i1 = OGRGeometry(intersect_geoms[i].wkt) + self.assertEqual(True, a.intersects(b)) + i2 = a.intersection(b) + self.assertEqual(i1, i2) + self.assertEqual(i1, a & b) # __and__ is intersection operator + a &= b # testing __iand__ + self.assertEqual(i1, a) + + def test12_symdifference(self): + "Testing sym_difference()." + for i in xrange(len(topology_geoms)): + g_tup = topology_geoms[i] + a = OGRGeometry(g_tup[0].wkt) + b = OGRGeometry(g_tup[1].wkt) + d1 = OGRGeometry(sdiff_geoms[i].wkt) + d2 = a.sym_difference(b) + self.assertEqual(d1, d2) + self.assertEqual(d1, a ^ b) # __xor__ is symmetric difference operator + a ^= b # testing __ixor__ + self.assertEqual(d1, a) + + def test13_union(self): + "Testing union()." + for i in xrange(len(topology_geoms)): + g_tup = topology_geoms[i] + a = OGRGeometry(g_tup[0].wkt) + b = OGRGeometry(g_tup[1].wkt) + u1 = OGRGeometry(union_geoms[i].wkt) + u2 = a.union(b) + self.assertEqual(u1, u2) + self.assertEqual(u1, a | b) # __or__ is union operator + a |= b # testing __ior__ + self.assertEqual(u1, a) + + def 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 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(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 suite(): + s = unittest.TestSuite() + s.addTest(unittest.makeSuite(OGRGeomTest)) + return s + +def run(verbosity=2): + unittest.TextTestRunner(verbosity=verbosity).run(suite()) diff --git a/django/contrib/gis/gdal/tests/test_srs.py b/django/contrib/gis/gdal/tests/test_srs.py new file mode 100644 index 0000000000..2742c7aad9 --- /dev/null +++ b/django/contrib/gis/gdal/tests/test_srs.py @@ -0,0 +1,169 @@ +import unittest +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='+proj=lcc +lat_1=30.28333333333333 +lat_2=28.38333333333333 +lat_0=27.83333333333333 +lon_0=-99 +x_0=600000 +y_0=4000000 +ellps=GRS80 +datum=NAD83 +units=m +no_defs ', + 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='+proj=lcc +lat_1=28.38333333333333 +lat_2=30.28333333333334 +lat_0=27.83333333333333 +lon_0=-99 +x_0=600000 +y_0=3999999.999999999 +ellps=GRS80 +datum=NAD83 +to_meter=0.3048006096012192 +no_defs ', + 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"]]',) + +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) + #self.assertEqual(srs1.wkt, srs2.wkt) + 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])) + #for i in range(7): self.assertEqual(0, int(s1['TOWGS84', i])) + self.assertEqual(None, s1['FOOBAR']) + +def suite(): + s = unittest.TestSuite() + s.addTest(unittest.makeSuite(SpatialRefTest)) + return s + +def run(verbosity=2): + unittest.TextTestRunner(verbosity=verbosity).run(suite()) diff --git a/django/contrib/gis/tests/__init__.py b/django/contrib/gis/tests/__init__.py index 53674afa62..a2abee9173 100644 --- a/django/contrib/gis/tests/__init__.py +++ b/django/contrib/gis/tests/__init__.py @@ -9,42 +9,45 @@ def geo_suite(): """ from django.conf import settings from django.contrib.gis.tests.utils import mysql, oracle, postgis - from django.contrib.gis.gdal import HAS_GDAL - from django.contrib.gis.utils import HAS_GEOIP + from django.contrib.gis import gdal, utils - # Tests that require use of a spatial database (e.g., creation of models) - test_models = ['geoapp',] + # The test suite. + s = unittest.TestSuite() + + # Adding the GEOS tests. (__future__) + #from django.contrib.gis.geos import tests as geos_tests + #s.addTest(geos_tests.suite()) - # Tests that do not require setting up and tearing down a spatial database. + # Test apps that require use of a spatial database (e.g., creation of models) + test_apps = ['geoapp', 'relatedapp'] + if oracle or postgis: + test_apps.append('distapp') + + # Tests that do not require setting up and tearing down a spatial database + # and are modules in `django.contrib.gis.tests`. test_suite_names = [ 'test_geos', 'test_measure', ] - if HAS_GDAL: - if oracle or postgis: - test_models += ['distapp', 'layermap', 'relatedapp'] - elif mysql: - test_models += ['relatedapp', 'layermap'] - - test_suite_names += [ - 'test_gdal_driver', - 'test_gdal_ds', - 'test_gdal_envelope', - 'test_gdal_geom', - 'test_gdal_srs', - 'test_spatialrefsys', - ] + + if gdal.HAS_GDAL: + # These tests require GDAL. + test_suite_names.append('test_spatialrefsys') + test_apps.append('layermap') + + # Adding the GDAL tests. + from django.contrib.gis.gdal import tests as gdal_tests + s.addTest(gdal_tests.suite()) else: print >>sys.stderr, "GDAL not available - no GDAL tests will be run." - if HAS_GEOIP and hasattr(settings, 'GEOIP_PATH'): + if utils.HAS_GEOIP and hasattr(settings, 'GEOIP_PATH'): test_suite_names.append('test_geoip') - s = unittest.TestSuite() - for test_suite in test_suite_names: - tsuite = getattr(__import__('django.contrib.gis.tests', globals(), locals(), [test_suite]),test_suite) + for suite_name in test_suite_names: + tsuite = getattr(__import__('django.contrib.gis.tests', globals(), locals(), [suite_name]), suite_name) s.addTest(tsuite.suite()) - return s, test_models + return s, test_apps def run_gis_tests(test_labels, **kwargs): """ @@ -80,9 +83,9 @@ def run_gis_tests(test_labels, **kwargs): # Creating the test suite, adding the test models to INSTALLED_APPS, and # adding the model test suites to our suite package. - gis_suite, test_models = geo_suite() - for test_model in test_models: - module_name = 'django.contrib.gis.tests.%s' % test_model + gis_suite, test_apps = geo_suite() + for test_app in test_apps: + module_name = 'django.contrib.gis.tests.%s' % test_app if mysql: test_module_name = 'tests_mysql' else: @@ -90,13 +93,13 @@ def run_gis_tests(test_labels, **kwargs): new_installed.append(module_name) # Getting the model test suite - tsuite = getattr(__import__('django.contrib.gis.tests.%s' % test_model, globals(), locals(), [test_module_name]), + tsuite = getattr(__import__('django.contrib.gis.tests.%s' % test_app, globals(), locals(), [test_module_name]), test_module_name) gis_suite.addTest(tsuite.suite()) - # Resetting the loaded flag to take into account what we appended to - # the INSTALLED_APPS (since this routine is invoked through - # django/core/management, it caches the apps; this ensures that syncdb + # Resetting the loaded flag to take into account what we appended to + # the INSTALLED_APPS (since this routine is invoked through + # django/core/management, it caches the apps; this ensures that syncdb # will see our appended models) settings.INSTALLED_APPS = new_installed loading.cache.loaded = False @@ -198,3 +201,25 @@ def run_tests(test_labels, verbosity=1, interactive=True, extra_tests=[], suite= # Returning the total failures and errors return len(result.failures) + len(result.errors) + +# Class for creating a fake module with a run method. This is for the +# GEOS and GDAL tests that were moved to their respective modules. +class _DeprecatedTestModule(object): + def __init__(self, tests, mod): + self.tests = tests + self.mod = mod + + def run(self): + from warnings import warn + warn('This test module is deprecated because it has moved to ' \ + '`django.contrib.gis.%s.tests` and will disappear in 1.2.' % + self.mod, DeprecationWarning) + self.tests.run() + +#from django.contrib.gis.geos import tests as _tests +#test_geos = _DeprecatedTestModule(_tests, 'geos') + +from django.contrib.gis.gdal import HAS_GDAL +if HAS_GDAL: + from django.contrib.gis.gdal import tests as _tests + test_gdal = _DeprecatedTestModule(_tests, 'gdal') diff --git a/django/contrib/gis/tests/test_gdal.py b/django/contrib/gis/tests/test_gdal.py deleted file mode 100644 index 030ac505fe..0000000000 --- a/django/contrib/gis/tests/test_gdal.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -Module for executing all of the GDAL tests. None -of these tests require the use of the database. -""" -from unittest import TestSuite, TextTestRunner - -# Importing the GDAL test modules. -from django.contrib.gis.tests import \ - test_gdal_driver, test_gdal_ds, test_gdal_envelope, \ - test_gdal_geom, test_gdal_srs - - -test_suites = [test_gdal_driver.suite(), - test_gdal_ds.suite(), - test_gdal_envelope.suite(), - test_gdal_geom.suite(), - test_gdal_srs.suite(), - ] - -def suite(): - "Builds a test suite for the GDAL tests." - s = TestSuite() - map(s.addTest, test_suites) - return s - -def run(verbosity=1): - "Runs the GDAL tests." - TextTestRunner(verbosity=verbosity).run(suite()) diff --git a/django/contrib/gis/tests/test_gdal_driver.py b/django/contrib/gis/tests/test_gdal_driver.py deleted file mode 100644 index 1ff65ac31b..0000000000 --- a/django/contrib/gis/tests/test_gdal_driver.py +++ /dev/null @@ -1,40 +0,0 @@ -import os, os.path, unittest -from django.contrib.gis.gdal import Driver, OGRException - -valid_drivers = ('ESRI Shapefile', 'MapInfo File', 'TIGER', 'S57', 'DGN', - 'Memory', 'CSV', 'GML', 'KML') - -invalid_drivers = ('Foo baz', 'clucka', 'ESRI Shp') - -aliases = {'eSrI' : 'ESRI Shapefile', - 'TigER/linE' : 'TIGER', - 'SHAPE' : 'ESRI Shapefile', - 'sHp' : 'ESRI Shapefile', - } - -class DriverTest(unittest.TestCase): - - def test01_valid_driver(self): - "Testing valid OGR Data Source Drivers." - for d in valid_drivers: - dr = Driver(d) - self.assertEqual(d, str(dr)) - - def test02_invalid_driver(self): - "Testing invalid OGR Data Source Drivers." - for i in invalid_drivers: - self.assertRaises(OGRException, Driver, i) - - def test03_aliases(self): - "Testing driver aliases." - for alias, full_name in aliases.items(): - dr = Driver(alias) - self.assertEqual(full_name, str(dr)) - -def suite(): - s = unittest.TestSuite() - s.addTest(unittest.makeSuite(DriverTest)) - return s - -def run(verbosity=2): - unittest.TextTestRunner(verbosity=verbosity).run(suite()) diff --git a/django/contrib/gis/tests/test_gdal_ds.py b/django/contrib/gis/tests/test_gdal_ds.py deleted file mode 100644 index 0be0984ff5..0000000000 --- a/django/contrib/gis/tests/test_gdal_ds.py +++ /dev/null @@ -1,198 +0,0 @@ -import os, os.path, unittest -from django.contrib.gis.gdal import DataSource, Envelope, OGRException, OGRIndexError -from django.contrib.gis.gdal.field import OFTReal, OFTInteger, OFTString - -# Path for SHP files -data_path = os.path.join(os.path.dirname(__file__), 'data') -def get_ds_file(name, ext): - return os.sep.join([data_path, name, name + '.%s' % ext]) - -# Test SHP data source object -class TestDS: - def __init__(self, name, **kwargs): - ext = kwargs.pop('ext', 'shp') - self.ds = get_ds_file(name, ext) - for key, value in kwargs.items(): - setattr(self, key, value) - -# 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' : 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=1, 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'), - ) - -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." - print "\nBEGIN - expecting out of range feature id error; safe to ignore.\n" - 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 - 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)) - print "\nEND - expecting out of range feature id error; safe to ignore." - - 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): - "Test to make sure Layer access is still available without the DataSource." - 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) - - 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, g.srs.wkt) - - -def suite(): - s = unittest.TestSuite() - s.addTest(unittest.makeSuite(DataSourceTest)) - return s - -def run(verbosity=2): - unittest.TextTestRunner(verbosity=verbosity).run(suite()) diff --git a/django/contrib/gis/tests/test_gdal_envelope.py b/django/contrib/gis/tests/test_gdal_envelope.py deleted file mode 100644 index fda27191ac..0000000000 --- a/django/contrib/gis/tests/test_gdal_envelope.py +++ /dev/null @@ -1,45 +0,0 @@ -import unittest -from django.contrib.gis.gdal import Envelope, OGRException - -class EnvelopeTest(unittest.TestCase): - - 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, u'foo') - - 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 suite(): - s = unittest.TestSuite() - s.addTest(unittest.makeSuite(EnvelopeTest)) - return s - -def run(verbosity=2): - unittest.TextTestRunner(verbosity=verbosity).run(suite()) diff --git a/django/contrib/gis/tests/test_gdal_geom.py b/django/contrib/gis/tests/test_gdal_geom.py deleted file mode 100644 index 8f5caa9615..0000000000 --- a/django/contrib/gis/tests/test_gdal_geom.py +++ /dev/null @@ -1,404 +0,0 @@ -import unittest -from django.contrib.gis.gdal import OGRGeometry, OGRGeomType, \ - OGRException, OGRIndexError, SpatialReference, CoordTransform, \ - gdal_version -from django.contrib.gis.tests.geometries import * - -class OGRGeomTest(unittest.TestCase): - "This tests the OGR Geometry." - - def test00_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(TypeError, OGRGeomType.__init__, 23) - self.assertRaises(TypeError, OGRGeomType.__init__, 'fooD') - self.assertRaises(TypeError, OGRGeomType.__init__, 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(None, OGRGeomType('Unknown').django) - self.assertEqual(None, OGRGeomType('none').django) - - def test01a_wkt(self): - "Testing WKT output." - for g in wkt_out: - geom = OGRGeometry(g.wkt) - self.assertEqual(g.wkt, geom.wkt) - - def test01b_gml(self): - "Testing GML output." - for g in wkt_out: - geom = OGRGeometry(g.wkt) - self.assertEqual(g.gml, geom.gml) - - def test01c_hex(self): - "Testing HEX input/output." - for g in hex_wkt: - geom1 = OGRGeometry(g.wkt) - self.assertEqual(g.hex, geom1.hex) - # Constructing w/HEX - geom2 = OGRGeometry(g.hex) - self.assertEqual(geom1, geom2) - - def test01d_wkb(self): - "Testing WKB input/output." - from binascii import b2a_hex - for g in hex_wkt: - geom1 = OGRGeometry(g.wkt) - wkb = geom1.wkb - self.assertEqual(b2a_hex(wkb).upper(), g.hex) - # Constructing w/WKB. - geom2 = OGRGeometry(wkb) - self.assertEqual(geom1, geom2) - - def test01e_json(self): - "Testing GeoJSON input/output." - from django.contrib.gis.gdal.prototypes.geom import GEOJSON - if not GEOJSON: return - for g in json_geoms: - geom = OGRGeometry(g.wkt) - if not hasattr(g, 'not_equal'): - self.assertEqual(g.json, geom.json) - self.assertEqual(g.json, 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 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 multipoints: - mgeom1 = OGRGeometry(mp.wkt) # First one from WKT - self.assertEqual(4, mgeom1.geom_type) - self.assertEqual('MULTIPOINT', mgeom1.geom_name) - mgeom2 = OGRGeometry('MULTIPOINT') # Creating empty multipoint - mgeom3 = OGRGeometry('MULTIPOINT') - for g in mgeom1: - mgeom2.add(g) # adding each point from the multipoints - mgeom3.add(g.wkt) # should take WKT as well - self.assertEqual(mgeom1, mgeom2) # they should equal - self.assertEqual(mgeom1, mgeom3) - self.assertEqual(mp.points, mgeom2.tuple) - self.assertEqual(mp.n_p, mgeom2.point_count) - - def test04_linestring(self): - "Testing LineString objects." - prev = OGRGeometry('POINT(0 0)') - for ls in linestrings: - linestr = OGRGeometry(ls.wkt) - self.assertEqual(2, linestr.geom_type) - self.assertEqual('LINESTRING', linestr.geom_name) - self.assertEqual(ls.n_p, linestr.point_count) - self.assertEqual(ls.tup, linestr.tuple) - 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.tup] - y = [tmpy for tmpx, tmpy in ls.tup] - 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 multilinestrings: - mlinestr = OGRGeometry(mls.wkt) - self.assertEqual(5, mlinestr.geom_type) - self.assertEqual('MULTILINESTRING', mlinestr.geom_name) - self.assertEqual(mls.n_p, mlinestr.point_count) - self.assertEqual(mls.tup, mlinestr.tuple) - 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 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." - prev = OGRGeometry('POINT(0 0)') - for p in polygons: - poly = OGRGeometry(p.wkt) - self.assertEqual(3, poly.geom_type) - self.assertEqual('POLYGON', poly.geom_name) - self.assertEqual(p.n_p, poly.point_count) - 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) - print "\nBEGIN - expecting IllegalArgumentException; safe to ignore.\n" - try: - c = poly.centroid - except OGRException: - # Should raise an OGR exception, rings are not closed - pass - else: - self.fail('Should have raised an OGRException!') - print "\nEND - expecting IllegalArgumentException; safe to ignore.\n" - - # Closing the rings -- doesn't work on GDAL versions 1.4.1 and below: - # http://trac.osgeo.org/gdal/ticket/1673 - major, minor1, minor2 = gdal_version().split('.') - if major == '1': - iminor1 = int(minor1) - if iminor1 < 4 or (iminor1 == 4 and minor2.startswith('1')): return - 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 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 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, b = topology_geoms[0] - a, b = OGRGeometry(a.wkt, sr), OGRGeometry(b.wkt, sr) - diff = a.difference(b) - union = a.union(b) - self.assertEqual(sr.wkt, diff.srs.wkt) - self.assertEqual(sr.srid, union.srs.srid) - - # Instantiating w/an integer SRID - mpoly = OGRGeometry(mp.wkt, 4326) - 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 test10_difference(self): - "Testing difference()." - for i in xrange(len(topology_geoms)): - g_tup = topology_geoms[i] - a = OGRGeometry(g_tup[0].wkt) - b = OGRGeometry(g_tup[1].wkt) - d1 = OGRGeometry(diff_geoms[i].wkt) - d2 = a.difference(b) - self.assertEqual(d1, d2) - self.assertEqual(d1, a - b) # __sub__ is difference operator - a -= b # testing __isub__ - self.assertEqual(d1, a) - - def test11_intersection(self): - "Testing intersects() and intersection()." - for i in xrange(len(topology_geoms)): - g_tup = topology_geoms[i] - a = OGRGeometry(g_tup[0].wkt) - b = OGRGeometry(g_tup[1].wkt) - i1 = OGRGeometry(intersect_geoms[i].wkt) - self.assertEqual(True, a.intersects(b)) - i2 = a.intersection(b) - self.assertEqual(i1, i2) - self.assertEqual(i1, a & b) # __and__ is intersection operator - a &= b # testing __iand__ - self.assertEqual(i1, a) - - def test12_symdifference(self): - "Testing sym_difference()." - for i in xrange(len(topology_geoms)): - g_tup = topology_geoms[i] - a = OGRGeometry(g_tup[0].wkt) - b = OGRGeometry(g_tup[1].wkt) - d1 = OGRGeometry(sdiff_geoms[i].wkt) - d2 = a.sym_difference(b) - self.assertEqual(d1, d2) - self.assertEqual(d1, a ^ b) # __xor__ is symmetric difference operator - a ^= b # testing __ixor__ - self.assertEqual(d1, a) - - def test13_union(self): - "Testing union()." - for i in xrange(len(topology_geoms)): - g_tup = topology_geoms[i] - a = OGRGeometry(g_tup[0].wkt) - b = OGRGeometry(g_tup[1].wkt) - u1 = OGRGeometry(union_geoms[i].wkt) - u2 = a.union(b) - self.assertEqual(u1, u2) - self.assertEqual(u1, a | b) # __or__ is union operator - a |= b # testing __ior__ - self.assertEqual(u1, a) - - def 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 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(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 suite(): - s = unittest.TestSuite() - s.addTest(unittest.makeSuite(OGRGeomTest)) - return s - -def run(verbosity=2): - unittest.TextTestRunner(verbosity=verbosity).run(suite()) diff --git a/django/contrib/gis/tests/test_gdal_srs.py b/django/contrib/gis/tests/test_gdal_srs.py deleted file mode 100644 index 2742c7aad9..0000000000 --- a/django/contrib/gis/tests/test_gdal_srs.py +++ /dev/null @@ -1,169 +0,0 @@ -import unittest -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='+proj=lcc +lat_1=30.28333333333333 +lat_2=28.38333333333333 +lat_0=27.83333333333333 +lon_0=-99 +x_0=600000 +y_0=4000000 +ellps=GRS80 +datum=NAD83 +units=m +no_defs ', - 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='+proj=lcc +lat_1=28.38333333333333 +lat_2=30.28333333333334 +lat_0=27.83333333333333 +lon_0=-99 +x_0=600000 +y_0=3999999.999999999 +ellps=GRS80 +datum=NAD83 +to_meter=0.3048006096012192 +no_defs ', - 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"]]',) - -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) - #self.assertEqual(srs1.wkt, srs2.wkt) - 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])) - #for i in range(7): self.assertEqual(0, int(s1['TOWGS84', i])) - self.assertEqual(None, s1['FOOBAR']) - -def suite(): - s = unittest.TestSuite() - s.addTest(unittest.makeSuite(SpatialRefTest)) - return s - -def run(verbosity=2): - unittest.TextTestRunner(verbosity=verbosity).run(suite()) -- cgit v1.3