Skip to content

Commit

Permalink
added test_api_files::test_geofilter_intersects and developer docs
Browse files Browse the repository at this point in the history
  • Loading branch information
barrydaniels-nl committed Dec 18, 2024
1 parent 5a3c7ca commit cb099f2
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 12 deletions.
20 changes: 12 additions & 8 deletions src/dso_api/dynamic_api/filters/values.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,8 @@ def _parse_wkt_geometry(value: str, srid: int | None) -> GEOSGeometry:
)

# check if the geometry is within the Netherlands, only warn if not
if geom.geom_type in ("Polygon", "MultiPolygon"):
_validate_bounds(geom, srid)
if geom.geom_type in ("Polygon", "MultiPolygon") and not _validate_bounds(geom, srid):
logger.warning("Geometry bounds outside Netherlands")

# Try parsing as point
if geom.geom_type == "Point":
Expand Down Expand Up @@ -229,21 +229,25 @@ def _parse_point(value: str) -> tuple[float, float]:
return x, y


def _validate_bounds(geom: GEOSGeometry, srid: int | None) -> None: # noqa: F811
def _validate_bounds(geom: GEOSGeometry, srid: int | None) -> bool: # noqa: F811
"""Validate if geometry bounds are within Netherlands extent.
Logs warning if bounds are outside the valid range.
Returns True if geometry is within bounds, False otherwise.
Args:
geom: GEOSGeometry object to validate
srid: Spatial reference system identifier
Returns:
bool: True if geometry is within Netherlands bounds, False otherwise
"""
bounds = geom.extent # (xmin, ymin, xmax, ymax)
corners = [(bounds[0], bounds[1]), (bounds[2], bounds[3])] # SW, NE corners

if srid == 4326 and not _validate_wgs84_bounds(corners):
logger.warning("WGS84 geometry bounds ", corners, " outside Netherlands")
elif srid == 28992 and not _validate_rd_bounds(corners):
logger.warning("RD geometry bounds ", corners, " outside Netherlands")
if srid == 4326:
return _validate_wgs84_bounds(corners)
elif srid == 28992:
return _validate_rd_bounds(corners)
return True # If srid is not 4326 or 28992, assume valid


def _validate_wgs84_bounds(corners: list[tuple[float, float]]) -> bool:
Expand Down
12 changes: 8 additions & 4 deletions src/templates/dso_api/dynamic_api/docs/rest/filtering.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,14 @@ als `exact`. Er is geen *escaping* van de jokertekens mogelijk.

### Bij waarden met een geometrie

| Operator | Werking | SQL Equivalent |
| --------------------------------- | -------------------------------------------------- | ------------------------------------------ |
| `?{geoveld}[contains]={x},{y}` | Geometrie moet voorkomen op een punt (intersectie) | `ST_Intersects({geoveld}, POINT({x} {y}))` |
| `?{geoveld}[contains]=POINT(x y)` | Idem, nu in de WKT (well-known text) notatie. | `ST_Intersects({geoveld}, POINT({x} {y}))` |
| Operator | Werking | SQL Equivalent |
| --------------------------------------------------- | ------------------------------------------------------ | -----------------------------------------------------|
| `?{geoveld}[contains]={x},{y}` | Geometrie moet voorkomen op een punt (intersectie) | `ST_Intersects({geoveld}, POINT({x} {y}))` |
| `?{geoveld}[contains]=POINT(x y)` | Idem, nu in de WKT (well-known text) notatie. | `ST_Intersects({geoveld}, POINT({x} {y}))` |
| `?{geoveld}[intersects]={x},{y}` | Geometrie moet voorkomen op een punt (intersectie) | `ST_Intersects({geoveld}, POINT({x} {y}))` |
| `?{geoveld}[intersects]=POINT(x y)` | Idem, nu in de WKT (well-known text) notatie. | `ST_Intersects({geoveld}, POINT({x} {y}))` |
| `?{geoveld}[intersects]=POLYGON ((4.89...))` | Geometry moet overlappen met een polygon (intersectie).| `ST_Intersects({geoveld}, POLYGON ((4.89...)))` |
| `?{geoveld}[intersects]=MULTIPOLYGON (((4.89...)))` | Geometry moet overlappen met een MULTIPOLYGON | `ST_Intersects({geoveld}, MULTIPOLYGON ((4.89...)))` |

Bij het doorzoeken van geometrievelden wordt gebruik gemaakt van de
projectie opgegeven in de header `Accept-CRS`. Afhankelijk van de
Expand Down
105 changes: 105 additions & 0 deletions src/tests/test_dynamic_api/views/test_api_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,111 @@ def test_geofilter_contains(api_client, parkeervakken_parkeervak_model, filled_r
)
assert response.status_code == 400, "Outside WGS84 range"

