LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/flatgeobuf - ogrflatgeobufdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 172 195 88.2 %
Date: 2024-11-21 22:18:42 Functions: 15 15 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  FlatGeobuf driver
       4             :  * Purpose:  Implements OGRFlatGeobufDataset class
       5             :  * Author:   Björn Harrtell <bjorn at wololo dot org>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2018-2020, Björn Harrtell <bjorn at wololo dot org>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "ogr_flatgeobuf.h"
      14             : 
      15             : #include <memory>
      16             : 
      17             : #include "header_generated.h"
      18             : 
      19             : // For users not using CMake...
      20             : #ifndef flatbuffers
      21             : #error                                                                         \
      22             :     "Make sure to build with -Dflatbuffers=gdal_flatbuffers (for example) to avoid potential conflict of flatbuffers"
      23             : #endif
      24             : 
      25             : using namespace flatbuffers;
      26             : using namespace FlatGeobuf;
      27             : 
      28       46304 : static int OGRFlatGeobufDriverIdentify(GDALOpenInfo *poOpenInfo)
      29             : {
      30       46304 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, "FGB:"))
      31           0 :         return TRUE;
      32             : 
      33       46304 :     if (poOpenInfo->bIsDirectory)
      34             :     {
      35        1052 :         return -1;
      36             :     }
      37             : 
      38       45252 :     const auto nHeaderBytes = poOpenInfo->nHeaderBytes;
      39       45252 :     const auto pabyHeader = poOpenInfo->pabyHeader;
      40             : 
      41       45252 :     if (nHeaderBytes < 4)
      42       42405 :         return FALSE;
      43             : 
      44        2847 :     if (pabyHeader[0] == 0x66 && pabyHeader[1] == 0x67 && pabyHeader[2] == 0x62)
      45             :     {
      46         294 :         if (pabyHeader[3] == 0x03)
      47             :         {
      48         294 :             CPLDebug("FlatGeobuf", "Verified magicbytes");
      49         294 :             return TRUE;
      50             :         }
      51             :         else
      52             :         {
      53           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
      54             :                      "Unsupported FlatGeobuf version %d.\n",
      55           0 :                      poOpenInfo->pabyHeader[3]);
      56             :         }
      57             :     }
      58             : 
      59        2553 :     return FALSE;
      60             : }
      61             : 
      62             : /************************************************************************/
      63             : /*                           Delete()                                   */
      64             : /************************************************************************/
      65             : 
      66         147 : static CPLErr OGRFlatGoBufDriverDelete(const char *pszDataSource)
      67             : 
      68             : {
      69             :     VSIStatBufL sStatBuf;
      70             : 
      71         147 :     if (VSIStatL(pszDataSource, &sStatBuf) != 0)
      72             :     {
      73           0 :         CPLError(CE_Failure, CPLE_AppDefined,
      74             :                  "%s does not appear to be a file or directory.",
      75             :                  pszDataSource);
      76             : 
      77           0 :         return CE_Failure;
      78             :     }
      79             : 
      80         147 :     if (VSI_ISREG(sStatBuf.st_mode))
      81             :     {
      82         146 :         VSIUnlink(pszDataSource);
      83         146 :         return CE_None;
      84             :     }
      85             : 
      86           1 :     if (VSI_ISDIR(sStatBuf.st_mode))
      87             :     {
      88           1 :         char **papszDirEntries = VSIReadDir(pszDataSource);
      89             : 
      90           3 :         for (int iFile = 0;
      91           3 :              papszDirEntries != nullptr && papszDirEntries[iFile] != nullptr;
      92             :              iFile++)
      93             :         {
      94           2 :             if (EQUAL(CPLGetExtension(papszDirEntries[iFile]), "fgb"))
      95             :             {
      96           2 :                 VSIUnlink(CPLFormFilename(pszDataSource, papszDirEntries[iFile],
      97             :                                           nullptr));
      98             :             }
      99             :         }
     100             : 
     101           1 :         CSLDestroy(papszDirEntries);
     102             : 
     103           1 :         VSIRmdir(pszDataSource);
     104             :     }
     105             : 
     106           1 :     return CE_None;
     107             : }
     108             : 
     109             : /************************************************************************/
     110             : /*                       RegisterOGRFlatGeobuf()                        */
     111             : /************************************************************************/
     112             : 
     113        1595 : void RegisterOGRFlatGeobuf()
     114             : {
     115        1595 :     if (GDALGetDriverByName("FlatGeobuf") != nullptr)
     116         302 :         return;
     117             : 
     118        1293 :     GDALDriver *poDriver = new GDALDriver();
     119        1293 :     poDriver->SetDescription("FlatGeobuf");
     120        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
     121        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
     122        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
     123        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_DELETE_FIELD, "YES");
     124        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_REORDER_FIELDS, "YES");
     125        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_CURVE_GEOMETRIES, "YES");
     126        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_MEASURED_GEOMETRIES, "YES");
     127        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
     128        1293 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "FlatGeobuf");
     129        1293 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "fgb");
     130        1293 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
     131        1293 :                               "drivers/vector/flatgeobuf.html");
     132        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     133        1293 :     poDriver->SetMetadataItem(
     134             :         GDAL_DMD_CREATIONFIELDDATATYPES,
     135        1293 :         "Integer Integer64 Real String Date DateTime Binary");
     136        1293 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES,
     137        1293 :                               "Boolean Int16 Float32");
     138        1293 :     poDriver->SetMetadataItem(GDAL_DMD_CREATION_FIELD_DEFN_FLAGS,
     139        1293 :                               "WidthPrecision Comment AlternativeName");
     140             : 
     141        1293 :     poDriver->SetMetadataItem(
     142             :         GDAL_DS_LAYER_CREATIONOPTIONLIST,
     143             :         "<LayerCreationOptionList>"
     144             :         "  <Option name='SPATIAL_INDEX' type='boolean' description='Whether to "
     145             :         "create a spatial index' default='YES'/>"
     146             :         "  <Option name='TEMPORARY_DIR' type='string' description='Directory "
     147             :         "where temporary file should be created'/>"
     148             :         "  <Option name='TITLE' type='string' description='Layer title'/>"
     149             :         "  <Option name='DESCRIPTION' type='string' "
     150             :         "description='Layer description'/>"
     151        1293 :         "</LayerCreationOptionList>");
     152        1293 :     poDriver->SetMetadataItem(
     153             :         GDAL_DMD_OPENOPTIONLIST,
     154             :         "<OpenOptionList>"
     155             :         "  <Option name='VERIFY_BUFFERS' type='boolean' description='Verify "
     156             :         "flatbuffers integrity' default='YES'/>"
     157        1293 :         "</OpenOptionList>");
     158             : 
     159        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_COORDINATE_EPOCH, "YES");
     160        1293 :     poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
     161        1293 :     poDriver->SetMetadataItem(GDAL_DMD_ALTER_FIELD_DEFN_FLAGS,
     162        1293 :                               "Name WidthPrecision AlternativeName Comment");
     163             : 
     164        1293 :     poDriver->pfnOpen = OGRFlatGeobufDataset::Open;
     165        1293 :     poDriver->pfnCreate = OGRFlatGeobufDataset::Create;
     166        1293 :     poDriver->pfnIdentify = OGRFlatGeobufDriverIdentify;
     167        1293 :     poDriver->pfnDelete = OGRFlatGoBufDriverDelete;
     168             : 
     169        1293 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     170             : }
     171             : 
     172             : /************************************************************************/
     173             : /*                         OGRFlatGeobufDataset()                       */
     174             : /************************************************************************/
     175             : 
     176         764 : OGRFlatGeobufDataset::OGRFlatGeobufDataset(const char *pszName, bool bIsDir,
     177         764 :                                            bool bCreate, bool bUpdate)
     178         764 :     : m_bCreate(bCreate), m_bUpdate(bUpdate), m_bIsDir(bIsDir)
     179             : {
     180         764 :     SetDescription(pszName);
     181         764 : }
     182             : 
     183             : /************************************************************************/
     184             : /*                         ~OGRFlatGeobufDataset()                      */
     185             : /************************************************************************/
     186             : 
     187        1528 : OGRFlatGeobufDataset::~OGRFlatGeobufDataset()
     188             : {
     189         764 :     OGRFlatGeobufDataset::Close();
     190        1528 : }
     191             : 
     192             : /************************************************************************/
     193             : /*                              Close()                                 */
     194             : /************************************************************************/
     195             : 
     196        1071 : CPLErr OGRFlatGeobufDataset::Close()
     197             : {
     198        1071 :     CPLErr eErr = CE_None;
     199        1071 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
     200             :     {
     201         764 :         if (OGRFlatGeobufDataset::FlushCache(true) != CE_None)
     202           0 :             eErr = CE_Failure;
     203             : 
     204        1077 :         for (auto &poLayer : m_apoLayers)
     205             :         {
     206         313 :             if (poLayer->Close() != CE_None)
     207           0 :                 eErr = CE_Failure;
     208             :         }
     209             : 
     210         764 :         if (GDALDataset::Close() != CE_None)
     211           0 :             eErr = CE_Failure;
     212             :     }
     213        1071 :     return eErr;
     214             : }
     215             : 
     216             : /************************************************************************/
     217             : /*                                Open()                                */
     218             : /************************************************************************/
     219             : 
     220         664 : GDALDataset *OGRFlatGeobufDataset::Open(GDALOpenInfo *poOpenInfo)
     221             : {
     222         664 :     if (OGRFlatGeobufDriverIdentify(poOpenInfo) == FALSE)
     223           0 :         return nullptr;
     224             : 
     225             :     const auto bVerifyBuffers =
     226         664 :         CPLFetchBool(poOpenInfo->papszOpenOptions, "VERIFY_BUFFERS", true);
     227             : 
     228         664 :     auto isDir = CPL_TO_BOOL(poOpenInfo->bIsDirectory);
     229         664 :     auto bUpdate = poOpenInfo->eAccess == GA_Update;
     230             : 
     231         664 :     if (isDir && bUpdate)
     232             :     {
     233          68 :         return nullptr;
     234             :     }
     235             : 
     236             :     auto poDS = std::unique_ptr<OGRFlatGeobufDataset>(new OGRFlatGeobufDataset(
     237        1192 :         poOpenInfo->pszFilename, isDir, false, bUpdate));
     238             : 
     239         596 :     if (poOpenInfo->bIsDirectory)
     240             :     {
     241         452 :         CPLStringList aosFiles(VSIReadDir(poOpenInfo->pszFilename));
     242         452 :         int nCountFGB = 0;
     243         452 :         int nCountNonFGB = 0;
     244       22806 :         for (int i = 0; i < aosFiles.size(); i++)
     245             :         {
     246       22354 :             if (strcmp(aosFiles[i], ".") == 0 || strcmp(aosFiles[i], "..") == 0)
     247         402 :                 continue;
     248       21952 :             if (EQUAL(CPLGetExtension(aosFiles[i]), "fgb"))
     249           2 :                 nCountFGB++;
     250             :             else
     251       21950 :                 nCountNonFGB++;
     252             :         }
     253             :         // Consider that a directory is a FlatGeobuf dataset if there is a
     254             :         // majority of .fgb files in it
     255         452 :         if (nCountFGB == 0 || nCountFGB < nCountNonFGB)
     256             :         {
     257         451 :             return nullptr;
     258             :         }
     259           3 :         for (int i = 0; i < aosFiles.size(); i++)
     260             :         {
     261           2 :             if (EQUAL(CPLGetExtension(aosFiles[i]), "fgb"))
     262             :             {
     263           2 :                 CPLString osFilename(CPLFormFilename(poOpenInfo->pszFilename,
     264           4 :                                                      aosFiles[i], nullptr));
     265           2 :                 VSILFILE *fp = VSIFOpenL(osFilename, "rb");
     266           2 :                 if (fp)
     267             :                 {
     268           2 :                     if (!poDS->OpenFile(osFilename, fp, bVerifyBuffers))
     269           0 :                         VSIFCloseL(fp);
     270             :                 }
     271             :             }
     272             :         }
     273             :     }
     274             :     else
     275             :     {
     276         144 :         if (poOpenInfo->fpL != nullptr)
     277             :         {
     278         144 :             if (poDS->OpenFile(poOpenInfo->pszFilename, poOpenInfo->fpL,
     279             :                                bVerifyBuffers))
     280         144 :                 poOpenInfo->fpL = nullptr;
     281             :         }
     282             :         else
     283             :         {
     284           0 :             return nullptr;
     285             :         }
     286             :     }
     287         145 :     return poDS.release();
     288             : }
     289             : 
     290             : /************************************************************************/
     291             : /*                           OpenFile()                                 */
     292             : /************************************************************************/
     293             : 
     294         146 : bool OGRFlatGeobufDataset::OpenFile(const char *pszFilename, VSILFILE *fp,
     295             :                                     bool bVerifyBuffers)
     296             : {
     297         146 :     CPLDebugOnly("FlatGeobuf", "Opening OGRFlatGeobufLayer");
     298             :     auto poLayer = std::unique_ptr<OGRFlatGeobufLayer>(
     299         292 :         OGRFlatGeobufLayer::Open(pszFilename, fp, bVerifyBuffers));
     300         146 :     if (!poLayer)
     301           0 :         return false;
     302             : 
     303         146 :     if (m_bUpdate)
     304             :     {
     305           1 :         CPLDebugOnly("FlatGeobuf", "Creating OGRFlatGeobufEditableLayer");
     306             :         auto poEditableLayer = std::unique_ptr<OGRFlatGeobufEditableLayer>(
     307           1 :             new OGRFlatGeobufEditableLayer(poLayer.release(),
     308           1 :                                            papszOpenOptions));
     309           1 :         m_apoLayers.push_back(std::move(poEditableLayer));
     310             :     }
     311             :     else
     312             :     {
     313         145 :         m_apoLayers.push_back(std::move(poLayer));
     314             :     }
     315             : 
     316         146 :     return true;
     317             : }
     318             : 
     319         168 : GDALDataset *OGRFlatGeobufDataset::Create(const char *pszName, int /* nBands */,
     320             :                                           CPL_UNUSED int nXSize,
     321             :                                           CPL_UNUSED int nYSize,
     322             :                                           CPL_UNUSED GDALDataType eDT,
     323             :                                           char ** /* papszOptions */)
     324             : {
     325             :     // First, ensure there isn't any such file yet.
     326             :     VSIStatBufL sStatBuf;
     327             : 
     328         168 :     if (VSIStatL(pszName, &sStatBuf) == 0)
     329             :     {
     330           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     331             :                  "It seems a file system object called '%s' already exists.",
     332             :                  pszName);
     333             : 
     334           0 :         return nullptr;
     335             :     }
     336             : 
     337         168 :     bool bIsDir = false;
     338         168 :     if (!EQUAL(CPLGetExtension(pszName), "fgb"))
     339             :     {
     340           1 :         if (VSIMkdir(pszName, 0755) != 0)
     341             :         {
     342           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     343             :                      "Failed to create directory %s:\n%s", pszName,
     344           0 :                      VSIStrerror(errno));
     345           0 :             return nullptr;
     346             :         }
     347           1 :         bIsDir = true;
     348             :     }
     349             : 
     350         168 :     return new OGRFlatGeobufDataset(pszName, bIsDir, true, false);
     351             : }
     352             : 
     353         565 : OGRLayer *OGRFlatGeobufDataset::GetLayer(int iLayer)
     354             : {
     355         565 :     if (iLayer < 0 || iLayer >= GetLayerCount())
     356           2 :         return nullptr;
     357         563 :     return m_apoLayers[iLayer]->GetLayer();
     358             : }
     359             : 
     360          90 : int OGRFlatGeobufDataset::TestCapability(const char *pszCap)
     361             : {
     362          90 :     if (EQUAL(pszCap, ODsCCreateLayer))
     363          37 :         return m_bCreate && (m_bIsDir || m_apoLayers.empty());
     364          53 :     else if (EQUAL(pszCap, ODsCCurveGeometries))
     365          22 :         return true;
     366          31 :     else if (EQUAL(pszCap, ODsCMeasuredGeometries))
     367           2 :         return true;
     368          29 :     else if (EQUAL(pszCap, ODsCZGeometries))
     369           2 :         return true;
     370          27 :     else if (EQUAL(pszCap, ODsCRandomLayerWrite))
     371           0 :         return m_bUpdate;
     372             :     else
     373          27 :         return false;
     374             : }
     375             : 
     376             : /************************************************************************/
     377             : /*                        LaunderLayerName()                            */
     378             : /************************************************************************/
     379             : 
     380           2 : static CPLString LaunderLayerName(const char *pszLayerName)
     381             : {
     382           4 :     std::string osRet(CPLLaunderForFilename(pszLayerName, nullptr));
     383           2 :     if (osRet != pszLayerName)
     384             :     {
     385           1 :         CPLError(CE_Warning, CPLE_AppDefined,
     386             :                  "Invalid layer name for a file name: %s. Laundered to %s.",
     387             :                  pszLayerName, osRet.c_str());
     388             :     }
     389           4 :     return osRet;
     390             : }
     391             : 
     392             : OGRLayer *
     393         185 : OGRFlatGeobufDataset::ICreateLayer(const char *pszLayerName,
     394             :                                    const OGRGeomFieldDefn *poGeomFieldDefn,
     395             :                                    CSLConstList papszOptions)
     396             : {
     397             :     // Verify we are in update mode.
     398         185 :     if (!m_bCreate)
     399             :     {
     400           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     401             :                  "Data source %s opened read-only.\n"
     402             :                  "New layer %s cannot be created.",
     403           0 :                  GetDescription(), pszLayerName);
     404             : 
     405           0 :         return nullptr;
     406             :     }
     407         185 :     if (!m_bIsDir && !m_apoLayers.empty())
     408             :     {
     409          16 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     410             :                  "Can create only one single layer in a .fgb file. "
     411             :                  "Use a directory output for multiple layers");
     412             : 
     413          16 :         return nullptr;
     414             :     }
     415             : 
     416         169 :     const auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
     417             :     const auto poSpatialRef =
     418         169 :         poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
     419             : 
     420             :     // Verify that the datasource is a directory.
     421             :     VSIStatBufL sStatBuf;
     422             : 
     423             :     // What filename would we use?
     424         338 :     CPLString osFilename;
     425             : 
     426         169 :     if (m_bIsDir)
     427             :         osFilename = CPLFormFilename(
     428           2 :             GetDescription(), LaunderLayerName(pszLayerName).c_str(), "fgb");
     429             :     else
     430         167 :         osFilename = GetDescription();
     431             : 
     432             :     // Does this directory/file already exist?
     433         169 :     if (VSIStatL(osFilename, &sStatBuf) == 0)
     434             :     {
     435           0 :         CPLError(CE_Failure, CPLE_FileIO,
     436             :                  "Attempt to create layer %s, but %s already exists.",
     437             :                  pszLayerName, osFilename.c_str());
     438           0 :         return nullptr;
     439             :     }
     440             : 
     441             :     bool bCreateSpatialIndexAtClose =
     442         169 :         CPLFetchBool(papszOptions, "SPATIAL_INDEX", true);
     443             : 
     444             :     auto poLayer =
     445             :         std::unique_ptr<OGRFlatGeobufLayer>(OGRFlatGeobufLayer::Create(
     446             :             this, pszLayerName, osFilename, poSpatialRef, eGType,
     447         338 :             bCreateSpatialIndexAtClose, papszOptions));
     448         169 :     if (poLayer == nullptr)
     449           2 :         return nullptr;
     450             : 
     451         167 :     m_apoLayers.push_back(std::move(poLayer));
     452             : 
     453         167 :     return m_apoLayers.back()->GetLayer();
     454             : }
     455             : 
     456             : /************************************************************************/
     457             : //                            GetFileList()                             */
     458             : /************************************************************************/
     459             : 
     460           2 : char **OGRFlatGeobufDataset::GetFileList()
     461             : {
     462           4 :     CPLStringList oFileList;
     463           5 :     for (const auto &poLayer : m_apoLayers)
     464             :     {
     465           3 :         oFileList.AddString(poLayer->GetFilename().c_str());
     466             :     }
     467           4 :     return oFileList.StealList();
     468             : }

Generated by: LCOV version 1.14