Skip to content

Commit

Permalink
Merge pull request #1930 from remyguillaume/st_dwithin
Browse files Browse the repository at this point in the history
Use ST_DWithin instead of ST_Distance for performance reasons
  • Loading branch information
svamaa authored Feb 8, 2024
2 parents 2768e6e + 5f3af15 commit e435503
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 31 deletions.
25 changes: 16 additions & 9 deletions pyramid_oereb/contrib/data_sources/interlis_2_3/sources/plr.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
GeometryCollection
from sqlalchemy import or_
from sqlalchemy.orm import selectinload
from geoalchemy2.functions import ST_DWithin

from pyramid_oereb import Config
from pyramid_oereb.core import b64
Expand Down Expand Up @@ -433,15 +434,21 @@ def collect_related_geometries_by_real_estate(self, session, real_estate):
else:
query = session.query(self._model_).filter(
or_(
self._model_.point.ST_Distance(
from_shape(real_estate.limit, srid=Config.get('srid'))
) < self._tolerances.get('ALL', self._tolerances.get('Point', 0)),
self._model_.line.ST_Distance(
from_shape(real_estate.limit, srid=Config.get('srid'))
) < self._tolerances.get('ALL', self._tolerances.get('LineString', 0)),
self._model_.surface.ST_Distance(
from_shape(real_estate.limit, srid=Config.get('srid'))
) < self._tolerances.get('ALL', self._tolerances.get('Polygon', 0))
ST_DWithin(
self._model_.point,
from_shape(real_estate.limit, srid=Config.get('srid')),
self._tolerances.get('ALL', self._tolerances.get('Point', 0))
),
ST_DWithin(
self._model_.line,
from_shape(real_estate.limit, srid=Config.get('srid')),
self._tolerances.get('ALL', self._tolerances.get('LineString', 0))
),
ST_DWithin(
self._model_.surface,
from_shape(real_estate.limit, srid=Config.get('srid')),
self._tolerances.get('ALL', self._tolerances.get('Polygon', 0))
)
))
return query.distinct(self._model_.public_law_restriction_id).options(
selectinload(self.models.Geometry.public_law_restriction)
Expand Down
32 changes: 22 additions & 10 deletions pyramid_oereb/contrib/data_sources/standard/sources/plr.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import importlib

from geoalchemy2.shape import to_shape, from_shape
from geoalchemy2.functions import ST_DWithin, ST_Intersects
from shapely.geometry import Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, \
GeometryCollection
from sqlalchemy import text, or_
Expand Down Expand Up @@ -523,7 +524,7 @@ def extract_geometry_collection_db(db_path, real_estate_geometry, tolerances=Non
]
clause_blocks = [
text(f'ST_Intersects({extract}, {geometry_string})') if tolerance is None
else text(f'ST_Distance({extract}, {geometry_string}) < {tolerance}')
else text(f'ST_DWithin({extract}, {geometry_string}, {tolerance})')
for extract, tolerance in zip([extract_point, extract_line, extract_polygon], tolerance_extracts)
]
return or_(*clause_blocks)
Expand Down Expand Up @@ -560,17 +561,28 @@ def handle_collection(self, session, geometry_to_check):
else:
# The PLR is not problematic at all cause we do not have a collection type here
if (self._tolerances is not None) and ('ALL' in self._tolerances):
query = session.query(self._model_).filter(self._model_.geom.ST_Distance(
from_shape(geometry_to_check, srid=Config.get('srid'))
) < self._tolerances['ALL'])
query = session.query(self._model_).filter(
ST_DWithin(
self._model_.geom,
from_shape(geometry_to_check, srid=Config.get('srid')),
self._tolerances['ALL']
)
)
elif (self._tolerances is not None) and (geometry_to_check.geom_type in self._tolerances):
query = session.query(self._model_).filter(self._model_.geom.ST_Distance(
from_shape(geometry_to_check, srid=Config.get('srid'))
) < self._tolerances[geometry_to_check.geom_type])
query = session.query(self._model_).filter(
ST_DWithin(
self._model_.geom,
from_shape(geometry_to_check, srid=Config.get('srid')),
self._tolerances[geometry_to_check.geom_type]
)
)
else:
query = session.query(self._model_).filter(self._model_.geom.ST_Intersects(
from_shape(geometry_to_check, srid=Config.get('srid'))
))
query = session.query(self._model_).filter(
ST_Intersects(
self._model_.geom,
from_shape(geometry_to_check, srid=Config.get('srid'))
)
)
return query

def collect_related_geometries_by_real_estate(self, session, real_estate):
Expand Down
23 changes: 11 additions & 12 deletions tests/contrib.data_sources.standard/sources/test_plr.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import datetime
import math

import pytest
from unittest.mock import patch
Expand Down Expand Up @@ -820,28 +821,26 @@ def test_handle_collection_tolerance(tolerances, with_collection, config_config,

# check results for 8 combinations of with_collection + tolerances
from sqlalchemy.sql.annotation import AnnotatedColumn
from sqlalchemy.sql.elements import BinaryExpression, BooleanClauseList, TextClause
from geoalchemy2.functions import ST_Intersects, ST_Distance, ST_GeomFromWKB
from sqlalchemy.sql.elements import BooleanClauseList, TextClause, BindParameter
from geoalchemy2.functions import ST_Intersects, ST_DWithin, ST_GeomFromWKB
if with_collection:
assert type(query.received_clause) is BooleanClauseList
for clause in query.received_clause.clauses:
assert type(clause) is TextClause
if tolerances:
assert 'ST_Distance' in clause.text
assert 'ST_DWithin' in clause.text
else:
assert 'ST_Intersects' in clause.text
else:
if tolerances:
assert type(query.received_clause) is BinaryExpression
test_clause = query.received_clause.left
assert type(test_clause) is ST_Distance
query.received_clause.right.value == 0.1
assert type(query.received_clause) is ST_DWithin
assert {type(el) for el in query.received_clause.clause_expr.element.clauses} == {AnnotatedColumn, ST_GeomFromWKB, BindParameter}
assert math.isclose(query.received_clause.clauses.clauses[2].value, 0.1, rel_tol=1e-9)
else:
test_clause = query.received_clause
assert type(test_clause) is ST_Intersects
assert {
type(el) for el in test_clause.clause_expr.element.clauses
} == {AnnotatedColumn, ST_GeomFromWKB}
assert type(query.received_clause) is ST_Intersects
assert {type(el) for el in query.received_clause.clause_expr.element.clauses} == {AnnotatedColumn, ST_GeomFromWKB}




@pytest.mark.parametrize('geom,length,geom_type', [
Expand Down

0 comments on commit e435503

Please sign in to comment.