Fixed #26920 -- Made GEOSGeometry equality check consider the srid

This commit is contained in:
Jackie Leng 2016-11-23 09:23:06 +01:00 committed by Claude Paroz
parent 10d49b96e6
commit 50613d957a
8 changed files with 56 additions and 15 deletions

View File

@ -174,12 +174,14 @@ class GEOSGeometry(GEOSBase, ListMixin):
def __eq__(self, other): def __eq__(self, other):
""" """
Equivalence testing, a Geometry may be compared with another Geometry Equivalence testing, a Geometry may be compared with another Geometry
or a WKT representation. or an EWKT representation.
""" """
if isinstance(other, six.string_types): 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): elif isinstance(other, GEOSGeometry):
return self.equals_exact(other) return self.srid == other.srid and self.equals_exact(other)
else: else:
return False return False

View File

@ -160,15 +160,23 @@ Geometries support set-like operators::
The :class:`~GEOSGeometry` equality operator uses The :class:`~GEOSGeometry` equality operator uses
:meth:`~GEOSGeometry.equals_exact`, not :meth:`~GEOSGeometry.equals`, i.e. :meth:`~GEOSGeometry.equals_exact`, not :meth:`~GEOSGeometry.equals`, i.e.
it requires the compared geometries to have the same coordinates in the 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 >>> from django.contrib.gis.geos import LineString
>>> ls1 = LineString((0, 0), (1, 1)) >>> ls1 = LineString((0, 0), (1, 1))
>>> ls2 = LineString((1, 1), (0, 0)) >>> ls2 = LineString((1, 1), (0, 0))
>>> ls3 = LineString((1, 1), (0, 0), srid=4326)
>>> ls1.equals(ls2) >>> ls1.equals(ls2)
True True
>>> ls1 == ls2 >>> ls1 == ls2
False 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 Geometry Objects
================ ================

View File

@ -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 the Google Maps API and seems to be unmaintained. If you're using it, `let
us know <https://code.djangoproject.com/ticket/14284>`_. us know <https://code.djangoproject.com/ticket/14284>`_.
* The ``GEOSGeometry`` equality operator now also compares SRID.
Database backend API Database backend API
-------------------- --------------------

View File

@ -266,7 +266,7 @@ class GISFunctionsTests(TestCase):
State.objects.create(name='invalid', poly=invalid_geom) State.objects.create(name='invalid', poly=invalid_geom)
invalid = State.objects.filter(name='invalid').annotate(repaired=functions.MakeValid('poly')).first() invalid = State.objects.filter(name='invalid').annotate(repaired=functions.MakeValid('poly')).first()
self.assertIs(invalid.repaired.valid, True) 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") @skipUnlessDBFeature("has_MemSize_function")
def test_memsize(self): def test_memsize(self):

View File

@ -63,9 +63,9 @@ class GeoModelTest(TestCase):
nullcity.point.x = 23 nullcity.point.x = 23
nullcity.point.y = 5 nullcity.point.y = 5
# Checking assignments pre & post-save. # 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() 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() nullcity.delete()
# Testing on a Polygon # Testing on a Polygon
@ -479,8 +479,11 @@ class GeoQuerySetTest(TestCase):
# SpatiaLite). # SpatiaLite).
pass pass
else: else:
self.assertEqual(c.mpoly.difference(geom), c.difference) if spatialite:
if not 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) self.assertEqual(c.mpoly.intersection(geom), c.intersection)
# Ordering might differ in collections # Ordering might differ in collections
self.assertSetEqual(set(g.wkt for g in c.mpoly.sym_difference(geom)), self.assertSetEqual(set(g.wkt for g in c.mpoly.sym_difference(geom)),

View File

@ -227,6 +227,27 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
self.assertNotEqual(g, {'foo': 'bar'}) self.assertNotEqual(g, {'foo': 'bar'})
self.assertNotEqual(g, False) 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): def test_points(self):
"Testing Point objects." "Testing Point objects."
prev = fromstr('POINT(0 0)') prev = fromstr('POINT(0 0)')

View File

@ -36,7 +36,7 @@ class RelatedGeoModelTest(TestCase):
nm, st, lon, lat = ref nm, st, lon, lat = ref
self.assertEqual(nm, c.name) self.assertEqual(nm, c.name)
self.assertEqual(st, c.state) 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") @skipUnlessDBFeature("has_transform_method")
def test03_transform_related(self): def test03_transform_related(self):

View File

@ -48,12 +48,17 @@ class GeometryFieldTest(SimpleTestCase):
# By default, all geometry types are allowed. # By default, all geometry types are allowed.
fld = forms.GeometryField() 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)'): 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') 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` # 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` # but rejected by `clean`
with self.assertRaises(forms.ValidationError): with self.assertRaises(forms.ValidationError):
pnt_fld.clean('LINESTRING(0 0, 1 1)') pnt_fld.clean('LINESTRING(0 0, 1 1)')
@ -66,7 +71,7 @@ class GeometryFieldTest(SimpleTestCase):
fld = forms.GeometryField() fld = forms.GeometryField()
# to_python returns the same GEOSGeometry for a WKT # 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)'): 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 # 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)'): 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): with self.assertRaises(forms.ValidationError):
@ -78,7 +83,7 @@ class GeometryFieldTest(SimpleTestCase):
form = PointForm() form = PointForm()
cleaned_pt = form.fields['pt'].clean('POINT(5 23)') 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) self.assertEqual(4326, cleaned_pt.srid)
point = GEOSGeometry('SRID=4326;POINT(5 23)') point = GEOSGeometry('SRID=4326;POINT(5 23)')