Skip to content

Commit f287447

Browse files
committed
PG: add a SPATIAL_FILTER_INTERSECTION=LOCAL/SERVER open option
- .. oo:: SPATIAL_FILTER_INTERSECTION :choices: LOCAL, SERVER :default: LOCAL :since: 3.13 Since GDAL 3.13, spatial filters are evaluated using full geometry intersection rather than only bounding box intersection, as in earlier versions. The ``SPATIAL_FILTER_INTERSECTION`` open option controls where this intersection operation is performed. Even when the intersection is evaluated locally (which is the default), the server still applies a bounding box-based spatial filter to efficiently retrieve candidate features.
1 parent 77eb999 commit f287447

File tree

7 files changed

+49
-10
lines changed

7 files changed

+49
-10
lines changed

autotest/ogr/ogr_pg.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6384,13 +6384,19 @@ def test_ogr_pg_field_truncation(pg_ds):
63846384

63856385
@gdaltest.enable_exceptions()
63866386
@pytest.mark.parametrize("GEOM_TYPE", ["geometry", "geography"])
6387-
def test_ogr_pg_geometry_intersection_spatial_filter(pg_ds, use_postgis, GEOM_TYPE):
6387+
@pytest.mark.parametrize("open_options", [None, ["SPATIAL_FILTER_INTERSECTION=SERVER"]])
6388+
@pytest.mark.require_geos
6389+
def test_ogr_pg_geometry_intersection_spatial_filter(
6390+
pg_ds, use_postgis, GEOM_TYPE, open_options
6391+
):
6392+
6393+
ds = reconnect(pg_ds, open_options=open_options)
63886394

