Skip to content

fix(sqlalchemy): multiply DWITHIN/BEYOND distance into metres - #176

Open
C1-BA-B1-F3 wants to merge 1 commit into
geopython:mainfrom
C1-BA-B1-F3:fix/sqlalchemy-dwithin-units
Open

C1-BA-B1-F3 wants to merge 1 commit into
geopython:mainfrom
C1-BA-B1-F3:fix/sqlalchemy-dwithin-units

Conversation

@C1-BA-B1-F3

Copy link
Copy Markdown

Fixes #164.

pygeofilter/backends/sqlalchemy/filters.py divided the distance for
kilometers/miles instead of multiplying, so a 5 km radius reached
ST_DWithin as 0.005.

Beyond the direction, the divisors were keyed on spellings the parsers do not
produce: the ECQL grammar emits statute miles (never miles), and it also
emits feet and nautical miles, neither of which was converted at all. The
conversion is now one table keyed by what the grammar accepts:

units metres
meters 1
kilometers 1000
feet 0.3048
miles / statute miles 1609.344
nautical miles 1852

An unrecognized unit still passes through unchanged, so nothing changes beyond
the scaling that was wrong.

Note from the issue, kept as a docstring caveat rather than "fixed" here: the
radius is in the coordinate units of the column's CRS, so the conversion is
exact only for a projected, metric CRS (or a geography cast). On a
geographic column (EPSG:4326) the radius is in degrees, and pygeofilter cannot
know the units of an arbitrary column.

Tests

New tests/backends/sqlalchemy/test_spatial_units.py, 14 cases, needing
neither a database nor a spatial extension — the existing test_evaluate.py
is skipped wholesale without mod_spatialite and its DWITHIN/BEYOND cases
are commented out because spatialite has no ST_DWithin. It covers the
per-unit factors, DWITHIN and BEYOND, an unknown unit passing through, and
an end-to-end ECQL parse → to_filter → compiled SQL check that the radius
reaches SQL as 5000, not 0.005.

  • On the pre-fix code, 9 of the 14 fail; after the fix, all 14 pass.
  • Full suite with the test extra (excluding the modules that need a live
    Elasticsearch/OpenSearch/Solr or GDAL): 330 passed / 62 skipped / 37
    errors
    , against a 316 / 62 / 37 baseline on main — the delta is
    exactly the 14 new tests, and the 37 errors are the network-backed backend
    suites, unrelated to this change.

`spatial()` divided the CQL2 distance by 1000 for `kilometers` and by 1609
for `miles`, so `DWITHIN(geometry, POINT(0 0), 5, kilometers)` reached
`ST_DWithin` as 0.005 instead of 5000.

The divisors were also keyed on the wrong spellings: the ECQL grammar emits
`statute miles`, not `miles`, and it also emits `feet` and `nautical miles`,
neither of which was converted. Replace the dead `UNITS_LOOKUP` with a single
metres-per-unit table keyed by the grammar's spellings (plus a bare `miles`
for direct callers), multiply instead of divide, and leave an unknown unit
untouched as before.

The radius is still in the coordinate units of the column's CRS, so the
conversion is exact only for a projected/metric CRS or a `geography` cast;
that caveat is documented on the table.

Adds `tests/backends/sqlalchemy/test_spatial_units.py`, which needs neither a
database nor a spatial extension: `test_evaluate.py` is skipped wholesale
without `mod_spatialite` and comments out its `DWITHIN` cases because
spatialite has no `ST_DWithin`.

Fixes geopython#164
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sqlalchemy backend: DWITHIN/BEYOND unit conversion divides instead of multiplying

1 participant