diff --git a/django/contrib/gis/geos/base.py b/django/contrib/gis/geos/base.py index fdb04d0b78..728e99e2be 100644 --- a/django/contrib/gis/geos/base.py +++ b/django/contrib/gis/geos/base.py @@ -87,16 +87,21 @@ class GEOSGeometry(object): else: raise GEOSException('Could not initialize GEOS Geometry with given input.') + # Post-initialization setup. + self._post_init(srid) + + def _post_init(self, srid): + "Helper routine for performing post-initialization setup." # Setting the SRID, if given. if srid and isinstance(srid, int): self.srid = srid - + # Setting the class type (e.g., Point, Polygon, etc.) self.__class__ = GEOS_CLASSES[self.geom_typeid] # Setting the coordinate sequence for the geometry (will be None on - # geometries that do not have coordinate sequences) + # geometries that do not have coordinate sequences) self._set_cs() - + @property def ptr(self): """ @@ -139,6 +144,20 @@ class GEOSGeometry(object): "Short-hand representation because WKT may be very large." return '<%s object at %s>' % (self.geom_type, hex(addressof(self.ptr))) + # Pickling support + def __getstate__(self): + # The pickled state is simply a tuple of the WKB (in string form) + # and the SRID. + return str(self.wkb), self.srid + + def __setstate__(self, state): + # Instantiating from the tuple state that was pickled. + wkb, srid = state + ptr = from_wkb(wkb, len(wkb)) + if not ptr: raise GEOSException('Invalid Geometry loaded from pickled state.') + self._ptr = ptr + self._post_init(srid) + # Comparison operators def __eq__(self, other): """ @@ -413,23 +432,32 @@ class GEOSGeometry(object): "Alias for `srs` property." return self.srs - def transform(self, ct): - "Transforms this Geometry; only works with GDAL." + def transform(self, ct, clone=False): + """ + Requires GDAL. Transforms the geometry according to the given + transformation object, which may be an integer SRID, and WKT or + PROJ.4 string. By default, the geometry is transformed in-place and + nothing is returned. However if the `clone` keyword is set, then this + geometry will not be modified and a transformed clone will be returned + instead. + """ srid = self.srid if HAS_GDAL and srid: g = OGRGeometry(self.wkb, srid) g.transform(ct) wkb = str(g.wkb) ptr = from_wkb(wkb, len(wkb)) + if clone: + # User wants a cloned transformed geometry returned. + return GEOSGeometry(ptr, srid=g.srid) if ptr: - # Reassigning pointer, and getting the new coordinate sequence pointer. + # Reassigning pointer, and performing post-initialization setup + # again due to the reassignment. destroy_geom(self.ptr) self._ptr = ptr - self._set_cs() - - # Some coordinate transformations do not have an SRID associated - # with them; only set if one exists. - if g.srid: self.srid = g.srid + self._post_init(g.srid) + else: + raise GEOSException('Transformed WKB was invalid.') #### Topology Routines #### def _topology(self, gptr): diff --git a/django/contrib/gis/tests/test_geos.py b/django/contrib/gis/tests/test_geos.py index 19d28cef88..0fd45a3b4d 100644 --- a/django/contrib/gis/tests/test_geos.py +++ b/django/contrib/gis/tests/test_geos.py @@ -703,8 +703,15 @@ class GEOSTest(unittest.TestCase): 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): + for p in (t1, t2, t3, k2): self.assertAlmostEqual(trans.x, p.x, prec) self.assertAlmostEqual(trans.y, p.y, prec) @@ -724,6 +731,27 @@ class GEOSTest(unittest.TestCase): xmax, ymax = max(x), max(y) self.assertEqual((xmin, ymin, xmax, ymax), poly.extent) + def test25_pickle(self): + "Testing pickling and unpickling support." + # Using both pickle and cPickle -- just 'cause. + import pickle, cPickle + + # Creating a list of test geometries for pickling, + # and setting the SRID on some of them. + def get_geoms(lst, srid=None): + return [GEOSGeometry(tg.wkt, srid) for tg in lst] + tgeoms = get_geoms(points) + tgeoms.extend(get_geoms(multilinestrings, 4326)) + tgeoms.extend(get_geoms(polygons, 3084)) + tgeoms.extend(get_geoms(multipolygons, 900913)) + + for geom in tgeoms: + s1, s2 = cPickle.dumps(geom), pickle.dumps(geom) + g1, g2 = cPickle.loads(s1), pickle.loads(s2) + for tmpg in (g1, g2): + self.assertEqual(geom, tmpg) + self.assertEqual(geom.srid, tmpg.srid) + def suite(): s = unittest.TestSuite() s.addTest(unittest.makeSuite(GEOSTest))