Skip to content

Commit

Permalink
GeoJSON: add a FOREIGN_MEMBERS=AUTO/ALL/NONE/STAC open option
Browse files Browse the repository at this point in the history
```
-  .. oo:: FOREIGN_MEMBERS
      :choices: AUTO, ALL, NONE, STAC
      :default: AUTO
      :since: 3.11.0

      Whether and how foreign members at the feature level should be
      processed as OGR fields:

      - ``AUTO`` mode behaves like ``STAC`` mode if a ``stac_version`` member is found at
        the Feature level, otherwise it behaves as ``NONE`` mode.

      - In ``ALL`` mode, all foreign members at the feature level are added.
        Whether to recursively explore nested objects and produce flatten OGR attributes
        or not is decided by the ``FLATTEN_NESTED_ATTRIBUTES`` open option.

      - In ``NONE`` mode, no foreign members at the feature level are added.

      - ``STAC`` mode (Spatio-Temporal Asset Catalog) behaves the same as ``ALL``,
        except content under the ``assets`` member is by default flattened
        as ``assets.{asset_name}.{asset_property}`` fields.
```
  • Loading branch information
rouault committed Dec 18, 2024
1 parent 3825328 commit 6b0c896
Show file tree
Hide file tree
Showing 7 changed files with 377 additions and 0 deletions.
109 changes: 109 additions & 0 deletions autotest/ogr/data/geojson/stac_item.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
{
"type": "Feature",
"stac_version": "1.0.0",
"stac_extensions": [
"https://stac-extensions.github.io/projection/v1.0.0/schema.json",
],
"id": "my_id",
"description": "Landsat Collection 2 Level-2 Surface Reflectance Product",
"bbox": [
-155.80483200278817,
17.736060531368373,
-153.68026753986817,
19.82593799656933
],
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-155.41188228421524,
19.82593799656933
],
[
-155.80483200278817,
18.09429339392015
],
[
-154.0866457786547,
17.736060531368373
],
[
-153.68026753986817,
19.470851166420736
],
[
-155.41188228421524,
19.82593799656933
]
]
]
},
"properties": {
"datetime": "2024-12-16T20:42:39.252121Z",
"eo:cloud_cover": 58.93,
"view:sun_azimuth": 150.92260609,
"view:sun_elevation": 42.2078599,
"platform": "LANDSAT_9",
"instruments": [
"OLI",
"TIRS"
],
"view:off_nadir": 0,
"landsat:cloud_cover_land": 43.14,
"landsat:wrs_type": "2",
"landsat:wrs_path": "062",
"landsat:wrs_row": "047",
"landsat:scene_id": "LC90620472024351LGN00",
"landsat:collection_category": "A1",
"landsat:collection_number": "02",
"landsat:correction": "L2SP",
"accuracy:geometric_x_bias": 0,
"accuracy:geometric_y_bias": 0,
"accuracy:geometric_x_stddev": 5.048,
"accuracy:geometric_y_stddev": 4.77,
"accuracy:geometric_rmse": 6.946,
"proj:epsg": null,
"proj:shape": [
7701,
7571
],
"proj:transform": [
30,
0,
124185,
0,
-30,
1862415
],
"proj:wkt2": "PROJCS[\"AEA WGS84\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"Albers_Conic_Equal_Area\"],PARAMETER[\"latitude_of_center\",3],PARAMETER[\"longitude_of_center\",-157],PARAMETER[\"standard_parallel_1\",8],PARAMETER[\"standard_parallel_2\",18],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH]]",
"card4l:specification": "SR",
"card4l:specification_version": "5.0",
"created": "2024-12-17T13:13:26.984Z",
"updated": "2024-12-18T04:59:46.657Z"
},
"assets": {
"thumbnail": {
"title": "Thumbnail image",
"type": "image/jpeg",
"roles": [
"thumbnail"
],
"href": "https://exmaple.com/thumb_small.jpeg",
"alternate": {
"s3": {
"storage:platform": "AWS",
"storage:requester_pays": true,
"href": "s3://example/thumb_small.jpeg"
}
}
}
},
"links": [
{
"rel": "self",
"href": "https://example.com/items/my_id"
}
],
"collection": "landsat-c2l2alb-sr"
}
36 changes: 36 additions & 0 deletions autotest/ogr/ogr_geojson.py
Original file line number Diff line number Diff line change
Expand Up @@ -5784,3 +5784,39 @@ def test_ogr_geojson_schema_override(
assert (
gdal.GetLastErrorMsg().find(expected_warning) != -1
), f"Warning {expected_warning} not found, got {gdal.GetLastErrorMsg()} instead"


###############################################################################
# Test FOREIGN_MEMBERS open option


@pytest.mark.parametrize(
"foreign_members_option", [None, "AUTO", "ALL", "NONE", "STAC"]
)
def test_ogr_geojson_foreign_members(foreign_members_option):

open_options = {}
if foreign_members_option:
open_options["FOREIGN_MEMBERS"] = foreign_members_option
ds = gdal.OpenEx(
"data/geojson/stac_item.json", gdal.OF_VECTOR, open_options=open_options
)
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
if foreign_members_option is None or foreign_members_option in ("AUTO", "STAC"):
assert lyr.GetLayerDefn().GetFieldCount() == 39
assert f["stac_version"] == "1.0.0"
assert f["assets.thumbnail.title"] == "Thumbnail image"
assert json.loads(f["assets.thumbnail.alternate"]) == {
"s3": {
"storage:platform": "AWS",
"storage:requester_pays": True,
"href": "s3://example/thumb_small.jpeg",
}
}
elif foreign_members_option == "ALL":
assert lyr.GetLayerDefn().GetFieldCount() == 35
assert f["stac_version"] == "1.0.0"
assert f["assets"] != ""
else:
assert lyr.GetLayerDefn().GetFieldCount() == 29
20 changes: 20 additions & 0 deletions doc/source/drivers/vector/geojson.rst
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,26 @@ This driver supports the following open options:
Can also be set with the :config:`OGR_GEOJSON_DATE_AS_STRING`
configuration option.

- .. oo:: FOREIGN_MEMBERS
:choices: AUTO, ALL, NONE, STAC
:default: AUTO
:since: 3.11.0

Whether and how foreign members at the feature level should be added.

- ``AUTO`` mode behaves like ``STAC`` mode is a ``stac_version`` member is found at
the Feature level, otherwise it behaves as ``NONE`` mode.

- In ``ALL`` mode, all foreign members at the feature level are added.
Whether to recursively explore nested objects and produce flatten OGR attributes
or not is decided by the ``FLATTEN_NESTED_ATTRIBUTES`` open option.

- In ``NONE`` mode, no foreign members at the feature level are added.

- ``STAC`` mode (Spatio-Temporal Asset Catalog) behaves the same as ``ALL``,
except content under the ``assets`` member is by default flattened
as ``assets.{asset_name}.{asset_property}`` fields.

- .. oo:: OGR_SCHEMA
:choices: <filename>|<json string>
:since: 3.11.0
Expand Down
23 changes: 23 additions & 0 deletions ogr/ogrsf_frmts/geojson/ogrgeojsondatasource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1225,6 +1225,29 @@ void OGRGeoJSONDataSource::SetOptionsOnReader(GDALOpenInfo *poOpenInfo,
poReader->SetDateAsString(CPLTestBool(CSLFetchNameValueDef(
poOpenInfo->papszOpenOptions, "DATE_AS_STRING",
CPLGetConfigOption("OGR_GEOJSON_DATE_AS_STRING", "NO"))));

const char *pszForeignMembers = CSLFetchNameValueDef(
poOpenInfo->papszOpenOptions, "FOREIGN_MEMBERS", "AUTO");
if (EQUAL(pszForeignMembers, "AUTO"))
{
poReader->SetForeignMemberProcessing(
OGRGeoJSONBaseReader::ForeignMemberProcessing::AUTO);
}
else if (EQUAL(pszForeignMembers, "ALL"))
{
poReader->SetForeignMemberProcessing(
OGRGeoJSONBaseReader::ForeignMemberProcessing::ALL);
}
else if (EQUAL(pszForeignMembers, "NONE"))
{
poReader->SetForeignMemberProcessing(
OGRGeoJSONBaseReader::ForeignMemberProcessing::NONE);
}
else if (EQUAL(pszForeignMembers, "STAC"))
{
poReader->SetForeignMemberProcessing(
OGRGeoJSONBaseReader::ForeignMemberProcessing::STAC);
}
}

/************************************************************************/
Expand Down
8 changes: 8 additions & 0 deletions ogr/ogrsf_frmts/geojson/ogrgeojsondriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,14 @@ void RegisterOGRGeoJSON()
" <Option name='DATE_AS_STRING' type='boolean' description='Whether "
"to expose date/time/date-time content using dedicated OGR "
"date/time/date-time types or as a OGR String' default='NO'/>"
" <Option name='FOREIGN_MEMBERS' type='string-select' "
"description='Whether and how foreign members at the feature level "
"should be added' default='string-select' default='AUTO'>"
" <Value>AUTO</Value>"
" <Value>ALL</Value>"
" <Value>NONE</Value>"
" <Value>STAC</Value>"
" </Option>"
" <Option name='OGR_SCHEMA' type='string' description='"
"Partially or totally overrides the auto-detected schema to use for "
"creating the layer. "
Expand Down
Loading

0 comments on commit 6b0c896

Please sign in to comment.