From 50613d957a853f83310a03e0f300bbf568f1f65b Mon Sep 17 00:00:00 2001 From: Jackie Leng Date: Wed, 23 Nov 2016 09:23:06 +0100 Subject: [PATCH] Fixed #26920 -- Made GEOSGeometry equality check consider the srid --- django/contrib/gis/geos/geometry.py | 8 +++++--- docs/ref/contrib/gis/geos.txt | 10 +++++++++- docs/releases/1.11.txt | 2 ++ tests/gis_tests/geoapp/test_functions.py | 2 +- tests/gis_tests/geoapp/tests.py | 11 +++++++---- tests/gis_tests/geos_tests/test_geos.py | 21 +++++++++++++++++++++ tests/gis_tests/relatedapp/tests.py | 2 +- tests/gis_tests/test_geoforms.py | 15 ++++++++++----- 8 files changed, 56 insertions(+), 15 deletions(-) diff --git a/django/contrib/gis/geos/geometry.py b/django/contrib/gis/geos/geometry.py index 66f78e70de053..7467a9c879989 100644 --- a/django/contrib/gis/geos/geometry.py +++ b/django/contrib/gis/geos/geometry.py @@ -174,12 +174,14 @@ def from_gml(cls, gml_string): def __eq__(self, other): """ Equivalence testing, a Geometry may be compared with another Geometry - or a WKT representation. + or an EWKT representation. """ if isinstance(other, six.string_types): - return self.wkt == other + if other.startswith('SRID=0;'): + return self.ewkt == other[7:] # Test only WKT part of other + return self.ewkt == other elif isinstance(other, GEOSGeometry): - return self.equals_exact(other) + return self.srid == other.srid and self.equals_exact(other) else: return False diff --git a/docs/ref/contrib/gis/geos.txt b/docs/ref/contrib/gis/geos.txt index e6d609736e67e..55bd5a3744e4f 100644 --- a/docs/ref/contrib/gis/geos.txt +++ b/docs/ref/contrib/gis/geos.txt @@ -160,15 +160,23 @@ Geometries support set-like operators:: The :class:`~GEOSGeometry` equality operator uses :meth:`~GEOSGeometry.equals_exact`, not :meth:`~GEOSGeometry.equals`, i.e. it requires the compared geometries to have the same coordinates in the - same positions:: + same positions with the same SRIDs:: >>> from django.contrib.gis.geos import LineString >>> ls1 = LineString((0, 0), (1, 1)) >>> ls2 = LineString((1, 1), (0, 0)) + >>> ls3 = LineString((1, 1), (0, 0), srid=4326) >>> ls1.equals(ls2) True >>> ls1 == ls2 False + >>> ls3 == ls2 # different SRIDs + False + + .. versionchanged:: 1.11 + + Older versions didn't check the ``srid`` when comparing + ``GEOSGeometry`` objects using the equality operator. Geometry Objects ================ diff --git a/docs/releases/1.11.txt b/docs/releases/1.11.txt index 5683f2fc0782c..53265272ca04b 100644 --- a/docs/releases/1.11.txt +++ b/docs/releases/1.11.txt @@ -423,6 +423,8 @@ Backwards incompatible changes in 1.11 the Google Maps API and seems to be unmaintained. If you're using it, `let us know `_. +* The ``GEOSGeometry`` equality operator now also compares SRID. + Database backend API -------------------- diff --git a/tests/gis_tests/geoapp/test_functions.py b/tests/gis_tests/geoapp/test_functions.py index 0e5f2ee693c05..3d809927b97d9 100644 --- a/tests/gis_tests/geoapp/test_functions.py +++ b/tests/gis_tests/geoapp/test_functions.py @@ -266,7 +266,7 @@ def test_make_valid(self): State.objects.create(name='invalid', poly=invalid_geom) invalid = State.objects.filter(name='invalid').annotate(repaired=functions.MakeValid('poly')).first() self.assertIs(invalid.repaired.valid, True) - self.assertEqual(invalid.repaired, fromstr('POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))')) + self.assertEqual(invalid.repaired, fromstr('POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))', srid=invalid.poly.srid)) @skipUnlessDBFeature("has_MemSize_function") def test_memsize(self): diff --git a/tests/gis_tests/geoapp/tests.py b/tests/gis_tests/geoapp/tests.py index d64ab24b4caca..6ac78502346b4 100644 --- a/tests/gis_tests/geoapp/tests.py +++ b/tests/gis_tests/geoapp/tests.py @@ -63,9 +63,9 @@ def test_proxy(self): nullcity.point.x = 23 nullcity.point.y = 5 # Checking assignments pre & post-save. - self.assertNotEqual(Point(23, 5), City.objects.get(name='NullCity').point) + self.assertNotEqual(Point(23, 5, srid=4326), City.objects.get(name='NullCity').point) nullcity.save() - self.assertEqual(Point(23, 5), City.objects.get(name='NullCity').point) + self.assertEqual(Point(23, 5, srid=4326), City.objects.get(name='NullCity').point) nullcity.delete() # Testing on a Polygon @@ -479,8 +479,11 @@ def test_diff_intersection_union(self): # SpatiaLite). pass else: - self.assertEqual(c.mpoly.difference(geom), c.difference) - if not spatialite: + if spatialite: + # Spatialite `difference` doesn't have an SRID + self.assertEqual(c.mpoly.difference(geom).wkt, c.difference.wkt) + else: + self.assertEqual(c.mpoly.difference(geom), c.difference) self.assertEqual(c.mpoly.intersection(geom), c.intersection) # Ordering might differ in collections self.assertSetEqual(set(g.wkt for g in c.mpoly.sym_difference(geom)), diff --git a/tests/gis_tests/geos_tests/test_geos.py b/tests/gis_tests/geos_tests/test_geos.py index a7724de9bcb52..8a82c161708df 100644 --- a/tests/gis_tests/geos_tests/test_geos.py +++ b/tests/gis_tests/geos_tests/test_geos.py @@ -227,6 +227,27 @@ def test_eq(self): self.assertNotEqual(g, {'foo': 'bar'}) self.assertNotEqual(g, False) + def test_eq_with_srid(self): + "Testing non-equivalence with different srids." + p0 = Point(5, 23) + p1 = Point(5, 23, srid=4326) + p2 = Point(5, 23, srid=32632) + # GEOS + self.assertNotEqual(p0, p1) + self.assertNotEqual(p1, p2) + # EWKT + self.assertNotEqual(p0, p1.ewkt) + self.assertNotEqual(p1, p0.ewkt) + self.assertNotEqual(p1, p2.ewkt) + # Equivalence with matching SRIDs + self.assertEqual(p2, p2) + self.assertEqual(p2, p2.ewkt) + # WKT contains no SRID so will not equal + self.assertNotEqual(p2, p2.wkt) + # SRID of 0 + self.assertEqual(p0, 'SRID=0;POINT (5 23)') + self.assertNotEqual(p1, 'SRID=0;POINT (5 23)') + def test_points(self): "Testing Point objects." prev = fromstr('POINT(0 0)') diff --git a/tests/gis_tests/relatedapp/tests.py b/tests/gis_tests/relatedapp/tests.py index 66507313e8c39..46796e2c2b931 100644 --- a/tests/gis_tests/relatedapp/tests.py +++ b/tests/gis_tests/relatedapp/tests.py @@ -36,7 +36,7 @@ def test02_select_related(self): nm, st, lon, lat = ref self.assertEqual(nm, c.name) self.assertEqual(st, c.state) - self.assertEqual(Point(lon, lat), c.location.point) + self.assertEqual(Point(lon, lat, srid=c.location.point.srid), c.location.point) @skipUnlessDBFeature("has_transform_method") def test03_transform_related(self): diff --git a/tests/gis_tests/test_geoforms.py b/tests/gis_tests/test_geoforms.py index 9149333814d94..1d850a7ca226f 100644 --- a/tests/gis_tests/test_geoforms.py +++ b/tests/gis_tests/test_geoforms.py @@ -48,12 +48,17 @@ def test_geom_type(self): # By default, all geometry types are allowed. fld = forms.GeometryField() for wkt in ('POINT(5 23)', 'MULTIPOLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)))', 'LINESTRING(0 0, 1 1)'): - self.assertEqual(GEOSGeometry(wkt), fld.clean(wkt)) + # `to_python` uses the SRID of OpenLayersWidget if the converted + # value doesn't have an SRID itself. + self.assertEqual(GEOSGeometry(wkt, srid=fld.widget.map_srid), fld.clean(wkt)) pnt_fld = forms.GeometryField(geom_type='POINT') - self.assertEqual(GEOSGeometry('POINT(5 23)'), pnt_fld.clean('POINT(5 23)')) + self.assertEqual(GEOSGeometry('POINT(5 23)', srid=pnt_fld.widget.map_srid), pnt_fld.clean('POINT(5 23)')) # a WKT for any other geom_type will be properly transformed by `to_python` - self.assertEqual(GEOSGeometry('LINESTRING(0 0, 1 1)'), pnt_fld.to_python('LINESTRING(0 0, 1 1)')) + self.assertEqual( + GEOSGeometry('LINESTRING(0 0, 1 1)', srid=pnt_fld.widget.map_srid), + pnt_fld.to_python('LINESTRING(0 0, 1 1)') + ) # but rejected by `clean` with self.assertRaises(forms.ValidationError): pnt_fld.clean('LINESTRING(0 0, 1 1)') @@ -66,7 +71,7 @@ def test_to_python(self): fld = forms.GeometryField() # to_python returns the same GEOSGeometry for a WKT for wkt in ('POINT(5 23)', 'MULTIPOLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)))', 'LINESTRING(0 0, 1 1)'): - self.assertEqual(GEOSGeometry(wkt), fld.to_python(wkt)) + self.assertEqual(GEOSGeometry(wkt, srid=fld.widget.map_srid), fld.to_python(wkt)) # but raises a ValidationError for any other string for wkt in ('POINT(5)', 'MULTI POLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)))', 'BLAH(0 0, 1 1)'): with self.assertRaises(forms.ValidationError): @@ -78,7 +83,7 @@ class PointForm(forms.Form): form = PointForm() cleaned_pt = form.fields['pt'].clean('POINT(5 23)') - self.assertEqual(cleaned_pt, GEOSGeometry('POINT(5 23)')) + self.assertEqual(cleaned_pt, GEOSGeometry('POINT(5 23)', srid=4326)) self.assertEqual(4326, cleaned_pt.srid) point = GEOSGeometry('SRID=4326;POINT(5 23)')