LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/geojson - ogrgeojsonseqdriver.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 404 450 89.8 %
Date: 2024-11-21 22:18:42 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();
      47             : 
      48          76 :     int GetLayerCount() override
      49             :     {
      50          76 :         return static_cast<int>(m_apoLayers.size());
      51             :     }
      52             : 
      53             :     OGRLayer *GetLayer(int) override;
      54             :     OGRLayer *ICreateLayer(const char *pszName,
      55             :                            const OGRGeomFieldDefn *poGeomFieldDefn,
      56             :                            CSLConstList papszOptions) override;
      57             :     int TestCapability(const char *pszCap) 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();
     104             : 
     105             :     bool Init(bool bLooseIdentification, bool bEstablishLayerDefn);
     106             : 
     107          61 :     const char *GetName() override
     108             :     {
     109          61 :         return GetDescription();
     110             :     }
     111             : 
     112             :     void ResetReading() override;
     113             :     OGRFeature *GetNextFeature() override;
     114             :     OGRFeatureDefn *GetLayerDefn() override;
     115             : 
     116          30 :     const char *GetFIDColumn() override
     117             :     {
     118          30 :         return m_osFIDColumn.c_str();
     119             :     }
     120             : 
     121             :     GIntBig GetFeatureCount(int) override;
     122             :     int TestCapability(const char *) 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 : OGRLayer *OGRGeoJSONSeqDataSource::GetLayer(int nIndex)
     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)
     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        1306 : OGRFeatureDefn *OGRGeoJSONSeqLayer::GetLayerDefn()
     380             : {
     381        1306 :     if (!m_bLayerDefnEstablished)
     382             :     {
     383           3 :         Init(/* bLooseIdentification = */ false,
     384             :              /* bEstablishLayerDefn = */ true);
     385             :     }
     386        1306 :     return m_poFeatureDefn;
     387             : }
     388             : 
     389             : /************************************************************************/
     390             : /*                               Init()                                 */
     391             : /************************************************************************/
     392             : 
     393          49 : bool OGRGeoJSONSeqLayer::Init(bool bLooseIdentification,
     394             :                               bool bEstablishLayerDefn)
     395             : {
     396          55 :     if (STARTS_WITH(m_poDS->GetDescription(), "/vsimem/") ||
     397           6 :         !STARTS_WITH(m_poDS->GetDescription(), "/vsi"))
     398             :     {
     399          49 :         VSIFSeekL(m_poDS->m_fp, 0, SEEK_END);
     400          49 :         m_nFileSize = VSIFTellL(m_poDS->m_fp);
     401             :     }
     402             : 
     403             :     // Set m_bLayerDefnEstablished = true early to avoid infinite recursive
     404             :     // calls.
     405          49 :     if (bEstablishLayerDefn)
     406          43 :         m_bLayerDefnEstablished = true;
     407             : 
     408          49 :     ResetReading();
     409             : 
     410          98 :     std::map<std::string, int> oMapFieldNameToIdx;
     411          98 :     std::vector<std::unique_ptr<OGRFieldDefn>> apoFieldDefn;
     412          49 :     gdal::DirectedAcyclicGraph<int, std::string> dag;
     413          49 :     bool bOK = false;
     414             : 
     415             :     while (true)
     416             :     {
     417         205 :         auto poObject = GetNextObject(bLooseIdentification);
     418         205 :         if (!poObject)
     419          43 :             break;
     420         162 :         const auto eObjectType = OGRGeoJSONGetType(poObject);
     421         162 :         if (bEstablishLayerDefn && eObjectType == GeoJSONObject::eFeature)
     422             :         {
     423         151 :             m_oReader.GenerateFeatureDefn(oMapFieldNameToIdx, apoFieldDefn, dag,
     424             :                                           this, poObject);
     425             :         }
     426         162 :         json_object_put(poObject);
     427         162 :         if (!bEstablishLayerDefn)
     428             :         {
     429           6 :             bOK = (eObjectType == GeoJSONObject::eFeature);
     430           6 :             break;
     431             :         }
     432         156 :         m_nTotalFeatures++;
     433         156 :     }
     434             : 
     435          49 :     if (bEstablishLayerDefn)
     436             :     {
     437             :         // CPLDebug("GEOJSONSEQ", "Establish layer definition");
     438             : 
     439          86 :         const auto sortedFields = dag.getTopologicalOrdering();
     440          43 :         CPLAssert(sortedFields.size() == apoFieldDefn.size());
     441         158 :         for (int idx : sortedFields)
     442             :         {
     443         115 :             m_poFeatureDefn->AddFieldDefn(apoFieldDefn[idx].get());
     444             :         }
     445          43 :         m_poFeatureDefn->Seal(true);
     446          43 :         m_oReader.FinalizeLayerDefn(this, m_osFIDColumn);
     447             :     }
     448             : 
     449          49 :     ResetReading();
     450             : 
     451          49 :     m_nFileSize = 0;
     452          49 :     m_nIter = 0;
     453             : 
     454          98 :     return bOK || m_nTotalFeatures > 0;
     455             : }
     456             : 
     457             : /************************************************************************/
     458             : /*                            ResetReading()                            */
     459             : /************************************************************************/
     460             : 
     461         216 : void OGRGeoJSONSeqLayer::ResetReading()
     462             : {
     463         432 :     if (!m_poDS->m_bSupportsRead ||
     464         216 :         (m_bWriteOnlyLayer && m_poDS->m_apoLayers.size() > 1))
     465             :     {
     466           0 :         return;
     467             :     }
     468             : 
     469         216 :     m_poDS->m_bAtEOF = false;
     470         216 :     VSIFSeekL(m_poDS->m_fp, 0, SEEK_SET);
     471             :     // Undocumented: for testing purposes only
     472         216 :     const size_t nBufferSize = static_cast<size_t>(std::max(
     473         216 :         1, atoi(CPLGetConfigOption("OGR_GEOJSONSEQ_CHUNK_SIZE", "40960"))));
     474         216 :     const size_t nBufferSizeValidated =
     475             :         nBufferSize > static_cast<size_t>(100 * 1000 * 1000)
     476             :             ? static_cast<size_t>(100 * 1000 * 1000)
     477             :             : nBufferSize;
     478         216 :     m_osBuffer.resize(nBufferSizeValidated);
     479         216 :     m_osFeatureBuffer.clear();
     480         216 :     m_nPosInBuffer = nBufferSizeValidated;
     481         216 :     m_nBufferValidSize = nBufferSizeValidated;
     482         216 :     m_nNextFID = 0;
     483             : }
     484             : 
     485             : /************************************************************************/
     486             : /*                           GetNextObject()                            */
     487             : /************************************************************************/
     488             : 
     489         388 : json_object *OGRGeoJSONSeqLayer::GetNextObject(bool bLooseIdentification)
     490             : {
     491         388 :     m_osFeatureBuffer.clear();
     492             :     while (true)
     493             :     {
     494             :         // If we read all the buffer, then reload it from file
     495         444 :         if (m_nPosInBuffer >= m_nBufferValidSize)
     496             :         {
     497         259 :             if (m_nBufferValidSize < m_osBuffer.size())
     498             :             {
     499          86 :                 return nullptr;
     500             :             }
     501         173 :             m_nBufferValidSize =
     502         173 :                 VSIFReadL(&m_osBuffer[0], 1, m_osBuffer.size(), m_poDS->m_fp);
     503         173 :             m_nPosInBuffer = 0;
     504         298 :             if (VSIFTellL(m_poDS->m_fp) == m_nBufferValidSize &&
     505         125 :                 m_nBufferValidSize > 0)
     506             :             {
     507         125 :                 m_poDS->m_bIsRSSeparated = (m_osBuffer[0] == RS);
     508         125 :                 if (m_poDS->m_bIsRSSeparated)
     509             :                 {
     510          26 :                     m_nPosInBuffer++;
     511             :                 }
     512             :             }
     513         173 :             m_nIter++;
     514             : 
     515         219 :             if (m_nFileSize > 0 && (m_nBufferValidSize < m_osBuffer.size() ||
     516          46 :                                     (m_nIter % 100) == 0))
     517             :             {
     518          48 :                 CPLDebug("GeoJSONSeq", "First pass: %.2f %%",
     519          48 :                          100.0 * VSIFTellL(m_poDS->m_fp) / m_nFileSize);
     520             :             }
     521         173 :             if (m_nPosInBuffer >= m_nBufferValidSize)
     522             :             {
     523           0 :                 return nullptr;
     524             :             }
     525             :         }
     526             : 
     527             :         // Find next feature separator in buffer
     528         358 :         const size_t nNextSepPos = m_osBuffer.find(
     529         358 :             m_poDS->m_bIsRSSeparated ? RS : '\n', m_nPosInBuffer);
     530         358 :         if (nNextSepPos != std::string::npos)
     531             :         {
     532         564 :             m_osFeatureBuffer.append(m_osBuffer.data() + m_nPosInBuffer,
     533         282 :                                      nNextSepPos - m_nPosInBuffer);
     534         282 :             m_nPosInBuffer = nNextSepPos + 1;
     535             :         }
     536             :         else
     537             :         {
     538             :             // No separator ? then accummulate
     539         152 :             m_osFeatureBuffer.append(m_osBuffer.data() + m_nPosInBuffer,
     540          76 :                                      m_nBufferValidSize - m_nPosInBuffer);
     541         145 :             if (m_nMaxObjectSize > 0 &&
     542          69 :                 m_osFeatureBuffer.size() > m_nMaxObjectSize)
     543             :             {
     544           1 :                 CPLError(CE_Failure, CPLE_NotSupported,
     545             :                          "Too large feature. You may define the "
     546             :                          "OGR_GEOJSON_MAX_OBJ_SIZE configuration option to "
     547             :                          "a value in megabytes (larger than %u) to allow "
     548             :                          "for larger features, or 0 to remove any size limit.",
     549           1 :                          static_cast<unsigned>(m_osFeatureBuffer.size() / 1024 /
     550             :                                                1024));
     551           1 :                 return nullptr;
     552             :             }
     553          75 :             m_nPosInBuffer = m_nBufferValidSize;
     554          75 :             if (m_nBufferValidSize == m_osBuffer.size())
     555             :             {
     556          48 :                 continue;
     557             :             }
     558             :         }
     559             : 
     560         756 :         while (!m_osFeatureBuffer.empty() &&
     561         377 :                (m_osFeatureBuffer.back() == '\r' ||
     562         377 :                 m_osFeatureBuffer.back() == '\n'))
     563             :         {
     564          70 :             m_osFeatureBuffer.pop_back();
     565             :         }
     566         309 :         if (!m_osFeatureBuffer.empty())
     567             :         {
     568         307 :             json_object *poObject = nullptr;
     569         307 :             CPL_IGNORE_RET_VAL(
     570         307 :                 OGRJSonParse(m_osFeatureBuffer.c_str(), &poObject));
     571         307 :             m_osFeatureBuffer.clear();
     572         307 :             if (json_object_get_type(poObject) == json_type_object)
     573             :             {
     574         301 :                 return poObject;
     575             :             }
     576           6 :             json_object_put(poObject);
     577           6 :             if (bLooseIdentification)
     578             :             {
     579           0 :                 return nullptr;
     580             :             }
     581             :         }
     582          56 :     }
     583             : }
     584             : 
     585             : /************************************************************************/
     586             : /*                           GetNextFeature()                           */
     587             : /************************************************************************/
     588             : 
     589         163 : OGRFeature *OGRGeoJSONSeqLayer::GetNextFeature()
     590             : {
     591         163 :     if (!m_poDS->m_bSupportsRead)
     592             :     {
     593           0 :         return nullptr;
     594             :     }
     595         163 :     if (m_bWriteOnlyLayer && m_poDS->m_apoLayers.size() > 1)
     596             :     {
     597           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     598             :                  "GetNextFeature() not supported when appending a new layer");
     599           0 :         return nullptr;
     600             :     }
     601             : 
     602         163 :     GetLayerDefn();  // force scan if not already done
     603             :     while (true)
     604             :     {
     605         183 :         auto poObject = GetNextObject(false);
     606         183 :         if (!poObject)
     607          44 :             return nullptr;
     608             :         OGRFeature *poFeature;
     609         139 :         auto type = OGRGeoJSONGetType(poObject);
     610         139 :         if (type == GeoJSONObject::eFeature)
     611             :         {
     612         135 :             poFeature = m_oReader.ReadFeature(this, poObject,
     613             :                                               m_osFeatureBuffer.c_str());
     614         135 :             json_object_put(poObject);
     615             :         }
     616           4 :         else if (type == GeoJSONObject::eFeatureCollection ||
     617             :                  type == GeoJSONObject::eUnknown)
     618             :         {
     619           0 :             json_object_put(poObject);
     620           0 :             continue;
     621             :         }
     622             :         else
     623             :         {
     624             :             OGRGeometry *poGeom =
     625           4 :                 m_oReader.ReadGeometry(poObject, GetSpatialRef());
     626           4 :             json_object_put(poObject);
     627           4 :             if (!poGeom)
     628             :             {
     629           0 :                 continue;
     630             :             }
     631           4 :             poFeature = new OGRFeature(m_poFeatureDefn);
     632           4 :             poFeature->SetGeometryDirectly(poGeom);
     633             :         }
     634             : 
     635         139 :         if (poFeature->GetFID() == OGRNullFID)
     636             :         {
     637         139 :             poFeature->SetFID(m_nNextFID);
     638         139 :             m_nNextFID++;
     639             :         }
     640         300 :         if ((m_poFilterGeom == nullptr ||
     641         270 :              FilterGeometry(poFeature->GetGeomFieldRef(m_iGeomFieldFilter))) &&
     642         131 :             (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
     643             :         {
     644         119 :             return poFeature;
     645             :         }
     646          20 :         delete poFeature;
     647          20 :     }
     648             : }
     649             : 
     650             : /************************************************************************/
     651             : /*                          GetFeatureCount()                           */
     652             : /************************************************************************/
     653             : 
     654          32 : GIntBig OGRGeoJSONSeqLayer::GetFeatureCount(int bForce)
     655             : {
     656          32 :     if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
     657             :     {
     658          26 :         GetLayerDefn();  // force scan if not already done
     659          26 :         return m_nTotalFeatures;
     660             :     }
     661           6 :     return OGRLayer::GetFeatureCount(bForce);
     662             : }
     663             : 
     664             : /************************************************************************/
     665             : /*                           TestCapability()                           */
     666             : /************************************************************************/
     667             : 
     668         185 : int OGRGeoJSONSeqLayer::TestCapability(const char *pszCap)
     669             : {
     670         185 :     if (EQUAL(pszCap, OLCStringsAsUTF8))
     671          13 :         return true;
     672         172 :     if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr &&
     673         172 :         EQUAL(pszCap, OLCFastFeatureCount))
     674             :     {
     675           0 :         return true;
     676             :     }
     677         172 :     if (EQUAL(pszCap, OLCCreateField) || EQUAL(pszCap, OLCSequentialWrite))
     678             :     {
     679          47 :         return m_poDS->GetAccess() == GA_Update;
     680             :     }
     681             : 
     682         125 :     return false;
     683             : }
     684             : 
     685             : /************************************************************************/
     686             : /*                           ICreateFeature()                           */
     687             : /************************************************************************/
     688             : 
     689         106 : OGRErr OGRGeoJSONSeqLayer::ICreateFeature(OGRFeature *poFeature)
     690             : {
     691         106 :     if (m_poDS->GetAccess() != GA_Update)
     692           3 :         return OGRERR_FAILURE;
     693             : 
     694         103 :     if (!m_poDS->m_bAtEOF)
     695             :     {
     696          50 :         m_poDS->m_bAtEOF = true;
     697          50 :         VSIFSeekL(m_poDS->m_fp, 0, SEEK_END);
     698             :     }
     699             : 
     700         103 :     std::unique_ptr<OGRFeature> poFeatureToWrite;
     701         103 :     if (m_poCT != nullptr)
     702             :     {
     703           3 :         poFeatureToWrite.reset(new OGRFeature(m_poFeatureDefn));
     704           3 :         poFeatureToWrite->SetFrom(poFeature);
     705           3 :         poFeatureToWrite->SetFID(poFeature->GetFID());
     706           3 :         OGRGeometry *poGeometry = poFeatureToWrite->GetGeometryRef();
     707           3 :         if (poGeometry)
     708             :         {
     709           3 :             const char *const apszOptions[] = {"WRAPDATELINE=YES", nullptr};
     710           3 :             OGRGeometry *poNewGeom = OGRGeometryFactory::transformWithOptions(
     711             :                 poGeometry, m_poCT.get(), const_cast<char **>(apszOptions),
     712           3 :                 m_oTransformCache);
     713           3 :             if (poNewGeom == nullptr)
     714             :             {
     715           0 :                 return OGRERR_FAILURE;
     716             :             }
     717             : 
     718           3 :             OGREnvelope sEnvelope;
     719           3 :             poNewGeom->getEnvelope(&sEnvelope);
     720           3 :             if (sEnvelope.MinX < -180.0 || sEnvelope.MaxX > 180.0 ||
     721           3 :                 sEnvelope.MinY < -90.0 || sEnvelope.MaxY > 90.0)
     722             :             {
     723           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     724             :                          "Geometry extent outside of "
     725             :                          "[-180.0,180.0]x[-90.0,90.0] bounds");
     726           0 :                 return OGRERR_FAILURE;
     727             :             }
     728             : 
     729           3 :             poFeatureToWrite->SetGeometryDirectly(poNewGeom);
     730             :         }
     731             :     }
     732             : 
     733         103 :     ++m_nTotalFeatures;
     734             : 
     735         106 :     json_object *poObj = OGRGeoJSONWriteFeature(
     736           3 :         poFeatureToWrite.get() ? poFeatureToWrite.get() : poFeature,
     737         103 :         m_oWriteOptions);
     738         103 :     CPLAssert(nullptr != poObj);
     739             : 
     740         103 :     const char *pszJson = json_object_to_json_string(poObj);
     741             : 
     742         103 :     char chEOL = '\n';
     743         103 :     OGRErr eErr = OGRERR_NONE;
     744         216 :     if ((m_poDS->m_bIsRSSeparated &&
     745          10 :          VSIFWriteL(&RS, 1, 1, m_poDS->m_fp) != 1) ||
     746         216 :         VSIFWriteL(pszJson, strlen(pszJson), 1, m_poDS->m_fp) != 1 ||
     747         103 :         VSIFWriteL(&chEOL, 1, 1, m_poDS->m_fp) != 1)
     748             :     {
     749           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot write feature");
     750           0 :         eErr = OGRERR_FAILURE;
     751             :     }
     752             : 
     753         103 :     json_object_put(poObj);
     754             : 
     755         103 :     return eErr;
     756             : }
     757             : 
     758             : /************************************************************************/
     759             : /*                           CreateField()                              */
     760             : /************************************************************************/
     761             : 
     762         111 : OGRErr OGRGeoJSONSeqLayer::CreateField(const OGRFieldDefn *poField,
     763             :                                        int /* bApproxOK */)
     764             : {
     765         111 :     if (m_poDS->GetAccess() != GA_Update)
     766           3 :         return OGRERR_FAILURE;
     767         108 :     m_poFeatureDefn->AddFieldDefn(poField);
     768         108 :     return OGRERR_NONE;
     769             : }
     770             : 
     771             : /************************************************************************/
     772             : /*                               Open()                                 */
     773             : /************************************************************************/
     774             : 
     775          55 : bool OGRGeoJSONSeqDataSource::Open(GDALOpenInfo *poOpenInfo,
     776             :                                    GeoJSONSourceType nSrcType)
     777             : {
     778          55 :     CPLAssert(nullptr == m_fp);
     779             : 
     780         110 :     CPLString osLayerName("GeoJSONSeq");
     781             : 
     782          55 :     const char *pszUnprefixedFilename = poOpenInfo->pszFilename;
     783          55 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, "GeoJSONSeq:"))
     784             :     {
     785           1 :         pszUnprefixedFilename = poOpenInfo->pszFilename + strlen("GeoJSONSeq:");
     786             :     }
     787             : 
     788          55 :     if (nSrcType == eGeoJSONSourceFile)
     789             :     {
     790          43 :         if (pszUnprefixedFilename != poOpenInfo->pszFilename)
     791             :         {
     792           1 :             osLayerName = CPLGetBasename(pszUnprefixedFilename);
     793           1 :             m_fp = VSIFOpenL(pszUnprefixedFilename,
     794           1 :                              poOpenInfo->eAccess == GA_Update ? "rb+" : "rb");
     795             :         }
     796             :         else
     797             :         {
     798          42 :             osLayerName = CPLGetBasename(poOpenInfo->pszFilename);
     799          42 :             std::swap(m_fp, poOpenInfo->fpL);
     800             :         }
     801             :     }
     802          12 :     else if (nSrcType == eGeoJSONSourceText)
     803             :     {
     804           3 :         if (poOpenInfo->eAccess == GA_Update)
     805           0 :             return false;
     806             : 
     807           3 :         m_osTmpFile = VSIMemGenerateHiddenFilename("geojsonseq");
     808           3 :         m_fp = VSIFileFromMemBuffer(
     809             :             m_osTmpFile.c_str(),
     810           3 :             reinterpret_cast<GByte *>(CPLStrdup(poOpenInfo->pszFilename)),
     811           3 :             strlen(poOpenInfo->pszFilename), true);
     812             :     }
     813           9 :     else if (nSrcType == eGeoJSONSourceService)
     814             :     {
     815           9 :         if (poOpenInfo->eAccess == GA_Update)
     816           0 :             return false;
     817             : 
     818             :         char *pszStoredContent =
     819           9 :             OGRGeoJSONDriverStealStoredContent(pszUnprefixedFilename);
     820           9 :         if (pszStoredContent)
     821             :         {
     822           9 :             if (EQUAL(pszStoredContent, INVALID_CONTENT_FOR_JSON_LIKE) ||
     823           0 :                 !GeoJSONSeqIsObject(pszStoredContent, poOpenInfo))
     824             :             {
     825           9 :                 OGRGeoJSONDriverStoreContent(poOpenInfo->pszFilename,
     826             :                                              pszStoredContent);
     827           9 :                 return false;
     828             :             }
     829             :             else
     830             :             {
     831           0 :                 m_osTmpFile = VSIMemGenerateHiddenFilename("geojsonseq");
     832           0 :                 m_fp = VSIFileFromMemBuffer(
     833             :                     m_osTmpFile.c_str(),
     834             :                     reinterpret_cast<GByte *>(pszStoredContent),
     835           0 :                     strlen(pszStoredContent), true);
     836             :             }
     837             :         }
     838             :         else
     839             :         {
     840           0 :             const char *const papsOptions[] = {
     841             :                 "HEADERS=Accept: text/plain, application/json", nullptr};
     842             : 
     843             :             CPLHTTPResult *pResult =
     844           0 :                 CPLHTTPFetch(pszUnprefixedFilename, papsOptions);
     845             : 
     846           0 :             if (nullptr == pResult || 0 == pResult->nDataLen ||
     847           0 :                 0 != CPLGetLastErrorNo())
     848             :             {
     849           0 :                 CPLHTTPDestroyResult(pResult);
     850           0 :                 return false;
     851             :             }
     852             : 
     853           0 :             if (0 != pResult->nStatus)
     854             :             {
     855           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     856             :                          "Curl reports error: %d: %s", pResult->nStatus,
     857             :                          pResult->pszErrBuf);
     858           0 :                 CPLHTTPDestroyResult(pResult);
     859           0 :                 return false;
     860             :             }
     861             : 
     862           0 :             m_osTmpFile = VSIMemGenerateHiddenFilename("geojsonseq");
     863           0 :             m_fp = VSIFileFromMemBuffer(m_osTmpFile.c_str(), pResult->pabyData,
     864           0 :                                         pResult->nDataLen, true);
     865           0 :             pResult->pabyData = nullptr;
     866           0 :             pResult->nDataLen = 0;
     867           0 :             CPLHTTPDestroyResult(pResult);
     868             :         }
     869             :     }
     870          46 :     if (m_fp == nullptr)
     871             :     {
     872           0 :         return false;
     873             :     }
     874          46 :     SetDescription(poOpenInfo->pszFilename);
     875          46 :     auto poLayer = new OGRGeoJSONSeqLayer(this, osLayerName.c_str());
     876          46 :     const bool bLooseIdentification =
     877          46 :         nSrcType == eGeoJSONSourceService &&
     878           0 :         !STARTS_WITH_CI(poOpenInfo->pszFilename, "GeoJSONSeq:");
     879          46 :     if (bLooseIdentification)
     880             :     {
     881           0 :         CPLPushErrorHandler(CPLQuietErrorHandler);
     882             :     }
     883          46 :     const bool bEstablishLayerDefn = poOpenInfo->eAccess != GA_Update;
     884          46 :     auto ret = poLayer->Init(bLooseIdentification, bEstablishLayerDefn);
     885          46 :     if (bLooseIdentification)
     886             :     {
     887           0 :         CPLPopErrorHandler();
     888           0 :         CPLErrorReset();
     889             :     }
     890          46 :     if (!ret)
     891             :     {
     892           1 :         delete poLayer;
     893           1 :         return false;
     894             :     }
     895          45 :     m_apoLayers.emplace_back(std::move(poLayer));
     896          45 :     eAccess = poOpenInfo->eAccess;
     897          45 :     return true;
     898             : }
     899             : 
     900             : /************************************************************************/
     901             : /*                              Create()                                */
     902             : /************************************************************************/
     903             : 
     904          44 : bool OGRGeoJSONSeqDataSource::Create(const char *pszName,
     905             :                                      char ** /* papszOptions */)
     906             : {
     907          44 :     CPLAssert(nullptr == m_fp);
     908             : 
     909          44 :     if (strcmp(pszName, "/dev/stdout") == 0)
     910           0 :         pszName = "/vsistdout/";
     911             : 
     912             :     /* -------------------------------------------------------------------- */
     913             :     /*      Create the output file.                                         */
     914             :     /* -------------------------------------------------------------------- */
     915          44 :     m_bSupportsRead =
     916          87 :         VSIFileManager::GetHandler(pszName)->SupportsRead(pszName) &&
     917          86 :         VSIFileManager::GetHandler(pszName)->SupportsRandomWrite(pszName,
     918          43 :                                                                  false);
     919          44 :     m_bAtEOF = !m_bSupportsRead;
     920          44 :     m_fp = VSIFOpenExL(pszName, m_bSupportsRead ? "wb+" : "wb", true);
     921          44 :     if (nullptr == m_fp)
     922             :     {
     923           1 :         CPLError(CE_Failure, CPLE_OpenFailed, "Failed to create %s: %s",
     924             :                  pszName, VSIGetLastErrorMsg());
     925           1 :         return false;
     926             :     }
     927             : 
     928          43 :     eAccess = GA_Update;
     929             : 
     930          43 :     m_bIsRSSeparated = EQUAL(CPLGetExtension(pszName), "GEOJSONS");
     931             : 
     932          43 :     return true;
     933             : }
     934             : 
     935             : /************************************************************************/
     936             : /*                       OGRGeoJSONSeqDriverIdentify()                  */
     937             : /************************************************************************/
     938             : 
     939       47475 : static int OGRGeoJSONSeqDriverIdentifyInternal(GDALOpenInfo *poOpenInfo,
     940             :                                                GeoJSONSourceType &nSrcType)
     941             : {
     942       47475 :     nSrcType = GeoJSONSeqGetSourceType(poOpenInfo);
     943       47475 :     if (nSrcType == eGeoJSONSourceUnknown)
     944       47356 :         return FALSE;
     945         119 :     if (nSrcType == eGeoJSONSourceService)
     946             :     {
     947          27 :         if (poOpenInfo->IsSingleAllowedDriver("GeoJSONSeq"))
     948           1 :             return TRUE;
     949          26 :         if (!STARTS_WITH_CI(poOpenInfo->pszFilename, "GeoJSONSeq:"))
     950             :         {
     951          26 :             return -1;
     952             :         }
     953             :     }
     954          92 :     return TRUE;
     955             : }
     956             : 
     957             : /************************************************************************/
     958             : /*                      OGRGeoJSONSeqDriverIdentify()                   */
     959             : /************************************************************************/
     960             : 
     961       47420 : static int OGRGeoJSONSeqDriverIdentify(GDALOpenInfo *poOpenInfo)
     962             : {
     963             :     GeoJSONSourceType nSrcType;
     964       94840 :     return OGRGeoJSONSeqDriverIdentifyInternal(poOpenInfo, nSrcType);
     965             : }
     966             : 
     967             : /************************************************************************/
     968             : /*                           Open()                                     */
     969             : /************************************************************************/
     970             : 
     971          55 : static GDALDataset *OGRGeoJSONSeqDriverOpen(GDALOpenInfo *poOpenInfo)
     972             : {
     973             :     GeoJSONSourceType nSrcType;
     974          55 :     if (OGRGeoJSONSeqDriverIdentifyInternal(poOpenInfo, nSrcType) == FALSE)
     975             :     {
     976           0 :         return nullptr;
     977             :     }
     978             : 
     979          55 :     OGRGeoJSONSeqDataSource *poDS = new OGRGeoJSONSeqDataSource();
     980             : 
     981          55 :     if (!poDS->Open(poOpenInfo, nSrcType))
     982             :     {
     983          10 :         delete poDS;
     984          10 :         poDS = nullptr;
     985             :     }
     986             : 
     987          55 :     return poDS;
     988             : }
     989             : 
     990             : /************************************************************************/
     991             : /*                               Create()                               */
     992             : /************************************************************************/
     993             : 
     994             : static GDALDataset *
     995          44 : OGRGeoJSONSeqDriverCreate(const char *pszName, int /* nBands */,
     996             :                           int /* nXSize */, int /* nYSize */,
     997             :                           GDALDataType /* eDT */, char **papszOptions)
     998             : {
     999          44 :     OGRGeoJSONSeqDataSource *poDS = new OGRGeoJSONSeqDataSource();
    1000             : 
    1001          44 :     if (!poDS->Create(pszName, papszOptions))
    1002             :     {
    1003           1 :         delete poDS;
    1004           1 :         poDS = nullptr;
    1005             :     }
    1006             : 
    1007          44 :     return poDS;
    1008             : }
    1009             : 
    1010             : /************************************************************************/
    1011             : /*                        RegisterOGRGeoJSONSeq()                       */
    1012             : /************************************************************************/
    1013             : 
    1014        1595 : void RegisterOGRGeoJSONSeq()
    1015             : {
    1016        1595 :     if (GDALGetDriverByName("GeoJSONSeq") != nullptr)
    1017         302 :         return;
    1018             : 
    1019        1293 :     GDALDriver *poDriver = new GDALDriver();
    1020             : 
    1021        1293 :     poDriver->SetDescription("GeoJSONSeq");
    1022        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
    1023        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
    1024        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
    1025        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
    1026        1293 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "GeoJSON Sequence");
    1027        1293 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "geojsonl geojsons");
    1028        1293 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
    1029        1293 :                               "drivers/vector/geojsonseq.html");
    1030             : 
    1031        1293 :     poDriver->SetMetadataItem(
    1032             :         GDAL_DS_LAYER_CREATIONOPTIONLIST,
    1033             :         "<LayerCreationOptionList>"
    1034             :         "  <Option name='RS' type='boolean' description='whether to prefix "
    1035             :         "records with RS=0x1e character' default='NO'/>"
    1036             :         "  <Option name='COORDINATE_PRECISION' type='int' description='Number "
    1037             :         "of decimal for coordinates. Default is 7'/>"
    1038             :         "  <Option name='SIGNIFICANT_FIGURES' type='int' description='Number "
    1039             :         "of significant figures for floating-point values' default='17'/>"
    1040             :         "  <Option name='ID_FIELD' type='string' description='Name of the "
    1041             :         "source field that must be used as the id member of Feature features'/>"
    1042             :         "  <Option name='ID_TYPE' type='string-select' description='Type of "
    1043             :         "the id member of Feature features'>"
    1044             :         "    <Value>AUTO</Value>"
    1045             :         "    <Value>String</Value>"
    1046             :         "    <Value>Integer</Value>"
    1047             :         "  </Option>"
    1048             :         "  <Option name='WRITE_BBOX' type='boolean' description='whether to "
    1049             :         "write a bbox property with the bounding box of each geometry' "
    1050             :         "default='NO'/>"
    1051        1293 :         "</LayerCreationOptionList>");
    1052             : 
    1053        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1054        1293 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES,
    1055             :                               "Integer Integer64 Real String IntegerList "
    1056        1293 :                               "Integer64List RealList StringList");
    1057        1293 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES, "Boolean");
    1058        1293 :     poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
    1059        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION, "YES");
    1060             : 
    1061        1293 :     poDriver->pfnOpen = OGRGeoJSONSeqDriverOpen;
    1062        1293 :     poDriver->pfnIdentify = OGRGeoJSONSeqDriverIdentify;
    1063        1293 :     poDriver->pfnCreate = OGRGeoJSONSeqDriverCreate;
    1064             : 
    1065        1293 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1066             : }

Generated by: LCOV version 1.14