@staticmethod
def test_geofilter_intersects(api_client, parkeervakken_parkeervak_model, filled_router):
"""
Prove that geofilter intersects filters work as expected.
"""
parkeervakken_parkeervak_model.objects.create(
id="121138489006",
type="File",
soort="MULDER",
aantal=1.0,
e_type="E6b",
buurtcode="A05d",
straatnaam="Zoutkeetsgracht",
geometry=GEOSGeometry(
"POLYGON((121140.66 489048.21, 121140.72 489047.1, 121140.8 489046.9, 121140.94 "
"489046.74,121141.11 489046.62, 121141.31 489046.55, 121141.52 489046.53, "
"121134.67 489045.85, 121134.47 489047.87, 121140.66 489048.21))",
28992,
),
)

# Inside using RD
response = api_client.get(
"/v1/parkeervakken/parkeervakken/",
data={"geometry[intersects]": "121137.7,489046.9"},
HTTP_ACCEPT_CRS="EPSG:28992",
)
data = read_response_json(response)
assert len(data["_embedded"]["parkeervakken"]) == 1, "inside with R/D"

# Inside using WGS84 x,y
response = api_client.get(
"/v1/parkeervakken/parkeervakken/",
data={"geometry[intersects]": "52.388231,4.8897865"},
HTTP_ACCEPT_CRS="EPSG:4326",
)
data = read_response_json(response)
assert response.status_code == 200, data
assert len(data["_embedded"]["parkeervakken"]) == 1, "inside with WGS84"

# Inside using WGS84 POLYGON
response = api_client.get(
"/v1/parkeervakken/parkeervakken/",
data={
"geometry[intersects]": "POLYGON ((4.8982259 52.3748037, 4.8989231 52.3744369, 4.9008431 52.3739718, 4.9011541 52.3751508, 4.899985 52.3761922, 4.8982367 52.3748037, 4.8982259 52.3748037))" # noqa: E501
},
HTTP_ACCEPT_CRS="EPSG:4326",
)
data = read_response_json(response)
assert response.status_code == 200, data

# Inside using WGS84 MULTIPOLYGON
response = api_client.get(
"/v1/parkeervakken/parkeervakken/",
data={
"geometry[intersects]": "MULTIPOLYGON (((5.639762878417969 51.70404205550062, 5.47222137451172 51.54050328936586, 5.984265804290771 51.445328182347936, 6.050591468811035 51.57152179108081, 5.639762878417969 51.70404205550062)), ((5.268545150756836 51.8780408523543, 5.039935111999512 51.700837165629764, 5.2266597747802725 51.65603805162954, 5.515286922454835 51.827171560252935, 5.268545150756836 51.8780408523543)))" # noqa: E501
},
HTTP_ACCEPT_CRS="EPSG:4326",
)
data = read_response_json(response)
print("Response data:", data) # Debug print
assert response.status_code == 200, data
assert len(data["_embedded"]["parkeervakken"]) == 0, "Outside using WGS84"

# Outside using WGS84
response = api_client.get(
"/v1/parkeervakken/parkeervakken/",
data={"geometry[intersects]": "52.3883019,4.8900356"},
HTTP_ACCEPT_CRS="EPSG:4326",
)
data = read_response_json(response)
assert len(data["_embedded"]["parkeervakken"]) == 0, "Outside using WGS84"

# Outside using WGS84 Polygons
response = api_client.get(
"/v1/parkeervakken/parkeervakken/",
data={
"geometry[intersects]": "POLYGON ((4.894892 52.5949621, 4.8935187 52.5957963, 4.9772894 52.6262334, 4.9395239 52.6982808, 4.8070014 52.6762211, 4.894892 52.5949621))" # noqa: E501
},
HTTP_ACCEPT_CRS="EPSG:4326",
)
data = read_response_json(response)
assert len(data["_embedded"]["parkeervakken"]) == 0, "Outside using WGS84"

# Outside using WGS84 Multipolygons
response = api_client.get(
"/v1/parkeervakken/parkeervakken/",
data={
"geometry[intersects]": "MULTIPOLYGON (((5.639762878417969 51.70404205550062, 5.47222137451172 51.54050328936586, 5.984265804290771 51.445328182347936, 6.050591468811035 51.57152179108081, 5.639762878417969 51.70404205550062)), ((5.268545150756836 51.8780408523543, 5.039935111999512 51.700837165629764, 5.2266597747802725 51.65603805162954, 5.515286922454835 51.827171560252935, 5.268545150756836 51.8780408523543)))" # noqa: E501
},
HTTP_ACCEPT_CRS="EPSG:4326",
)
data = read_response_json(response)
print("Response data:", data) # Debug print
assert response.status_code == 200, data
assert len(data["_embedded"]["parkeervakken"]) == 0, "Outside using WGS84"

# Invalid WGS84 coords
response = api_client.get(
"/v1/parkeervakken/parkeervakken/",
data={"geometry[intersects]": "52.388231,48897865"},
HTTP_ACCEPT_CRS="EPSG:4326",
)
assert response.status_code == 400, "Outside WGS84 range"

@staticmethod
def test_filter_isempty(api_client, parkeervakken_parkeervak_model, filled_router):
""" "Prove that the [isempty] operator works"""
Expand Down

0 comments on commit cb099f2

Please sign in to comment.