63896395
if use_postgis:
63906396
srs = osr.SpatialReference(epsg=4326)
6391-
lyr = pg_ds.CreateLayer("test", srs, options=["GEOM_TYPE=" + GEOM_TYPE])
6397+
lyr = ds.CreateLayer("test", srs, options=["GEOM_TYPE=" + GEOM_TYPE])
63926398
else:
6393-
lyr = pg_ds.CreateLayer("test")
6399+
lyr = ds.CreateLayer("test")
63946400
f = ogr.Feature(lyr.GetLayerDefn())
63956401
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT (0.5 0.5)"))
63966402
lyr.CreateFeature(f)
@@ -6413,7 +6419,7 @@ def test_ogr_pg_geometry_intersection_spatial_filter(pg_ds, use_postgis, GEOM_TY
64136419

64146420
lyr.SetSpatialFilter(None)
64156421

6416-
with pg_ds.ExecuteSQL("SELECT * FROM test") as sql_lyr:
6422+
with ds.ExecuteSQL("SELECT * FROM test") as sql_lyr:
64176423
sql_lyr.SetSpatialFilter(
64186424
ogr.CreateGeometryFromWkt(
64196425
"POLYGON ((0 0,0 1,1 1,1 0.6,0.8 0.6,0.8 0.4,1 0.4,1 0,0 0))"

doc/source/drivers/vector/pg.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,19 @@ The following open options are supported:
243243
:oo::`PRELUDE_STATEMENTS`, the appropriate CLOSING_STATEMENTS would be
244244
"COMMIT".
245245

246+
- .. oo:: SPATIAL_FILTER_INTERSECTION
247+
:choices: LOCAL, SERVER
248+
:default: LOCAL
249+
:since: 3.13
250+
251+
Since GDAL 3.13, spatial filters are evaluated using full geometry intersection
252+
rather than only bounding box intersection, as in earlier versions.
253+
The ``SPATIAL_FILTER_INTERSECTION`` open option controls where this
254+
intersection operation is performed. Even when the intersection is evaluated
255+
locally (which is the default), the server still applies a bounding
256+
box-based spatial filter to efficiently retrieve candidate features.
257+
258+
246259
Dataset Creation Options
247260
~~~~~~~~~~~~~~~~~~~~~~~~
248261

ogr/ogrsf_frmts/pg/ogr_pg.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,8 @@ class OGRPGDataSource final : public GDALDataset
604604
bool m_bHasWritePermissionsOnMetadataTableRun = false;
605605
bool m_bHasWritePermissionsOnMetadataTableSuccess = false;
606606

607+
bool m_bSpatialFilterIntersectionIsLocal = true;
608+
607609
void LoadTables();
608610

609611
CPLString osDebugLastTransactionCommand{};
@@ -714,6 +716,11 @@ class OGRPGDataSource final : public GDALDataset
714716
bool CreateMetadataTableIfNeeded();
715717
bool HasOgrSystemTablesMetadataTable();
716718
bool HasWritePermissionsOnMetadataTable();
719+
720+
bool IsSpatialFilterIntersectionLocal() const
721+
{
722+
return m_bSpatialFilterIntersectionIsLocal;
723+
}
717724
};
718725

719726
#endif /* ndef OGR_PG_H_INCLUDED */

ogr/ogrsf_frmts/pg/ogrpgdatasource.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,11 @@ int OGRPGDataSource::Open(const char *pszNewName, int bUpdate, int bTestOpen,
261261
CPLAssert(nLayers == 0);
262262
papszOpenOptions = CSLDuplicate(papszOpenOptionsIn);
263263

264+
m_bSpatialFilterIntersectionIsLocal =
265+
EQUAL(CSLFetchNameValueDef(papszOpenOptions,
266+
"SPATIAL_FILTER_INTERSECTION", "LOCAL"),
267+
"LOCAL");
268+
264269
const char *pszPreludeStatements =
265270
CSLFetchNameValue(papszOpenOptions, "PRELUDE_STATEMENTS");
266271
if (pszPreludeStatements)

ogr/ogrsf_frmts/pg/ogrpgdrivercore.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ void OGRPGDriverSetCommonMetadata(GDALDriver *poDriver)
7979
" <Option name='SKIP_VIEWS' type='boolean' description='Whether "
8080
"views should be omitted from the list' "
8181
"default='NO'/>"
82+
" <Option name='SPATIAL_FILTER_INTERSECTION' type='string-select' "
83+
"description='Whether geometry intersection of spatial filter is "
84+
"evaluated locally or on the server' default='LOCAL'>"
85+
" <Value>LOCAL</Value>"
86+
" <Value>DATABASE</Value>"
87+
" </Option>"
8288
"</OpenOptionList>");
8389

8490
poDriver->SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST,

ogr/ogrsf_frmts/pg/ogrpgresultlayer.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,8 @@ OGRFeature *OGRPGResultLayer::GetNextFeature()
275275

276276
if ((m_poFilterGeom == nullptr || poGeomFieldDefn == nullptr ||
277277
(poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY &&
278-
poGeomFieldDefn->nSRSId > 0) ||
278+
poGeomFieldDefn->nSRSId > 0 &&
279+
!poDS->IsSpatialFilterIntersectionLocal()) ||
279280
FilterGeometry(poFeature->GetGeomFieldRef(m_iGeomFieldFilter))) &&
280281
(m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
281282
return poFeature;
@@ -305,7 +306,8 @@ OGRErr OGRPGResultLayer::ISetSpatialFilter(int iGeomField,
305306
if (m_poFilterGeom != nullptr && poDS->sPostGISVersion.nMajor >= 0)
306307
{
307308
if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY &&
308-
poGeomFieldDefn->nSRSId > 0)
309+
poGeomFieldDefn->nSRSId > 0 &&
310+
!poDS->IsSpatialFilterIntersectionLocal())
309311
{
310312
char *pszHexEWKB = OGRGeometryToHexEWKB(
311313
m_poFilterGeom, poGeomFieldDefn->nSRSId,

ogr/ogrsf_frmts/pg/ogrpgtablelayer.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,7 +1060,8 @@ void OGRPGTableLayer::BuildWhere()
10601060
poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY))
10611061
{
10621062
poGeomFieldDefn->GetSpatialRef(); // make sure nSRSId is resolved
1063-
if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY)
1063+
if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY &&
1064+
!poDS->IsSpatialFilterIntersectionLocal())
10641065
{
10651066
char *pszHexEWKB = OGRGeometryToHexEWKB(
10661067
m_poFilterGeom, poGeomFieldDefn->nSRSId,
@@ -1188,12 +1189,11 @@ OGRFeature *OGRPGTableLayer::GetNextFeature()
11881189
return nullptr;
11891190

11901191
/* We just have to look if there is a geometry filter */
1191-
/* If there's a PostGIS geometry column, the spatial filter */
1192-
/* is already taken into account in the select request */
11931192
/* The attribute filter is always taken into account by the select
11941193
* request */
11951194
if (m_poFilterGeom == nullptr || poGeomFieldDefn == nullptr ||
1196-
poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ||
1195+
(poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY &&
1196+
!poDS->IsSpatialFilterIntersectionLocal()) ||
11971197
FilterGeometry(poFeature->GetGeomFieldRef(m_iGeomFieldFilter)))
11981198
{
11991199
if (iFIDAsRegularColumnIndex >= 0)

0 commit comments

Comments
 (0)