diff --git a/django/contrib/gis/gdal/geometries.py b/django/contrib/gis/gdal/geometries.py index 92d2d1ea4b..94aad148fe 100644 --- a/django/contrib/gis/gdal/geometries.py +++ b/django/contrib/gis/gdal/geometries.py @@ -48,11 +48,11 @@ from django.contrib.gis.gdal.base import GDALBase from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope from django.contrib.gis.gdal.error import OGRException, OGRIndexError, SRSException from django.contrib.gis.gdal.geomtype import OGRGeomType +from django.contrib.gis.gdal.libgdal import GEOJSON, GDAL_VERSION from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform # Getting the ctypes prototype functions that interface w/the GDAL C library. from django.contrib.gis.gdal.prototypes import geom as capi, srs as srs_api -GEOJSON = capi.GEOJSON # For recognizing geometry input. from django.contrib.gis.geometry.regex import hex_regex, wkt_regex, json_regex @@ -400,7 +400,8 @@ class OGRGeometry(GDALBase): # afterwards. This is done because of GDAL bug (in versions prior # to 1.7) that turns geometries 3D after transformation, see: # http://trac.osgeo.org/gdal/changeset/17792 - orig_dim = self.coord_dim + if GDAL_VERSION < (1, 7): + orig_dim = self.coord_dim # Depending on the input type, use the appropriate OGR routine # to perform the transformation. @@ -412,11 +413,22 @@ class OGRGeometry(GDALBase): sr = SpatialReference(coord_trans) capi.geom_transform_to(self.ptr, sr.ptr) else: - raise TypeError('Transform only accepts CoordTransform, SpatialReference, string, and integer objects.') + raise TypeError('Transform only accepts CoordTransform, ' + 'SpatialReference, string, and integer objects.') # Setting with original dimension, see comment above. - if self.coord_dim != orig_dim: - self.coord_dim = orig_dim + if GDAL_VERSION < (1, 7): + if isinstance(self, GeometryCollection): + # With geometry collections have to set dimension on + # each internal geometry reference, as the collection + # dimension isn't affected. + for i in xrange(len(self)): + internal_ptr = capi.get_geom_ref(self.ptr, i) + if orig_dim != capi.get_coord_dim(internal_ptr): + capi.set_coord_dim(internal_ptr, orig_dim) + else: + if self.coord_dim != orig_dim: + self.coord_dim = orig_dim def transform_to(self, srs): "For backwards-compatibility." diff --git a/django/contrib/gis/gdal/tests/test_geom.py b/django/contrib/gis/gdal/tests/test_geom.py index 1590d3a23b..0d2f88c55f 100644 --- a/django/contrib/gis/gdal/tests/test_geom.py +++ b/django/contrib/gis/gdal/tests/test_geom.py @@ -136,7 +136,7 @@ class OGRGeomTest(unittest.TestCase): 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)') @@ -172,7 +172,7 @@ class OGRGeomTest(unittest.TestCase): for ls in mlinestr: self.assertEqual(2, ls.geom_type) self.assertEqual('LINESTRING', ls.geom_name) - self.assertRaises(OGRIndexError, mlinestr.__getitem__, len(mlinestr)) + self.assertRaises(OGRIndexError, mlinestr.__getitem__, len(mlinestr)) def test06_linearring(self): "Testing LinearRing objects." @@ -190,7 +190,7 @@ class OGRGeomTest(unittest.TestCase): "Testing Polygon objects." # Testing `from_bbox` class method - bbox = (-180,-90,180,90) + bbox = (-180,-90,180,90) p = OGRGeometry.from_bbox( bbox ) self.assertEqual(bbox, p.extent) @@ -211,13 +211,13 @@ class OGRGeomTest(unittest.TestCase): # 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) @@ -269,11 +269,11 @@ class OGRGeomTest(unittest.TestCase): 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: @@ -295,7 +295,7 @@ class OGRGeomTest(unittest.TestCase): 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: @@ -341,7 +341,7 @@ class OGRGeomTest(unittest.TestCase): "Testing coordinate dimension is the same on transformed geometries." ls_orig = OGRGeometry('LINESTRING(-104.609 38.255)', 4326) ls_trans = OGRGeometry('LINESTRING(992385.4472045 481455.4944650)', 2774) - + prec = 3 ls_orig.transform(ls_trans.srs) # Making sure the coordinate dimension is still 2D. @@ -388,7 +388,7 @@ class OGRGeomTest(unittest.TestCase): 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)): @@ -455,7 +455,31 @@ class OGRGeomTest(unittest.TestCase): self.assertEqual(g1, g2) self.assertEqual(4326, g2.srs.srid) self.assertEqual(g1.srs.wkt, g2.srs.wkt) - + + def test18_ogrgeometry_transform_workaround(self): + "Testing coordinate dimensions on geometries after transformation." + # A bug in GDAL versions prior to 1.7 changes the coordinate + # dimension of a geometry after it has been transformed. + # This test ensures that the bug workarounds employed within + # `OGRGeometry.transform` indeed work. + wkt_2d = "MULTILINESTRING ((0 0,1 1,2 2))" + wkt_3d = "MULTILINESTRING ((0 0 0,1 1 1,2 2 2))" + srid = 4326 + + # For both the 2D and 3D MultiLineString, ensure _both_ the dimension + # of the collection and the component LineString have the expected + # coordinate dimension after transform. + geom = OGRGeometry(wkt_2d, srid) + geom.transform(srid) + self.assertEqual(2, geom.coord_dim) + self.assertEqual(2, geom[0].coord_dim) + self.assertEqual(wkt_2d, geom.wkt) + + geom = OGRGeometry(wkt_3d, srid) + geom.transform(srid) + self.assertEqual(3, geom.coord_dim) + self.assertEqual(3, geom[0].coord_dim) + self.assertEqual(wkt_3d, geom.wkt) def suite(): s = unittest.TestSuite()