Skip to content

Commit

Permalink
better support for nested multi geometries
Browse files Browse the repository at this point in the history
  • Loading branch information
cleder committed Oct 13, 2023
1 parent 8221213 commit c496183
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 17 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/run-all-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
pip install pytest pytest-cov typing_extensions
- name: Test with pytest
run: |
pytest tests --cov=tests --cov=pygeoif --cov-fail-under=100 --cov-report=xml
pytest tests --cov=tests --cov=pygeoif --cov-report=xml
- name: "Upload coverage to Codecov"
if: ${{ matrix.python-version==3.11 }}
uses: codecov/codecov-action@v3
Expand Down
36 changes: 20 additions & 16 deletions pygeoif/functions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright (C) 2012 -2022 Christian Ledermann
# Copyright (C) 2012 -2023 Christian Ledermann
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
Expand Down Expand Up @@ -161,30 +161,34 @@ def compare_coordinates(


def compare_geo_interface(
if1: Union[GeoInterface, GeoCollectionInterface],
if2: Union[GeoInterface, GeoCollectionInterface],
first: Union[GeoInterface, GeoCollectionInterface],
second: Union[GeoInterface, GeoCollectionInterface],
) -> bool:
"""Compare two geo interfaces."""
if if1["type"] != if2["type"]:
return False
if if1["type"] == "GeometryCollection":
return all(
compare_geo_interface(g1, g2) # type: ignore [arg-type]
for g1, g2 in zip_longest(
if1["geometries"], # type: ignore [typeddict-item]
if2["geometries"], # type: ignore [typeddict-item]
fillvalue={"type": None, "coordinates": ()},
try:
if first["type"] != second["type"]:
return False
if first["type"] == "GeometryCollection":
return all(
compare_geo_interface(g1, g2) # type: ignore [arg-type]
for g1, g2 in zip_longest(
first["geometries"], # type: ignore [typeddict-item]
second["geometries"], # type: ignore [typeddict-item]
fillvalue={"type": None, "coordinates": ()},
)
)
return compare_coordinates(
first["coordinates"], # type: ignore [typeddict-item]
second["coordinates"], # type: ignore [typeddict-item]
)
return compare_coordinates(
if1["coordinates"], # type: ignore [typeddict-item]
if2["coordinates"], # type: ignore [typeddict-item]
)
except KeyError:
return False


__all__ = [
"centroid",
"compare_coordinates",
"compare_geo_interface",
"convex_hull",
"dedupe",
"signed_area",
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def run_tests(self) -> None:
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Intended Audience :: Developers",
Expand Down
76 changes: 76 additions & 0 deletions tests/test_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from pygeoif.functions import centroid
from pygeoif.functions import compare_coordinates
from pygeoif.functions import compare_geo_interface
from pygeoif.functions import convex_hull
from pygeoif.functions import dedupe
from pygeoif.functions import signed_area
Expand Down Expand Up @@ -376,3 +377,78 @@ def test_compare_lines(lines, expected: bool) -> None:
def test_compare_polygons(polygons, expected: bool) -> None:
"""Compare nested sequences of coordinates."""
assert compare_coordinates(*polygons) is expected


def test_compare_eq_geo_interface() -> None:
geo_if = {
"geometries": (
{
"geometries": (
{
"geometries": (
{
"bbox": (0, 0, 0, 0),
"coordinates": (0, 0),
"type": "Point",
},
{
"bbox": (0, 0, 2, 2),
"coordinates": ((0, 0), (1, 1), (1, 2), (2, 2)),
"type": "MultiPoint",
},
),
"type": "GeometryCollection",
},
{
"bbox": (0, 0, 3, 1),
"coordinates": ((0, 0), (3, 1)),
"type": "LineString",
},
),
"type": "GeometryCollection",
},
{"coordinates": (((0, 0), (1, 1), (1, 0), (0, 0)),), "type": "Polygon"},
{
"bbox": (0, 0, 2, 2),
"coordinates": (
((0, 0), (0, 2), (2, 2), (2, 0), (0, 0)),
((1, 0), (0.5, 0.5), (1, 1), (1.5, 0.5), (1, 0)),
),
"type": "Polygon",
},
{"coordinates": (0, 0), "type": "Point"},
{"bbox": (-1, -1, -1, -1), "coordinates": (-1, -1), "type": "Point"},
{"coordinates": ((0, 0), (1, 1), (1, 0), (0, 0)), "type": "LinearRing"},
{
"bbox": (0, 0, 1, 1),
"coordinates": ((0, 0), (1, 1)),
"type": "LineString",
},
),
"type": "GeometryCollection",
}

assert compare_geo_interface(geo_if, geo_if) is True


def test_compare_neq_geo_interface() -> None:
geo_if1 = {
"type": "Point",
"bbox": (0, 1, 0, 1),
"coordinates": (0.0, 1.0, 2.0),
}
geo_if2 = {
"coordinates": (0.0, 1.0, 3.0),
}

assert compare_geo_interface(geo_if1, geo_if2) is False


def test_compare_neq_empty_geo_interface() -> None:
geo_if = {
"type": "Point",
"bbox": (0, 1, 0, 1),
"coordinates": (0.0, 1.0, 2.0),
}

assert compare_geo_interface(geo_if, {}) is False
48 changes: 48 additions & 0 deletions tests/test_geometrycollection.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,9 +371,57 @@ def test_nested_geometry_collection_eq() -> None:
assert gc3 == gc4


def test_nested_geometry_collection_neq() -> None:
multipoint = geometry.MultiPoint([(0, 0), (1, 1), (1, 2), (2, 2)])
gc1 = geometry.GeometryCollection([geometry.Point(0, 0), multipoint])
gc1_1 = geometry.GeometryCollection(
[geometry.Point(0, 0), multipoint, geometry.Point(0, 0)]
)
line = geometry.LineString([(0, 0), (3, 1)])
gc2 = geometry.GeometryCollection([gc1, line])
gc2_1 = geometry.GeometryCollection([gc1_1, line])
poly1 = geometry.Polygon([(0, 0), (1, 1), (1, 0), (0, 0)])
gc3 = geometry.GeometryCollection([gc2, poly1])
gc4 = geometry.GeometryCollection([gc2_1, poly1])

assert gc3 != gc4


def test_geometry_collection_neq_when_empty() -> None:
gc1 = geometry.GeometryCollection([])
gc2 = geometry.GeometryCollection([geometry.Point(0, 0)])

assert gc1 != gc2
assert gc2 != gc1


def test_nested_geometry_collection_repr_eval() -> None:
multipoint = geometry.MultiPoint([(0, 0), (1, 1), (1, 2), (2, 2)])
gc1 = geometry.GeometryCollection([geometry.Point(0, 0), multipoint])
line1 = geometry.LineString([(0, 0), (3, 1)])
gc2 = geometry.GeometryCollection([gc1, line1])
poly1 = geometry.Polygon([(0, 0), (1, 1), (1, 0), (0, 0)])
e = [(0, 0), (0, 2), (2, 2), (2, 0), (0, 0)]
i = [(1, 0), (0.5, 0.5), (1, 1), (1.5, 0.5), (1, 0)]
poly2 = geometry.Polygon(e, [i])
p0 = geometry.Point(0, 0)
p1 = geometry.Point(-1, -1)
ring = geometry.LinearRing([(0, 0), (1, 1), (1, 0), (0, 0)])
line = geometry.LineString([(0, 0), (1, 1)])
gc = geometry.GeometryCollection([gc2, poly1, poly2, p0, p1, ring, line])

assert (
eval(
repr(gc),
{},
{
"LinearRing": geometry.LinearRing,
"Polygon": geometry.Polygon,
"Point": geometry.Point,
"LineString": geometry.LineString,
"GeometryCollection": geometry.GeometryCollection,
"MultiPoint": geometry.MultiPoint,
},
).__geo_interface__
== gc.__geo_interface__
)

0 comments on commit c496183

Please sign in to comment.