Coverage Report

Created: 2025-11-16 06:25

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