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

Generated by: LCOV version 1.14