Coverage Report

Created: 2025-12-31 06:48

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_ext(
742
0
        poObj, JSON_C_TO_STRING_PLAIN
743
0
#ifdef JSON_C_TO_STRING_NOSLASHESCAPE
744
0
                   | JSON_C_TO_STRING_NOSLASHESCAPE
745
0
#endif
746
0
    );
747
748
0
    char chEOL = '\n';
749
0
    OGRErr eErr = OGRERR_NONE;
750
0
    if ((m_poDS->m_bIsRSSeparated &&
751
0
         VSIFWriteL(&RS, 1, 1, m_poDS->m_fp) != 1) ||
752
0
        VSIFWriteL(pszJson, strlen(pszJson), 1, m_poDS->m_fp) != 1 ||
753
0
        VSIFWriteL(&chEOL, 1, 1, m_poDS->m_fp) != 1)
754
0
    {
755
0
        CPLError(CE_Failure, CPLE_FileIO, "Cannot write feature");
756
0
        eErr = OGRERR_FAILURE;
757
0
    }
758
759
0
    json_object_put(poObj);
760
761
0
    return eErr;
762
0
}
763
764
/************************************************************************/
765
/*                           CreateField()                              */
766
/************************************************************************/
767
768
OGRErr OGRGeoJSONSeqLayer::CreateField(const OGRFieldDefn *poField,
769
                                       int /* bApproxOK */)
770
0
{
771
0
    if (m_poDS->GetAccess() != GA_Update)
772
0
        return OGRERR_FAILURE;
773
0
    m_poFeatureDefn->AddFieldDefn(poField);
774
0
    return OGRERR_NONE;
775
0
}
776
777
/************************************************************************/
778
/*                               Open()                                 */
779
/************************************************************************/
780
781
bool OGRGeoJSONSeqDataSource::Open(GDALOpenInfo *poOpenInfo,
782
                                   GeoJSONSourceType nSrcType)
783
0
{
784
0
    CPLAssert(nullptr == m_fp);
785
786
0
    CPLString osLayerName("GeoJSONSeq");
787
788
0
    const char *pszUnprefixedFilename = poOpenInfo->pszFilename;
789
0
    if (STARTS_WITH_CI(poOpenInfo->pszFilename, "GeoJSONSeq:"))
790
0
    {
791
0
        pszUnprefixedFilename = poOpenInfo->pszFilename + strlen("GeoJSONSeq:");
792
0
    }
793
794
0
    if (nSrcType == eGeoJSONSourceFile)
795
0
    {
796
0
        if (pszUnprefixedFilename != poOpenInfo->pszFilename)
797
0
        {
798
0
            osLayerName = CPLGetBasenameSafe(pszUnprefixedFilename);
799
0
            m_fp = VSIFOpenL(pszUnprefixedFilename,
800
0
                             poOpenInfo->eAccess == GA_Update ? "rb+" : "rb");
801
0
        }
802
0
        else
803
0
        {
804
0
            osLayerName = CPLGetBasenameSafe(poOpenInfo->pszFilename);
805
0
            std::swap(m_fp, poOpenInfo->fpL);
806
0
        }
807
0
    }
808
0
    else if (nSrcType == eGeoJSONSourceText)
809
0
    {
810
0
        if (poOpenInfo->eAccess == GA_Update)
811
0
            return false;
812
813
0
        m_osTmpFile = VSIMemGenerateHiddenFilename("geojsonseq");
814
0
        m_fp = VSIFileFromMemBuffer(
815
0
            m_osTmpFile.c_str(),
816
0
            reinterpret_cast<GByte *>(CPLStrdup(poOpenInfo->pszFilename)),
817
0
            strlen(poOpenInfo->pszFilename), true);
818
0
    }
819
0
    else if (nSrcType == eGeoJSONSourceService)
820
0
    {
821
0
        if (poOpenInfo->eAccess == GA_Update)
822
0
            return false;
823
824
0
        char *pszStoredContent =
825
0
            OGRGeoJSONDriverStealStoredContent(pszUnprefixedFilename);
826
0
        if (pszStoredContent)
827
0
        {
828
0
            if (EQUAL(pszStoredContent, INVALID_CONTENT_FOR_JSON_LIKE) ||
829
0
                !GeoJSONSeqIsObject(pszStoredContent, poOpenInfo))
830
0
            {
831
0
                OGRGeoJSONDriverStoreContent(poOpenInfo->pszFilename,
832
0
                                             pszStoredContent);
833
0
                return false;
834
0
            }
835
0
            else
836
0
            {
837
0
                m_osTmpFile = VSIMemGenerateHiddenFilename("geojsonseq");
838
0
                m_fp = VSIFileFromMemBuffer(
839
0
                    m_osTmpFile.c_str(),
840
0
                    reinterpret_cast<GByte *>(pszStoredContent),
841
0
                    strlen(pszStoredContent), true);
842
0
            }
843
0
        }
844
0
        else
845
0
        {
846
0
            CPLHTTPResult *pResult =
847
0
                GeoJSONHTTPFetchWithContentTypeHeader(pszUnprefixedFilename);
848
0
            if (!pResult)
849
0
            {
850
0
                return FALSE;
851
0
            }
852
853
0
            m_osTmpFile = VSIMemGenerateHiddenFilename("geojsonseq");
854
0
            m_fp = VSIFileFromMemBuffer(m_osTmpFile.c_str(), pResult->pabyData,
855
0
                                        pResult->nDataLen, true);
856
0
            pResult->pabyData = nullptr;
857
0
            pResult->nDataLen = 0;
858
0
            CPLHTTPDestroyResult(pResult);
859
0
        }
860
0
    }
861
0
    if (m_fp == nullptr)
862
0
    {
863
0
        return false;
864
0
    }
865
0
    SetDescription(poOpenInfo->pszFilename);
866
0
    auto poLayer = new OGRGeoJSONSeqLayer(this, osLayerName.c_str());
867
0
    const bool bLooseIdentification =
868
0
        nSrcType == eGeoJSONSourceService &&
869
0
        !STARTS_WITH_CI(poOpenInfo->pszFilename, "GeoJSONSeq:");
870
0
    if (bLooseIdentification)
871
0
    {
872
0
        CPLPushErrorHandler(CPLQuietErrorHandler);
873
0
    }
874
0
    const bool bEstablishLayerDefn = poOpenInfo->eAccess != GA_Update;
875
0
    auto ret = poLayer->Init(bLooseIdentification, bEstablishLayerDefn);
876
0
    if (bLooseIdentification)
877
0
    {
878
0
        CPLPopErrorHandler();
879
0
        CPLErrorReset();
880
0
    }
881
0
    if (!ret)
882
0
    {
883
0
        delete poLayer;
884
0
        return false;
885
0
    }
886
0
    m_apoLayers.emplace_back(std::move(poLayer));
887
0
    eAccess = poOpenInfo->eAccess;
888
0
    return true;
889
0
}
890
891
/************************************************************************/
892
/*                              Create()                                */
893
/************************************************************************/
894
895
bool OGRGeoJSONSeqDataSource::Create(const char *pszName,
896
                                     char ** /* papszOptions */)
