Coverage Report

Created: 2025-06-22 06:59

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