Skip to content

Commit

Permalink
GEOJSON: add GetExtent3D "fast" implementation (#9017)
Browse files Browse the repository at this point in the history
  • Loading branch information
elpaso authored Jan 4, 2024
1 parent 6d6587d commit bf5289c
Show file tree
Hide file tree
Showing 5 changed files with 475 additions and 12 deletions.
247 changes: 242 additions & 5 deletions autotest/ogr/ogr_geojson.py
Original file line number Diff line number Diff line change
Expand Up @@ -4734,24 +4734,22 @@ def test_ogr_geojson_foreign_members_feature(tmp_vsimem):


###############################################################################


def test_ogr_json_getextent3d(tmp_vsimem):

jdata = r"""{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"properties": {"foo": "bar"},
"geometry": {
"type": "%s",
"coordinates": %s
}
},
{
"type": "Feature",
"properties": {},
"properties": {"foo": "baz"},
"geometry": {
"type": "%s",
"coordinates": %s
Expand Down Expand Up @@ -4792,14 +4790,41 @@ def test_ogr_json_getextent3d(tmp_vsimem):

assert gdal.GetLastErrorMsg() == ""
lyr = ds.GetLayer(0)
assert not lyr.TestCapability(ogr.OLCFastGetExtent3D)
assert lyr.TestCapability(ogr.OLCFastGetExtent3D)
assert lyr.TestCapability(ogr.OLCFastGetExtent)
dfn = lyr.GetLayerDefn()
assert dfn.GetGeomFieldCount() == 1
ext2d = lyr.GetExtent()
assert ext2d == (1.0, 2.0, 1.0, 2.0)
ext3d = lyr.GetExtent3D()
assert ext3d == (1.0, 2.0, 1.0, 2.0, float("inf"), float("-inf"))

# Test capabilities and extent with filters and round trip
lyr.SetAttributeFilter("foo = 'baz'")
assert not lyr.TestCapability(ogr.OLCFastGetExtent3D)
assert not lyr.TestCapability(ogr.OLCFastGetExtent)
ext2d = lyr.GetExtent()
assert ext2d == (2.0, 2.0, 2.0, 2.0)
ext3d = lyr.GetExtent3D()
assert ext3d == (2.0, 2.0, 2.0, 2.0, float("inf"), float("-inf"))

lyr.SetAttributeFilter(None)
assert lyr.TestCapability(ogr.OLCFastGetExtent3D)
assert lyr.TestCapability(ogr.OLCFastGetExtent)
ext2d = lyr.GetExtent()
assert ext2d == (1.0, 2.0, 1.0, 2.0)
ext3d = lyr.GetExtent3D()
assert ext3d == (1.0, 2.0, 1.0, 2.0, float("inf"), float("-inf"))

# Test capability with geometry filter
lyr.SetSpatialFilterRect(1.5, 1.5, 2.5, 2.5)
assert not lyr.TestCapability(ogr.OLCFastGetExtent3D)
assert not lyr.TestCapability(ogr.OLCFastGetExtent)
ext2d = lyr.GetExtent()
assert ext2d == (2.0, 2.0, 2.0, 2.0)
ext3d = lyr.GetExtent3D()
assert ext3d == (2.0, 2.0, 2.0, 2.0, float("inf"), float("-inf"))

# Test mixed 2D
gdal.FileFromMemBuffer(
tmp_vsimem / "test.json",
Expand All @@ -4819,3 +4844,215 @@ def test_ogr_json_getextent3d(tmp_vsimem):
assert ext2d == (1.0, 2.0, 1.0, 2.0)
ext3d = lyr.GetExtent3D()
assert ext3d == (1.0, 2.0, 1.0, 2.0, 1.0, 1.0)

# Text mixed geometry types
gdal.FileFromMemBuffer(
tmp_vsimem / "test.json",
jdata % ("Point", "[1, 1, 1]", "LineString", "[[2, 2, 2], [3, 3, 3]]"),
)

ds = gdal.OpenEx(tmp_vsimem / "test.json", gdal.OF_VECTOR)

assert gdal.GetLastErrorMsg() == ""

lyr = ds.GetLayer(0)
dfn = lyr.GetLayerDefn()
assert dfn.GetGeomFieldCount() == 1
# Check geometry type is unknown
assert dfn.GetGeomFieldDefn(0).GetType() == ogr.wkbUnknown

# Test a polygon
gdal.FileFromMemBuffer(
tmp_vsimem / "test.json",
"""{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [[[1, 1], [2, 1], [2, 2], [1, 2], [1, 1]]]
}
}
]
}""",
)

ds = gdal.OpenEx(tmp_vsimem / "test.json", gdal.OF_VECTOR)

assert gdal.GetLastErrorMsg() == ""

lyr = ds.GetLayer(0)
dfn = lyr.GetLayerDefn()
assert dfn.GetGeomFieldCount() == 1
ext2d = lyr.GetExtent()
assert ext2d == (1.0, 2.0, 1.0, 2.0)
ext3d = lyr.GetExtent3D()
assert ext3d == (1.0, 2.0, 1.0, 2.0, float("inf"), float("-inf"))

# Test a polygon with a hole
gdal.FileFromMemBuffer(
tmp_vsimem / "test.json",
"""{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [[[1, 1], [2, 1], [2, 2], [1, 2], [1, 1]], [[1.5, 1.5], [1.5, 1.6], [1.6, 1.6], [1.6, 1.5], [1.5, 1.5]]]
}
}
]
}""",
)

ds = gdal.OpenEx(tmp_vsimem / "test.json", gdal.OF_VECTOR)

assert gdal.GetLastErrorMsg() == ""

lyr = ds.GetLayer(0)
dfn = lyr.GetLayerDefn()
assert dfn.GetGeomFieldCount() == 1
ext2d = lyr.GetExtent()
assert ext2d == (1.0, 2.0, 1.0, 2.0)
ext3d = lyr.GetExtent3D()
assert ext3d == (1.0, 2.0, 1.0, 2.0, float("inf"), float("-inf"))

# Test a series of different 2D geometries including polygons with holes
gdal.FileFromMemBuffer(
tmp_vsimem / "test.json",
"""{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [1, 1]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [[2, 2], [3, 3]]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [[[4, 4], [5, 4], [5, 5], [4, 5], [4, 4]]]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [[[6, 6], [7, 6], [7, 7], [6, 7], [6, 6]], [[6.5, 6.5], [6.5, 6.6], [6.6, 6.6], [6.6, 6.5], [6.5, 6.5]]]
}
}
]
}""",
)

ds = gdal.OpenEx(tmp_vsimem / "test.json", gdal.OF_VECTOR)

assert gdal.GetLastErrorMsg() == ""

lyr = ds.GetLayer(0)

assert lyr.GetExtent() == (1.0, 7.0, 1.0, 7.0)
assert lyr.GetExtent3D() == (1.0, 7.0, 1.0, 7.0, float("inf"), float("-inf"))

# Test a series of different 3D geometries including polygons with holes

gdal.FileFromMemBuffer(
tmp_vsimem / "test.json",
"""{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [1, 1, 1]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [[2, 2, 2], [3, 3, 3]]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [[[4, 4, 4], [5, 4, 4], [5, 5, 5], [4, 5, 5], [4, 4, 4]]]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [[[6, 6, 6], [7, 6, 6], [7, 7, 7], [6, 7, 7], [6, 6, 6]], [[6.5, 6.5, 6.5], [6.5, 6.6, 6.5], [6.6, 6.6, 6.5], [6.6, 6.5, 6.5], [6.5, 6.5, 6.5]]]
}
}
]
}""",
)

ds = gdal.OpenEx(tmp_vsimem / "test.json", gdal.OF_VECTOR)

assert gdal.GetLastErrorMsg() == ""

lyr = ds.GetLayer(0)

assert lyr.GetExtent() == (1.0, 7.0, 1.0, 7.0)
assert lyr.GetExtent3D() == (1.0, 7.0, 1.0, 7.0, 1.0, 7.0)

# Test geometrycollection
gdal.FileFromMemBuffer(
tmp_vsimem / "test.json",
r"""
{
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": {},
"geometry": {
"type": "GeometryCollection",
"geometries": [{
"type": "Point",
"coordinates": [6, 7]
}, {
"type": "Polygon",
"coordinates": [[[3, 4, 2], [5, 4, 4], [5, 5, 5], [4, 5, 5], [3, 4, 2]]]
}]
}
}]
}
""",
)

ds = gdal.OpenEx(tmp_vsimem / "test.json", gdal.OF_VECTOR)

assert gdal.GetLastErrorMsg() == ""

lyr = ds.GetLayer(0)

assert lyr.GetExtent() == (3.0, 6.0, 4.0, 7.0)
assert lyr.GetExtent3D() == (3.0, 6.0, 4.0, 7.0, 2.0, 5.0)
5 changes: 5 additions & 0 deletions ogr/ogrsf_frmts/geojson/ogr_geojson.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ class OGRGeoJSONLayer final : public OGRMemLayer
int nFlags) override;
virtual OGRErr CreateGeomField(const OGRGeomFieldDefn *poGeomField,
int bApproxOK = TRUE) override;
virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE) override;
virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent,
int bForce = TRUE) override;
virtual OGRErr GetExtent3D(int iGeomField, OGREnvelope3D *psExtent3D,
int bForce = TRUE) override;

//
// OGRGeoJSONLayer Interface
Expand Down
49 changes: 49 additions & 0 deletions ogr/ogrsf_frmts/geojson/ogrgeojsonlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,52 @@ OGRErr OGRGeoJSONLayer::CreateGeomField(const OGRGeomFieldDefn *poGeomField,
return OGRMemLayer::CreateGeomField(poGeomField, bApproxOK);
}

OGRErr OGRGeoJSONLayer::GetExtent(OGREnvelope *psExtent, int bForce)
{
return GetExtent(0, psExtent, bForce);
}

OGRErr OGRGeoJSONLayer::GetExtent(int iGeomField, OGREnvelope *psExtent,
int bForce)
{
if (iGeomField != 0)
{
return OGRERR_FAILURE;
}

if (poReader_ && poReader_->ExtentRead() &&
TestCapability(OLCFastGetExtent))
{
*psExtent = poReader_->GetExtent3D();
return OGRERR_NONE;
}
else
{
return OGRMemLayer::GetExtentInternal(iGeomField, psExtent, bForce);
}
}

OGRErr OGRGeoJSONLayer::GetExtent3D(int iGeomField, OGREnvelope3D *psExtent3D,
int bForce)
{

if (iGeomField != 0)
{
return OGRERR_FAILURE;
}

if (poReader_ && poReader_->ExtentRead() &&
TestCapability(OLCFastGetExtent3D))
{
*psExtent3D = poReader_->GetExtent3D();
return OGRERR_NONE;
}
else
{
return OGRMemLayer::GetExtent3D(iGeomField, psExtent3D, bForce);
}
}

/************************************************************************/
/* TestCapability() */
/************************************************************************/
Expand All @@ -451,6 +497,9 @@ int OGRGeoJSONLayer::TestCapability(const char *pszCap)
return TRUE;
else if (EQUAL(pszCap, OLCStringsAsUTF8))
return TRUE;
else if (EQUAL(pszCap, OLCFastGetExtent) ||
EQUAL(pszCap, OLCFastGetExtent3D))
return m_poFilterGeom == nullptr && m_poAttrQuery == nullptr;
return OGRMemLayer::TestCapability(pszCap);
}

Expand Down
Loading

0 comments on commit bf5289c

Please sign in to comment.