897
0
{
898
0
    CPLAssert(nullptr == m_fp);
899
900
0
    if (strcmp(pszName, "/dev/stdout") == 0)
901
0
        pszName = "/vsistdout/";
902
903
    /* -------------------------------------------------------------------- */
904
    /*      Create the output file.                                         */
905
    /* -------------------------------------------------------------------- */
906
0
    m_bSupportsRead =
907
0
        VSIFileManager::GetHandler(pszName)->SupportsRead(pszName) &&
908
0
        VSIFileManager::GetHandler(pszName)->SupportsRandomWrite(pszName,
909
0
                                                                 false);
910
0
    m_bAtEOF = !m_bSupportsRead;
911
0
    m_fp = VSIFOpenExL(pszName, m_bSupportsRead ? "wb+" : "wb", true);
912
0
    if (nullptr == m_fp)
913
0
    {
914
0
        CPLError(CE_Failure, CPLE_OpenFailed, "Failed to create %s: %s",
915
0
                 pszName, VSIGetLastErrorMsg());
916
0
        return false;
917
0
    }
918
919
0
    eAccess = GA_Update;
920
921
0
    m_bIsRSSeparated = EQUAL(CPLGetExtensionSafe(pszName).c_str(), "GEOJSONS");
922
923
0
    return true;
924
0
}
925
926
/************************************************************************/
927
/*                       OGRGeoJSONSeqDriverIdentify()                  */
928
/************************************************************************/
929
930
static int OGRGeoJSONSeqDriverIdentifyInternal(GDALOpenInfo *poOpenInfo,
931
                                               GeoJSONSourceType &nSrcType)
