LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/geojson - ogrgeojsonseqdriver.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 405 444 91.2 %
Date: 2025-09-10 17:48:50 Functions: 30 30 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  GeoJSON feature sequence driver
       5             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2018, Even Rouault <even.rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : #include "cpl_vsi_virtual.h"
      15             : #include "cpl_http.h"
      16             : #include "cpl_vsi_error.h"
      17             : 
      18             : #include "ogr_geojson.h"
      19             : #include "ogrlibjsonutils.h"
      20             : #include "ogrgeojsonreader.h"
      21             : #include "ogrgeojsonwriter.h"
      22             : #include "ogrgeojsongeometry.h"
      23             : 
      24             : #include <algorithm>
      25             : #include <memory>
      26             : 
      27             : constexpr char RS = '\x1e';
      28             : 
      29             : /************************************************************************/
      30             : /*                        OGRGeoJSONSeqDataSource                       */
      31             : /************************************************************************/
      32             : 
      33             : class OGRGeoJSONSeqDataSource final : public GDALDataset
      34             : {
      35             :     friend class OGRGeoJSONSeqLayer;
      36             : 
      37             :     std::vector<std::unique_ptr<OGRLayer>> m_apoLayers{};
      38             :     CPLString m_osTmpFile;
      39             :     VSILFILE *m_fp = nullptr;
      40             :     bool m_bSupportsRead = true;
      41             :     bool m_bAtEOF = false;
      42             :     bool m_bIsRSSeparated = false;
      43             : 
      44             :   public:
      45             :     OGRGeoJSONSeqDataSource();
      46             :     ~OGRGeoJSONSeqDataSource() override;
      47             : 
      48          76 :     int GetLayerCount() const override
      49             :     {
      50          76 :         return static_cast<int>(m_apoLayers.size());
      51             :     }
      52             : 
      53             :     const OGRLayer *GetLayer(int) const override;
      54             :     OGRLayer *ICreateLayer(const char *pszName,
      55             :                            const OGRGeomFieldDefn *poGeomFieldDefn,
      56             :                            CSLConstList papszOptions) override;
      57             :     int TestCapability(const char *pszCap) const override;
      58             : 
      59             :     bool Open(GDALOpenInfo *poOpenInfo, GeoJSONSourceType nSrcType);
      60             :     bool Create(const char *pszName, char **papszOptions);
      61             : };
      62             : 
      63             : /************************************************************************/
      64             : /*                           OGRGeoJSONSeqLayer                         */
      65             : /************************************************************************/
      66             : 
      67             : class OGRGeoJSONSeqLayer final : public OGRLayer
      68             : {
      69             :     OGRGeoJSONSeqDataSource *m_poDS = nullptr;
      70             :     OGRFeatureDefn *m_poFeatureDefn = nullptr;
      71             :     bool m_bLayerDefnEstablished = false;
      72             :     bool m_bWriteOnlyLayer = false;
      73             : 
      74             :     OGRGeoJSONBaseReader m_oReader;
      75             :     CPLString m_osFIDColumn;
      76             : 
      77             :     size_t m_nMaxObjectSize = 0;
      78             :     std::string m_osBuffer;
      79             :     std::string m_osFeatureBuffer;
      80             :     size_t m_nPosInBuffer = 0;
      81             :     size_t m_nBufferValidSize = 0;
      82             : 
      83             :     vsi_l_offset m_nFileSize = 0;
      84             :     GIntBig m_nIter = 0;
      85             : 
      86             :     GIntBig m_nTotalFeatures = 0;
      87             :     GIntBig m_nNextFID = 0;
      88             : 
      89             :     std::unique_ptr<OGRCoordinateTransformation> m_poCT{};
      90             :     OGRGeometryFactory::TransformWithOptionsCache m_oTransformCache;
      91             :     OGRGeoJSONWriteOptions m_oWriteOptions;
      92             : 
      93             :     json_object *GetNextObject(bool bLooseIdentification);
      94             : 
      95             :   public:
      96             :     OGRGeoJSONSeqLayer(OGRGeoJSONSeqDataSource *poDS, const char *pszName);
      97             : 
      98             :     // Write-only constructor
      99             :     OGRGeoJSONSeqLayer(OGRGeoJSONSeqDataSource *poDS, const char *pszName,
     100             :                        CSLConstList papszOptions,
     101             :                        std::unique_ptr<OGRCoordinateTransformation> &&poCT);
     102             : 
     103             :     ~OGRGeoJSONSeqLayer() override;
     104             : 
     105             :     bool Init(bool bLooseIdentification, bool bEstablishLayerDefn);
     106             : 
     107          61 :     const char *GetName() const override
     108             :     {
     109          61 :         return GetDescription();
     110             :     }
     111             : 
     112             :     void ResetReading() override;
     113             :     OGRFeature *GetNextFeature() override;
     114             :     const OGRFeatureDefn *GetLayerDefn() const override;
     115             : 
     116          30 :     const char *GetFIDColumn() const override
     117             :     {
     118          30 :         return m_osFIDColumn.c_str();
     119             :     }
     120             : 
     121             :     GIntBig GetFeatureCount(int) override;
     122             :     int TestCapability(const char *) const override;
     123             :     OGRErr ICreateFeature(OGRFeature *poFeature) override;
     124             :     OGRErr CreateField(const OGRFieldDefn *, int) override;
     125             : 
     126          22 :     GDALDataset *GetDataset() override
     127             :     {
     128          22 :         return m_poDS;
     129             :     }
     130             : };
     131             : 
     132             : /************************************************************************/
     133             : /*                       OGRGeoJSONSeqDataSource()                      */
     134             : /************************************************************************/
     135             : 
     136          99 : OGRGeoJSONSeqDataSource::OGRGeoJSONSeqDataSource()
     137             : {
     138          99 : }
     139             : 
     140             : /************************************************************************/
     141             : /*                      ~OGRGeoJSONSeqDataSource()                      */
     142             : /************************************************************************/
     143             : 
     144         198 : OGRGeoJSONSeqDataSource::~OGRGeoJSONSeqDataSource()
     145             : {
     146          99 :     if (m_fp)
     147             :     {
     148          89 :         VSIFCloseL(m_fp);
     149             :     }
     150          99 :     if (!m_osTmpFile.empty())
     151             :     {
     152           3 :         VSIUnlink(m_osTmpFile);
     153             :     }
     154         198 : }
     155             : 
     156             : /************************************************************************/
     157             : /*                               GetLayer()                             */
     158             : /************************************************************************/
     159             : 
     160          44 : const OGRLayer *OGRGeoJSONSeqDataSource::GetLayer(int nIndex) const
     161             : {
     162          44 :     if (nIndex < 0 || nIndex >= GetLayerCount())
     163           2 :         return nullptr;
     164          42 :     return m_apoLayers[nIndex].get();
     165             : }
     166             : 
     167             : /************************************************************************/
     168             : /*                           ICreateLayer()                             */
     169             : /************************************************************************/
     170             : 
     171          65 : OGRLayer *OGRGeoJSONSeqDataSource::ICreateLayer(
     172             :     const char *pszNameIn, const OGRGeomFieldDefn *poSrcGeomFieldDefn,
     173             :     CSLConstList papszOptions)
     174             : {
     175          65 :     if (!TestCapability(ODsCCreateLayer))
     176           3 :         return nullptr;
     177             : 
     178             :     const auto poSRS =
     179          62 :         poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetSpatialRef() : nullptr;
     180             : 
     181          62 :     std::unique_ptr<OGRCoordinateTransformation> poCT;
     182          62 :     if (poSRS == nullptr)
     183             :     {
     184          50 :         CPLError(
     185             :             CE_Warning, CPLE_AppDefined,
     186             :             "No SRS set on layer. Assuming it is long/lat on WGS84 ellipsoid");
     187             :     }
     188             :     else
     189             :     {
     190          12 :         OGRSpatialReference oSRSWGS84;
     191          12 :         oSRSWGS84.SetWellKnownGeogCS("WGS84");
     192          12 :         oSRSWGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     193          12 :         const char *const apszOptions[] = {
     194             :             "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES", nullptr};
     195          12 :         if (!poSRS->IsSame(&oSRSWGS84, apszOptions))
     196             :         {
     197           3 :             poCT.reset(OGRCreateCoordinateTransformation(poSRS, &oSRSWGS84));
     198           3 :             if (poCT == nullptr)
     199             :             {
     200           0 :                 CPLError(
     201             :                     CE_Warning, CPLE_AppDefined,
     202             :                     "Failed to create coordinate transformation between the "
     203             :                     "input coordinate system and WGS84.");
     204             : 
     205           0 :                 return nullptr;
     206             :             }
     207             :         }
     208             :     }
     209             : 
     210          62 :     const char *pszRS = CSLFetchNameValue(papszOptions, "RS");
     211          62 :     if (pszRS)
     212             :     {
     213           1 :         m_bIsRSSeparated = CPLTestBool(pszRS);
     214             :     }
     215             : 
     216          62 :     CPLStringList aosOptions(papszOptions);
     217             : 
     218          62 :     double dfXYResolution = OGRGeomCoordinatePrecision::UNKNOWN;
     219          62 :     double dfZResolution = OGRGeomCoordinatePrecision::UNKNOWN;
     220          62 :     if (const char *pszCoordPrecision =
     221          62 :             CSLFetchNameValue(papszOptions, "COORDINATE_PRECISION"))
     222             :     {
     223           1 :         dfXYResolution = std::pow(10.0, -CPLAtof(pszCoordPrecision));
     224           1 :         dfZResolution = dfXYResolution;
     225             :     }
     226          61 :     else if (poSrcGeomFieldDefn)
     227             :     {
     228          58 :         const auto &oCoordPrec = poSrcGeomFieldDefn->GetCoordinatePrecision();
     229         116 :         OGRSpatialReference oSRSWGS84;
     230          58 :         oSRSWGS84.SetWellKnownGeogCS("WGS84");
     231             :         const auto oCoordPrecWGS84 =
     232         116 :             oCoordPrec.ConvertToOtherSRS(poSRS, &oSRSWGS84);
     233             : 
     234          58 :         if (oCoordPrec.dfXYResolution != OGRGeomCoordinatePrecision::UNKNOWN)
     235             :         {
     236           2 :             dfXYResolution = oCoordPrecWGS84.dfXYResolution;
     237             : 
     238             :             aosOptions.SetNameValue(
     239             :                 "XY_COORD_PRECISION",
     240             :                 CPLSPrintf("%d",
     241             :                            OGRGeomCoordinatePrecision::ResolutionToPrecision(
     242           2 :                                dfXYResolution)));
     243             :         }
     244          58 :         if (oCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN)
     245             :         {
     246           2 :             dfZResolution = oCoordPrecWGS84.dfZResolution;
     247             : 
     248             :             aosOptions.SetNameValue(
     249             :                 "Z_COORD_PRECISION",
     250             :                 CPLSPrintf("%d",
     251             :                            OGRGeomCoordinatePrecision::ResolutionToPrecision(
     252           2 :                                dfZResolution)));
     253             :         }
     254             :     }
     255             : 
     256          62 :     m_apoLayers.emplace_back(std::make_unique<OGRGeoJSONSeqLayer>(
     257          62 :         this, pszNameIn, aosOptions.List(), std::move(poCT)));
     258             : 
     259          62 :     auto poLayer = m_apoLayers.back().get();
     260          62 :     if (poLayer->GetGeomType() != wkbNone &&
     261             :         dfXYResolution != OGRGeomCoordinatePrecision::UNKNOWN)
     262             :     {
     263           3 :         auto poGeomFieldDefn = poLayer->GetLayerDefn()->GetGeomFieldDefn(0);
     264             :         OGRGeomCoordinatePrecision oCoordPrec(
     265           6 :             poGeomFieldDefn->GetCoordinatePrecision());
     266           3 :         oCoordPrec.dfXYResolution = dfXYResolution;
     267           3 :         poGeomFieldDefn->SetCoordinatePrecision(oCoordPrec);
     268             :     }
     269             : 
     270          62 :     if (poLayer->GetGeomType() != wkbNone &&
     271             :         dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN)
     272             :     {
     273           3 :         auto poGeomFieldDefn = poLayer->GetLayerDefn()->GetGeomFieldDefn(0);
     274             :         OGRGeomCoordinatePrecision oCoordPrec(
     275           6 :             poGeomFieldDefn->GetCoordinatePrecision());
     276           3 :         oCoordPrec.dfZResolution = dfZResolution;
     277           3 :         poGeomFieldDefn->SetCoordinatePrecision(oCoordPrec);
     278             :     }
     279             : 
     280          62 :     return poLayer;
     281             : }
     282             : 
     283             : /************************************************************************/
     284             : /*                           TestCapability()                           */
     285             : /************************************************************************/
     286             : 
     287         128 : int OGRGeoJSONSeqDataSource::TestCapability(const char *pszCap) const
     288             : {
     289         128 :     if (EQUAL(pszCap, ODsCCreateLayer))
     290         105 :         return eAccess == GA_Update;
     291             : 
     292          23 :     return FALSE;
     293             : }
     294             : 
     295             : /************************************************************************/
     296             : /*                           OGRGeoJSONSeqLayer()                       */
     297             : /************************************************************************/
     298             : 
     299          46 : OGRGeoJSONSeqLayer::OGRGeoJSONSeqLayer(OGRGeoJSONSeqDataSource *poDS,
     300          46 :                                        const char *pszName)
     301          46 :     : m_poDS(poDS)
     302             : {
     303          46 :     SetDescription(pszName);
     304          46 :     m_poFeatureDefn = new OGRFeatureDefn(pszName);
     305          46 :     m_poFeatureDefn->Reference();
     306             : 
     307          46 :     OGRSpatialReference *poSRSWGS84 = new OGRSpatialReference();
     308          46 :     poSRSWGS84->SetWellKnownGeogCS("WGS84");
     309          46 :     poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     310          46 :     m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRSWGS84);
     311          46 :     poSRSWGS84->Release();
     312             : 
     313             :     const double dfTmp =
     314          46 :         CPLAtof(CPLGetConfigOption("OGR_GEOJSON_MAX_OBJ_SIZE", "200"));
     315          46 :     m_nMaxObjectSize = dfTmp > 0 ? static_cast<size_t>(dfTmp * 1024 * 1024) : 0;
     316          46 : }
     317             : 
     318             : /************************************************************************/
     319             : /*                           OGRGeoJSONSeqLayer()                       */
     320             : /************************************************************************/
     321             : 
     322             : // Write-only constructor
     323          62 : OGRGeoJSONSeqLayer::OGRGeoJSONSeqLayer(
     324             :     OGRGeoJSONSeqDataSource *poDS, const char *pszName,
     325             :     CSLConstList papszOptions,
     326          62 :     std::unique_ptr<OGRCoordinateTransformation> &&poCT)
     327          62 :     : m_poDS(poDS), m_bWriteOnlyLayer(true)
     328             : {
     329          62 :     m_bLayerDefnEstablished = true;
     330             : 
     331          62 :     SetDescription(pszName);
     332          62 :     m_poFeatureDefn = new OGRFeatureDefn(pszName);
     333          62 :     m_poFeatureDefn->Reference();
     334          62 :     m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(
     335          62 :         OGRSpatialReference::GetWGS84SRS());
     336          62 :     m_poCT = std::move(poCT);
     337             : 
     338          62 :     m_oWriteOptions.bWriteBBOX =
     339          62 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "WRITE_BBOX", "FALSE"));
     340          62 :     m_oWriteOptions.SetRFC7946Settings();
     341          62 :     m_oWriteOptions.SetIDOptions(papszOptions);
     342             : 
     343             :     const char *pszCoordPrecision =
     344          62 :         CSLFetchNameValue(papszOptions, "COORDINATE_PRECISION");
     345          62 :     if (pszCoordPrecision)
     346             :     {
     347           1 :         m_oWriteOptions.nXYCoordPrecision = atoi(pszCoordPrecision);
     348           1 :         m_oWriteOptions.nZCoordPrecision = atoi(pszCoordPrecision);
     349             :     }
     350             :     else
     351             :     {
     352          61 :         m_oWriteOptions.nXYCoordPrecision =
     353          61 :             atoi(CSLFetchNameValueDef(papszOptions, "XY_COORD_PRECISION", "7"));
     354          61 :         m_oWriteOptions.nZCoordPrecision =
     355          61 :             atoi(CSLFetchNameValueDef(papszOptions, "Z_COORD_PRECISION", "3"));
     356             :     }
     357             : 
     358          62 :     m_oWriteOptions.nSignificantFigures =
     359          62 :         atoi(CSLFetchNameValueDef(papszOptions, "SIGNIFICANT_FIGURES", "-1"));
     360          62 :     m_oWriteOptions.bAllowNonFiniteValues = CPLTestBool(
     361             :         CSLFetchNameValueDef(papszOptions, "WRITE_NON_FINITE_VALUES", "FALSE"));
     362          62 :     m_oWriteOptions.bAutodetectJsonStrings = CPLTestBool(
     363             :         CSLFetchNameValueDef(papszOptions, "AUTODETECT_JSON_STRINGS", "TRUE"));
     364          62 : }
     365             : 
     366             : /************************************************************************/
     367             : /*                          ~OGRGeoJSONSeqLayer()                       */
     368             : /************************************************************************/
     369             : 
     370         216 : OGRGeoJSONSeqLayer::~OGRGeoJSONSeqLayer()
     371             : {
     372         108 :     m_poFeatureDefn->Release();
     373         216 : }
     374             : 
     375             : /************************************************************************/
     376             : /*                           GetLayerDefn()                             */
     377             : /************************************************************************/
     378             : 
     379        1184 : const OGRFeatureDefn *OGRGeoJSONSeqLayer::GetLayerDefn() const
     380             : {
     381        1184 :     if (!m_bLayerDefnEstablished)
     382             :     {
     383           3 :         const_cast<OGRGeoJSONSeqLayer *>(this)->Init(
     384             :             /* bLooseIdentification = */ false,
     385             :             /* bEstablishLayerDefn = */ true);
     386             :     }
     387        1184 :     return m_poFeatureDefn;
     388             : }
     389             : 
     390             : /************************************************************************/
     391             : /*                               Init()                                 */
     392             : /************************************************************************/
     393             : 
     394          49 : bool OGRGeoJSONSeqLayer::Init(bool bLooseIdentification,
     395             :                               bool bEstablishLayerDefn)
     396             : {
     397          55 :     if (STARTS_WITH(m_poDS->GetDescription(), "/vsimem/") ||
     398           6 :         !STARTS_WITH(m_poDS->GetDescription(), "/vsi"))
     399             :     {
     400          49 :         VSIFSeekL(m_poDS->m_fp, 0, SEEK_END);
     401          49 :         m_nFileSize = VSIFTellL(m_poDS->m_fp);
     402             :     }
     403             : 
     404             :     // Set m_bLayerDefnEstablished = true early to avoid infinite recursive
     405             :     // calls.
     406          49 :     if (bEstablishLayerDefn)
     407          43 :         m_bLayerDefnEstablished = true;
     408             : 
     409          49 :     ResetReading();
     410             : 
     411          98 :     std::map<std::string, int> oMapFieldNameToIdx;
     412          98 :     std::vector<std::unique_ptr<OGRFieldDefn>> apoFieldDefn;
     413          49 :     gdal::DirectedAcyclicGraph<int, std::string> dag;
     414          49 :     bool bOK = false;
     415             : 
     416             :     while (true)
     417             :     {
     418         205 :         auto poObject = GetNextObject(bLooseIdentification);
     419         205 :         if (!poObject)
     420          43 :             break;
     421         162 :         const auto eObjectType = OGRGeoJSONGetType(poObject);
     422         162 :         if (bEstablishLayerDefn && eObjectType == GeoJSONObject::eFeature)
     423             :         {
     424         151 :             m_oReader.GenerateFeatureDefn(oMapFieldNameToIdx, apoFieldDefn, dag,
     425             :                                           this, poObject);
     426             :         }
     427         162 :         json_object_put(poObject);
     428         162 :         if (!bEstablishLayerDefn)
     429             :         {
     430           6 :             bOK = (eObjectType == GeoJSONObject::eFeature);
     431           6 :             break;
     432             :         }
     433         156 :         m_nTotalFeatures++;
     434         156 :     }
     435             : 
     436          49 :     if (bEstablishLayerDefn)
     437             :     {
     438             :         // CPLDebug("GEOJSONSEQ", "Establish layer definition");
     439             : 
     440          86 :         const auto sortedFields = dag.getTopologicalOrdering();
     441          43 :         CPLAssert(sortedFields.size() == apoFieldDefn.size());
     442         158 :         for (int idx : sortedFields)
     443             :         {
     444         115 :             m_poFeatureDefn->AddFieldDefn(apoFieldDefn[idx].get());
     445             :         }
     446          43 :         m_poFeatureDefn->Seal(true);
     447          43 :         m_oReader.FinalizeLayerDefn(this, m_osFIDColumn);
     448             :     }
     449             : 
     450          49 :     ResetReading();
     451             : 
     452          49 :     m_nFileSize = 0;
     453          49 :     m_nIter = 0;
     454             : 
     455          98 :     return bOK || m_nTotalFeatures > 0;
     456             : }
     457             : 
     458             : /************************************************************************/
     459             : /*                            ResetReading()                            */
     460             : /************************************************************************/
     461             : 
     462         218 : void OGRGeoJSONSeqLayer::ResetReading()
     463             : {
     464         436 :     if (!m_poDS->m_bSupportsRead ||
     465         218 :         (m_bWriteOnlyLayer && m_poDS->m_apoLayers.size() > 1))
     466             :     {
     467           0 :         return;
     468             :     }
     469             : 
     470         218 :     m_poDS->m_bAtEOF = false;
     471         218 :     VSIFSeekL(m_poDS->m_fp, 0, SEEK_SET);
     472             :     // Undocumented: for testing purposes only
     473         218 :     const size_t nBufferSize = static_cast<size_t>(std::max(
     474         218 :         1, atoi(CPLGetConfigOption("OGR_GEOJSONSEQ_CHUNK_SIZE", "40960"))));
     475         218 :     const size_t nBufferSizeValidated =
     476             :         nBufferSize > static_cast<size_t>(100 * 1000 * 1000)
     477             :             ? static_cast<size_t>(100 * 1000 * 1000)
     478             :             : nBufferSize;
     479         218 :     m_osBuffer.resize(nBufferSizeValidated);
     480         218 :     m_osFeatureBuffer.clear();
     481         218 :     m_nPosInBuffer = nBufferSizeValidated;
     482         218 :     m_nBufferValidSize = nBufferSizeValidated;
     483         218 :     m_nNextFID = 0;
     484             : }
     485             : 
     486             : /************************************************************************/
     487             : /*                           GetNextObject()                            */
     488             : /************************************************************************/
     489             : 
     490         394 : json_object *OGRGeoJSONSeqLayer::GetNextObject(bool bLooseIdentification)
     491             : {
     492         394 :     m_osFeatureBuffer.clear();
     493             :     while (true)
     494             :     {
     495             :         // If we read all the buffer, then reload it from file
     496         450 :         if (m_nPosInBuffer >= m_nBufferValidSize)
     497             :         {
     498         264 :             if (m_nBufferValidSize < m_osBuffer.size())
     499             :             {
     500          89 :                 return nullptr;
     501             :             }
     502         175 :             m_nBufferValidSize =
     503         175 :                 VSIFReadL(&m_osBuffer[0], 1, m_osBuffer.size(), m_poDS->m_fp);
     504         175 :             m_nPosInBuffer = 0;
     505         302 :             if (VSIFTellL(m_poDS->m_fp) == m_nBufferValidSize &&
     506         127 :                 m_nBufferValidSize > 0)
     507             :             {
     508         127 :                 m_poDS->m_bIsRSSeparated = (m_osBuffer[0] == RS);
     509         127 :                 if (m_poDS->m_bIsRSSeparated)
     510             :                 {
     511          26 :                     m_nPosInBuffer++;
     512             :                 }
     513             :             }
     514         175 :             m_nIter++;
     515             : 
     516         221 :             if (m_nFileSize > 0 && (m_nBufferValidSize < m_osBuffer.size() ||
     517          46 :                                     (m_nIter % 100) == 0))
     518             :             {
     519          48 :                 CPLDebug("GeoJSONSeq", "First pass: %.2f %%",
     520          48 :                          100.0 * VSIFTellL(m_poDS->m_fp) / m_nFileSize);
     521             :             }
     522         175 :             if (m_nPosInBuffer >= m_nBufferValidSize)
     523             :             {
     524           0 :                 return nullptr;
     525             :             }
     526             :         }
     527             : 
     528             :         // Find next feature separator in buffer
     529         361 :         const size_t nNextSepPos = m_osBuffer.find(
     530         361 :             m_poDS->m_bIsRSSeparated ? RS : '\n', m_nPosInBuffer);
     531         361 :         if (nNextSepPos != std::string::npos)
     532             :         {
     533         570 :             m_osFeatureBuffer.append(m_osBuffer.data() + m_nPosInBuffer,
     534         285 :                                      nNextSepPos - m_nPosInBuffer);
     535         285 :             m_nPosInBuffer = nNextSepPos + 1;
     536             :         }
     537             :         else
     538             :         {
     539             :             // No separator ? then accummulate
     540         152 :             m_osFeatureBuffer.append(m_osBuffer.data() + m_nPosInBuffer,
     541          76 :                                      m_nBufferValidSize - m_nPosInBuffer);
     542         145 :             if (m_nMaxObjectSize > 0 &&
     543          69 :                 m_osFeatureBuffer.size() > m_nMaxObjectSize)
     544             :             {
     545           1 :                 CPLError(CE_Failure, CPLE_NotSupported,
     546             :                          "Too large feature. You may define the "
     547             :                          "OGR_GEOJSON_MAX_OBJ_SIZE configuration option to "
     548             :                          "a value in megabytes (larger than %u) to allow "
     549             :                          "for larger features, or 0 to remove any size limit.",
     550           1 :                          static_cast<unsigned>(m_osFeatureBuffer.size() / 1024 /
     551             :                                                1024));
     552           1 :                 return nullptr;
     553             :             }
     554          75 :             m_nPosInBuffer = m_nBufferValidSize;
     555          75 :             if (m_nBufferValidSize == m_osBuffer.size())
     556             :             {
     557          48 :                 continue;
     558             :             }
     559             :         }
     560             : 
     561         762 :         while (!m_osFeatureBuffer.empty() &&
     562         380 :                (m_osFeatureBuffer.back() == '\r' ||
     563         380 :                 m_osFeatureBuffer.back() == '\n'))
     564             :         {
     565          70 :             m_osFeatureBuffer.pop_back();
     566             :         }
     567         312 :         if (!m_osFeatureBuffer.empty())
     568             :         {
     569         310 :             json_object *poObject = nullptr;
     570         310 :             CPL_IGNORE_RET_VAL(
     571         310 :                 OGRJSonParse(m_osFeatureBuffer.c_str(), &poObject));
     572         310 :             m_osFeatureBuffer.clear();
     573         310 :             if (json_object_get_type(poObject) == json_type_object)
     574             :             {
     575         304 :                 return poObject;
     576             :             }
     577           6 :             json_object_put(poObject);
     578           6 :             if (bLooseIdentification)
     579             :             {
     580           0 :                 return nullptr;
     581             :             }
     582             :         }
     583          56 :     }
     584             : }
     585             : 
     586             : /************************************************************************/
     587             : /*                           GetNextFeature()                           */
     588             : /************************************************************************/
     589             : 
     590         169 : OGRFeature *OGRGeoJSONSeqLayer::GetNextFeature()
     591             : {
     592         169 :     if (!m_poDS->m_bSupportsRead)
     593             :     {
     594           0 :         return nullptr;
     595             :     }
     596         169 :     if (m_bWriteOnlyLayer && m_poDS->m_apoLayers.size() > 1)
     597             :     {
     598           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     599             :                  "GetNextFeature() not supported when appending a new layer");
     600           0 :         return nullptr;
     601             :     }
     602             : 
     603         169 :     GetLayerDefn();  // force scan if not already done
     604             :     while (true)
     605             :     {
     606         189 :         auto poObject = GetNextObject(false);
     607         189 :         if (!poObject)
     608          47 :             return nullptr;
     609             :         OGRFeature *poFeature;
     610         142 :         auto type = OGRGeoJSONGetType(poObject);
     611         142 :         if (type == GeoJSONObject::eFeature)
     612             :         {
     613         138 :             poFeature = m_oReader.ReadFeature(this, poObject,
     614             :                                               m_osFeatureBuffer.c_str());
     615         138 :             json_object_put(poObject);
     616             :         }
     617           4 :         else if (type == GeoJSONObject::eFeatureCollection ||
     618             :                  type == GeoJSONObject::eUnknown)
     619             :         {
     620           0 :             json_object_put(poObject);
     621           0 :             continue;
     622             :         }
     623             :         else
     624             :         {
     625             :             OGRGeometry *poGeom =
     626           4 :                 m_oReader.ReadGeometry(poObject, GetSpatialRef());
     627           4 :             json_object_put(poObject);
     628           4 :             if (!poGeom)
     629             :             {
     630           0 :                 continue;
     631             :             }
     632           4 :             poFeature = new OGRFeature(m_poFeatureDefn);
     633           4 :             poFeature->SetGeometryDirectly(poGeom);
     634             :         }
     635             : 
     636         142 :         if (poFeature->GetFID() == OGRNullFID)
     637             :         {
     638         142 :             poFeature->SetFID(m_nNextFID);
     639         142 :             m_nNextFID++;
     640             :         }
     641         306 :         if ((m_poFilterGeom == nullptr ||
     642         276 :              FilterGeometry(poFeature->GetGeomFieldRef(m_iGeomFieldFilter))) &&
     643         134 :             (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
     644             :         {
     645         122 :             return poFeature;
     646             :         }
     647          20 :         delete poFeature;
     648          20 :     }
     649             : }
     650             : 
     651             : /************************************************************************/
     652             : /*                          GetFeatureCount()                           */
     653             : /************************************************************************/
     654             : 
     655          32 : GIntBig OGRGeoJSONSeqLayer::GetFeatureCount(int bForce)
     656             : {
     657          32 :     if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
     658             :     {
     659          26 :         GetLayerDefn();  // force scan if not already done
     660          26 :         return m_nTotalFeatures;
     661             :     }
     662           6 :     return OGRLayer::GetFeatureCount(bForce);
     663             : }
     664             : 
     665             : /************************************************************************/
     666             : /*                           TestCapability()                           */
     667             : /************************************************************************/
     668             : 
     669         186 : int OGRGeoJSONSeqLayer::TestCapability(const char *pszCap) const
     670             : {
     671         186 :     if (EQUAL(pszCap, OLCStringsAsUTF8))
     672          13 :         return true;
     673         173 :     if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr &&
     674         173 :         EQUAL(pszCap, OLCFastFeatureCount))
     675             :     {
     676           0 :         return true;
     677             :     }
     678         173 :     if (EQUAL(pszCap, OLCCreateField) || EQUAL(pszCap, OLCSequentialWrite))
     679             :     {
     680          47 :         return m_poDS->GetAccess() == GA_Update;
     681             :     }
     682             : 
     683         126 :     return false;
     684             : }
     685             : 
     686             : /************************************************************************/
     687             : /*                           ICreateFeature()                           */
     688             : /************************************************************************/
     689             : 
     690         106 : OGRErr OGRGeoJSONSeqLayer::ICreateFeature(OGRFeature *poFeature)
     691             : {
     692         106 :     if (m_poDS->GetAccess() != GA_Update)
     693           3 :         return OGRERR_FAILURE;
     694             : 
     695         103 :     if (!m_poDS->m_bAtEOF)
     696             :     {
     697          50 :         m_poDS->m_bAtEOF = true;
     698          50 :         VSIFSeekL(m_poDS->m_fp, 0, SEEK_END);
     699             :     }
     700             : 
     701         103 :     std::unique_ptr<OGRFeature> poFeatureToWrite;
     702         103 :     if (m_poCT != nullptr)
     703             :     {
     704           3 :         poFeatureToWrite.reset(new OGRFeature(m_poFeatureDefn));
     705           3 :         poFeatureToWrite->SetFrom(poFeature);
     706           3 :         poFeatureToWrite->SetFID(poFeature->GetFID());
     707           3 :         OGRGeometry *poGeometry = poFeatureToWrite->GetGeometryRef();
     708           3 :         if (poGeometry)
     709             :         {
     710           3 :             const char *const apszOptions[] = {"WRAPDATELINE=YES", nullptr};
     711           3 :             OGRGeometry *poNewGeom = OGRGeometryFactory::transformWithOptions(
     712             :                 poGeometry, m_poCT.get(), const_cast<char **>(apszOptions),
     713           3 :                 m_oTransformCache);
     714           3 :             if (poNewGeom == nullptr)
     715             :             {
     716           0 :                 return OGRERR_FAILURE;
     717             :             }
     718             : 
     719           3 :             OGREnvelope sEnvelope;
     720           3 :             poNewGeom->getEnvelope(&sEnvelope);
     721           3 :             if (sEnvelope.MinX < -180.0 || sEnvelope.MaxX > 180.0 ||
     722           3 :                 sEnvelope.MinY < -90.0 || sEnvelope.MaxY > 90.0)
     723             :             {
     724           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     725             :                          "Geometry extent outside of "
     726             :                          "[-180.0,180.0]x[-90.0,90.0] bounds");
     727           0 :                 return OGRERR_FAILURE;
     728             :             }
     729             : 
     730           3 :             poFeatureToWrite->SetGeometryDirectly(poNewGeom);
     731             :         }
     732             :     }
     733             : 
     734         103 :     ++m_nTotalFeatures;
     735             : 
     736         106 :     json_object *poObj = OGRGeoJSONWriteFeature(
     737           3 :         poFeatureToWrite.get() ? poFeatureToWrite.get() : poFeature,
     738         103 :         m_oWriteOptions);
     739         103 :     CPLAssert(nullptr != poObj);
     740             : 
     741         103 :     const char *pszJson = json_object_to_json_string(poObj);
     742             : 
     743         103 :     char chEOL = '\n';
     744         103 :     OGRErr eErr = OGRERR_NONE;
     745         216 :     if ((m_poDS->m_bIsRSSeparated &&
     746          10 :          VSIFWriteL(&RS, 1, 1, m_poDS->m_fp) != 1) ||
     747         216 :         VSIFWriteL(pszJson, strlen(pszJson), 1, m_poDS->m_fp) != 1 ||
     748         103 :         VSIFWriteL(&chEOL, 1, 1, m_poDS->m_fp) != 1)
     749             :     {
     750           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot write feature");
     751           0 :         eErr = OGRERR_FAILURE;
     752             :     }
     753             : 
     754         103 :     json_object_put(poObj);
     755             : 
     756         103 :     return eErr;
     757             : }
     758             : 
     759             : /************************************************************************/
     760             : /*                           CreateField()                              */
     761             : /************************************************************************/
     762             : 
     763         111 : OGRErr OGRGeoJSONSeqLayer::CreateField(const OGRFieldDefn *poField,
     764             :                                        int /* bApproxOK */)
     765             : {
     766         111 :     if (m_poDS->GetAccess() != GA_Update)
     767           3 :         return OGRERR_FAILURE;
     768         108 :     m_poFeatureDefn->AddFieldDefn(poField);
     769         108 :     return OGRERR_NONE;
     770             : }
     771             : 
     772             : /************************************************************************/
     773             : /*                               Open()                                 */
     774             : /************************************************************************/
     775             : 
     776          55 : bool OGRGeoJSONSeqDataSource::Open(GDALOpenInfo *poOpenInfo,
     777             :                                    GeoJSONSourceType nSrcType)
     778             : {
     779          55 :     CPLAssert(nullptr == m_fp);
     780             : 
     781         110 :     CPLString osLayerName("GeoJSONSeq");
     782             : 
     783          55 :     const char *pszUnprefixedFilename = poOpenInfo->pszFilename;
     784          55 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, "GeoJSONSeq:"))
     785             :     {
     786           1 :         pszUnprefixedFilename = poOpenInfo->pszFilename + strlen("GeoJSONSeq:");
     787             :     }
     788             : 
     789          55 :     if (nSrcType == eGeoJSONSourceFile)
     790             :     {
     791          43 :         if (pszUnprefixedFilename != poOpenInfo->pszFilename)
     792             :         {
     793           1 :             osLayerName = CPLGetBasenameSafe(pszUnprefixedFilename);
     794           1 :             m_fp = VSIFOpenL(pszUnprefixedFilename,
     795           1 :                              poOpenInfo->eAccess == GA_Update ? "rb+" : "rb");
     796             :         }
     797             :         else
     798             :         {
     799          42 :             osLayerName = CPLGetBasenameSafe(poOpenInfo->pszFilename);
     800          42 :             std::swap(m_fp, poOpenInfo->fpL);
     801             :         }
     802             :     }
     803          12 :     else if (nSrcType == eGeoJSONSourceText)
     804             :     {
     805           3 :         if (poOpenInfo->eAccess == GA_Update)
     806           0 :             return false;
     807             : 
     808           3 :         m_osTmpFile = VSIMemGenerateHiddenFilename("geojsonseq");
     809           3 :         m_fp = VSIFileFromMemBuffer(
     810             :             m_osTmpFile.c_str(),
     811           3 :             reinterpret_cast<GByte *>(CPLStrdup(poOpenInfo->pszFilename)),
     812           3 :             strlen(poOpenInfo->pszFilename), true);
     813             :     }
     814           9 :     else if (nSrcType == eGeoJSONSourceService)
     815             :     {
     816           9 :         if (poOpenInfo->eAccess == GA_Update)
     817           0 :             return false;
     818             : 
     819             :         char *pszStoredContent =
     820           9 :             OGRGeoJSONDriverStealStoredContent(pszUnprefixedFilename);
     821           9 :         if (pszStoredContent)
     822             :         {
     823           9 :             if (EQUAL(pszStoredContent, INVALID_CONTENT_FOR_JSON_LIKE) ||
     824           0 :                 !GeoJSONSeqIsObject(pszStoredContent, poOpenInfo))
     825             :             {
     826           9 :                 OGRGeoJSONDriverStoreContent(poOpenInfo->pszFilename,
     827             :                                              pszStoredContent);
     828           9 :                 return false;
     829             :             }
     830             :             else
     831             :             {
     832           0 :                 m_osTmpFile = VSIMemGenerateHiddenFilename("geojsonseq");
     833           0 :                 m_fp = VSIFileFromMemBuffer(
     834             :                     m_osTmpFile.c_str(),
     835             :                     reinterpret_cast<GByte *>(pszStoredContent),
     836           0 :                     strlen(pszStoredContent), true);
     837             :             }
     838             :         }
     839             :         else
     840             :         {
     841             :             CPLHTTPResult *pResult =
     842           0 :                 GeoJSONHTTPFetchWithContentTypeHeader(pszUnprefixedFilename);
     843           0 :             if (!pResult)
     844             :             {
     845           0 :                 return FALSE;
     846             :             }
     847             : 
     848           0 :             m_osTmpFile = VSIMemGenerateHiddenFilename("geojsonseq");
     849           0 :             m_fp = VSIFileFromMemBuffer(m_osTmpFile.c_str(), pResult->pabyData,
     850           0 :                                         pResult->nDataLen, true);
     851           0 :             pResult->pabyData = nullptr;
     852           0 :             pResult->nDataLen = 0;
     853           0 :             CPLHTTPDestroyResult(pResult);
     854             :         }
     855             :     }
     856          46 :     if (m_fp == nullptr)
     857             :     {
     858           0 :         return false;
     859             :     }
     860          46 :     SetDescription(poOpenInfo->pszFilename);
     861          46 :     auto poLayer = new OGRGeoJSONSeqLayer(this, osLayerName.c_str());
     862          46 :     const bool bLooseIdentification =
     863          46 :         nSrcType == eGeoJSONSourceService &&
     864           0 :         !STARTS_WITH_CI(poOpenInfo->pszFilename, "GeoJSONSeq:");
     865          46 :     if (bLooseIdentification)
     866             :     {
     867           0 :         CPLPushErrorHandler(CPLQuietErrorHandler);
     868             :     }
     869          46 :     const bool bEstablishLayerDefn = poOpenInfo->eAccess != GA_Update;
     870          46 :     auto ret = poLayer->Init(bLooseIdentification, bEstablishLayerDefn);
     871          46 :     if (bLooseIdentification)
     872             :     {
     873           0 :         CPLPopErrorHandler();
     874           0 :         CPLErrorReset();
     875             :     }
     876          46 :     if (!ret)
     877             :     {
     878           1 :         delete poLayer;
     879           1 :         return false;
     880             :     }
     881          45 :     m_apoLayers.emplace_back(std::move(poLayer));
     882          45 :     eAccess = poOpenInfo->eAccess;
     883          45 :     return true;
     884             : }
     885             : 
     886             : /************************************************************************/
     887             : /*                              Create()                                */
     888             : /************************************************************************/
     889             : 
     890          44 : bool OGRGeoJSONSeqDataSource::Create(const char *pszName,
     891             :                                      char ** /* papszOptions */)
     892             : {
     893          44 :     CPLAssert(nullptr == m_fp);
     894             : 
     895          44 :     if (strcmp(pszName, "/dev/stdout") == 0)
     896           0 :         pszName = "/vsistdout/";
     897             : 
     898             :     /* -------------------------------------------------------------------- */
     899             :     /*      Create the output file.                                         */
     900             :     /* -------------------------------------------------------------------- */
     901          44 :     m_bSupportsRead =
     902          87 :         VSIFileManager::GetHandler(pszName)->SupportsRead(pszName) &&
     903          86 :         VSIFileManager::GetHandler(pszName)->SupportsRandomWrite(pszName,
     904          43 :                                                                  false);
     905          44 :     m_bAtEOF = !m_bSupportsRead;
     906          44 :     m_fp = VSIFOpenExL(pszName, m_bSupportsRead ? "wb+" : "wb", true);
     907          44 :     if (nullptr == m_fp)
     908             :     {
     909           1 :         CPLError(CE_Failure, CPLE_OpenFailed, "Failed to create %s: %s",
     910             :                  pszName, VSIGetLastErrorMsg());
     911           1 :         return false;
     912             :     }
     913             : 
     914          43 :     eAccess = GA_Update;
     915             : 
     916          43 :     m_bIsRSSeparated = EQUAL(CPLGetExtensionSafe(pszName).c_str(), "GEOJSONS");
     917             : 
     918          43 :     return true;
     919             : }
     920             : 
     921             : /************************************************************************/
     922             : /*                       OGRGeoJSONSeqDriverIdentify()                  */
     923             : /************************************************************************/
     924             : 
     925       54754 : static int OGRGeoJSONSeqDriverIdentifyInternal(GDALOpenInfo *poOpenInfo,
     926             :                                                GeoJSONSourceType &nSrcType)
     927             : {
     928       54754 :     nSrcType = GeoJSONSeqGetSourceType(poOpenInfo);
     929       54754 :     if (nSrcType == eGeoJSONSourceUnknown)
     930       54635 :         return FALSE;
     931         119 :     if (nSrcType == eGeoJSONSourceService)
     932             :     {
     933          27 :         if (poOpenInfo->IsSingleAllowedDriver("GeoJSONSeq"))
     934           1 :             return TRUE;
     935          26 :         if (!STARTS_WITH_CI(poOpenInfo->pszFilename, "GeoJSONSeq:"))
     936             :         {
     937          26 :             return -1;
     938             :         }
     939             :     }
     940          92 :     return TRUE;
     941             : }
     942             : 
     943             : /************************************************************************/
     944             : /*                      OGRGeoJSONSeqDriverIdentify()                   */
     945             : /************************************************************************/
     946             : 
     947       54699 : static int OGRGeoJSONSeqDriverIdentify(GDALOpenInfo *poOpenInfo)
     948             : {
     949             :     GeoJSONSourceType nSrcType;
     950      109398 :     return OGRGeoJSONSeqDriverIdentifyInternal(poOpenInfo, nSrcType);
     951             : }
     952             : 
     953             : /************************************************************************/
     954             : /*                           Open()                                     */
     955             : /************************************************************************/
     956             : 
     957          55 : static GDALDataset *OGRGeoJSONSeqDriverOpen(GDALOpenInfo *poOpenInfo)
     958             : {
     959             :     GeoJSONSourceType nSrcType;
     960          55 :     if (OGRGeoJSONSeqDriverIdentifyInternal(poOpenInfo, nSrcType) == FALSE)
     961             :     {
     962           0 :         return nullptr;
     963             :     }
     964             : 
     965          55 :     OGRGeoJSONSeqDataSource *poDS = new OGRGeoJSONSeqDataSource();
     966             : 
     967          55 :     if (!poDS->Open(poOpenInfo, nSrcType))
     968             :     {
     969          10 :         delete poDS;
     970          10 :         poDS = nullptr;
     971             :     }
     972             : 
     973          55 :     return poDS;
     974             : }
     975             : 
     976             : /************************************************************************/
     977             : /*                               Create()                               */
     978             : /************************************************************************/
     979             : 
     980             : static GDALDataset *
     981          44 : OGRGeoJSONSeqDriverCreate(const char *pszName, int /* nBands */,
     982             :                           int /* nXSize */, int /* nYSize */,
     983             :                           GDALDataType /* eDT */, char **papszOptions)
     984             : {
     985          44 :     OGRGeoJSONSeqDataSource *poDS = new OGRGeoJSONSeqDataSource();
     986             : 
     987          44 :     if (!poDS->Create(pszName, papszOptions))
     988             :     {
     989           1 :         delete poDS;
     990           1 :         poDS = nullptr;
     991             :     }
     992             : 
     993          44 :     return poDS;
     994             : }
     995             : 
     996             : /************************************************************************/
     997             : /*                        RegisterOGRGeoJSONSeq()                       */
     998             : /************************************************************************/
     999             : 
    1000        2024 : void RegisterOGRGeoJSONSeq()
    1001             : {
    1002        2024 :     if (GDALGetDriverByName("GeoJSONSeq") != nullptr)
    1003         283 :         return;
    1004             : 
    1005        1741 :     GDALDriver *poDriver = new GDALDriver();
    1006             : 
    1007        1741 :     poDriver->SetDescription("GeoJSONSeq");
    1008        1741 :     poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
    1009        1741 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
    1010        1741 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
    1011        1741 :     poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
    1012        1741 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "GeoJSON Sequence");
    1013        1741 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "geojsonl geojsons");
    1014        1741 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
    1015        1741 :                               "drivers/vector/geojsonseq.html");
    1016             : 
    1017        1741 :     poDriver->SetMetadataItem(
    1018             :         GDAL_DS_LAYER_CREATIONOPTIONLIST,
    1019             :         "<LayerCreationOptionList>"
    1020             :         "  <Option name='RS' type='boolean' description='whether to prefix "
    1021             :         "records with RS=0x1e character' default='NO'/>"
    1022             :         "  <Option name='COORDINATE_PRECISION' type='int' description='Number "
    1023             :         "of decimal for coordinates. Default is 7'/>"
    1024             :         "  <Option name='SIGNIFICANT_FIGURES' type='int' description='Number "
    1025             :         "of significant figures for floating-point values' default='17'/>"
    1026             :         "  <Option name='ID_FIELD' type='string' description='Name of the "
    1027             :         "source field that must be used as the id member of Feature features'/>"
    1028             :         "  <Option name='ID_TYPE' type='string-select' description='Type of "
    1029             :         "the id member of Feature features'>"
    1030             :         "    <Value>AUTO</Value>"
    1031             :         "    <Value>String</Value>"
    1032             :         "    <Value>Integer</Value>"
    1033             :         "  </Option>"
    1034             :         "  <Option name='WRITE_BBOX' type='boolean' description='whether to "
    1035             :         "write a bbox property with the bounding box of each geometry' "
    1036             :         "default='NO'/>"
    1037        1741 :         "</LayerCreationOptionList>");
    1038             : 
    1039        1741 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1040        1741 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES,
    1041             :                               "Integer Integer64 Real String IntegerList "
    1042        1741 :                               "Integer64List RealList StringList");
    1043        1741 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES, "Boolean");
    1044        1741 :     poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
    1045        1741 :     poDriver->SetMetadataItem(GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION, "YES");
    1046        1741 :     poDriver->SetMetadataItem(GDAL_DCAP_APPEND, "YES");
    1047             : 
    1048        1741 :     poDriver->pfnOpen = OGRGeoJSONSeqDriverOpen;
    1049        1741 :     poDriver->pfnIdentify = OGRGeoJSONSeqDriverIdentify;
    1050        1741 :     poDriver->pfnCreate = OGRGeoJSONSeqDriverCreate;
    1051             : 
    1052        1741 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1053             : }

Generated by: LCOV version 1.14