Skip to content

Commit 1dd374b

Browse files
committed
Make base objects hashable, use it for equality check, including srid
srid defaults to 0, as ST_SRID does: ``` st_srid --------- 0 (1 row) ```
1 parent 23ede13 commit 1dd374b

File tree

7 files changed

+67
-24
lines changed

7 files changed

+67
-24
lines changed

postgis/geometry.py

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import warnings
22

3+
from .ewkb import Reader, Typed, Writer
4+
from .geojson import GeoJSON
5+
36
try:
47
# Do not make psycopg2 a requirement.
58
from psycopg2.extensions import ISQLQuote
69
except ImportError:
710
warnings.warn('psycopg2 not installed', ImportWarning)
811

912

10-
from .ewkb import Reader, Typed, Writer
11-
from .geojson import GeoJSON
1213

1314

1415
class Geometry(object, metaclass=Typed):
@@ -56,9 +57,13 @@ def __repr__(self):
5657
return '<{} {}>'.format(self.__class__.__name__, self.wkt)
5758

5859
def __eq__(self, other):
59-
if isinstance(other, self.__class__):
60-
other = other.coords
61-
return self.coords == other
60+
return hash(self) == hash(other)
61+
62+
def __hash__(self):
63+
values = self.coords
64+
if self.srid:
65+
values = values + (self.srid,)
66+
return hash(values)
6267

6368
@property
6469
def name(self):

postgis/geometrycollection.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
1-
from .geometry import Geometry
21
from .geojson import GeoJSON
2+
from .geometry import Geometry
33

44

55
class GeometryCollection(Geometry):
66

77
TYPE = 7
88

9-
def __init__(self, geoms, srid=None):
9+
def __init__(self, geoms, srid=0):
1010
for geom in geoms:
1111
if not isinstance(geom, Geometry):
1212
raise ValueError('{} is not instance of Geometry'.format(geom))
1313
self.geoms = list(geoms)
14-
if srid:
15-
self.srid = srid
14+
self.srid = srid
1615

1716
def __iter__(self):
1817
return self.geoms.__iter__()

postgis/multi.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
from .geometry import Geometry
22
from .point import Point
33

4+
45
class Multi(Geometry):
56

6-
__slots__ = ['geoms', 'srid']
7+
__slots__ = ["geoms", "srid"]
78
SUBCLASS = None
89

9-
def __init__(self, geoms, srid=None):
10+
def __init__(self, geoms, srid=0):
1011
self.geoms = [self.SUBCLASS(g, srid=srid) for g in geoms]
11-
if srid:
12-
self.srid = srid
12+
self.srid = srid
1313

1414
def __iter__(self):
1515
return iter(self.geoms)
@@ -31,8 +31,8 @@ def from_ewkb_body(cls, reader, srid=None):
3131

3232
@property
3333
def wkt_coords(self):
34-
fmt = '{}' if self.SUBCLASS == Point else '({})'
35-
return ', '.join(fmt.format(g.wkt_coords) for g in self)
34+
fmt = "{}" if self.SUBCLASS == Point else "({})"
35+
return ", ".join(fmt.format(g.wkt_coords) for g in self)
3636

3737
def write_ewkb_body(self, writer):
3838
writer.write_int(len(self.geoms))

postgis/point.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class Point(Geometry):
66
__slots__ = ['x', 'y', 'z', 'm', 'srid']
77
TYPE = 1
88

9-
def __init__(self, x, y=None, z=None, m=None, srid=None):
9+
def __init__(self, x, y=None, z=None, m=None, srid=0):
1010
if y is None and isinstance(x, (tuple, list)):
1111
x, y, *extra = x
1212
if extra:
@@ -17,8 +17,7 @@ def __init__(self, x, y=None, z=None, m=None, srid=None):
1717
self.y = float(y)
1818
self.z = float(z) if z is not None else None
1919
self.m = float(m) if m is not None else None
20-
if srid is not None:
21-
self.srid = srid
20+
self.srid = srid
2221

