From 48c9f87e1f3ba9523d79c09f52c0ccc6221f08bf Mon Sep 17 00:00:00 2001 From: Justin Bronn Date: Fri, 15 Jun 2007 06:02:04 +0000 Subject: [PATCH] gis: GDAL improvements; added Driver class, improved tests, removed non-existent exception, updated geometries for tests, updated documentation. git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@5478 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/contrib/gis/gdal/DataSource.py | 53 +++-- django/contrib/gis/gdal/Driver.py | 86 ++++++++ django/contrib/gis/gdal/Feature.py | 4 +- django/contrib/gis/gdal/Field.py | 2 +- django/contrib/gis/gdal/OGRGeometry.py | 218 +++++++++++++++++-- django/contrib/gis/gdal/SpatialReference.py | 42 +++- django/contrib/gis/gdal/__init__.py | 1 + django/contrib/gis/gdal/libgdal.py | 11 +- django/contrib/gis/tests/__init__.py | 5 +- django/contrib/gis/tests/geometries.py | 27 +-- django/contrib/gis/tests/test_gdal_driver.py | 40 ++++ django/contrib/gis/tests/test_gdal_ds.py | 2 +- django/contrib/gis/tests/test_gdal_geom.py | 82 ++++++- django/contrib/gis/tests/test_geos.py | 5 +- 14 files changed, 500 insertions(+), 78 deletions(-) create mode 100644 django/contrib/gis/gdal/Driver.py create mode 100644 django/contrib/gis/tests/test_gdal_driver.py diff --git a/django/contrib/gis/gdal/DataSource.py b/django/contrib/gis/gdal/DataSource.py index 404e55b678..5ad434348b 100644 --- a/django/contrib/gis/gdal/DataSource.py +++ b/django/contrib/gis/gdal/DataSource.py @@ -6,12 +6,20 @@ from ctypes import c_char_p, c_int, c_void_p, byref, string_at from django.contrib.gis.gdal.libgdal import lgdal from django.contrib.gis.gdal.OGRError import OGRException, check_err from django.contrib.gis.gdal.Layer import Layer +from django.contrib.gis.gdal.Driver import Driver """ DataSource is a wrapper for the OGR Data Source object, which provides an interface for reading vector geometry data from many different file formats (including ESRI shapefiles). + When instantiating a DataSource object, use the filename of a + GDAL-supported data source. For example, a SHP file or a + TIGER/Line file from the government. + + The ds_driver keyword is used internally when a ctypes pointer + is passed in directly. + Example: ds = DataSource('/home/foo/bar.shp') for layer in ds: @@ -22,13 +30,24 @@ from django.contrib.gis.gdal.Layer import Layer # Getting the 'description' field for the feature. desc = feature['description'] - More documentation forthcoming. + # We can also increment through all of the fields + # attached to this feature. + for field in feature: + # Get the name of the field (e.g. 'description') + nm = field.name + + # Get the type (integer) of the field, e.g. 0 => OFTInteger + t = field.type + + # Returns the value the field; OFTIntegers return ints, + # OFTReal returns floats, all else returns string. + val = field.value """ # For more information, see the OGR C API source code: # http://www.gdal.org/ogr/ogr__api_8h.html # -# The OGR_DS* routines are relevant here. +# The OGR_DS_* routines are relevant here. class DataSource(object): "Wraps an OGR Data Source object." @@ -36,26 +55,32 @@ class DataSource(object): _ds = 0 # Initially NULL #### Python 'magic' routines #### - def __init__(self, ds_file): + def __init__(self, ds_input, ds_driver=False): # Registering all the drivers, this needs to be done # _before_ we try to open up a data source. - if not lgdal.OGRRegisterAll(): - raise OGRException, 'Could not register all data source drivers!' + if not lgdal.OGRGetDriverCount() and not lgdal.OGRRegisterAll(): + raise OGRException, 'Could not register all the OGR data source drivers!' - # The data source driver is a void pointer. - ds_driver = c_void_p() + if isinstance(ds_input, StringType): - # OGROpen will auto-detect the data source type. - ds = lgdal.OGROpen(c_char_p(ds_file), c_int(0), byref(ds_driver)) + # The data source driver is a void pointer. + ds_driver = c_void_p() + + # OGROpen will auto-detect the data source type. + ds = lgdal.OGROpen(c_char_p(ds_input), c_int(0), byref(ds_driver)) + elif isinstance(ds_input, c_void_p) and isinstance(ds_driver, c_void_p): + ds = ds_input + else: + raise OGRException, 'Invalid data source input type: %s' % str(type(ds_input)) # Raise an exception if the returned pointer is NULL if not ds: self._ds = False - raise OGRException, 'Invalid data source file "%s"' % ds_file + raise OGRException, 'Invalid data source file "%s"' % ds_input else: self._ds = ds - self._driver = ds_driver + self._driver = Driver(ds_driver) def __del__(self): "This releases the reference to the data source (destroying it if it's the only one)." @@ -83,13 +108,13 @@ class DataSource(object): def __str__(self): "Returns OGR GetName and Driver for the Data Source." - return '%s (%s)' % (self.name, self.driver) + return '%s (%s)' % (self.name, str(self.driver)) #### DataSource Properties #### @property def driver(self): - "Returns the name of the data source driver." - return string_at(lgdal.OGR_Dr_GetName(self._driver)) + "Returns the Driver object for this Data Source." + return self._driver @property def layer_count(self): diff --git a/django/contrib/gis/gdal/Driver.py b/django/contrib/gis/gdal/Driver.py new file mode 100644 index 0000000000..977dbe5358 --- /dev/null +++ b/django/contrib/gis/gdal/Driver.py @@ -0,0 +1,86 @@ +# types and ctypes +from types import StringType +from ctypes import c_char_p, c_int, c_void_p, byref, string_at + +# The GDAL C library, OGR exceptions, and the Layer object. +from django.contrib.gis.gdal.libgdal import lgdal +from django.contrib.gis.gdal.OGRError import OGRException + +# For more information, see the OGR C API source code: +# http://www.gdal.org/ogr/ogr__api_8h.html +# +# The OGR_Dr_* routines are relevant here. + +class Driver(object): + "Wraps an OGR Data Source Driver." + + _dr = 0 # Initially NULL + + # Case-insensitive aliases for OGR Drivers. + _alias = {'esri' : 'ESRI Shapefile', + 'shp' : 'ESRI Shapefile', + 'shape' : 'ESRI Shapefile', + 'tiger' : 'TIGER', + 'tiger/line' : 'TIGER', + } + + def __init__(self, input, ptr=False): + "Initializes an OGR driver on either a string or integer input." + + if isinstance(input, StringType): + # If a string name of the driver was passed in + self._register() + + # Checking the alias dictionary (case-insensitive) to see if an alias + # exists for the given driver. + if input.lower() in self._alias: + name = c_char_p(self._alias[input.lower()]) + else: + name = c_char_p(input) + + # Attempting to get the OGR driver by the string name. + dr = lgdal.OGRGetDriverByName(name) + elif isinstance(input, int): + self._register() + dr = lgdal.OGRGetDriver(c_int(input)) + elif isinstance(input, c_void_p): + dr = input + else: + raise OGRException, 'Unrecognized input type for OGR Driver: %s' % str(type(input)) + + # Making sure we get a valid pointer to the OGR Driver + if not dr: + raise OGRException, 'Could not initialize OGR Driver on input: %s' % str(input) + self._dr = dr + + def __str__(self): + "Returns the string name of the OGR Driver." + return string_at(lgdal.OGR_Dr_GetName(self._dr)) + + def _register(self): + "Attempts to register all the data source drivers." + # Only register all if the driver count is 0 (or else all drivers + # will be registered over and over again) + if not self.driver_count and not lgdal.OGRRegisterAll(): + raise OGRException, 'Could not register all the OGR data source drivers!' + + # Driver properties + @property + def driver_count(self): + "Returns the number of OGR data source drivers registered." + return lgdal.OGRGetDriverCount() + + def create_ds(self, **kwargs): + "Creates a data source using the keyword args as name value options." + raise NotImplementedError + # Getting the options string + #options = '' + #n_opts = len(kwargs) + #for i in xrange(n_opts): + # options += '%s=%s' % (str(k), str(v)) + # if i < n_opts-1: options += ',' + #opts = c_char_p(options) + + + + diff --git a/django/contrib/gis/gdal/Feature.py b/django/contrib/gis/gdal/Feature.py index e5a02091dd..03d97f304e 100644 --- a/django/contrib/gis/gdal/Feature.py +++ b/django/contrib/gis/gdal/Feature.py @@ -1,5 +1,5 @@ # types and ctypes -import types +from types import StringType from ctypes import c_char_p, c_int, string_at # The GDAL C library, OGR exception, and the Field object @@ -31,7 +31,7 @@ class Feature(object): def __getitem__(self, index): "Gets the Field at the specified index." - if isinstance(index, types.StringType): + if isinstance(index, StringType): i = self.index(index) else: if index < 0 or index > self.num_fields: diff --git a/django/contrib/gis/gdal/Field.py b/django/contrib/gis/gdal/Field.py index c1416641b9..ce183990bc 100644 --- a/django/contrib/gis/gdal/Field.py +++ b/django/contrib/gis/gdal/Field.py @@ -8,7 +8,7 @@ from django.contrib.gis.gdal.OGRError import OGRException # # The OGR_Fld_* routines are relevant here. class Field(object): - "A class that wraps an OGR Field." + "A class that wraps an OGR Field, needs to be instantiated from a Feature object." _fld = 0 # Initially NULL diff --git a/django/contrib/gis/gdal/OGRGeometry.py b/django/contrib/gis/gdal/OGRGeometry.py index e16d2be9c1..d1b334ec43 100644 --- a/django/contrib/gis/gdal/OGRGeometry.py +++ b/django/contrib/gis/gdal/OGRGeometry.py @@ -1,14 +1,53 @@ # types & ctypes -from types import StringType -from ctypes import \ - byref, string_at, create_string_buffer, POINTER, \ - c_char_p, c_double, c_int, c_void_p +from types import IntType, StringType +from ctypes import byref, string_at, c_char_p, c_double, c_int, c_void_p # Getting the GDAL C library and error checking facilities from django.contrib.gis.gdal.libgdal import lgdal from django.contrib.gis.gdal.OGRError import check_err, OGRException from django.contrib.gis.gdal.SpatialReference import SpatialReference, CoordTransform +""" + The OGRGeometry is a wrapper for using the OGR Geometry class + (see http://www.gdal.org/ogr/classOGRGeometry.html). OGRGeometry + may be instantiated when reading geometries from OGR Data Sources + (e.g. SHP files), or when given OGC WKT (a string). + + While the 'full' API is not present yet, the API is "pythonic" unlike + the traditional and "next-generation" OGR Python bindings. One major + advantage OGR Geometries have over their GEOS counterparts is support + for spatial reference systems and their transformation. + + Example: + >>> from django.contrib.gis.gdal import OGRGeometry, OGRGeomType, SpatialReference + >>> wkt1, wkt2 = 'POINT(-90 30)', 'POLYGON((0 0, 5 0, 5 5, 0 5)' + >>> pnt = OGRGeometry(wkt1) + >>> print pnt + POINT (-90 30) + >>> mpnt = OGRGeometry(OGRGeomType('MultiPoint'), SpatialReference('WGS84')) + >>> mpnt.add(wkt1) + >>> mpnt.add(wkt1) + >>> print mpnt + MULTIPOINT (-90 30,-90 30) + >>> print mpnt.srs.name + WGS 84 + >>> print mpnt.srs.proj + +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs + >>> mpnt.transform_to(SpatialReference('NAD27')) + >>> print mpnt.proj + +proj=longlat +ellps=clrk66 +datum=NAD27 +no_defs + >>> print mpnt + MULTIPOINT (-89.999930378602485 29.999797886557641,-89.999930378602485 29.999797886557641) + + The OGRGeomType class is to make it easy to specify an OGR geometry type: + >>> from django.contrib.gis.gdal import OGRGeomType + >>> gt1 = OGRGeomType(3) # Using an integer for the type + >>> gt2 = OGRGeomType('Polygon') # Using a string + >>> gt3 = OGRGeomType('POLYGON') # It's case-insensitive + >>> print gt1 == 3, gt1 == 'Polygon' # Equivalence works w/non-OGRGeomType objects + True +""" + # For more information, see the OGR C API source code: # http://www.gdal.org/ogr/ogr__api_8h.html # @@ -29,10 +68,10 @@ class OGRGeomType(object): "Encapulates OGR Geometry Types." # Ordered array of acceptable strings and their corresponding OGRwkbGeometryType - __ogr_str = ['Unknown', 'Point', 'LineString', 'Polygon', 'MultiPoint', + __ogr_str = ['Point', 'LineString', 'Polygon', 'MultiPoint', 'MultiLineString', 'MultiPolygon', 'GeometryCollection', - 'None', 'LinearRing'] - __ogr_int = [0, 1, 2, 3, 4, 5, 6, 7, 100, 101] + 'LinearRing'] + __ogr_int = [1, 2, 3, 4, 5, 6, 7, 101] def __init__(self, input): "Figures out the correct OGR Type based upon the input." @@ -40,7 +79,7 @@ class OGRGeomType(object): self._index = input._index elif isinstance(input, StringType): idx = self._has_str(self.__ogr_str, input) - if not idx: + if idx == None: raise OGRException, 'Invalid OGR String Type "%s"' % input self._index = idx elif isinstance(input, int): @@ -61,7 +100,7 @@ class OGRGeomType(object): return self._index == other._index elif isinstance(other, StringType): idx = self._has_str(self.__ogr_str, other) - if idx: return self._index == idx + if not (idx == None): return self._index == idx return False elif isinstance(other, int): if not other in self.__ogr_int: return False @@ -70,9 +109,10 @@ class OGRGeomType(object): raise TypeError, 'Cannot compare with type: %s' % str(type(other)) def _has_str(self, arr, s): - slow = s.lower() + "Case-insensitive search of the string array for the given pattern." + s_low = s.lower() for i in xrange(len(arr)): - if slow == arr[i].lower(): return i + if s_low == arr[i].lower(): return i return None @property @@ -99,18 +139,15 @@ class OGRGeometry(object): "Initializes Geometry on either WKT or an OGR pointer as input." if isinstance(input, StringType): + # Getting the spatial reference + self._init_srs(srs) + # First, trying the input as WKT buf = c_char_p(input) g = c_void_p() - # Getting the spatial - if not isinstance(srs, SpatialReference): - s = SpatialReference() # creating an empty spatial reference - else: - s = srs.clone() # cloning the given spatial reference - try: - check_err(lgdal.OGR_G_CreateFromWkt(byref(buf), s._srs, byref(g))) + check_err(lgdal.OGR_G_CreateFromWkt(byref(buf), self._s._srs, byref(g))) except OGRException, msg: try: ogr_t = OGRGeomType(input) # Seeing if the input is a valid short-hand string @@ -118,8 +155,10 @@ class OGRGeometry(object): except: raise OGRException, 'Could not initialize on WKT "%s"' % input elif isinstance(input, OGRGeomType): + self._init_srs(srs) g = lgdal.OGR_G_CreateGeometry(input.num) - elif isinstance(input, int): + lgdal.OGR_G_AssignSpatialReference(g, self._s._srs) + elif isinstance(input, IntType): # OGR Pointer (integer) was the input g = input else: @@ -133,13 +172,24 @@ class OGRGeometry(object): # Setting the class depending upon the OGR Geometry Type self.__class__ = GEO_CLASSES[self.geom_type.num] + def _init_srs(self, srs): + # Getting the spatial + if not isinstance(srs, SpatialReference): + self._s = SpatialReference() # creating an empty spatial reference + else: + self._s = srs.clone() # cloning the given spatial reference + + def __add__(self, other): + "Returns the union of the two geometries." + return self.union(other) + def __del__(self): "Deletes this Geometry." if self._g: lgdal.OGR_G_DestroyGeometry(self._g) def __eq__(self, other): "Is this Geometry equal to the other?" - return lgdal.OGR_G_Equals(self._g, other._g) + return self.equals(other) def __str__(self): "WKT is used for the string representation." @@ -163,9 +213,14 @@ class OGRGeometry(object): @property def point_count(self): - "The number of Points in this Geometry." + "Returns the number of Points in this Geometry." return lgdal.OGR_G_GetPointCount(self._g) + @property + def num_coords(self): + "Returns the number of Points in this Geometry." + return self.point_count + @property def srs(self): "Returns the Spatial Reference for this Geometry." @@ -187,12 +242,27 @@ class OGRGeometry(object): buf = c_char_p() check_err(lgdal.OGR_G_ExportToWkt(self._g, byref(buf))) return string_at(buf) + + @property + def area(self): + "Returns the area for a LinearRing, Polygon, or MultiPolygon; 0 otherwise." + a = lgdal.OGR_G_GetArea(self._g) + return a.value #### Geometry Methods #### def clone(self): "Clones this OGR Geometry." return OGRGeometry(lgdal.OGR_G_Clone(self._g)) + def close_rings(self): + """If there are any rings within this geometry that have not been + closed, this routine will do so by adding the starting point at the + end.""" + # Closing the open rings. + lgdal.OGR_G_CloseRings(self._g) + # This "fixes" a GDAL bug. See http://trac.osgeo.org/gdal/ticket/1673 + foo = self.wkt + def transform(self, coord_trans): "Transforms this Geometry with the given CoordTransform object." if not isinstance(coord_trans, CoordTransform): @@ -205,6 +275,78 @@ class OGRGeometry(object): raise OGRException, 'SpatialReference object required for transform_to.' check_err(lgdal.OGR_G_TransformTo(self._g, srs._srs)) + #### Topology Methods #### + def _topology(self, topo_func, other): + """A generalized function for topology operations, takes a GDAL function and + the other geometry to perform the operation on.""" + if not isinstance(other, OGRGeometry): + raise OGRException, 'Must use another OGRGeometry object for topology operations!' + + # Calling the passed-in topology function with the other geometry + status = topo_func(self._g, other._g) + + # Returning based on the status code (an integer) + if status: return True + else: return False + + def intersects(self, other): + "Returns True if this geometry intersects with the other." + return self._topology(lgdal.OGR_G_Intersects, other) + + def equals(self, other): + "Returns True if this geometry is equivalent to the other." + return self._topology(lgdal.OGR_G_Equals, other) + + def disjoint(self, other): + "Returns True if this geometry and the other are spatially disjoint." + return self._topology(lgdal.OGR_G_Disjoint, other) + + def touches(self, other): + "Returns True if this geometry touches the other." + return self._topology(lgdal.OGR_G_Touches, other) + + def crosses(self, other): + "Returns True if this geometry crosses the other." + return self._topology(lgdal.OGR_G_Crosses, other) + + def within(self, other): + "Returns True if this geometry is within the other." + return self._topology(lgdal.OGR_G_Within, other) + + def contains(self, other): + "Returns True if this geometry contains the other." + return self._topology(lgdal.OGR_G_Contains, other) + + def overlaps(self, other): + "Returns True if this geometry overlaps the other." + return self._topology(lgdal.OGR_G_Overlaps, other) + + #### Geometry-generation Methods #### + def _geomgen(self, gen_func, other): + if not isinstance(other, OGRGeometry): + raise OGRException, 'Must use another OGRGeometry object for geometry-generating operations!' + return OGRGeometry(gen_func(self._g, other._g)) + + def union(self, other): + """Returns a new geometry consisting of the region which is the union of + this geometry and the other.""" + return self._geomgen(lgdal.OGR_G_Union, other) + + def difference(self, other): + """Returns a new geometry consisting of the region which is the difference + of this geometry and the other.""" + return self._geomgen(lgdal.OGR_G_Difference, other) + + def sym_difference(self, other): + """Returns a new geometry which is the symmetric difference of this + geometry and the other.""" + return self._geomgen(lgdal.OGR_G_SymmetricDifference, other) + + def intersection(self, other): + """Returns a new geometry consisting of the region of intersection of this + geometry and the other.""" + return self._geomgen(lgdal.OGR_G_Intersection, other) + # The subclasses for OGR Geometry. class Point(OGRGeometry): @@ -298,6 +440,20 @@ class Polygon(OGRGeometry): "Returns a tuple of LinearRing coordinate tuples." return tuple(self.__getitem__(i).tuple for i in xrange(self.geom_count)) + @property + def point_count(self): + "The number of Points in this Polygon." + # Summing up the number of points in each ring of the Polygon. + return sum([self.__getitem__(i).point_count for i in xrange(self.geom_count)]) + + @property + def centroid(self): + "Returns the centroid (a Point) of this Polygon." + # The centroid is a Point, create a geometry for this. + p = OGRGeometry(OGRGeomType('Point')) + check_err(lgdal.OGR_G_Centroid(self._g, p._g)) + return p + # Geometry Collection base class. class GeometryCollection(OGRGeometry): "The Geometry Collection class." @@ -320,9 +476,25 @@ class GeometryCollection(OGRGeometry): def add(self, geom): "Add the geometry to this Geometry Collection." - if not isinstance(geom, OGRGeometry): + if isinstance(geom, OGRGeometry): + ptr = geom._g + elif isinstance(geom, StringType): + tmp = OGRGeometry(geom) + ptr = tmp._g + else: raise OGRException, 'Must add an OGRGeometry.' - lgdal.OGR_G_AddGeometry(self._g, geom._g) + lgdal.OGR_G_AddGeometry(self._g, ptr) + + @property + def point_count(self): + "The number of Points in this Geometry Collection." + # Summing up the number of points in each geometry in this collection + return sum([self.__getitem__(i).point_count for i in xrange(self.geom_count)]) + + @property + def tuple(self): + "Returns a tuple representation of this Geometry Collection." + return tuple(self.__getitem__(i).tuple for i in xrange(self.geom_count)) # Multiple Geometry types. class MultiPoint(GeometryCollection): pass diff --git a/django/contrib/gis/gdal/SpatialReference.py b/django/contrib/gis/gdal/SpatialReference.py index ddd306d3ee..54e338e854 100644 --- a/django/contrib/gis/gdal/SpatialReference.py +++ b/django/contrib/gis/gdal/SpatialReference.py @@ -11,6 +11,36 @@ from django.contrib.gis.gdal.libgdal import lgdal # Getting the error checking routine and exceptions from django.contrib.gis.gdal.OGRError import check_err, OGRException, SRSException +""" + The Spatial Reference class, represensents OGR Spatial Reference objects. + + Example: + >>> from django.contrib.gis.gdal import SpatialReference + >>> srs = SpatialReference('WGS84') + >>> print srs + GEOGCS["WGS 84", + DATUM["WGS_1984", + SPHEROID["WGS 84",6378137,298.257223563, + AUTHORITY["EPSG","7030"]], + TOWGS84[0,0,0,0,0,0,0], + AUTHORITY["EPSG","6326"]], + PRIMEM["Greenwich",0, + AUTHORITY["EPSG","8901"]], + UNIT["degree",0.01745329251994328, + AUTHORITY["EPSG","9122"]], + AUTHORITY["EPSG","4326"]] + >>> print srs.proj + +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs + >>> print srs.ellipsoid + (6378137.0, 6356752.3142451793, 298.25722356300003) + >>> print srs.projected, srs.geographic + False True + >>> srs.import_epsg(32140) + >>> print srs.name + NAD83 / Texas South Central +""" + + #### ctypes function prototypes #### def ellipsis_func(f): """Creates a ctypes function prototype for OSR ellipsis property functions, @@ -183,7 +213,13 @@ class SpatialReference(object): self._cache_angular() return self._angular_units - #### Spheroid/Ellipsis Properties #### + #### Spheroid/Ellipsoid Properties #### + @property + def ellipsoid(self): + """Returns a tuple of the ellipsoid parameters: + (semimajor axis, semiminor axis, and inverse flattening).""" + return (self.semi_major, self.semi_minor, self.inverse_flattening) + @property def semi_major(self): "Gets the Semi Major Axis for this Spatial Reference." @@ -267,6 +303,10 @@ class SpatialReference(object): check_err(lgdal.OSRExportToProj4(self._srs, byref(w))) return string_at(w) + def proj4(self): + "Alias for proj()." + return self.proj + @property def xml(self, dialect=''): "Returns the XML representation of this Spatial Reference." diff --git a/django/contrib/gis/gdal/__init__.py b/django/contrib/gis/gdal/__init__.py index 21220a221d..891a60675b 100644 --- a/django/contrib/gis/gdal/__init__.py +++ b/django/contrib/gis/gdal/__init__.py @@ -1,3 +1,4 @@ +from Driver import Driver from DataSource import DataSource from SpatialReference import SpatialReference, CoordTransform from OGRGeometry import OGRGeometry, OGRGeomType diff --git a/django/contrib/gis/gdal/libgdal.py b/django/contrib/gis/gdal/libgdal.py index f25d5daaf4..151823731c 100644 --- a/django/contrib/gis/gdal/libgdal.py +++ b/django/contrib/gis/gdal/libgdal.py @@ -1,8 +1,9 @@ import os, sys from ctypes import CDLL +from django.contrib.gis.gdal.OGRError import OGRException if os.name == 'nt': - # Windows NT library + # Windows NT shared library lib_name = 'libgdal-1.dll' elif os.name == 'posix': platform = os.uname()[0] @@ -10,13 +11,13 @@ elif os.name == 'posix': # Linux shared library lib_name = 'libgdal.so' elif platform == 'Darwin': - # Mac OSX Shared Library + # Mac OSX shared library lib_name = 'libgdal.dylib' else: - raise GDALException, 'Unknown POSIX platform "%s"' % platform + raise OGRException, 'Unknown POSIX platform "%s"' % platform else: - raise GDALException, 'Unsupported OS "%s"' % os.name + raise OGRException, 'Unsupported OS "%s"' % os.name -# The GDAL C library +# This loads the GDAL/OGR C library lgdal = CDLL(lib_name) diff --git a/django/contrib/gis/tests/__init__.py b/django/contrib/gis/tests/__init__.py index 59c494c838..4aed2e136b 100644 --- a/django/contrib/gis/tests/__init__.py +++ b/django/contrib/gis/tests/__init__.py @@ -1,14 +1,15 @@ from unittest import TestSuite, makeSuite, TextTestRunner -import test_geos, test_gdal_ds, test_gdal_srs, test_gdal_geom, test_spatialrefsys +import test_geos, test_gdal_ds, test_gdal_driver, test_gdal_srs, test_gdal_geom, test_spatialrefsys def suite(): s = TestSuite() s.addTest(test_geos.suite()) s.addTest(test_gdal_ds.suite()) + s.addTest(test_gdal_driver.suite()) s.addTest(test_gdal_srs.suite()) s.addTest(test_gdal_geom.suite()) s.addTest(test_spatialrefsys.suite()) return s -def run(verbosity=2): +def run(verbosity=1): TextTestRunner(verbosity=verbosity).run(suite()) diff --git a/django/contrib/gis/tests/geometries.py b/django/contrib/gis/tests/geometries.py index 33f875ef39..ae28d81929 100644 --- a/django/contrib/gis/tests/geometries.py +++ b/django/contrib/gis/tests/geometries.py @@ -53,26 +53,25 @@ errors = (TestGeom('GEOMETR##!@#%#............a32515', bad=True, hex=False), # Polygons polygons = (TestGeom('POLYGON ((0 0, 0 100, 100 100, 100 0, 0 0), (10 10, 10 90, 90 90, 90 10, 10 10))', - n_i=1, ext_ring_cs=((0, 0), (0, 100), (100, 100), (100, 0), (0, 0)), pc=10, + n_i=1, ext_ring_cs=((0, 0), (0, 100), (100, 100), (100, 0), (0, 0)), n_p=10, area=3600.0, centroid=(50., 50.), ), TestGeom('POLYGON ((0 0, 0 100, 100 100, 100 0, 0 0), (10 10, 10 20, 20 20, 20 10, 10 10), (80 80, 80 90, 90 90, 90 80, 80 80))', - n_i=2, ext_ring_cs=((0, 0), (0, 100), (100, 100), (100, 0), (0, 0)), pc=15, + n_i=2, ext_ring_cs=((0, 0), (0, 100), (100, 100), (100, 0), (0, 0)), n_p=15, area=9800.0, centroid=(50., 50.), ), TestGeom('POLYGON ((0 0, 0 100, 100 100, 100 0, 0 0))', - n_i=0, ext_ring_cs=((0, 0), (0, 100), (100, 100), (100, 0), (0, 0)), pc=10, + n_i=0, ext_ring_cs=((0, 0), (0, 100), (100, 100), (100, 0), (0, 0)), n_p=5, area=10000.0, centroid=(50., 50.), ), TestGeom('POLYGON ((-95.3848703124799471 29.7056021479768511, -95.3851905195191847 29.7046588196500281, -95.3859356966379011 29.7025053545605502, -95.3860723000647539 29.7020963367038391, -95.3871517697222089 29.6989779021280995, -95.3865578518265522 29.6990856888057202, -95.3862634205175226 29.6999471753441782, -95.3861991779541967 29.6999591988978615, -95.3856773799358137 29.6998323107113578, -95.3856209915427229 29.6998005235473741, -95.3855833545501639 29.6996619391729801, -95.3855776331865002 29.6996232659570047, -95.3850162731712885 29.6997236706530536, -95.3831047357410284 29.7000847603095082, -95.3829800724914776 29.7000676365023502, -95.3828084594470909 29.6999969684031200, -95.3828131504821499 29.6999090511531065, -95.3828022942979601 29.6998152117366025, -95.3827893930918833 29.6997790953076759, -95.3825174668099862 29.6998267772748825, -95.3823521544804862 29.7000451723151606, -95.3820491918785223 29.6999682034582335, -95.3817932841505893 29.6999640407204772, -95.3815438924600443 29.7005983712500630, -95.3807812390843424 29.7007538492921590, -95.3778578936435935 29.7012966201172048, -95.3770817300034679 29.7010555145969093, -95.3772763716395957 29.7004995005932031, -95.3769891024414420 29.7005797730360186, -95.3759855007185990 29.7007754783987821, -95.3759516423090474 29.7007305400669388, -95.3765252155960042 29.6989549173240874, -95.3766842746727832 29.6985134987163164, -95.3768510987262914 29.6980530300744938, -95.3769198676258014 29.6977137204527573, -95.3769616670751930 29.6973351617272172, -95.3770309229297766 29.6969821084304186, -95.3772352596880637 29.6959751305871613, -95.3776232419333354 29.6945439060847463, -95.3776849628727064 29.6943364710766069, -95.3779699491714723 29.6926548349458947, -95.3781945479573494 29.6920088336742545, -95.3785807118394189 29.6908279316076005, -95.3787441368896651 29.6908846275832197, -95.3787903214163890 29.6907152912461640, -95.3791765069353659 29.6893335376821526, -95.3794935959513026 29.6884781789101595, -95.3796592071232112 29.6880066681407619, -95.3799788182090111 29.6873687353035081, -95.3801545516183893 29.6868782380716993, -95.3801258908302145 29.6867756621337762, -95.3801104284899566 29.6867229678809572, -95.3803803523746154 29.6863753372986459, -95.3821028558287622 29.6837392961470421, -95.3827289584682205 29.6828097375216160, -95.3827494698109035 29.6790739156259278, -95.3826022014838486 29.6776502228345507, -95.3825047356438063 29.6765773006280753, -95.3823473035336917 29.6750405250369127, -95.3824540163482055 29.6750076408228587, -95.3838984230304305 29.6745679207378679, -95.3916547074937426 29.6722459226508377, -95.3926154662749468 29.6719609085105489, -95.3967246645118081 29.6707316485589736, -95.3974588054406780 29.6705065336410989, -95.3978523748756828 29.6703795547846845, -95.3988598162279970 29.6700874981900853, -95.3995628600665952 29.6698505300412414, -95.4134721665944170 29.6656841279906232, -95.4143262068232616 29.6654291174019278, -95.4159685142480214 29.6649750989232288, -95.4180067396277565 29.6643253024318021, -95.4185886692196590 29.6641482768691063, -95.4234155309609662 29.6626925393704788, -95.4287785503196346 29.6611023620959706, -95.4310287312749352 29.6604222580752648, -95.4320295629628959 29.6603361318136720, -95.4332899683975739 29.6600560661713608, -95.4342675748811047 29.6598454934599900, -95.4343110414310871 29.6598411486215490, -95.4345576779282538 29.6598147020668499, -95.4348823041721630 29.6597875803673112, -95.4352827715209457 29.6597762346946681, -95.4355290431309982 29.6597827926562374, -95.4359197997999331 29.6598014511782715, -95.4361907884752156 29.6598444333523368, -95.4364608955807228 29.6598901433108217, -95.4367250147512323 29.6599494499910712, -95.4364898759758091 29.6601880616540186, -95.4354501111810691 29.6616378572201107, -95.4381459623171224 29.6631265631655126, -95.4367852490863129 29.6642266600024023, -95.4370040894557263 29.6643425389568769, -95.4367078350812648 29.6645492592343238, -95.4366081749871285 29.6646291473027297, -95.4358539359938192 29.6652308742342932, -95.4350327668927889 29.6658995989314462, -95.4350580905272921 29.6678812477895271, -95.4349710541447536 29.6680054925936965, -95.4349500440473548 29.6671410080890006, -95.4341492724148850 29.6678790545191688, -95.4340248868274728 29.6680353198492135, -95.4333227845797438 29.6689245624945990, -95.4331325652123326 29.6691616138940901, -95.4321314741096955 29.6704473333237253, -95.4320435792664341 29.6702578985411982, -95.4320147929883547 29.6701800936425109, -95.4319764538662980 29.6683246590817085, -95.4317490976340679 29.6684974372577166, -95.4305958185342718 29.6694049049170374, -95.4296600735653016 29.6701723430938493, -95.4284928989940937 29.6710931793380972, -95.4274630532378580 29.6719378813640091, -95.4273056811974811 29.6720684984625791, -95.4260554084574864 29.6730668861566969, -95.4253558063699643 29.6736342467365724, -95.4249278826026028 29.6739557343648919, -95.4248648873821423 29.6745400910786152, -95.4260016131471929 29.6750987014005858, -95.4258567183010911 29.6753452063069929, -95.4260238081486847 29.6754322077221353, -95.4258707374502393 29.6756647377294307, -95.4257951755816691 29.6756407098663360, -95.4257701599566985 29.6761077719536068, -95.4257726684792260 29.6761711204603955, -95.4257980187195614 29.6770219651929423, -95.4252712669032519 29.6770161558853758, -95.4249234392992065 29.6770068683962300, -95.4249574272905789 29.6779707498635759, -95.4244725881033702 29.6779825646764159, -95.4222269476429545 29.6780711474441716, -95.4223032371999267 29.6796029391538809, -95.4239133706588945 29.6795331493690355, -95.4224579084327331 29.6813706893847780, -95.4224290108823965 29.6821953228763924, -95.4230916478977349 29.6822130268724109, -95.4222928279595521 29.6832041816675343, -95.4228763710016352 29.6832087677714505, -95.4223401691637179 29.6838987872753748, -95.4211655906087088 29.6838784024852984, -95.4201984153205558 29.6851319258758082, -95.4206156387716362 29.6851623398125319, -95.4213438084897660 29.6851763011334739, -95.4212071118618752 29.6853679931624974, -95.4202651399651245 29.6865313962980508, -95.4172061157659783 29.6865816431043932, -95.4182217951255183 29.6872251197301544, -95.4178664826439160 29.6876750901471631, -95.4180678442928780 29.6877960336377207, -95.4188763472917572 29.6882826379510938, -95.4185374500596311 29.6887137897831934, -95.4182121713132290 29.6885097429738813, -95.4179857231741551 29.6888118367840086, -95.4183106010563620 29.6890048676118212, -95.4179489865331334 29.6894546700979056, -95.4175581746284820 29.6892323606815438, -95.4173439957341571 29.6894990139807007, -95.4177411199311081 29.6897435034738422, -95.4175789200209721 29.6899207529979208, -95.4170598559864800 29.6896042165807508, -95.4166733682539814 29.6900891174451367, -95.4165941362704331 29.6900347214235047, -95.4163537218065301 29.6903529467753238, -95.4126843270708775 29.6881086357212780, -95.4126604121378392 29.6880942378803496, -95.4126672298953338 29.6885951670109982, -95.4126680884821923 29.6887052446594275, -95.4158080137241882 29.6906382377959339, -95.4152061403821961 29.6910871045531586, -95.4155842583188161 29.6917382915894308, -95.4157426793520358 29.6920726941677096, -95.4154520563662203 29.6922052332446427, -95.4151389936167078 29.6923261661269571, -95.4148649784384872 29.6924343866430256, -95.4144051352401590 29.6925623927348106, -95.4146792019416665 29.6926770338507744, -95.4148824479948985 29.6928117893696388, -95.4149851734360226 29.6929823719519774, -95.4140436551925291 29.6929626643100946, -95.4140465993023241 29.6926545917254892, -95.4137269186733334 29.6927395764256090, -95.4137372859685513 29.6935432485666624, -95.4135702836218655 29.6933186678088283, -95.4133925235973237 29.6930415229852152, -95.4133017035615580 29.6928685062036166, -95.4129588921634593 29.6929391128977862, -95.4125107395559695 29.6930481664661485, -95.4102647423187307 29.6935850183258019, -95.4081931340840157 29.6940907430947760, -95.4078783596459772 29.6941703429951609, -95.4049213975000043 29.6948723732981961, -95.4045944244127071 29.6949626434239207, -95.4045865139788134 29.6954109019001358, -95.4045953345484037 29.6956972800496963, -95.4038879332535146 29.6958296089365490, -95.4040366394459340 29.6964389004769842, -95.4032774779020798 29.6965643341263892, -95.4026066501239853 29.6966646227683881, -95.4024991226393837 29.6961389766619703, -95.4011781398631911 29.6963566063186377, -95.4011524097636112 29.6962596176762190, -95.4018184046368276 29.6961399466727336, -95.4016995838361908 29.6956442609415099, -95.4007100753964608 29.6958900524002978, -95.4008032469935188 29.6962639900781404, -95.3995660267125487 29.6965636449370329, -95.3996140564775601 29.6967877962763644, -95.3996364430014410 29.6968901984825280, -95.3984003269631842 29.6968679634805746, -95.3981442026887265 29.6983660679730335, -95.3980178461957706 29.6990890276252415, -95.3977097967130163 29.7008526152273049, -95.3962347157626027 29.7009697553607630, -95.3951949050136250 29.7004740386619019, -95.3957564950617183 29.6990281830553187, -95.3965927101519924 29.6968771129030706, -95.3957496517238184 29.6970800358387095, -95.3957720559467361 29.6972264611230727, -95.3957391586571788 29.6973548894558732, -95.3956286413405365 29.6974949857280883, -95.3955111053256957 29.6975661086270186, -95.3953215342724121 29.6976022763384790, -95.3951795558443365 29.6975846977491038, -95.3950369632041060 29.6975175779330200, -95.3949401089966500 29.6974269267953304, -95.3948740281415581 29.6972903308506346, -95.3946650813866910 29.6973397326847923, -95.3947654059391112 29.6974882560192022, -95.3949627316619768 29.6980355864961858, -95.3933200807862249 29.6984590863712796, -95.3932606497523494 29.6984464798710839, -95.3932983699113350 29.6983154306484352, -95.3933058014696655 29.6982165816983610, -95.3932946347785133 29.6981089778195759, -95.3931780601756287 29.6977068906794841, -95.3929928222970602 29.6977541771878180, -95.3930873169846478 29.6980676264932946, -95.3932743746374570 29.6981249406449663, -95.3929512584706316 29.6989526513922222, -95.3919850280655197 29.7014358632108646, -95.3918950918929056 29.7014169320765724, -95.3916928317890296 29.7019232352846423, -95.3915424614970959 29.7022988712928289, -95.3901530441668939 29.7058519502930061, -95.3899656322116698 29.7059156823562418, -95.3897628748670883 29.7059900058266777, -95.3896062677805787 29.7060738276384946, -95.3893941800512266 29.7061891695242046, -95.3892150365492455 29.7062641292949436, -95.3890502563035199 29.7063339729630940, -95.3888717930715586 29.7063896908080736, -95.3886925428988945 29.7064453871994978, -95.3885376849411983 29.7064797304524149, -95.3883284158984139 29.7065153575050189, -95.3881046767627794 29.7065368368267357, -95.3878809284696132 29.7065363048447537, -95.3876046356120924 29.7065288525102424, -95.3873060894974714 29.7064822806001452, -95.3869851943158409 29.7063993367575350, -95.3865967896568065 29.7062870572919202, -95.3861785624983156 29.7061492099008184, -95.3857375009733488 29.7059887337478798, -95.3854573290902152 29.7058683664514618, -95.3848703124799471 29.7056021479768511))', - n_i=0, ext_ring_cs=False + n_i=0, ext_ring_cs=False, n_p=264, area=0.00129917360654, centroid=(-95.403569179437341, 29.681772571690402), ), ) # MultiPolygons -multipolygons = (TestGeom('MULTIPOLYGON (((100 20, 180 20, 180 100, 100 100, 100 20)), ((20 100, 100 100, 100 180, 20 180, 20 100)), ((100 180, 180 180, 180 260, 100 260, 100 180)), ((180 100, 260 100, 260 180, 180 180, 180 100)))', valid=True, n_p=4), +multipolygons = (TestGeom('MULTIPOLYGON (((100 20, 180 20, 180 100, 100 100, 100 20)), ((20 100, 100 100, 100 180, 20 180, 20 100)), ((100 180, 180 180, 180 260, 100 260, 100 180)), ((180 100, 260 100, 260 180, 180 180, 180 100)))', valid=True, num_geom=4, n_p=20), TestGeom('MULTIPOLYGON (((60 300, 320 220, 260 60, 60 100, 60 300)), ((60 300, 320 220, 260 60, 60 100, 60 300)))', valid=False), - TestGeom('MULTIPOLYGON (((180 60, 240 160, 300 60, 180 60)), ((80 80, 180 60, 160 140, 240 160, 360 140, 300 60, 420 100, 320 280, 120 260, 80 80)))', valid=True, n_p=2), + TestGeom('MULTIPOLYGON (((180 60, 240 160, 300 60, 180 60)), ((80 80, 180 60, 160 140, 240 160, 360 140, 300 60, 420 100, 320 280, 120 260, 80 80)))', valid=True, num_geom=2, n_p=14), ) - # Points points = (TestGeom('POINT (5 23)', x=5.0, y=23.0, centroid=(5.0, 23.0)), TestGeom('POINT (-95.338492 29.723893)', x=-95.338492, y=29.723893, centroid=(-95.338492, 29.723893)), @@ -83,23 +82,21 @@ points = (TestGeom('POINT (5 23)', x=5.0, y=23.0, centroid=(5.0, 23.0)), ) # MultiPoints -multipoints = (TestGeom('MULTIPOINT(10 10, 20 20 )', points=((10., 10.), (20., 20.)), centroid=(15., 15.)), +multipoints = (TestGeom('MULTIPOINT(10 10, 20 20 )', n_p=2, points=((10., 10.), (20., 20.)), centroid=(15., 15.)), TestGeom('MULTIPOINT(10 10, 20 20, 10 20, 20 10)', - points=((10., 10.), (20., 20.), (10., 20.), (20., 10.)), + n_p=4, points=((10., 10.), (20., 20.), (10., 20.), (20., 10.)), centroid=(15., 15.)), ) # LineStrings -linestrings = (TestGeom('LINESTRING (60 180, 120 100, 180 180)', centroid=(120, 140)), -# TestGeom('LINESTRING (80 0, 80 120, 120 120, 120 0))', centroid=(100, 69)), - TestGeom('LINESTRING (0 0, 5 5, 10 5, 10 10)', centroid=(6.1611652351681556, 4.6966991411008934)), +linestrings = (TestGeom('LINESTRING (60 180, 120 100, 180 180)', n_p=3, centroid=(120, 140), tup=((60, 180), (120, 100), (180, 180))), + TestGeom('LINESTRING (0 0, 5 5, 10 5, 10 10)', n_p=4, centroid=(6.1611652351681556, 4.6966991411008934), tup=((0, 0), (5, 5), (10, 5), (10, 10)),) ) # MultiLineStrings -multilinestrings = (TestGeom('MULTILINESTRING ((0 0, 0 100), (100 0, 100 100))', centroid=(50, 50)), -# TestGeom('MULTILINESTRING ((0 0, 0 200, 200 200, 200 0, 0 0), (60 180, 20 180, 20 140, 60 140, 60 180))', centroid=(90, 100)), +multilinestrings = (TestGeom('MULTILINESTRING ((0 0, 0 100), (100 0, 100 100))', n_p=4, centroid=(50, 50), tup=(((0, 0), (0, 100)), ((100, 0), (100, 100)))), TestGeom('MULTILINESTRING ((20 20, 60 60), (20 -20, 60 -60), (-20 -20, -60 -60), (-20 20, -60 60), (-80 0, 0 80, 80 0, 0 -80, -80 0), (-40 20, -40 -20), (-20 40, 20 40), (40 20, 40 -20), (20 -40, -20 -40))', - centroid=(0, 0)), + n_p=21, centroid=(0, 0), tup=(((20., 20.), (60., 60.)), ((20., -20.), (60., -60.)), ((-20., -20.), (-60., -60.)), ((-20., 20.), (-60., 60.)), ((-80., 0.), (0., 80.), (80., 0.), (0., -80.), (-80., 0.)), ((-40., 20.), (-40., -20.)), ((-20., 40.), (20., 40.)), ((40., 20.), (40., -20.)), ((20., -40.), (-20., -40.)))) ) # ==================================================== diff --git a/django/contrib/gis/tests/test_gdal_driver.py b/django/contrib/gis/tests/test_gdal_driver.py new file mode 100644 index 0000000000..1ff65ac31b --- /dev/null +++ b/django/contrib/gis/tests/test_gdal_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/tests/test_gdal_ds.py b/django/contrib/gis/tests/test_gdal_ds.py index f70b5225f2..658f2c1935 100644 --- a/django/contrib/gis/tests/test_gdal_ds.py +++ b/django/contrib/gis/tests/test_gdal_ds.py @@ -40,7 +40,7 @@ class DataSourceTest(unittest.TestCase): self.assertEqual(source.ds, ds.name) # Making sure the driver name matches up - self.assertEqual('ESRI Shapefile', ds.driver) + self.assertEqual('ESRI Shapefile', str(ds.driver)) # Making sure indexing works try: diff --git a/django/contrib/gis/tests/test_gdal_geom.py b/django/contrib/gis/tests/test_gdal_geom.py index a622d4a3f8..8d143dd825 100644 --- a/django/contrib/gis/tests/test_gdal_geom.py +++ b/django/contrib/gis/tests/test_gdal_geom.py @@ -10,11 +10,11 @@ class OGRGeomTest(unittest.TestCase): # OGRGeomType should initialize on all these inputs. try: - g = OGRGeomType(0) g = OGRGeomType(1) g = OGRGeomType(7) g = OGRGeomType('point') g = OGRGeomType('GeometrycollectioN') + g = OGRGeomType('LINearrING') except: self.fail('Could not create an OGRGeomType object!') @@ -42,15 +42,54 @@ class OGRGeomTest(unittest.TestCase): for p in points: if not hasattr(p, 'z'): # No 3D pnt = OGRGeometry(p.wkt) - self.assertEqual(pnt.geom_type, 1) + self.assertEqual(1, pnt.geom_type) + self.assertEqual('POINT', pnt.geom_name) self.assertEqual(p.x, pnt.x) self.assertEqual(p.y, pnt.y) self.assertEqual((p.x, p.y), pnt.tuple) - def test03_polygons(self): + def test03_multipoints(self): + "Testing MultiPoint objects." + + for mp in multipoints: + mgeom1 = OGRGeometry(mp.wkt) # First one from WKT + self.assertEqual(4, mgeom1.geom_type) + self.assertEqual('MULTIPOINT', mgeom1.geom_name) + mgeom2 = OGRGeometry('MULTIPOINT') # Creating empty multipoint + mgeom3 = OGRGeometry('MULTIPOINT') + for g in mgeom1: + mgeom2.add(g) # adding each point from the multipoints + mgeom3.add(g.wkt) # should take WKT as well + self.assertEqual(mgeom1, mgeom2) # they should equal + self.assertEqual(mgeom1, mgeom3) + self.assertEqual(mp.points, mgeom2.tuple) + self.assertEqual(mp.n_p, mgeom2.point_count) + + def test04_linestring(self): + "Testing LineString objects." + for ls in linestrings: + linestr = OGRGeometry(ls.wkt) + self.assertEqual(2, linestr.geom_type) + self.assertEqual('LINESTRING', linestr.geom_name) + self.assertEqual(ls.n_p, linestr.point_count) + self.assertEqual(ls.tup, linestr.tuple) + + def test05_multilinestring(self): + "Testing MultiLineString objects." + for mls in multilinestrings: + mlinestr = OGRGeometry(mls.wkt) + self.assertEqual(5, mlinestr.geom_type) + self.assertEqual('MULTILINESTRING', mlinestr.geom_name) + self.assertEqual(mls.n_p, mlinestr.point_count) + self.assertEqual(mls.tup, mlinestr.tuple) + + def test06_polygons(self): "Testing Polygon objects." for p in polygons: poly = OGRGeometry(p.wkt) + self.assertEqual(3, poly.geom_type) + self.assertEqual('POLYGON', poly.geom_name) + self.assertEqual(p.n_p, poly.point_count) first = True for r in poly: if first and p.ext_ring_cs: @@ -60,16 +99,35 @@ class OGRGeomTest(unittest.TestCase): self.assertEqual(len(p.ext_ring_cs), r.point_count) self.assertEqual(p.ext_ring_cs, r.tuple) - def test04_multipoints(self): - "Testing MultiPoint objects." + def test07_closepolygons(self): + "Testing closing Polygon objects." + + # Both rings in this geometry are not closed. + poly = OGRGeometry('POLYGON((0 0, 5 0, 5 5, 0 5), (1 1, 2 1, 2 2, 2 1))') + self.assertEqual(8, poly.point_count) + try: + c = poly.centroid + except OGRException: + # Should raise an OGR exception, rings are not closed + pass + else: + self.fail('Should have raised an OGRException!') + + # Closing the rings + poly.close_rings() + self.assertEqual(10, poly.point_count) # Two closing points should've been added + self.assertEqual(OGRGeometry('POINT(2.5 2.5)'), poly.centroid) + + def test08_multipolygons(self): + "Testing MultiPolygon objects." + for mp in multipolygons: + mpoly = OGRGeometry(mp.wkt) + self.assertEqual(6, mpoly.geom_type) + self.assertEqual('MULTIPOLYGON', mpoly.geom_name) + if mp.valid: + self.assertEqual(mp.n_p, mpoly.point_count) + self.assertEqual(mp.num_geom, len(mpoly)) - for mp in multipoints: - mgeom1 = OGRGeometry(mp.wkt) # First one from WKT - mgeom2 = OGRGeometry('MULTIPOINT') # Creating empty multipoint - for g in mgeom1: - mgeom2.add(g) # adding each point from the multipoint - self.assertEqual(mgeom1, mgeom2) # they should equal - def suite(): s = unittest.TestSuite() s.addTest(unittest.makeSuite(OGRGeomTest)) diff --git a/django/contrib/gis/tests/test_geos.py b/django/contrib/gis/tests/test_geos.py index 6e91e0fccf..b7edaa531b 100644 --- a/django/contrib/gis/tests/test_geos.py +++ b/django/contrib/gis/tests/test_geos.py @@ -115,8 +115,9 @@ class GeosTest2(unittest.TestCase): self.assertEqual(mp.valid, mpoly.valid) if mp.valid: - self.assertEqual(mp.n_p, mpoly.num_geom) - self.assertEqual(mp.n_p, len(mpoly)) + self.assertEqual(mp.num_geom, mpoly.num_geom) + self.assertEqual(mp.n_p, mpoly.num_coords) + self.assertEqual(mp.num_geom, len(mpoly)) for p in mpoly: self.assertEqual(p.geom_type, 'Polygon') self.assertEqual(p.geom_typeid, 3)