From bf109508980c834f692c2cb054a69d519f6544de Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sun, 28 Jan 2024 18:55:47 +0100 Subject: [PATCH] MySQL: fix/workaround server-side spatial filtering when SRS is geographic with MySQL >= 8 (fixes qgis/QGIS#55463) It seems spatial predicates are not directly usable when using geographic SRS. For some reason select MBRIntersects(ST_GeomFromText('POLYGON((-90 -90, 90 -90, 90 90, -90 90, -90 -90))', 4326), ST_GeomFromText('POINT(0 0)', 4326)); returns true as expected But select MBRIntersects(ST_GeomFromText('POLYGON((-179 -89, 179 -89, 179 89, -179 89, -179 -89))', 4326, 'axis-order=long-lat'), ST_GeomFromText('POINT(0 0)', 4326)); returns false !!!! And select MBRIntersects(ST_GeomFromText('POLYGON((-179 -89, 179 -89, 179 89, -179 89, -179 -89))', 32631), ST_GeomFromText('POINT(0 0)', 32631)); returns true as expected Consequence, we need to force a projected SRS for the arguments of MBRIntersects() --- autotest/ogr/ogr_mysql.py | 5 ++ ogr/ogrsf_frmts/mysql/ogrmysqltablelayer.cpp | 50 ++++++++++++++------ 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/autotest/ogr/ogr_mysql.py b/autotest/ogr/ogr_mysql.py index 217937099251..11f83273b178 100755 --- a/autotest/ogr/ogr_mysql.py +++ b/autotest/ogr/ogr_mysql.py @@ -960,6 +960,11 @@ def test_ogr_mysql_longlat(mysql_ds, mysql_is_8_or_later): pytest.fail("Not found SRID definition with GEOMETORY field.") mysql_ds.ReleaseResultSet(sql_lyr) + lyr.SetSpatialFilterRect(-181, -91, 181, 91) + lyr.ResetReading() + f = lyr.GetNextFeature() + ogrtest.check_feature_geometry(f, geom) + ############################################################################### # Test writing and reading back geometries diff --git a/ogr/ogrsf_frmts/mysql/ogrmysqltablelayer.cpp b/ogr/ogrsf_frmts/mysql/ogrmysqltablelayer.cpp index 6e36d495e57d..164e7e70a5dd 100644 --- a/ogr/ogrsf_frmts/mysql/ogrmysqltablelayer.cpp +++ b/ogr/ogrsf_frmts/mysql/ogrmysqltablelayer.cpp @@ -32,6 +32,8 @@ #include "cpl_string.h" #include "ogr_mysql.h" +#include + /************************************************************************/ /* OGRMySQLTableLayer() */ /************************************************************************/ @@ -467,26 +469,41 @@ void OGRMySQLTableLayer::BuildWhere() // POLYGON((MINX MINY, MAXX MINY, MAXX MAXY, MINX MAXY, MINX MINY)) m_poFilterGeom->getEnvelope(&sEnvelope); + const OGRSpatialReference *l_poSRS = GetSpatialRef(); + const bool bIsGeography = + (poDS->GetMajorVersion() >= 8 && !poDS->IsMariaDB() && l_poSRS && + l_poSRS->IsGeographic()); + + const double dfMinX = sEnvelope.MinX; + const double dfMinY = sEnvelope.MinY; + const double dfMaxX = sEnvelope.MaxX; + const double dfMaxY = sEnvelope.MaxY; + CPLsnprintf(szEnvelope, sizeof(szEnvelope), "POLYGON((%.18g %.18g, %.18g %.18g, %.18g %.18g, %.18g " "%.18g, %.18g %.18g))", - sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX, - sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY, - sEnvelope.MinX, sEnvelope.MaxY, sEnvelope.MinX, - sEnvelope.MinY); - - const char *pszAxisOrder = ""; - OGRSpatialReference *l_poSRS = GetSpatialRef(); - if (poDS->GetMajorVersion() >= 8 && !poDS->IsMariaDB() && l_poSRS && - l_poSRS->IsGeographic()) + dfMinX, dfMinY, dfMaxX, dfMinY, dfMaxX, dfMaxY, dfMinX, + dfMaxY, dfMinX, dfMinY); + + if (bIsGeography) { - pszAxisOrder = ", 'axis-order=long-lat'"; + // Force any projected CRS so that the spatial filter works as + // we expect. + // This is due to the following returning false + // select MBRIntersects(ST_GeomFromText('POLYGON((-179 -89, 179 -89, 179 89, -179 89, -179 -89))', 4326, 'axis-order=long-lat'), ST_GeomFromText('POINT(0 0)', 4326)); + snprintf(pszWHERE, nWHERELen, + "WHERE MBRIntersects(ST_GeomFromText('%s', 32631), " + "ST_SRID(`%s`,32631))", + szEnvelope, pszGeomColumn); + } + else + { + snprintf(pszWHERE, nWHERELen, + "WHERE MBRIntersects(%s('%s', %d), `%s`)", + poDS->GetMajorVersion() >= 8 ? "ST_GeomFromText" + : "GeomFromText", + szEnvelope, nSRSId, pszGeomColumn); } - - snprintf( - pszWHERE, nWHERELen, "WHERE MBRIntersects(%s('%s', %d%s), `%s`)", - poDS->GetMajorVersion() >= 8 ? "ST_GeomFromText" : "GeomFromText", - szEnvelope, nSRSId, pszAxisOrder, pszGeomColumn); } if (pszQuery != nullptr) @@ -497,6 +514,9 @@ void OGRMySQLTableLayer::BuildWhere() snprintf(pszWHERE + strlen(pszWHERE), nWHERELen - strlen(pszWHERE), "&& (%s) ", pszQuery); } + + if (pszWHERE[0]) + CPLDebug("MYSQL", "Filter: %s", pszWHERE); } /************************************************************************/