2322
def __getitem__(self, item):
2423
if item in (0, 'x'):

tests/test_linestring.py

+10
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,13 @@ def test_geom_should_compare_with_coords():
2121
def test_linestring_get_item():
2222
line = LineString(((30, 10), (10, 30), (40, 40)))
2323
assert line[0] == (30, 10)
24+
25+
26+
def test_linestring_is_hashable():
27+
l1 = LineString(((1, 2), (3, 4)))
28+
l2 = LineString(((1, 2), (3, 4)))
29+
l3 = LineString(((3, 4), (5, 6)))
30+
assert {l1, l2, l3} == {l1, l3}
31+
l1 = LineString(((1, 2), (3, 4)), srid=4326)
32+
l2 = LineString(((1, 2), (3, 4)), srid=3857)
33+
assert len({l1, l2}) == 2

tests/test_point.py

+10
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,13 @@ def test_0_as_m_is_considered():
9898
assert point.y == 2.0
9999
assert point.z == 3
100100
assert point.m == 0
101+
102+
103+
def test_point_is_hashable():
104+
p1 = Point(1, 1)
105+
p2 = Point(1, 1)
106+
p3 = Point(2, 2)
107+
assert {p1, p2, p3} == {p1, p3}
108+
p1 = Point(1, 1, srid=4326)
109+
p2 = Point(1, 1, srid=3857)
110+
assert len({p1, p2}) == 2

tests/test_polygon.py

+26-6
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,38 @@
22

33

44
def test_geom_should_compare_with_coords():
5-
assert (((35, 10), (45, 45), (15, 40), (10, 20), (35, 10)), ((20, 30), (35, 35), (30, 20), (20, 30))) == Polygon((((35, 10), (45, 45), (15, 40), (10, 20), (35, 10)), ((20, 30), (35, 35), (30, 20), (20, 30)))) # noqa
5+
assert (
6+
((35, 10), (45, 45), (15, 40), (10, 20), (35, 10)),
7+
((20, 30), (35, 35), (30, 20), (20, 30)),
8+
) == Polygon(
9+
(
10+
((35, 10), (45, 45), (15, 40), (10, 20), (35, 10)),
11+
((20, 30), (35, 35), (30, 20), (20, 30)),
12+
)
13+
) # noqa
614

715

816
def test_polygon_geojson():
917
poly = Polygon((((1, 2), (3, 4), (5, 6), (1, 2)),))
10-
assert poly.geojson == {"type": "Polygon",
11-
"coordinates": (((1, 2), (3, 4), (5, 6), (1, 2)),)}
18+
assert poly.geojson == {
19+
"type": "Polygon",
20+
"coordinates": (((1, 2), (3, 4), (5, 6), (1, 2)),),
21+
}
1222

1323

1424
def test_polygon_wkt():
1525
poly = Polygon((((1, 2), (3, 4), (5, 6), (1, 2)),))
1626
wkt = poly.wkt
17-
wkt = wkt.replace('.0','')
18-
wkt = wkt.replace(', ',',')
19-
assert wkt == 'POLYGON((1 2,3 4,5 6,1 2))'
27+
wkt = wkt.replace(".0", "")
28+
wkt = wkt.replace(", ", ",")
29+
assert wkt == "POLYGON((1 2,3 4,5 6,1 2))"
30+
31+
32+
def test_polygon_is_hashable():
33+
p1 = Polygon((((1, 2), (3, 4), (5, 6), (1, 2)),))
34+
p2 = Polygon((((1, 2), (3, 4), (5, 6), (1, 2)),))
35+
p3 = Polygon((((1, 2), (3, 4), (6, 7), (1, 2)),))
36+
assert {p1, p2, p3} == {p1, p3}
37+
p1 = Polygon((((1, 2), (3, 4), (5, 6), (1, 2)),), srid=4326)
38+
p2 = Polygon((((1, 2), (3, 4), (5, 6), (1, 2)),), srid=3857)
39+
assert len({p1, p2}) == 2

0 commit comments

Comments
 (0)