932
0
{
933
0
    nSrcType = GeoJSONSeqGetSourceType(poOpenInfo);
934
0
    if (nSrcType == eGeoJSONSourceUnknown)
935
0
        return FALSE;
936
0
    if (nSrcType == eGeoJSONSourceService)
937
0
    {
938
0
        if (poOpenInfo->IsSingleAllowedDriver("GeoJSONSeq"))
939
0
            return TRUE;
940
0
        if (!STARTS_WITH_CI(poOpenInfo->pszFilename, "GeoJSONSeq:"))
941
0
        {
942
0
            return -1;
943
0
        }
944
0
    }
945
0
    return TRUE;
946
0
}
947
948
/************************************************************************/
949
/*                      OGRGeoJSONSeqDriverIdentify()                   */
950
/************************************************************************/
951
952
static int OGRGeoJSONSeqDriverIdentify(GDALOpenInfo *poOpenInfo)
953
0
{
954
0
    GeoJSONSourceType nSrcType;
955
0
    return OGRGeoJSONSeqDriverIdentifyInternal(poOpenInfo, nSrcType);
956
0
}
957
958
/************************************************************************/
959
/*                           Open()                                     */
960
/************************************************************************/
961
962
static GDALDataset *OGRGeoJSONSeqDriverOpen(GDALOpenInfo *poOpenInfo)
963
0
{
964
0
    GeoJSONSourceType nSrcType;
965
0
    if (OGRGeoJSONSeqDriverIdentifyInternal(poOpenInfo, nSrcType) == FALSE)
966
0
    {
967
0
        return nullptr;
968
0
    }
969
970
0
    OGRGeoJSONSeqDataSource *poDS = new OGRGeoJSONSeqDataSource();
971
972
0
    if (!poDS->Open(poOpenInfo, nSrcType))
973
0
    {
974
0
        delete poDS;
975
0
        poDS = nullptr;
976
0
    }
977
978
0
    return poDS;
979
0
}
980
981
/************************************************************************/
982
/*                               Create()                               */
983
/************************************************************************/
984
985
static GDALDataset *
986
OGRGeoJSONSeqDriverCreate(const char *pszName, int /* nBands */,
987
                          int /* nXSize */, int /* nYSize */,
988
                          GDALDataType /* eDT */, char **papszOptions)
989
0
{
990
0
    OGRGeoJSONSeqDataSource *poDS = new OGRGeoJSONSeqDataSource();
991
992
0
    if (!poDS->Create(pszName, papszOptions))
993
0
    {
994
0
        delete poDS;
995
0
        poDS = nullptr;
996
0
    }
997
998
0
    return poDS;
999
0
}
1000
1001
/************************************************************************/
1002
/*                        RegisterOGRGeoJSONSeq()                       */
1003
/************************************************************************/
1004
1005
void RegisterOGRGeoJSONSeq()
1006
0
{
1007
0
    if (GDALGetDriverByName("GeoJSONSeq") != nullptr)
1008
0
        return;
1009
1010
0
    GDALDriver *poDriver = new GDALDriver();
1011
1012
0
    poDriver->SetDescription("GeoJSONSeq");
1013
0
    poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
1014
0
    poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
1015
0
    poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
1016
0
    poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
1017
0
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "GeoJSON Sequence");
1018
0
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "geojsonl geojsons");
1019
0
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
1020
0
                              "drivers/vector/geojsonseq.html");
1021
1022
0
    poDriver->SetMetadataItem(
1023
0
        GDAL_DS_LAYER_CREATIONOPTIONLIST,
1024
0
        "<LayerCreationOptionList>"
1025
0
        "  <Option name='RS' type='boolean' description='whether to prefix "
1026
0
        "records with RS=0x1e character' default='NO'/>"
1027
0
        "  <Option name='COORDINATE_PRECISION' type='int' description='Number "
1028
0
        "of decimal for coordinates. Default is 7'/>"
1029
0
        "  <Option name='SIGNIFICANT_FIGURES' type='int' description='Number "
1030
0
        "of significant figures for floating-point values' default='17'/>"
1031
0
        "  <Option name='ID_FIELD' type='string' description='Name of the "
1032
0
        "source field that must be used as the id member of Feature features'/>"
1033
0
        "  <Option name='ID_TYPE' type='string-select' description='Type of "
1034
0
        "the id member of Feature features'>"
1035
0
        "    <Value>AUTO</Value>"
1036
0
        "    <Value>String</Value>"
1037
0
        "    <Value>Integer</Value>"
1038
0
        "  </Option>"
1039
0
        "  <Option name='WRITE_BBOX' type='boolean' description='whether to "
1040
0
        "write a bbox property with the bounding box of each geometry' "
1041
0
        "default='NO'/>"
1042
0
        "</LayerCreationOptionList>");
1043
1044
0
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1045
0
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES,
1046
0
                              "Integer Integer64 Real String IntegerList "
1047
0
                              "Integer64List RealList StringList");
1048
0
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES, "Boolean");
1049
0
    poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
1050
0
    poDriver->SetMetadataItem(GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION, "YES");
1051
0
    poDriver->SetMetadataItem(GDAL_DCAP_APPEND, "YES");
1052
1053
0
    poDriver->pfnOpen = OGRGeoJSONSeqDriverOpen;
1054
0
    poDriver->pfnIdentify = OGRGeoJSONSeqDriverIdentify;
1055
0
    poDriver->pfnCreate = OGRGeoJSONSeqDriverCreate;
1056
1057
0
    GetGDALDriverManager()->RegisterDriver(poDriver);
1058
0
}