From 16ae592157ce5f677221149699b64b747c7e7de0 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 28 Nov 2023 22:56:13 +0100 Subject: [PATCH] FlatGeobuf: add support for reading and writing layer title, description and metadata --- autotest/ogr/ogr_flatgeobuf.py | 40 +++++++ doc/source/drivers/vector/flatgeobuf.rst | 15 +++ ogr/ogrsf_frmts/flatgeobuf/ogr_flatgeobuf.h | 16 ++- .../flatgeobuf/ogrflatgeobufdataset.cpp | 5 +- .../flatgeobuf/ogrflatgeobufeditablelayer.cpp | 5 +- .../flatgeobuf/ogrflatgeobuflayer.cpp | 111 +++++++++++++++--- 6 files changed, 166 insertions(+), 26 deletions(-) diff --git a/autotest/ogr/ogr_flatgeobuf.py b/autotest/ogr/ogr_flatgeobuf.py index 40849801ac6d..bd6b1a4fc0b2 100644 --- a/autotest/ogr/ogr_flatgeobuf.py +++ b/autotest/ogr/ogr_flatgeobuf.py @@ -1217,3 +1217,43 @@ def test_ogr_flatgeobuf_issue_7401(): ogr.GetDriverByName("FlatGeobuf").DeleteDataSource("/vsimem/test.fgb") assert not gdal.VSIStatL("/vsimem/test.fgb") + + +############################################################################### +# Test reading and writing layer title, description and metadata + + +def test_ogr_flatgeobuf_title_description_metadata(tmp_vsimem): + + filename = str(tmp_vsimem / "test.fgb") + ds = ogr.GetDriverByName("FlatGeobuf").CreateDataSource(filename) + lyr = ds.CreateLayer( + "test", + geom_type=ogr.wkbPoint, + options=["SPATIAL_INDEX=NO", "TITLE=title", "DESCRIPTION=description"], + ) + lyr.SetMetadata({"foo": "bar", "bar": "baz"}) + f = ogr.Feature(lyr.GetLayerDefn()) + f.SetGeometry(ogr.CreateGeometryFromWkt("POINT (0 0)")) + lyr.CreateFeature(f) + ds = None + + ds = ogr.Open(filename) + lyr = ds.GetLayer(0) + assert lyr.GetMetadata_Dict() == { + "TITLE": "title", + "DESCRIPTION": "description", + "foo": "bar", + "bar": "baz", + } + + # Test that on FlatGeoBuf -> FlatGeoBuf TITLE and DESCRIPTION get properly propagated. + filename2 = str(tmp_vsimem / "test2.fgb") + ds = gdal.VectorTranslate(filename2, filename) + lyr = ds.GetLayer(0) + assert lyr.GetMetadata_Dict() == { + "TITLE": "title", + "DESCRIPTION": "description", + "foo": "bar", + "bar": "baz", + } diff --git a/doc/source/drivers/vector/flatgeobuf.rst b/doc/source/drivers/vector/flatgeobuf.rst index 0656c42668df..b3c289ddc166 100644 --- a/doc/source/drivers/vector/flatgeobuf.rst +++ b/doc/source/drivers/vector/flatgeobuf.rst @@ -34,6 +34,9 @@ On creation, passing a filename without a .fgb suffix will instruct the driver to create a directory of that name, and create layers as .fgb files in that directory. +Starting with GDAL 3.9, metadata set at the layer level will be written in the +FlatGeobuf header, and retrieved on reading as layer metadata. + Open options ------------ @@ -65,6 +68,18 @@ Layer Creation Options the :cpp:func:`CPLGenerateTempFilename` function. "/vsimem/" can be used for in-memory temporary files. +- .. lco:: TITLE + :choices: + :since: 3.9 + + Dataset title (should be relatively short) + +- .. lco:: DESCRIPTION + :choices: + :since: 3.9 + + Dataset description (intended for free form long text) + Creation Issues --------------- diff --git a/ogr/ogrsf_frmts/flatgeobuf/ogr_flatgeobuf.h b/ogr/ogrsf_frmts/flatgeobuf/ogr_flatgeobuf.h index 1a8aacd7cdd5..6621cdcc394b 100644 --- a/ogr/ogrsf_frmts/flatgeobuf/ogr_flatgeobuf.h +++ b/ogr/ogrsf_frmts/flatgeobuf/ogr_flatgeobuf.h @@ -103,13 +103,15 @@ class OGRFlatGeobufLayer final : public OGRLayer, bool m_ignoreAttributeFilter = false; // creation + GDALDataset *m_poDS = nullptr; // parent dataset to get metadata from it bool m_create = false; std::deque m_featureItems; // feature item description used to // create spatial index bool m_bCreateSpatialIndexAtClose = true; bool m_bVerifyBuffers = true; VSILFILE *m_poFpWrite = nullptr; - uint64_t m_writeOffset = 0; // current write offset + CPLStringList m_aosCreationOption{}; // layer creation options + uint64_t m_writeOffset = 0; // current write offset uint64_t m_offsetAfterHeader = 0; // offset after dummy header writing (when creating a file without // spatial index) @@ -142,11 +144,12 @@ class OGRFlatGeobufLayer final : public OGRLayer, OGRFlatGeobufLayer(const FlatGeobuf::Header *, GByte *headerBuf, const char *pszFilename, VSILFILE *poFp, uint64_t offset); - OGRFlatGeobufLayer(const char *pszLayerName, const char *pszFilename, + OGRFlatGeobufLayer(GDALDataset *poDS, const char *pszLayerName, + const char *pszFilename, const OGRSpatialReference *poSpatialRef, OGRwkbGeometryType eGType, bool bCreateSpatialIndexAtClose, VSILFILE *poFpWrite, - std::string &osTempFile); + std::string &osTempFile, CSLConstList papszOptions); protected: virtual int GetNextArrowArray(struct ArrowArrayStream *, @@ -163,7 +166,7 @@ class OGRFlatGeobufLayer final : public OGRLayer, static OGRFlatGeobufLayer *Open(const char *pszFilename, VSILFILE *fp, bool bVerifyBuffers); static OGRFlatGeobufLayer * - Create(const char *pszLayerName, const char *pszFilename, + Create(GDALDataset *poDS, const char *pszLayerName, const char *pszFilename, const OGRSpatialReference *poSpatialRef, OGRwkbGeometryType eGType, bool bCreateSpatialIndexAtClose, char **papszOptions); @@ -192,6 +195,11 @@ class OGRFlatGeobufLayer final : public OGRLayer, m_bVerifyBuffers = CPL_TO_BOOL(bFlag); } + GDALDataset *GetDataset() override + { + return m_poDS; + } + const std::string &GetFilename() const override { return m_osFilename; diff --git a/ogr/ogrsf_frmts/flatgeobuf/ogrflatgeobufdataset.cpp b/ogr/ogrsf_frmts/flatgeobuf/ogrflatgeobufdataset.cpp index 65190a3c6617..39261755d63f 100644 --- a/ogr/ogrsf_frmts/flatgeobuf/ogrflatgeobufdataset.cpp +++ b/ogr/ogrsf_frmts/flatgeobuf/ogrflatgeobufdataset.cpp @@ -155,6 +155,9 @@ void RegisterOGRFlatGeobuf() "create a spatial index' default='YES'/>" "