Coverage Report

Created: 2025-06-09 08:44

/src/gdal/ogr/ogrsf_frmts/mvt/ogrmvtdataset.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  MVT Translator
4
 * Purpose:  Mapbox Vector Tile decoder
5
 * Author:   Even Rouault, Even Rouault <even dot rouault at spatialys dot com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2018, Even Rouault <even dot rouault at spatialys dot com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#if defined(HAVE_SQLITE) && defined(HAVE_GEOS)
14
// Needed by mvtutils.h
15
#define HAVE_MVT_WRITE_SUPPORT
16
#endif
17
18
#include "ogrsf_frmts.h"
19
#include "cpl_conv.h"
20
#include "cpl_json.h"
21
#include "cpl_http.h"
22
#include "ogr_p.h"
23
24
#include "mvt_tile.h"
25
#include "mvtutils.h"
26
27
#include "ogr_geos.h"
28
29
#include "gpb.h"
30
31
#include <algorithm>
32
#include <memory>
33
#include <vector>
34
#include <set>
35
36
const char *SRS_EPSG_3857 =
37
    "PROJCS[\"WGS 84 / Pseudo-Mercator\",GEOGCS[\"WGS "
38
    "84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS "
39
    "84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY["
40
    "\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
41
    "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
42
    "AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"Mercator_1SP\"],PARAMETER["
43
    "\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_"
44
    "easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY["
45
    "\"EPSG\",\"9001\"]],AXIS[\"X\",EAST],AXIS[\"Y\",NORTH],EXTENSION["
46
    "\"PROJ4\",\"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 "
47
    "+x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext  "
48
    "+no_defs\"],AUTHORITY[\"EPSG\",\"3857\"]]";
49
50
// WebMercator related constants
51
constexpr double kmSPHERICAL_RADIUS = 6378137.0;
52
53
constexpr int knMAX_FILES_PER_DIR = 10000;
54
55
#ifdef HAVE_MVT_WRITE_SUPPORT
56
57
#include <sqlite3.h>
58
#include "../sqlite/ogrsqliteutility.h"
59
60
#include "../sqlite/ogrsqlitevfs.h"
61
62
#include "cpl_worker_thread_pool.h"
63
64
#include <mutex>
65
66
// Limitations from https://github.com/mapbox/mapbox-geostats
67
constexpr size_t knMAX_COUNT_LAYERS = 1000;
68
constexpr size_t knMAX_REPORT_LAYERS = 100;
69
constexpr size_t knMAX_COUNT_FIELDS = 1000;
70
constexpr size_t knMAX_REPORT_FIELDS = 100;
71
constexpr size_t knMAX_COUNT_VALUES = 1000;
72
constexpr size_t knMAX_REPORT_VALUES = 100;
73
constexpr size_t knMAX_STRING_VALUE_LENGTH = 256;
74
constexpr size_t knMAX_LAYER_NAME_LENGTH = 256;
75
constexpr size_t knMAX_FIELD_NAME_LENGTH = 256;
76
77
#undef SQLITE_STATIC
78
#define SQLITE_STATIC ((sqlite3_destructor_type) nullptr)
79
80
#endif
81
82
/************************************************************************/
83
/*                    InitWebMercatorTilingScheme()                     */
84
/************************************************************************/
85
86
static void InitWebMercatorTilingScheme(OGRSpatialReference *poSRS,
87
                                        double &dfTopX, double &dfTopY,
88
                                        double &dfTileDim0)
89
172k
{
90
172k
    constexpr double kmMAX_GM =
91
172k
        kmSPHERICAL_RADIUS * M_PI;  // 20037508.342789244
92
172k
    poSRS->SetFromUserInput(SRS_EPSG_3857);
93
172k
    dfTopX = -kmMAX_GM;
94
172k
    dfTopY = kmMAX_GM;
95
172k
    dfTileDim0 = 2 * kmMAX_GM;
96
172k
}
97
98
/************************************************************************/
99
/*                           GetCmdId()                                 */
100
/************************************************************************/
101
102
/* For a drawing instruction combining a command id and a command count,
103
 * return the command id */
104
static unsigned GetCmdId(unsigned int nCmdCountCombined)
105
121k
{
106
121k
    return nCmdCountCombined & 0x7;
107
121k
}
108
109
/************************************************************************/
110
/*                           GetCmdCount()                              */
111
/************************************************************************/
112
113
/* For a drawing instruction combining a command id and a command count,
114
 * return the command count */
115
static unsigned GetCmdCount(unsigned int nCmdCountCombined)
116
280k
{
117
280k
    return nCmdCountCombined >> 3;
118
280k
}
119
120
/************************************************************************/
121
/*                          OGRMVTLayerBase                             */
122
/************************************************************************/
123
124
class OGRMVTLayerBase CPL_NON_FINAL
125
    : public OGRLayer,
126
      public OGRGetNextFeatureThroughRaw<OGRMVTLayerBase>
127
{
128
    virtual OGRFeature *GetNextRawFeature() = 0;
129
130
  protected:
131
    OGRFeatureDefn *m_poFeatureDefn = nullptr;
132
133
    void InitFields(const CPLJSONObject &oFields,
134
                    const CPLJSONArray &oAttributesFromTileStats);
135
136
  public:
137
    virtual ~OGRMVTLayerBase();
138
139
    virtual OGRFeatureDefn *GetLayerDefn() override
140
536k
    {
141
536k
        return m_poFeatureDefn;
142
536k
    }
143
144
    DEFINE_GET_NEXT_FEATURE_THROUGH_RAW(OGRMVTLayerBase)
145
146
    virtual int TestCapability(const char *) override;
147
};
148
149
/************************************************************************/
150
/*                             OGRMVTLayer                              */
151
/************************************************************************/
152
153
class OGRMVTDataset;
154
155
class OGRMVTLayer final : public OGRMVTLayerBase
156
{
157
    OGRMVTDataset *m_poDS;
158
    const GByte *m_pabyDataStart;
159
    const GByte *m_pabyDataEnd;
160
    const GByte *m_pabyDataCur = nullptr;
161
    const GByte *m_pabyDataFeatureStart = nullptr;
162
    bool m_bError = false;
163
    unsigned int m_nExtent = knDEFAULT_EXTENT;
164
    std::vector<CPLString> m_aosKeys;
165
166
    typedef struct
167
    {
168
        OGRFieldType eType;
169
        OGRFieldSubType eSubType;
170
        OGRField sValue;
171
    } Value;
172
173
    std::vector<Value> m_asValues;
174
    GIntBig m_nFID = 0;
175
    GIntBig m_nFeatureCount = -1;
176
    OGRPolygon m_oClipPoly;
177
    double m_dfTileMinX = 0;
178
    double m_dfTileMinY = 0;
179
    double m_dfTileMaxX = 0;
180
    double m_dfTileMaxY = 0;
181
    bool m_bEnforceExternalIsClockwise = false;
182
183
    void Init(const CPLJSONObject &oFields,
184
              const CPLJSONArray &oAttributesFromTileStats);
185
    bool QuickScanFeature(const GByte *pabyData,
186
                          const GByte *pabyDataFeatureEnd, bool bScanFields,
187
                          bool bScanGeometries, bool &bGeomTypeSet);
188
    void GetXY(int nX, int nY, double &dfX, double &dfY);
189
    OGRGeometry *ParseGeometry(unsigned int nGeomType,
190
                               const GByte *pabyDataGeometryEnd);
191
    void SanitizeClippedGeometry(OGRGeometry *&poGeom);
192
193
    virtual OGRFeature *GetNextRawFeature() override;
194
195
  public:
196
    OGRMVTLayer(OGRMVTDataset *poDS, const char *pszLayerName,
197
                const GByte *pabyData, int nLayerSize,
198
                const CPLJSONObject &oFields,
199
                const CPLJSONArray &oAttributesFromTileStats,
200
                OGRwkbGeometryType eGeomType);
201
    virtual ~OGRMVTLayer();
202
203
    virtual void ResetReading() override;
204
205
    virtual GIntBig GetFeatureCount(int bForce) override;
206
207
    GDALDataset *GetDataset() override;
208
};
209
210
/************************************************************************/
211
/*                        OGRMVTDirectoryLayer                          */
212
/************************************************************************/
213
214
class OGRMVTDirectoryLayer final : public OGRMVTLayerBase
215
{
216
    OGRMVTDataset *m_poDS;
217
    int m_nZ = 0;
218
    bool m_bUseReadDir = true;
219
    CPLString m_osDirName;
220
    CPLStringList m_aosDirContent;
221
    CPLString m_aosSubDirName;
222
    CPLStringList m_aosSubDirContent;
223
    bool m_bEOF = false;
224
    int m_nXIndex = 0;
225
    int m_nYIndex = 0;
226
    GDALDataset *m_poCurrentTile = nullptr;
227
    bool m_bJsonField = false;
228
    GIntBig m_nFIDBase = 0;
229
    OGREnvelope m_sExtent;
230
    int m_nFilterMinX = 0;
231
    int m_nFilterMinY = 0;
232
    int m_nFilterMaxX = 0;
233
    int m_nFilterMaxY = 0;
234
235
    virtual OGRFeature *GetNextRawFeature() override;
236
    OGRFeature *CreateFeatureFrom(OGRFeature *poSrcFeature);
237
    void ReadNewSubDir();
238
    void OpenTile();
239
    void OpenTileIfNeeded();
240
241
  public:
242
    OGRMVTDirectoryLayer(OGRMVTDataset *poDS, const char *pszLayerName,
243
                         const char *pszDirectoryName,
244
                         const CPLJSONObject &oFields,
245
                         const CPLJSONArray &oAttributesFromTileStats,
246
                         bool bJsonField, OGRwkbGeometryType eGeomType,
247
                         const OGREnvelope *psExtent);
248
    virtual ~OGRMVTDirectoryLayer();
249
250
    virtual void ResetReading() override;
251
252
    virtual GIntBig GetFeatureCount(int bForce) override;
253
    OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
254
                      bool bForce) override;
255
256
    OGRErr ISetSpatialFilter(int iGeomField,
257
                             const OGRGeometry *poGeom) override;
258
259
    virtual OGRFeature *GetFeature(GIntBig nFID) override;
260
261
    virtual int TestCapability(const char *) override;
262
263
    GDALDataset *GetDataset() override;
264
};
265
266
/************************************************************************/
267
/*                           OGRMVTDataset                              */
268
/************************************************************************/
269
270
class OGRMVTDataset final : public GDALDataset
271
{
272
    friend class OGRMVTLayer;
273
    friend class OGRMVTDirectoryLayer;
274
275
    GByte *m_pabyData;
276
    std::vector<std::unique_ptr<OGRLayer>> m_apoLayers;
277
    bool m_bGeoreferenced = false;
278
    double m_dfTileDimX = 0.0;
279
    double m_dfTileDimY = 0.0;
280
    double m_dfTopX = 0.0;
281
    double m_dfTopY = 0.0;
282
    CPLString m_osMetadataMemFilename;
283
    bool m_bClip = true;
284
    CPLString m_osTileExtension{"pbf"};
285
    OGRSpatialReference *m_poSRS = nullptr;
286
    double m_dfTileDim0 =
287
        0.0;  // Extent (in CRS units) of a tile at zoom level 0
288
    double m_dfTopXOrigin = 0.0;  // top-left X of tile matrix scheme
289
    double m_dfTopYOrigin = 0.0;  // top-left Y of tile matrix scheme
290
    int m_nTileMatrixWidth0 =
291
        1;  // Number of tiles along X axis at zoom level 0
292
    int m_nTileMatrixHeight0 =
293
        1;  // Number of tiles along Y axis at zoom level 0
294
295
    static GDALDataset *OpenDirectory(GDALOpenInfo *);
296
297
  public:
298
    explicit OGRMVTDataset(GByte *pabyData);
299
    virtual ~OGRMVTDataset();
300
301
    virtual int GetLayerCount() override
302
1.10M
    {
303
1.10M
        return static_cast<int>(m_apoLayers.size());
304
1.10M
    }
305
306
    virtual OGRLayer *GetLayer(int) override;
307
308
    virtual int TestCapability(const char *) override
309
0
    {
310
0
        return FALSE;
311
0
    }
312
313
    static GDALDataset *Open(GDALOpenInfo *);
314
    static GDALDataset *Open(GDALOpenInfo *, bool bRecurseAllowed);
315
316
    OGRSpatialReference *GetSRS()
317
236k
    {
318
236k
        return m_poSRS;
319
236k
    }
320
321
    inline double GetTileDim0() const
322
0
    {
323
0
        return m_dfTileDim0;
324
0
    }
325
326
    inline double GetTopXOrigin() const
327
0
    {
328
0
        return m_dfTopXOrigin;
329
0
    }
330
331
    inline double GetTopYOrigin() const
332
0
    {
333
0
        return m_dfTopYOrigin;
334
0
    }
335
336
    inline int GetTileMatrixWidth0() const
337
0
    {
338
0
        return m_nTileMatrixWidth0;
339
0
    }
340
341
    inline int GetTileMatrixHeight0() const
342
0
    {
343
0
        return m_nTileMatrixHeight0;
344
0
    }
345
};
346
347
/************************************************************************/
348
/*                        ~OGRMVTLayerBase()                            */
349
/************************************************************************/
350
351
OGRMVTLayerBase::~OGRMVTLayerBase()
352
446k
{
353
446k
    m_poFeatureDefn->Release();
354
446k
}
355
356
/************************************************************************/
357
/*                           InitFields()                               */
358
/************************************************************************/
359
360
void OGRMVTLayerBase::InitFields(const CPLJSONObject &oFields,
361
                                 const CPLJSONArray &oAttributesFromTileStats)
362
293k
{
363
293k
    OGRMVTInitFields(m_poFeatureDefn, oFields, oAttributesFromTileStats);
364
293k
}
365
366
/************************************************************************/
367
/*                           TestCapability()                           */
368
/************************************************************************/
369
370
int OGRMVTLayerBase::TestCapability(const char *pszCap)
371
0
{
372
0
    if (EQUAL(pszCap, OLCStringsAsUTF8) || EQUAL(pszCap, OLCFastSpatialFilter))
373
0
    {
374
0
        return TRUE;
375
0
    }
376
0
    return FALSE;
377
0
}
378
379
/************************************************************************/
380
/*                           OGRMVTLayer()                              */
381
/************************************************************************/
382
383
OGRMVTLayer::OGRMVTLayer(OGRMVTDataset *poDS, const char *pszLayerName,
384
                         const GByte *pabyData, int nLayerSize,
385
                         const CPLJSONObject &oFields,
386
                         const CPLJSONArray &oAttributesFromTileStats,
387
                         OGRwkbGeometryType eGeomType)
388
446k
    : m_poDS(poDS), m_pabyDataStart(pabyData),
389
446k
      m_pabyDataEnd(pabyData + nLayerSize)
390
446k
{
391
446k
    m_poFeatureDefn = new OGRFeatureDefn(pszLayerName);
392
446k
    SetDescription(m_poFeatureDefn->GetName());
393
446k
    m_poFeatureDefn->SetGeomType(eGeomType);
394
446k
    m_poFeatureDefn->Reference();
395
396
446k
    if (m_poDS->m_bGeoreferenced)
397
236k
    {
398
236k
        m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_poDS->GetSRS());
399
236k
    }
400
401
446k
    Init(oFields, oAttributesFromTileStats);
402
403
446k
    GetXY(0, 0, m_dfTileMinX, m_dfTileMaxY);
404
446k
    GetXY(m_nExtent, m_nExtent, m_dfTileMaxX, m_dfTileMinY);
405
446k
    OGRLinearRing *poLR = new OGRLinearRing();
406
446k
    poLR->addPoint(m_dfTileMinX, m_dfTileMinY);
407
446k
    poLR->addPoint(m_dfTileMinX, m_dfTileMaxY);
408
446k
    poLR->addPoint(m_dfTileMaxX, m_dfTileMaxY);
409
446k
    poLR->addPoint(m_dfTileMaxX, m_dfTileMinY);
410
446k
    poLR->addPoint(m_dfTileMinX, m_dfTileMinY);
411
446k
    m_oClipPoly.addRingDirectly(poLR);
412
413
    // Config option only for tests for now. When set, it ensures that
414
    // the first ring (exterior ring) of a polygon is clockwise oriented,
415
    // as per the MVT spec.
416
    // By default, we are more tolerant and only use reversal of winding order
417
    // to detect inner rings.
418
446k
    m_bEnforceExternalIsClockwise = CPLTestBool(
419
446k
        CPLGetConfigOption("OGR_MVT_ENFORE_EXTERNAL_RING_IS_CLOCKWISE", "NO"));
420
446k
}
421
422
/************************************************************************/
423
/*                          ~OGRMVTLayer()                              */
424
/************************************************************************/
425
426
OGRMVTLayer::~OGRMVTLayer()
427
446k
{
428
446k
    for (auto &sValue : m_asValues)
429
205k
    {
430
205k
        if (sValue.eType == OFTString)
431
133k
        {
432
133k
            CPLFree(sValue.sValue.String);
433
133k
        }
434
205k
    }
435
446k
}
436
437
/************************************************************************/
438
/*                               Init()                                 */
439
/************************************************************************/
440
441
void OGRMVTLayer::Init(const CPLJSONObject &oFields,
442
                       const CPLJSONArray &oAttributesFromTileStats)
443
446k
{
444
    // First pass to collect keys and values
445
446k
    const GByte *pabyData = m_pabyDataStart;
446
446k
    const GByte *pabyDataLimit = m_pabyDataEnd;
447
446k
    unsigned int nKey = 0;
448
446k
    bool bGeomTypeSet = false;
449
446k
    const bool bScanFields = !oFields.IsValid();
450
446k
    const bool bScanGeometries = m_poFeatureDefn->GetGeomType() == wkbUnknown;
451
446k
    const bool bQuickScanFeature = bScanFields || bScanGeometries;
452
453
446k
    try
454
446k
    {
455
12.4M
        while (pabyData < pabyDataLimit)
456
12.1M
        {
457
12.1M
            READ_FIELD_KEY(nKey);
458
12.1M
            if (nKey == MAKE_KEY(knLAYER_KEYS, WT_DATA))
459
320k
            {
460
320k
                char *pszKey = nullptr;
461
320k
                READ_TEXT(pabyData, pabyDataLimit, pszKey);
462
277k
                m_aosKeys.push_back(pszKey);
463
277k
                CPLFree(pszKey);
464
277k
            }
465
11.8M
            else if (nKey == MAKE_KEY(knLAYER_VALUES, WT_DATA))
466
424k
            {
467
424k
                unsigned int nValueLength = 0;
468
424k
                READ_SIZE(pabyData, pabyDataLimit, nValueLength);
469
417k
                const GByte *pabyDataValueEnd = pabyData + nValueLength;
470
417k
                READ_VARUINT32(pabyData, pabyDataLimit, nKey);
471
415k
                if (nKey == MAKE_KEY(knVALUE_STRING, WT_DATA))
472
139k
                {
473
139k
                    char *pszValue = nullptr;
474
139k
                    READ_TEXT(pabyData, pabyDataLimit, pszValue);
475
133k
                    Value sValue;
476
133k
                    sValue.eType = OFTString;
477
133k
                    sValue.eSubType = OFSTNone;
478
133k
                    sValue.sValue.String = pszValue;
479
133k
                    m_asValues.push_back(sValue);
480
133k
                }
481
276k
                else if (nKey == MAKE_KEY(knVALUE_FLOAT, WT_32BIT))
482
4.12k
                {
483
4.12k
                    Value sValue;
484
4.12k
                    sValue.eType = OFTReal;
485
4.12k
                    sValue.eSubType = OFSTFloat32;
486
4.12k
                    sValue.sValue.Real = ReadFloat32(&pabyData, pabyDataLimit);
487
4.12k
                    m_asValues.push_back(sValue);
488
4.12k
                }
489
272k
                else if (nKey == MAKE_KEY(knVALUE_DOUBLE, WT_64BIT))
490
1.85k
                {
491
1.85k
                    Value sValue;
492
1.85k
                    sValue.eType = OFTReal;
493
1.85k
                    sValue.eSubType = OFSTNone;
494
1.85k
                    sValue.sValue.Real = ReadFloat64(&pabyData, pabyDataLimit);
495
1.85k
                    m_asValues.push_back(sValue);
496
1.85k
                }
497
270k
                else if (nKey == MAKE_KEY(knVALUE_INT, WT_VARINT))
498
36.0k
                {
499
36.0k
                    GIntBig nVal = 0;
500
36.0k
                    READ_VARINT64(pabyData, pabyDataLimit, nVal);
501
36.0k
                    Value sValue;
502
36.0k
                    sValue.eType = (nVal >= INT_MIN && nVal <= INT_MAX)
503
36.0k
                                       ? OFTInteger
504
36.0k
                                       : OFTInteger64;
505
36.0k
                    sValue.eSubType = OFSTNone;
506
36.0k
                    if (sValue.eType == OFTInteger)
507
13.4k
                        sValue.sValue.Integer = static_cast<int>(nVal);
508
22.5k
                    else
509
22.5k
                        sValue.sValue.Integer64 = nVal;
510
36.0k
                    m_asValues.push_back(sValue);
511
36.0k
                }
512
234k
                else if (nKey == MAKE_KEY(knVALUE_UINT, WT_VARINT))
513
6.96k
                {
514
6.96k
                    GUIntBig nVal = 0;
515
6.96k
                    READ_VARUINT64(pabyData, pabyDataLimit, nVal);
516
6.90k
                    Value sValue;
517
6.90k
                    sValue.eType =
518
6.90k
                        (nVal <= INT_MAX) ? OFTInteger : OFTInteger64;
519
6.90k
                    sValue.eSubType = OFSTNone;
520
6.90k
                    if (sValue.eType == OFTInteger)
521
1.56k
                        sValue.sValue.Integer = static_cast<int>(nVal);
522
5.34k
                    else
523
5.34k
                        sValue.sValue.Integer64 = static_cast<GIntBig>(nVal);
524
6.90k
                    m_asValues.push_back(sValue);
525
6.90k
                }
526
227k
                else if (nKey == MAKE_KEY(knVALUE_SINT, WT_VARINT))
527
17.8k
                {
528
17.8k
                    GIntBig nVal = 0;
529
17.8k
                    READ_VARSINT64(pabyData, pabyDataLimit, nVal);
530
17.8k
                    Value sValue;
531
17.8k
                    sValue.eType = (nVal >= INT_MIN && nVal <= INT_MAX)
532
17.8k
                                       ? OFTInteger
533
17.8k
                                       : OFTInteger64;
534
17.8k
                    sValue.eSubType = OFSTNone;
535
17.8k
                    if (sValue.eType == OFTInteger)
536
6.17k
                        sValue.sValue.Integer = static_cast<int>(nVal);
537
11.6k
                    else
538
11.6k
                        sValue.sValue.Integer64 = nVal;
539
17.8k
                    m_asValues.push_back(sValue);
540
17.8k
                }
541
209k
                else if (nKey == MAKE_KEY(knVALUE_BOOL, WT_VARINT))
542
6.28k
                {
543
6.28k
                    unsigned nVal = 0;
544
6.28k
                    READ_VARUINT32(pabyData, pabyDataLimit, nVal);
545
6.07k
                    Value sValue;
546
6.07k
                    sValue.eType = OFTInteger;
547
6.07k
                    sValue.eSubType = OFSTBoolean;
548
6.07k
                    sValue.sValue.Integer = static_cast<int>(nVal);
549
6.07k
                    m_asValues.push_back(sValue);
550
6.07k
                }
551
552
409k
                pabyData = pabyDataValueEnd;
553
409k
            }
554
11.3M
            else if (nKey == MAKE_KEY(knLAYER_EXTENT, WT_VARINT))
555
449k
            {
556
449k
                GUInt32 nExtent = 0;
557
449k
                READ_VARUINT32(pabyData, pabyDataLimit, nExtent);
558
447k
                m_nExtent = std::max(1U, nExtent);  // to avoid divide by zero
559
447k
            }
560
10.9M
            else
561
10.9M
            {
562
10.9M
                SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, FALSE);
563
10.8M
            }
564
12.1M
        }
565
566
293k
        InitFields(oFields, oAttributesFromTileStats);
567
568
293k
        m_nFeatureCount = 0;
569
293k
        pabyData = m_pabyDataStart;
570
        // Second pass to iterate over features to figure out the geometry type
571
        // and attribute schema
572
4.42M
        while (pabyData < pabyDataLimit)
573
4.19M
        {
574
4.19M
            const GByte *pabyDataBefore = pabyData;
575
4.19M
            READ_FIELD_KEY(nKey);
576
4.19M
            if (nKey == MAKE_KEY(knLAYER_FEATURES, WT_DATA))
577
298k
            {
578
298k
                if (m_pabyDataFeatureStart == nullptr)
579
264k
                {
580
264k
                    m_pabyDataFeatureStart = pabyDataBefore;
581
264k
                    m_pabyDataCur = pabyDataBefore;
582
264k
                }
583
584
298k
                unsigned int nFeatureLength = 0;
585
298k
                READ_SIZE(pabyData, pabyDataLimit, nFeatureLength);
586
298k
                const GByte *pabyDataFeatureEnd = pabyData + nFeatureLength;
587
298k
                if (bQuickScanFeature)
588
297k
                {
589
297k
                    if (!QuickScanFeature(pabyData, pabyDataFeatureEnd,
590
297k
                                          bScanFields, bScanGeometries,
591
297k
                                          bGeomTypeSet))
592
66.2k
                    {
593
66.2k
                        return;
594
66.2k
                    }
595
297k
                }
596
231k
                pabyData = pabyDataFeatureEnd;
597
598
231k
                m_nFeatureCount++;
599
231k
            }
600
3.89M
            else
601
3.89M
            {
602
3.89M
                SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, FALSE);
603
3.89M
            }
604
4.19M
        }
605
293k
    }
606
446k
    catch (const GPBException &e)
607
446k
    {
608
153k
        CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
609
153k
    }
610
446k
}
611
612
/************************************************************************/
613
/*                          MergeFieldDefn()                            */
614
/************************************************************************/
615
616
static void MergeFieldDefn(OGRFieldDefn *poFieldDefn, OGRFieldType eSrcType,
617
                           OGRFieldSubType eSrcSubType)
618
123
{
619
123
    if (eSrcType == OFTString)
620
1
    {
621
1
        poFieldDefn->SetSubType(OFSTNone);
622
1
        poFieldDefn->SetType(OFTString);
623
1
    }
624
122
    else if (poFieldDefn->GetType() == OFTInteger && eSrcType == OFTInteger64)
625
20
    {
626
20
        poFieldDefn->SetSubType(OFSTNone);
627
20
        poFieldDefn->SetType(OFTInteger64);
628
20
    }
629
102
    else if ((poFieldDefn->GetType() == OFTInteger ||
630
102
              poFieldDefn->GetType() == OFTInteger64) &&
631
102
             eSrcType == OFTReal)
632
78
    {
633
78
        poFieldDefn->SetSubType(OFSTNone);
634
78
        poFieldDefn->SetType(OFTReal);
635
78
        poFieldDefn->SetSubType(eSrcSubType);
636
78
    }
637
24
    else if (poFieldDefn->GetType() == OFTReal && eSrcType == OFTReal &&
638
24
             eSrcSubType == OFSTNone)
639
0
    {
640
0
        poFieldDefn->SetSubType(OFSTNone);
641
0
    }
642
24
    else if (poFieldDefn->GetType() == OFTInteger && eSrcType == OFTInteger &&
643
24
             eSrcSubType == OFSTNone)
644
3
    {
645
3
        poFieldDefn->SetSubType(OFSTNone);
646
3
    }
647
123
}
648
649
/************************************************************************/
650
/*                         QuickScanFeature()                           */
651
/************************************************************************/
652
653
bool OGRMVTLayer::QuickScanFeature(const GByte *pabyData,
654
                                   const GByte *pabyDataFeatureEnd,
655
                                   bool bScanFields, bool bScanGeometries,
656
                                   bool &bGeomTypeSet)
657
297k
{
658
297k
    unsigned int nKey = 0;
659
297k
    unsigned int nGeomType = 0;
660
297k
    try
661
297k
    {
662
2.31M
        while (pabyData < pabyDataFeatureEnd)
663
2.08M
        {
664
2.08M
            READ_VARUINT32(pabyData, pabyDataFeatureEnd, nKey);
665
2.08M
            if (nKey == MAKE_KEY(knFEATURE_TYPE, WT_VARINT))
666
553k
            {
667
553k
                READ_VARUINT32(pabyData, pabyDataFeatureEnd, nGeomType);
668
552k
            }
669
1.52M
            else if (nKey == MAKE_KEY(knFEATURE_TAGS, WT_DATA) && bScanFields)
670
79.5k
            {
671
79.5k
                unsigned int nTagsSize = 0;
672
79.5k
                READ_SIZE(pabyData, pabyDataFeatureEnd, nTagsSize);
673
75.4k
                const GByte *pabyDataTagsEnd = pabyData + nTagsSize;
674
106k
                while (pabyData < pabyDataTagsEnd)
675
59.7k
                {
676
59.7k
                    unsigned int nKeyIdx = 0;
677
59.7k
                    unsigned int nValIdx = 0;
678
59.7k
                    READ_VARUINT32(pabyData, pabyDataTagsEnd, nKeyIdx);
679
59.4k
                    READ_VARUINT32(pabyData, pabyDataTagsEnd, nValIdx);
680
53.9k
                    if (nKeyIdx >= m_aosKeys.size())
681
18.7k
                    {
682
18.7k
                        CPLError(CE_Failure, CPLE_AppDefined,
683
18.7k
                                 "Invalid tag key index: %u", nKeyIdx);
684
18.7k
                        m_bError = true;
685
18.7k
                        return false;
686
18.7k
                    }
687
35.1k
                    if (nValIdx >= m_asValues.size())
688
4.58k
                    {
689
4.58k
                        CPLError(CE_Failure, CPLE_AppDefined,
690
4.58k
                                 "Invalid tag value index: %u", nValIdx);
691
4.58k
                        m_bError = true;
692
4.58k
                        return false;
693
4.58k
                    }
694
30.5k
                    const int nFieldIdx =
695
30.5k
                        m_poFeatureDefn->GetFieldIndex(m_aosKeys[nKeyIdx]);
696
30.5k
                    if (nFieldIdx < 0)
697
29.4k
                    {
698
29.4k
                        OGRFieldDefn oFieldDefn(m_aosKeys[nKeyIdx],
699
29.4k
                                                m_asValues[nValIdx].eType);
700
29.4k
                        oFieldDefn.SetSubType(m_asValues[nValIdx].eSubType);
701
29.4k
                        m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
702
29.4k
                    }
703
1.10k
                    else if (m_poFeatureDefn->GetFieldDefn(nFieldIdx)
704
1.10k
                                     ->GetType() != m_asValues[nValIdx].eType ||
705
1.10k
                             m_poFeatureDefn->GetFieldDefn(nFieldIdx)
706
999
                                     ->GetSubType() !=
707
999
                                 m_asValues[nValIdx].eSubType)
708
123
                    {
709
123
                        OGRFieldDefn *poFieldDefn =
710
123
                            m_poFeatureDefn->GetFieldDefn(nFieldIdx);
711
123
                        OGRFieldType eSrcType(m_asValues[nValIdx].eType);
712
123
                        OGRFieldSubType eSrcSubType(
713
123
                            m_asValues[nValIdx].eSubType);
714
123
                        MergeFieldDefn(poFieldDefn, eSrcType, eSrcSubType);
715
123
                    }
716
30.5k
                }
717
75.4k
            }
718
1.45M
            else if (nKey == MAKE_KEY(knFEATURE_GEOMETRY, WT_DATA) &&
719
1.45M
                     bScanGeometries && nGeomType >= knGEOM_TYPE_POINT &&
720
1.45M
                     nGeomType <= knGEOM_TYPE_POLYGON)
721
184k
            {
722
184k
                unsigned int nGeometrySize = 0;
723
184k
                READ_SIZE(pabyData, pabyDataFeatureEnd, nGeometrySize);
724
183k
                const GByte *pabyDataGeometryEnd = pabyData + nGeometrySize;
725
183k
                OGRwkbGeometryType eType = wkbUnknown;
726
727
183k
                if (nGeomType == knGEOM_TYPE_POINT)
728
105k
                {
729
105k
                    eType = wkbPoint;
730
105k
                    unsigned int nCmdCountCombined = 0;
731
105k
                    READ_VARUINT32(pabyData, pabyDataGeometryEnd,
732
105k
                                   nCmdCountCombined);
733
104k
                    if (GetCmdId(nCmdCountCombined) == knCMD_MOVETO &&
734
104k
                        GetCmdCount(nCmdCountCombined) > 1)
735
14.0k
                    {
736
14.0k
                        eType = wkbMultiPoint;
737
14.0k
                    }
738
104k
                }
739
78.2k
                else if (nGeomType == knGEOM_TYPE_LINESTRING)
740
27.4k
                {
741
27.4k
                    eType = wkbLineString;
742
50.8k
                    for (int iIter = 0; pabyData < pabyDataGeometryEnd; iIter++)
743
48.8k
                    {
744
48.8k
                        if (iIter == 1)
745
21.8k
                        {
746
21.8k
                            eType = wkbMultiLineString;
747
21.8k
                            break;
748
21.8k
                        }
749
26.9k
                        unsigned int nCmdCountCombined = 0;
750
26.9k
                        unsigned int nLineToCount;
751
                        // Should be a moveto
752
26.9k
                        SKIP_VARINT(pabyData, pabyDataGeometryEnd);
753
26.9k
                        SKIP_VARINT(pabyData, pabyDataGeometryEnd);
754
26.9k
                        SKIP_VARINT(pabyData, pabyDataGeometryEnd);
755
26.7k
                        READ_VARUINT32(pabyData, pabyDataGeometryEnd,
756
26.7k
                                       nCmdCountCombined);
757
26.7k
                        nLineToCount = GetCmdCount(nCmdCountCombined);
758
409k
                        for (unsigned i = 0; i < 2 * nLineToCount; i++)
759
386k
                        {
760
386k
                            SKIP_VARINT(pabyData, pabyDataGeometryEnd);
761
382k
                        }
762
26.7k
                    }
763
27.4k
                }
764
50.8k
                else /* if( nGeomType == knGEOM_TYPE_POLYGON ) */
765
50.8k
                {
766
50.8k
                    eType = wkbPolygon;
767
99.4k
                    for (int iIter = 0; pabyData < pabyDataGeometryEnd; iIter++)
768
99.3k
                    {
769
99.3k
                        if (iIter == 1)
770
48.6k
                        {
771
48.6k
                            eType = wkbMultiPolygon;
772
48.6k
                            break;
773
48.6k
                        }
774
50.7k
                        unsigned int nCmdCountCombined = 0;
775
50.7k
                        unsigned int nLineToCount;
776
                        // Should be a moveto
777
50.7k
                        SKIP_VARINT(pabyData, pabyDataGeometryEnd);
778
50.7k
                        SKIP_VARINT(pabyData, pabyDataGeometryEnd);
779
50.7k
                        SKIP_VARINT(pabyData, pabyDataGeometryEnd);
780
50.7k
                        READ_VARUINT32(pabyData, pabyDataGeometryEnd,
781
50.7k
                                       nCmdCountCombined);
782
50.7k
                        nLineToCount = GetCmdCount(nCmdCountCombined);
783
785k
                        for (unsigned i = 0; i < 2 * nLineToCount; i++)
784
737k
                        {
785
737k
                            SKIP_VARINT(pabyData, pabyDataGeometryEnd);
786
735k
                        }
787
                        // Should be a closepath
788
48.6k
                        SKIP_VARINT(pabyData, pabyDataGeometryEnd);
789
48.6k
                    }
790
50.8k
                }
791
792
176k
                if (bGeomTypeSet && m_poFeatureDefn->GetGeomType() ==
793
3.92k
                                        OGR_GT_GetCollection(eType))
794
2.17k
                {
795
                    // do nothing
796
2.17k
                }
797
174k
                else if (bGeomTypeSet &&
798
174k
                         eType == OGR_GT_GetCollection(
799
1.74k
                                      m_poFeatureDefn->GetGeomType()))
800
94
                {
801
94
                    m_poFeatureDefn->SetGeomType(eType);
802
94
                }
803
174k
                else if (bGeomTypeSet &&
804
174k
                         m_poFeatureDefn->GetGeomType() != eType)
805
839
                {
806
839
                    m_poFeatureDefn->SetGeomType(wkbUnknown);
807
839
                }
808
173k
                else
809
173k
                {
810
173k
                    m_poFeatureDefn->SetGeomType(eType);
811
173k
                }
812
176k
                bGeomTypeSet = true;
813
814
176k
                pabyData = pabyDataGeometryEnd;
815
176k
            }
816
1.26M
            else
817
1.26M
            {
818
1.26M
                SKIP_UNKNOWN_FIELD(pabyData, pabyDataFeatureEnd, FALSE);
819
1.24M
            }
820
2.08M
        }
821
231k
        return true;
822
297k
    }
823
297k
    catch (const GPBException &e)
824
297k
    {
825
42.9k
        CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
826
42.9k
        return false;
827
42.9k
    }
828
297k
}
829
830
/************************************************************************/
831
/*                         GetFeatureCount()                            */
832
/************************************************************************/
833
834
GIntBig OGRMVTLayer::GetFeatureCount(int bForce)
835
0
{
836
0
    if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr &&
837
0
        m_nFeatureCount >= 0)
838
0
    {
839
0
        return m_nFeatureCount;
840
0
    }
841
0
    return OGRLayer::GetFeatureCount(bForce);
842
0
}
843
844
/************************************************************************/
845
/*                          ResetReading()                              */
846
/************************************************************************/
847
848
void OGRMVTLayer::ResetReading()
849
0
{
850
0
    m_nFID = 0;
851
0
    m_pabyDataCur = m_pabyDataFeatureStart;
852
0
}
853
854
/************************************************************************/
855
/*                              GetXY()                                 */
856
/************************************************************************/
857
858
void OGRMVTLayer::GetXY(int nX, int nY, double &dfX, double &dfY)
859
1.36M
{
860
1.36M
    if (m_poDS->m_bGeoreferenced)
861
935k
    {
862
935k
        dfX = m_poDS->m_dfTopX + nX * m_poDS->m_dfTileDimX / m_nExtent;
863
935k
        dfY = m_poDS->m_dfTopY - nY * m_poDS->m_dfTileDimY / m_nExtent;
864
935k
    }
865
425k
    else
866
425k
    {
867
425k
        dfX = nX;
868
425k
        dfY = static_cast<double>(m_nExtent) - nY;
869
425k
    }
870
1.36M
}
871
872
/************************************************************************/
873
/*                     AddWithOverflowAccepted()                        */
874
/************************************************************************/
875
876
CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
877
static int AddWithOverflowAccepted(int a, int b)
878
917k
{
879
    // In fact in normal situations a+b should not overflow. That can only
880
    // happen with corrupted datasets. But we don't really want to add code
881
    // to detect that situation, so basically this is just a trick to perform
882
    // the addition without the various sanitizers to yell about the overflow.
883
    //
884
    // Assumes complement-to-two signed integer representation and that
885
    // the compiler will safely cast a big unsigned to negative integer.
886
917k
    return static_cast<int>(static_cast<unsigned>(a) +
887
917k
                            static_cast<unsigned>(b));
888
917k
}
889
890
/************************************************************************/
891
/*                           ParseGeometry()                            */
892
/************************************************************************/
893
894
OGRGeometry *OGRMVTLayer::ParseGeometry(unsigned int nGeomType,
895
                                        const GByte *pabyDataGeometryEnd)
896
45.1k
{
897
45.1k
    OGRMultiPoint *poMultiPoint = nullptr;
898
45.1k
    OGRMultiLineString *poMultiLS = nullptr;
899
45.1k
    OGRLineString *poLine = nullptr;
900
45.1k
    OGRMultiPolygon *poMultiPoly = nullptr;
901
45.1k
    OGRPolygon *poPoly = nullptr;
902
45.1k
    OGRLinearRing *poRing = nullptr;
903
904
45.1k
    try
905
45.1k
    {
906
45.1k
        if (nGeomType == knGEOM_TYPE_POINT)
907
14.7k
        {
908
14.7k
            unsigned int nCmdCountCombined = 0;
909
14.7k
            unsigned int nCount;
910
14.7k
            READ_VARUINT32(m_pabyDataCur, pabyDataGeometryEnd,
911
14.7k
                           nCmdCountCombined);
912
14.5k
            nCount = GetCmdCount(nCmdCountCombined);
913
14.5k
            if (GetCmdId(nCmdCountCombined) == knCMD_MOVETO && nCount == 1)
914
12.4k
            {
915
12.4k
                int nX = 0;
916
12.4k
                int nY = 0;
917
12.4k
                READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nX);
918
12.4k
                READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nY);
919
8.64k
                double dfX;
920
8.64k
                double dfY;
921
8.64k
                GetXY(nX, nY, dfX, dfY);
922
8.64k
                OGRPoint *poPoint = new OGRPoint(dfX, dfY);
923
8.64k
                if (m_poFeatureDefn->GetGeomType() == wkbMultiPoint)
924
223
                {
925
223
                    poMultiPoint = new OGRMultiPoint();
926
223
                    poMultiPoint->addGeometryDirectly(poPoint);
927
223
                    return poMultiPoint;
928
223
                }
929
8.42k
                else
930
8.42k
                {
931
8.42k
                    return poPoint;
932
8.42k
                }
933
8.64k
            }
934
2.10k
            else if (GetCmdId(nCmdCountCombined) == knCMD_MOVETO && nCount > 1)
935
1.96k
            {
936
1.96k
                int nX = 0;
937
1.96k
                int nY = 0;
938
1.96k
                poMultiPoint = new OGRMultiPoint();
939
3.17k
                for (unsigned i = 0; i < nCount; i++)
940
3.00k
                {
941
3.00k
                    int nDX = 0;
942
3.00k
                    int nDY = 0;
943
3.00k
                    READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDX);
944
2.53k
                    READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDY);
945
                    // if( nDX != 0 || nDY != 0 )
946
1.22k
                    {
947
1.22k
                        nX = AddWithOverflowAccepted(nX, nDX);
948
1.22k
                        nY = AddWithOverflowAccepted(nY, nDY);
949
1.22k
                        double dfX;
950
1.22k
                        double dfY;
951
1.22k
                        GetXY(nX, nY, dfX, dfY);
952
1.22k
                        OGRPoint *poPoint = new OGRPoint(dfX, dfY);
953
1.22k
                        if (i == 0 && nCount == 2 &&
954
1.22k
                            m_pabyDataCur == pabyDataGeometryEnd)
955
20
                        {
956
                            // Current versions of Mapserver at time of writing
957
                            // wrongly encode a point with nCount = 2
958
20
                            static bool bWarned = false;
959
20
                            if (!bWarned)
960
1
                            {
961
1
                                CPLDebug(
962
1
                                    "MVT",
963
1
                                    "Reading likely a broken point as "
964
1
                                    "produced by some versions of Mapserver");
965
1
                                bWarned = true;
966
1
                            }
967
20
                            delete poMultiPoint;
968
20
                            return poPoint;
969
20
                        }
970
1.20k
                        poMultiPoint->addGeometryDirectly(poPoint);
971
1.20k
                    }
972
1.20k
                }
973
163
                return poMultiPoint;
974
1.96k
            }
975
14.5k
        }
976
30.3k
        else if (nGeomType == knGEOM_TYPE_LINESTRING)
977
8.43k
        {
978
8.43k
            int nX = 0;
979
8.43k
            int nY = 0;
980
27.7k
            while (m_pabyDataCur < pabyDataGeometryEnd)
981
27.3k
            {
982
27.3k
                unsigned int nCmdCountCombined = 0;
983
27.3k
                unsigned int nLineToCount;
984
                // Should be a moveto
985
27.3k
                SKIP_VARINT(m_pabyDataCur, pabyDataGeometryEnd);
986
27.3k
                int nDX = 0;
987
27.3k
                int nDY = 0;
988
27.3k
                READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDX);
989
23.7k
                READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDY);
990
21.7k
                nX = AddWithOverflowAccepted(nX, nDX);
991
21.7k
                nY = AddWithOverflowAccepted(nY, nDY);
992
21.7k
                double dfX;
993
21.7k
                double dfY;
994
21.7k
                GetXY(nX, nY, dfX, dfY);
995
21.7k
                if (poLine != nullptr)
996
13.4k
                {
997
13.4k
                    if (poMultiLS == nullptr)
998
6.00k
                    {
999
6.00k
                        poMultiLS = new OGRMultiLineString();
1000
6.00k
                        poMultiLS->addGeometryDirectly(poLine);
1001
6.00k
                    }
1002
13.4k
                    poLine = new OGRLineString();
1003
13.4k
                    poMultiLS->addGeometryDirectly(poLine);
1004
13.4k
                }
1005
8.26k
                else
1006
8.26k
                {
1007
8.26k
                    poLine = new OGRLineString();
1008
8.26k
                }
1009
21.7k
                poLine->addPoint(dfX, dfY);
1010
21.7k
                READ_VARUINT32(m_pabyDataCur, pabyDataGeometryEnd,
1011
21.7k
                               nCmdCountCombined);
1012
21.6k
                nLineToCount = GetCmdCount(nCmdCountCombined);
1013
112k
                for (unsigned i = 0; i < nLineToCount; i++)
1014
93.3k
                {
1015
93.3k
                    READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDX);
1016
92.8k
                    READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDY);
1017
                    // if( nDX != 0 || nDY != 0 )
1018
91.0k
                    {
1019
91.0k
                        nX = AddWithOverflowAccepted(nX, nDX);
1020
91.0k
                        nY = AddWithOverflowAccepted(nY, nDY);
1021
91.0k
                        GetXY(nX, nY, dfX, dfY);
1022
91.0k
                        poLine->addPoint(dfX, dfY);
1023
91.0k
                    }
1024
91.0k
                }
1025
21.6k
            }
1026
405
            if (poMultiLS == nullptr && poLine != nullptr &&
1027
405
                m_poFeatureDefn->GetGeomType() == wkbMultiLineString)
1028
359
            {
1029
359
                poMultiLS = new OGRMultiLineString();
1030
359
                poMultiLS->addGeometryDirectly(poLine);
1031
359
            }
1032
405
            if (poMultiLS)
1033
373
            {
1034
373
                return poMultiLS;
1035
373
            }
1036
32
            else
1037
32
            {
1038
32
                return poLine;
1039
32
            }
1040
405
        }
1041
21.9k
        else if (nGeomType == knGEOM_TYPE_POLYGON)
1042
21.9k
        {
1043
21.9k
            int externalIsClockwise = 0;
1044
21.9k
            int nX = 0;
1045
21.9k
            int nY = 0;
1046
77.2k
            while (m_pabyDataCur < pabyDataGeometryEnd)
1047
67.2k
            {
1048
67.2k
                unsigned int nCmdCountCombined = 0;
1049
67.2k
                unsigned int nLineToCount;
1050
                // Should be a moveto
1051
67.2k
                SKIP_VARINT(m_pabyDataCur, pabyDataGeometryEnd);
1052
67.2k
                int nDX = 0;
1053
67.2k
                int nDY = 0;
1054
67.2k
                READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDX);
1055
64.5k
                READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDY);
1056
64.1k
                nX = AddWithOverflowAccepted(nX, nDX);
1057
64.1k
                nY = AddWithOverflowAccepted(nY, nDY);
1058
64.1k
                double dfX;
1059
64.1k
                double dfY;
1060
64.1k
                GetXY(nX, nY, dfX, dfY);
1061
64.1k
                poRing = new OGRLinearRing();
1062
64.1k
                poRing->addPoint(dfX, dfY);
1063
64.1k
                READ_VARUINT32(m_pabyDataCur, pabyDataGeometryEnd,
1064
64.1k
                               nCmdCountCombined);
1065
63.9k
                nLineToCount = GetCmdCount(nCmdCountCombined);
1066
344k
                for (unsigned i = 0; i < nLineToCount; i++)
1067
288k
                {
1068
288k
                    READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDX);
1069
281k
                    READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDY);
1070
                    // if( nDX != 0 || nDY != 0 )
1071
280k
                    {
1072
280k
                        nX = AddWithOverflowAccepted(nX, nDX);
1073
280k
                        nY = AddWithOverflowAccepted(nY, nDY);
1074
280k
                        GetXY(nX, nY, dfX, dfY);
1075
280k
                        poRing->addPoint(dfX, dfY);
1076
280k
                    }
1077
280k
                }
1078
                // Should be a closepath
1079
55.8k
                SKIP_VARINT(m_pabyDataCur, pabyDataGeometryEnd);
1080
55.3k
                poRing->closeRings();
1081
55.3k
                if (poPoly == nullptr)
1082
21.0k
                {
1083
21.0k
                    poPoly = new OGRPolygon();
1084
21.0k
                    poPoly->addRingDirectly(poRing);
1085
21.0k
                    externalIsClockwise = poRing->isClockwise();
1086
21.0k
                    if (!externalIsClockwise && m_bEnforceExternalIsClockwise)
1087
0
                    {
1088
0
                        CPLError(CE_Failure, CPLE_AppDefined,
1089
0
                                 "Bad ring orientation detected");
1090
0
                        delete poPoly;
1091
0
                        delete poMultiPoly;
1092
0
                        return nullptr;
1093
0
                    }
1094
21.0k
                }
1095
34.2k
                else
1096
34.2k
                {
1097
                    // Detect change of winding order to figure out if this is
1098
                    // an interior or exterior ring
1099
34.2k
                    if (externalIsClockwise != poRing->isClockwise())
1100
9.72k
                    {
1101
9.72k
                        poPoly->addRingDirectly(poRing);
1102
9.72k
                    }
1103
24.5k
                    else
1104
24.5k
                    {
1105
24.5k
                        if (poMultiPoly == nullptr)
1106
18.3k
                        {
1107
18.3k
                            poMultiPoly = new OGRMultiPolygon();
1108
18.3k
                            poMultiPoly->addGeometryDirectly(poPoly);
1109
18.3k
                        }
1110
1111
24.5k
                        poPoly = new OGRPolygon();
1112
24.5k
                        poMultiPoly->addGeometryDirectly(poPoly);
1113
24.5k
                        poPoly->addRingDirectly(poRing);
1114
24.5k
                    }
1115
34.2k
                }
1116
55.3k
                poRing = nullptr;
1117
55.3k
            }
1118
10.0k
            if (poMultiPoly == nullptr && poPoly != nullptr &&
1119
10.0k
                m_poFeatureDefn->GetGeomType() == wkbMultiPolygon)
1120
34
            {
1121
34
                poMultiPoly = new OGRMultiPolygon();
1122
34
                poMultiPoly->addGeometryDirectly(poPoly);
1123
34
            }
1124
10.0k
            if (poMultiPoly)
1125
9.96k
            {
1126
9.96k
                return poMultiPoly;
1127
9.96k
            }
1128
39
            else
1129
39
            {
1130
39
                return poPoly;
1131
39
            }
1132
10.0k
        }
1133
45.1k
    }
1134
45.1k
    catch (const GPBException &e)
1135
45.1k
    {
1136
25.7k
        CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1137
25.7k
        delete poMultiPoint;
1138
25.7k
        if (poMultiPoly)
1139
8.39k
            delete poMultiPoly;
1140
17.3k
        else if (poPoly)
1141
2.71k
            delete poPoly;
1142
25.7k
        if (poMultiLS)
1143
5.98k
            delete poMultiLS;
1144
19.7k
        else if (poLine)
1145
1.90k
            delete poLine;
1146
25.7k
        delete poRing;
1147
25.7k
    }
1148
25.8k
    return nullptr;
1149
45.1k
}
1150
1151
/************************************************************************/
1152
/*                      SanitizeClippedGeometry()                       */
1153
/************************************************************************/
1154
1155
void OGRMVTLayer::SanitizeClippedGeometry(OGRGeometry *&poGeom)
1156
0
{
1157
0
    OGRwkbGeometryType eInGeomType = wkbFlatten(poGeom->getGeometryType());
1158
0
    const OGRwkbGeometryType eLayerGeomType = GetGeomType();
1159
0
    if (eLayerGeomType == wkbUnknown)
1160
0
    {
1161
0
        return;
1162
0
    }
1163
1164
    // GEOS intersection may return a mix of polygon and linestrings when
1165
    // intersection a multipolygon and a polygon
1166
0
    if (eInGeomType == wkbGeometryCollection)
1167
0
    {
1168
0
        OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
1169
0
        OGRGeometry *poTargetSingleGeom = nullptr;
1170
0
        OGRGeometryCollection *poTargetGC = nullptr;
1171
0
        OGRwkbGeometryType ePartGeom;
1172
0
        if (eLayerGeomType == wkbPoint || eLayerGeomType == wkbMultiPoint)
1173
0
        {
1174
0
            ePartGeom = wkbPoint;
1175
0
        }
1176
0
        else if (eLayerGeomType == wkbLineString ||
1177
0
                 eLayerGeomType == wkbMultiLineString)
1178
0
        {
1179
0
            ePartGeom = wkbLineString;
1180
0
        }
1181
0
        else
1182
0
        {
1183
0
            ePartGeom = wkbPolygon;
1184
0
        }
1185
0
        for (auto &&poSubGeom : poGC)
1186
0
        {
1187
0
            if (wkbFlatten(poSubGeom->getGeometryType()) == ePartGeom)
1188
0
            {
1189
0
                if (poTargetSingleGeom != nullptr)
1190
0
                {
1191
0
                    if (poTargetGC == nullptr)
1192
0
                    {
1193
0
                        poTargetGC = OGRGeometryFactory::createGeometry(
1194
0
                                         OGR_GT_GetCollection(ePartGeom))
1195
0
                                         ->toGeometryCollection();
1196
0
                        poGeom = poTargetGC;
1197
0
                        poTargetGC->addGeometryDirectly(poTargetSingleGeom);
1198
0
                    }
1199
1200
0
                    poTargetGC->addGeometry(poSubGeom);
1201
0
                }
1202
0
                else
1203
0
                {
1204
0
                    poTargetSingleGeom = poSubGeom->clone();
1205
0
                    poGeom = poTargetSingleGeom;
1206
0
                }
1207
0
            }
1208
0
        }
1209
0
        if (poGeom != poGC)
1210
0
        {
1211
0
            delete poGC;
1212
0
        }
1213
0
        eInGeomType = wkbFlatten(poGeom->getGeometryType());
1214
0
    }
1215
1216
    // Wrap single into multi if requested by the layer geometry type
1217
0
    if (OGR_GT_GetCollection(eInGeomType) == eLayerGeomType)
1218
0
    {
1219
0
        OGRGeometryCollection *poGC =
1220
0
            OGRGeometryFactory::createGeometry(eLayerGeomType)
1221
0
                ->toGeometryCollection();
1222
0
        poGC->addGeometryDirectly(poGeom);
1223
0
        poGeom = poGC;
1224
0
        return;
1225
0
    }
1226
0
}
1227
1228
/************************************************************************/
1229
/*                         GetNextRawFeature()                          */
1230
/************************************************************************/
1231
1232
OGRFeature *OGRMVTLayer::GetNextRawFeature()
1233
114k
{
1234
114k
    if (m_pabyDataCur == nullptr || m_pabyDataCur >= m_pabyDataEnd || m_bError)
1235
55.9k
    {
1236
55.9k
        return nullptr;
1237
55.9k
    }
1238
1239
58.2k
    unsigned int nKey = 0;
1240
58.2k
    const GByte *pabyDataLimit = m_pabyDataEnd;
1241
58.2k
    OGRFeature *poFeature = nullptr;
1242
58.2k
    unsigned int nFeatureLength = 0;
1243
58.2k
    unsigned int nGeomType = 0;
1244
1245
58.2k
    try
1246
58.2k
    {
1247
58.2k
        while (true)
1248
58.2k
        {
1249
58.2k
            bool bOK = true;
1250
1251
498k
            while (m_pabyDataCur < pabyDataLimit)
1252
496k
            {
1253
496k
                READ_VARUINT32(m_pabyDataCur, pabyDataLimit, nKey);
1254
496k
                if (nKey == MAKE_KEY(knLAYER_FEATURES, WT_DATA))
1255
56.5k
                {
1256
56.5k
                    poFeature = new OGRFeature(m_poFeatureDefn);
1257
56.5k
                    break;
1258
56.5k
                }
1259
440k
                else
1260
440k
                {
1261
440k
                    SKIP_UNKNOWN_FIELD(m_pabyDataCur, pabyDataLimit, FALSE);
1262
440k
                }
1263
496k
            }
1264
1265
58.2k
            if (poFeature == nullptr)
1266
1.67k
                return nullptr;
1267
1268
113k
            READ_SIZE(m_pabyDataCur, pabyDataLimit, nFeatureLength);
1269
56.5k
            const GByte *pabyDataFeatureEnd = m_pabyDataCur + nFeatureLength;
1270
513k
            while (m_pabyDataCur < pabyDataFeatureEnd)
1271
460k
            {
1272
460k
                READ_VARUINT32(m_pabyDataCur, pabyDataFeatureEnd, nKey);
1273
460k
                if (nKey == MAKE_KEY(knFEATURE_ID, WT_VARINT))
1274
3.23k
                {
1275
3.23k
                    GUIntBig nID = 0;
1276
3.23k
                    READ_VARUINT64(m_pabyDataCur, pabyDataFeatureEnd, nID);
1277
3.23k
                    poFeature->SetField("mvt_id", static_cast<GIntBig>(nID));
1278
3.23k
                }
1279
456k
                else if (nKey == MAKE_KEY(knFEATURE_TYPE, WT_VARINT))
1280
92.3k
                {
1281
92.3k
                    READ_VARUINT32(m_pabyDataCur, pabyDataFeatureEnd,
1282
92.3k
                                   nGeomType);
1283
92.3k
                }
1284
364k
                else if (nKey == MAKE_KEY(knFEATURE_TAGS, WT_DATA))
1285
30.4k
                {
1286
30.4k
                    unsigned int nTagsSize = 0;
1287
30.4k
                    READ_SIZE(m_pabyDataCur, pabyDataFeatureEnd, nTagsSize);
1288
30.2k
                    const GByte *pabyDataTagsEnd = m_pabyDataCur + nTagsSize;
1289
67.4k
                    while (m_pabyDataCur < pabyDataTagsEnd)
1290
37.4k
                    {
1291
37.4k
                        unsigned int nKeyIdx = 0;
1292
37.4k
                        unsigned int nValIdx = 0;
1293
37.4k
                        READ_VARUINT32(m_pabyDataCur, pabyDataTagsEnd, nKeyIdx);
1294
37.4k
                        READ_VARUINT32(m_pabyDataCur, pabyDataTagsEnd, nValIdx);
1295
37.1k
                        if (nKeyIdx < m_aosKeys.size() &&
1296
37.1k
                            nValIdx < m_asValues.size())
1297
16.1k
                        {
1298
16.1k
                            const int nFieldIdx =
1299
16.1k
                                m_poFeatureDefn->GetFieldIndex(
1300
16.1k
                                    m_aosKeys[nKeyIdx]);
1301
16.1k
                            if (nFieldIdx >= 0)
1302
11.9k
                            {
1303
11.9k
                                if (m_asValues[nValIdx].eType == OFTString)
1304
11.9k
                                {
1305
11.9k
                                    poFeature->SetField(
1306
11.9k
                                        nFieldIdx,
1307
11.9k
                                        m_asValues[nValIdx].sValue.String);
1308
11.9k
                                }
1309
47
                                else if (m_asValues[nValIdx].eType ==
1310
47
                                         OFTInteger)
1311
17
                                {
1312
17
                                    poFeature->SetField(
1313
17
                                        nFieldIdx,
1314
17
                                        m_asValues[nValIdx].sValue.Integer);
1315
17
                                }
1316
30
                                else if (m_asValues[nValIdx].eType ==
1317
30
                                         OFTInteger64)
1318
26
                                {
1319
26
                                    poFeature->SetField(
1320
26
                                        nFieldIdx,
1321
26
                                        m_asValues[nValIdx].sValue.Integer64);
1322
26
                                }
1323
4
                                else if (m_asValues[nValIdx].eType == OFTReal)
1324
4
                                {
1325
4
                                    poFeature->SetField(
1326
4
                                        nFieldIdx,
1327
4
                                        m_asValues[nValIdx].sValue.Real);
1328
4
                                }
1329
11.9k
                            }
1330
16.1k
                        }
1331
37.1k
                    }
1332
30.2k
                }
1333
334k
                else if (nKey == MAKE_KEY(knFEATURE_GEOMETRY, WT_DATA) &&
1334
334k
                         nGeomType >= 1 && nGeomType <= 3)
1335
45.1k
                {
1336
45.1k
                    unsigned int nGeometrySize = 0;
1337
45.1k
                    READ_SIZE(m_pabyDataCur, pabyDataFeatureEnd, nGeometrySize);
1338
45.1k
                    const GByte *pabyDataGeometryEnd =
1339
45.1k
                        m_pabyDataCur + nGeometrySize;
1340
45.1k
                    OGRGeometry *poGeom =
1341
45.1k
                        ParseGeometry(nGeomType, pabyDataGeometryEnd);
1342
45.1k
                    if (poGeom)
1343
19.1k
                    {
1344
                        // Clip geometry to tile extent if requested
1345
19.1k
                        if (m_poDS->m_bClip && OGRGeometryFactory::haveGEOS())
1346
0
                        {
1347
0
                            OGREnvelope sEnvelope;
1348
0
                            poGeom->getEnvelope(&sEnvelope);
1349
0
                            if (sEnvelope.MinX >= m_dfTileMinX &&
1350
0
                                sEnvelope.MinY >= m_dfTileMinY &&
1351
0
                                sEnvelope.MaxX <= m_dfTileMaxX &&
1352
0
                                sEnvelope.MaxY <= m_dfTileMaxY)
1353
0
                            {
1354
                                // do nothing
1355
0
                            }
1356
0
                            else if (sEnvelope.MinX < m_dfTileMaxX &&
1357
0
                                     sEnvelope.MinY < m_dfTileMaxY &&
1358
0
                                     sEnvelope.MaxX > m_dfTileMinX &&
1359
0
                                     sEnvelope.MaxY > m_dfTileMinY)
1360
0
                            {
1361
0
                                OGRGeometry *poClipped =
1362
0
                                    poGeom->Intersection(&m_oClipPoly);
1363
0
                                if (poClipped)
1364
0
                                {
1365
0
                                    SanitizeClippedGeometry(poClipped);
1366
0
                                    if (poClipped->IsEmpty())
1367
0
                                    {
1368
0
                                        delete poClipped;
1369
0
                                        bOK = false;
1370
0
                                    }
1371
0
                                    else
1372
0
                                    {
1373
0
                                        poClipped->assignSpatialReference(
1374
0
                                            GetSpatialRef());
1375
0
                                        poFeature->SetGeometryDirectly(
1376
0
                                            poClipped);
1377
0
                                        delete poGeom;
1378
0
                                        poGeom = nullptr;
1379
0
                                    }
1380
0
                                }
1381
0
                            }
1382
0
                            else
1383
0
                            {
1384
0
                                bOK = false;
1385
0
                            }
1386
0
                        }
1387
1388
19.1k
                        if (poGeom)
1389
19.1k
                        {
1390
19.1k
                            poGeom->assignSpatialReference(GetSpatialRef());
1391
19.1k
                            poFeature->SetGeometryDirectly(poGeom);
1392
19.1k
                        }
1393
19.1k
                    }
1394
1395
45.1k
                    m_pabyDataCur = pabyDataGeometryEnd;
1396
45.1k
                }
1397
288k
                else
1398
288k
                {
1399
288k
                    SKIP_UNKNOWN_FIELD(m_pabyDataCur, pabyDataFeatureEnd,
1400
286k
                                       FALSE);
1401
286k
                }
1402
460k
            }
1403
53.8k
            m_pabyDataCur = pabyDataFeatureEnd;
1404
1405
53.8k
            if (bOK)
1406
53.8k
            {
1407
53.8k
                poFeature->SetFID(m_nFID);
1408
53.8k
                m_nFID++;
1409
53.8k
                return poFeature;
1410
53.8k
            }
1411
0
            else
1412
0
            {
1413
0
                delete poFeature;
1414
0
                poFeature = nullptr;
1415
0
            }
1416
53.8k
        }
1417
58.2k
    }
1418
58.2k
    catch (const GPBException &e)
1419
58.2k
    {
1420
2.77k
        CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1421
2.77k
        delete poFeature;
1422
2.77k
        return nullptr;
1423
2.77k
    }
1424
58.2k
}
1425
1426
/************************************************************************/
1427
/*                             GetDataset()                             */
1428
/************************************************************************/
1429
1430
GDALDataset *OGRMVTLayer::GetDataset()
1431
0
{
1432
0
    return m_poDS;
1433
0
}
1434
1435
/************************************************************************/
1436
/*                         StripDummyEntries()                           */
1437
/************************************************************************/
1438
1439
static CPLStringList StripDummyEntries(const CPLStringList &aosInput)
1440
74
{
1441
74
    CPLStringList aosOutput;
1442
74
    for (int i = 0; i < aosInput.Count(); i++)
1443
0
    {
1444
0
        if (aosInput[i] != CPLString(".") && aosInput[i] != CPLString("..") &&
1445
0
            CPLString(aosInput[i]).find(".properties") == std::string::npos)
1446
0
        {
1447
0
            aosOutput.AddString(aosInput[i]);
1448
0
        }
1449
0
    }
1450
74
    return aosOutput.Sort();
1451
74
}
1452
1453
/************************************************************************/
1454
/*                       OGRMVTDirectoryLayer()                         */
1455
/************************************************************************/
1456
1457
OGRMVTDirectoryLayer::OGRMVTDirectoryLayer(
1458
    OGRMVTDataset *poDS, const char *pszLayerName, const char *pszDirectoryName,
1459
    const CPLJSONObject &oFields, const CPLJSONArray &oAttributesFromTileStats,
1460
    bool bJsonField, OGRwkbGeometryType eGeomType, const OGREnvelope *psExtent)
1461
0
    : m_poDS(poDS), m_osDirName(pszDirectoryName), m_bJsonField(bJsonField)
1462
0
{
1463
0
    m_poFeatureDefn = new OGRFeatureDefn(pszLayerName);
1464
0
    SetDescription(m_poFeatureDefn->GetName());
1465
0
    m_poFeatureDefn->SetGeomType(eGeomType);
1466
0
    m_poFeatureDefn->Reference();
1467
1468
0
    m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poDS->GetSRS());
1469
1470
0
    if (m_bJsonField)
1471
0
    {
1472
0
        OGRFieldDefn oFieldDefnId("mvt_id", OFTInteger64);
1473
0
        m_poFeatureDefn->AddFieldDefn(&oFieldDefnId);
1474
0
    }
1475
0
    else
1476
0
    {
1477
0
        InitFields(oFields, oAttributesFromTileStats);
1478
0
    }
1479
1480
0
    m_nZ = atoi(CPLGetFilename(m_osDirName));
1481
0
    SetMetadataItem("ZOOM_LEVEL", CPLSPrintf("%d", m_nZ));
1482
0
    m_bUseReadDir = CPLTestBool(CPLGetConfigOption(
1483
0
        "MVT_USE_READDIR", (!STARTS_WITH(m_osDirName, "/vsicurl") &&
1484
0
                            !STARTS_WITH(m_osDirName, "http://") &&
1485
0
                            !STARTS_WITH(m_osDirName, "https://"))
1486
0
                               ? "YES"
1487
0
                               : "NO"));
1488
0
    if (m_bUseReadDir)
1489
0
    {
1490
0
        m_aosDirContent = VSIReadDirEx(m_osDirName, knMAX_FILES_PER_DIR);
1491
0
        if (m_aosDirContent.Count() >= knMAX_FILES_PER_DIR)
1492
0
        {
1493
0
            CPLDebug("MVT", "Disabling readdir");
1494
0
            m_aosDirContent.Clear();
1495
0
            m_bUseReadDir = false;
1496
0
        }
1497
0
        m_aosDirContent = StripDummyEntries(m_aosDirContent);
1498
0
    }
1499
0
    OGRMVTDirectoryLayer::ResetReading();
1500
1501
0
    if (psExtent)
1502
0
    {
1503
0
        m_sExtent = *psExtent;
1504
0
    }
1505
1506
0
    OGRMVTDirectoryLayer::SetSpatialFilter(nullptr);
1507
1508
    // If the metadata contains an empty fields object, this may be a sign
1509
    // that it doesn't know the schema. In that case check if a tile has
1510
    // attributes, and in that case create a json field.
1511
0
    if (!m_bJsonField && oFields.IsValid() && oFields.GetChildren().empty())
1512
0
    {
1513
0
        m_bJsonField = true;
1514
0
        OpenTileIfNeeded();
1515
0
        m_bJsonField = false;
1516
1517
0
        if (m_poCurrentTile)
1518
0
        {
1519
0
            OGRLayer *poUnderlyingLayer =
1520
0
                m_poCurrentTile->GetLayerByName(GetName());
1521
            // There is at least the mvt_id field
1522
0
            if (poUnderlyingLayer->GetLayerDefn()->GetFieldCount() > 1)
1523
0
            {
1524
0
                m_bJsonField = true;
1525
0
            }
1526
0
        }
1527
0
        OGRMVTDirectoryLayer::ResetReading();
1528
0
    }
1529
1530
0
    if (m_bJsonField)
1531
0
    {
1532
0
        OGRFieldDefn oFieldDefn("json", OFTString);
1533
0
        m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
1534
0
    }
1535
0
}
1536
1537
/************************************************************************/
1538
/*                      ~OGRMVTDirectoryLayer()                         */
1539
/************************************************************************/
1540
1541
OGRMVTDirectoryLayer::~OGRMVTDirectoryLayer()
1542
0
{
1543
0
    delete m_poCurrentTile;
1544
0
}
1545
1546
/************************************************************************/
1547
/*                          ResetReading()                              */
1548
/************************************************************************/
1549
1550
void OGRMVTDirectoryLayer::ResetReading()
1551
0
{
1552
0
    m_bEOF = false;
1553
0
    m_nXIndex = -1;
1554
0
    m_nYIndex = -1;
1555
0
    delete m_poCurrentTile;
1556
0
    m_poCurrentTile = nullptr;
1557
0
}
1558
1559
/************************************************************************/
1560
/*                            IsBetween()                               */
1561
/************************************************************************/
1562
1563
static bool IsBetween(int nVal, int nMin, int nMax)
1564
0
{
1565
0
    return nVal >= nMin && nVal <= nMax;
1566
0
}
1567
1568
/************************************************************************/
1569
/*                          ReadNewSubDir()                             */
1570
/************************************************************************/
1571
1572
void OGRMVTDirectoryLayer::ReadNewSubDir()
1573
0
{
1574
0
    delete m_poCurrentTile;
1575
0
    m_poCurrentTile = nullptr;
1576
0
    if (m_bUseReadDir || !m_aosDirContent.empty())
1577
0
    {
1578
0
        while (
1579
0
            m_nXIndex < m_aosDirContent.Count() &&
1580
0
            (CPLGetValueType(m_aosDirContent[m_nXIndex]) != CPL_VALUE_INTEGER ||
1581
0
             !IsBetween(atoi(m_aosDirContent[m_nXIndex]), m_nFilterMinX,
1582
0
                        m_nFilterMaxX)))
1583
0
        {
1584
0
            m_nXIndex++;
1585
0
        }
1586
0
    }
1587
0
    else
1588
0
    {
1589
0
        if (m_nXIndex < m_nFilterMinX)
1590
0
            m_nXIndex = m_nFilterMinX;
1591
0
        else if (m_nXIndex > m_nFilterMaxX)
1592
0
            m_nXIndex = (1 << m_nZ);
1593
0
    }
1594
0
    if (m_nXIndex < ((m_bUseReadDir || !m_aosDirContent.empty())
1595
0
                         ? m_aosDirContent.Count()
1596
0
                         : (1 << m_nZ)))
1597
0
    {
1598
0
        m_aosSubDirName =
1599
0
            CPLFormFilenameSafe(m_osDirName,
1600
0
                                (m_bUseReadDir || !m_aosDirContent.empty())
1601
0
                                    ? m_aosDirContent[m_nXIndex]
1602
0
                                    : CPLSPrintf("%d", m_nXIndex),
1603
0
                                nullptr);
1604
0
        if (m_bUseReadDir)
1605
0
        {
1606
0
            m_aosSubDirContent =
1607
0
                VSIReadDirEx(m_aosSubDirName, knMAX_FILES_PER_DIR);
1608
0
            if (m_aosSubDirContent.Count() >= knMAX_FILES_PER_DIR)
1609
0
            {
1610
0
                CPLDebug("MVT", "Disabling readdir");
1611
0
                m_aosSubDirContent.Clear();
1612
0
                m_bUseReadDir = false;
1613
0
            }
1614
0
            m_aosSubDirContent = StripDummyEntries(m_aosSubDirContent);
1615
0
        }
1616
0
        m_nYIndex = -1;
1617
0
        OpenTileIfNeeded();
1618
0
    }
1619
0
    else
1620
0
    {
1621
0
        m_bEOF = true;
1622
0
    }
1623
0
}
1624
1625
/************************************************************************/
1626
/*                            OpenTile()                                */
1627
/************************************************************************/
1628
1629
void OGRMVTDirectoryLayer::OpenTile()
1630
0
{
1631
0
    delete m_poCurrentTile;
1632
0
    m_poCurrentTile = nullptr;
1633
0
    if (m_nYIndex < (m_bUseReadDir ? m_aosSubDirContent.Count() : (1 << m_nZ)))
1634
0
    {
1635
0
        CPLString osFilename = CPLFormFilenameSafe(
1636
0
            m_aosSubDirName,
1637
0
            m_bUseReadDir ? m_aosSubDirContent[m_nYIndex]
1638
0
                          : CPLSPrintf("%d.%s", m_nYIndex,
1639
0
                                       m_poDS->m_osTileExtension.c_str()),
1640
0
            nullptr);
1641
0
        GDALOpenInfo oOpenInfo(("MVT:" + osFilename).c_str(), GA_ReadOnly);
1642
0
        oOpenInfo.papszOpenOptions = CSLSetNameValue(
1643
0
            nullptr, "METADATA_FILE",
1644
0
            m_bJsonField ? "" : m_poDS->m_osMetadataMemFilename.c_str());
1645
0
        oOpenInfo.papszOpenOptions = CSLSetNameValue(
1646
0
            oOpenInfo.papszOpenOptions, "DO_NOT_ERROR_ON_MISSING_TILE", "YES");
1647
0
        m_poCurrentTile =
1648
0
            OGRMVTDataset::Open(&oOpenInfo, /* bRecurseAllowed = */ false);
1649
0
        CSLDestroy(oOpenInfo.papszOpenOptions);
1650
1651
0
        int nX = (m_bUseReadDir || !m_aosDirContent.empty())
1652
0
                     ? atoi(m_aosDirContent[m_nXIndex])
1653
0
                     : m_nXIndex;
1654
0
        int nY =
1655
0
            m_bUseReadDir ? atoi(m_aosSubDirContent[m_nYIndex]) : m_nYIndex;
1656
0
        m_nFIDBase = (static_cast<GIntBig>(nX) << m_nZ) | nY;
1657
0
    }
1658
0
}
1659
1660
/************************************************************************/
1661
/*                         OpenTileIfNeeded()                           */
1662
/************************************************************************/
1663
1664
void OGRMVTDirectoryLayer::OpenTileIfNeeded()
1665
0
{
1666
0
    if (m_nXIndex < 0)
1667
0
    {
1668
0
        m_nXIndex = 0;
1669
0
        ReadNewSubDir();
1670
0
    }
1671
0
    while ((m_poCurrentTile == nullptr && !m_bEOF) ||
1672
0
           (m_poCurrentTile != nullptr &&
1673
0
            m_poCurrentTile->GetLayerByName(GetName()) == nullptr))
1674
0
    {
1675
0
        m_nYIndex++;
1676
0
        if (m_bUseReadDir)
1677
0
        {
1678
0
            while (m_nYIndex < m_aosSubDirContent.Count() &&
1679
0
                   (CPLGetValueType(
1680
0
                        CPLGetBasenameSafe(m_aosSubDirContent[m_nYIndex])
1681
0
                            .c_str()) != CPL_VALUE_INTEGER ||
1682
0
                    !IsBetween(atoi(m_aosSubDirContent[m_nYIndex]),
1683
0
                               m_nFilterMinY, m_nFilterMaxY)))
1684
0
            {
1685
0
                m_nYIndex++;
1686
0
            }
1687
0
        }
1688
0
        else
1689
0
        {
1690
0
            if (m_nYIndex < m_nFilterMinY)
1691
0
                m_nYIndex = m_nFilterMinY;
1692
0
            else if (m_nYIndex > m_nFilterMaxY)
1693
0
                m_nYIndex = (1 << m_nZ);
1694
0
        }
1695
0
        if (m_nYIndex ==
1696
0
            (m_bUseReadDir ? m_aosSubDirContent.Count() : (1 << m_nZ)))
1697
0
        {
1698
0
            m_nXIndex++;
1699
0
            ReadNewSubDir();
1700
0
        }
1701
0
        else
1702
0
        {
1703
0
            OpenTile();
1704
0
        }
1705
0
    }
1706
0
}
1707
1708
/************************************************************************/
1709
/*                         GetFeatureCount()                            */
1710
/************************************************************************/
1711
1712
GIntBig OGRMVTDirectoryLayer::GetFeatureCount(int bForce)
1713
0
{
1714
0
    if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
1715
0
    {
1716
0
        GIntBig nFeatureCount = 0;
1717
0
        ResetReading();
1718
0
        while (true)
1719
0
        {
1720
0
            OpenTileIfNeeded();
1721
0
            if (m_poCurrentTile == nullptr)
1722
0
                break;
1723
0
            OGRLayer *poUnderlyingLayer =
1724
0
                m_poCurrentTile->GetLayerByName(GetName());
1725
0
            nFeatureCount += poUnderlyingLayer->GetFeatureCount(bForce);
1726
0
            delete m_poCurrentTile;
1727
0
            m_poCurrentTile = nullptr;
1728
0
        }
1729
0
        ResetReading();
1730
0
        return nFeatureCount;
1731
0
    }
1732
0
    return OGRLayer::GetFeatureCount(bForce);
1733
0
}
1734
1735
/************************************************************************/
1736
/*                         ISetSpatialFilter()                          */
1737
/************************************************************************/
1738
1739
OGRErr OGRMVTDirectoryLayer::ISetSpatialFilter(int iGeomField,
1740
                                               const OGRGeometry *poGeomIn)
1741
0
{
1742
0
    OGRLayer::ISetSpatialFilter(iGeomField, poGeomIn);
1743
1744
0
    OGREnvelope sEnvelope;
1745
0
    if (m_poFilterGeom != nullptr)
1746
0
        sEnvelope = m_sFilterEnvelope;
1747
0
    if (m_sExtent.IsInit())
1748
0
    {
1749
0
        if (sEnvelope.IsInit())
1750
0
            sEnvelope.Intersect(m_sExtent);
1751
0
        else
1752
0
            sEnvelope = m_sExtent;
1753
0
    }
1754
1755
0
    if (sEnvelope.IsInit() && sEnvelope.MinX >= -10 * m_poDS->GetTileDim0() &&
1756
0
        sEnvelope.MinY >= -10 * m_poDS->GetTileDim0() &&
1757
0
        sEnvelope.MaxX <=
1758
0
            10 * m_poDS->GetTileDim0() * m_poDS->GetTileMatrixWidth0() &&
1759
0
        sEnvelope.MaxY <=
1760
0
            10 * m_poDS->GetTileDim0() * m_poDS->GetTileMatrixHeight0())
1761
0
    {
1762
0
        const double dfTileDim = m_poDS->GetTileDim0() / (1 << m_nZ);
1763
0
        m_nFilterMinX = std::max(
1764
0
            0, static_cast<int>(floor(
1765
0
                   (sEnvelope.MinX - m_poDS->GetTopXOrigin()) / dfTileDim)));
1766
0
        m_nFilterMinY = std::max(
1767
0
            0, static_cast<int>(floor(
1768
0
                   (m_poDS->GetTopYOrigin() - sEnvelope.MaxY) / dfTileDim)));
1769
0
        m_nFilterMaxX = std::min(
1770
0
            static_cast<int>(
1771
0
                ceil((sEnvelope.MaxX - m_poDS->GetTopXOrigin()) / dfTileDim)),
1772
0
            static_cast<int>(std::min<int64_t>(
1773
0
                INT_MAX, (static_cast<int64_t>(1) << m_nZ) *
1774
0
                                 m_poDS->GetTileMatrixWidth0() -
1775
0
                             1)));
1776
0
        m_nFilterMaxY = std::min(
1777
0
            static_cast<int>(
1778
0
                ceil((m_poDS->GetTopYOrigin() - sEnvelope.MinY) / dfTileDim)),
1779
0
            static_cast<int>(std::min<int64_t>(
1780
0
                INT_MAX, (static_cast<int64_t>(1) << m_nZ) *
1781
0
                                 m_poDS->GetTileMatrixHeight0() -
1782
0
                             1)));
1783
0
    }
1784
0
    else
1785
0
    {
1786
0
        m_nFilterMinX = 0;
1787
0
        m_nFilterMinY = 0;
1788
0
        m_nFilterMaxX = static_cast<int>(
1789
0
            std::min<int64_t>(INT_MAX, (static_cast<int64_t>(1) << m_nZ) *
1790
0
                                               m_poDS->GetTileMatrixWidth0() -
1791
0
                                           1));
1792
0
        m_nFilterMaxY = static_cast<int>(
1793
0
            std::min<int64_t>(INT_MAX, (static_cast<int64_t>(1) << m_nZ) *
1794
0
                                               m_poDS->GetTileMatrixHeight0() -
1795
0
                                           1));
1796
0
    }
1797
1798
0
    return OGRERR_NONE;
1799
0
}
1800
1801
/************************************************************************/
1802
/*                           TestCapability()                           */
1803
/************************************************************************/
1804
1805
int OGRMVTDirectoryLayer::TestCapability(const char *pszCap)
1806
0
{
1807
0
    if (EQUAL(pszCap, OLCFastGetExtent))
1808
0
    {
1809
0
        return TRUE;
1810
0
    }
1811
0
    return OGRMVTLayerBase::TestCapability(pszCap);
1812
0
}
1813
1814
/************************************************************************/
1815
/*                            IGetExtent()                              */
1816
/************************************************************************/
1817
1818
OGRErr OGRMVTDirectoryLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
1819
                                        bool bForce)
1820
0
{
1821
0
    if (m_sExtent.IsInit())
1822
0
    {
1823
0
        *psExtent = m_sExtent;
1824
0
        return OGRERR_NONE;
1825
0
    }
1826
0
    return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
1827
0
}
1828
1829
/************************************************************************/
1830
/*                         CreateFeatureFrom()                          */
1831
/************************************************************************/
1832
1833
OGRFeature *OGRMVTDirectoryLayer::CreateFeatureFrom(OGRFeature *poSrcFeature)
1834
0
{
1835
1836
0
    return OGRMVTCreateFeatureFrom(poSrcFeature, m_poFeatureDefn, m_bJsonField,
1837
0
                                   GetSpatialRef());
1838
0
}
1839
1840
/************************************************************************/
1841
/*                         GetNextRawFeature()                          */
1842
/************************************************************************/
1843
1844
OGRFeature *OGRMVTDirectoryLayer::GetNextRawFeature()
1845
0
{
1846
0
    while (true)
1847
0
    {
1848
0
        OpenTileIfNeeded();
1849
0
        if (m_poCurrentTile == nullptr)
1850
0
            return nullptr;
1851
0
        OGRLayer *poUnderlyingLayer =
1852
0
            m_poCurrentTile->GetLayerByName(GetName());
1853
0
        OGRFeature *poUnderlyingFeature = poUnderlyingLayer->GetNextFeature();
1854
0
        if (poUnderlyingFeature != nullptr)
1855
0
        {
1856
0
            OGRFeature *poFeature = CreateFeatureFrom(poUnderlyingFeature);
1857
0
            poFeature->SetFID(m_nFIDBase +
1858
0
                              (poUnderlyingFeature->GetFID() << (2 * m_nZ)));
1859
0
            delete poUnderlyingFeature;
1860
0
            return poFeature;
1861
0
        }
1862
0
        else
1863
0
        {
1864
0
            delete m_poCurrentTile;
1865
0
            m_poCurrentTile = nullptr;
1866
0
        }
1867
0
    }
1868
0
}
1869
1870
/************************************************************************/
1871
/*                           GetFeature()                               */
1872
/************************************************************************/
1873
1874
OGRFeature *OGRMVTDirectoryLayer::GetFeature(GIntBig nFID)
1875
0
{
1876
0
    const int nX = static_cast<int>(nFID & ((1 << m_nZ) - 1));
1877
0
    const int nY = static_cast<int>((nFID >> m_nZ) & ((1 << m_nZ) - 1));
1878
0
    const GIntBig nTileFID = nFID >> (2 * m_nZ);
1879
0
    const CPLString osFilename = CPLFormFilenameSafe(
1880
0
        CPLFormFilenameSafe(m_osDirName, CPLSPrintf("%d", nX), nullptr).c_str(),
1881
0
        CPLSPrintf("%d.%s", nY, m_poDS->m_osTileExtension.c_str()), nullptr);
1882
0
    GDALOpenInfo oOpenInfo(("MVT:" + osFilename).c_str(), GA_ReadOnly);
1883
0
    oOpenInfo.papszOpenOptions = CSLSetNameValue(
1884
0
        nullptr, "METADATA_FILE",
1885
0
        m_bJsonField ? "" : m_poDS->m_osMetadataMemFilename.c_str());
1886
0
    oOpenInfo.papszOpenOptions = CSLSetNameValue(
1887
0
        oOpenInfo.papszOpenOptions, "DO_NOT_ERROR_ON_MISSING_TILE", "YES");
1888
0
    GDALDataset *poTile =
1889
0
        OGRMVTDataset::Open(&oOpenInfo, /* bRecurseAllowed = */ false);
1890
0
    CSLDestroy(oOpenInfo.papszOpenOptions);
1891
0
    OGRFeature *poFeature = nullptr;
1892
0
    if (poTile)
1893
0
    {
1894
0
        OGRLayer *poLayer = poTile->GetLayerByName(GetName());
1895
0
        if (poLayer)
1896
0
        {
1897
0
            OGRFeature *poUnderlyingFeature = poLayer->GetFeature(nTileFID);
1898
0
            if (poUnderlyingFeature)
1899
0
            {
1900
0
                poFeature = CreateFeatureFrom(poUnderlyingFeature);
1901
0
                poFeature->SetFID(nFID);
1902
0
            }
1903
0
            delete poUnderlyingFeature;
1904
0
        }
1905
0
    }
1906
0
    delete poTile;
1907
0
    return poFeature;
1908
0
}
1909
1910
/************************************************************************/
1911
/*                             GetDataset()                             */
1912
/************************************************************************/
1913
1914
GDALDataset *OGRMVTDirectoryLayer::GetDataset()
1915
0
{
1916
0
    return m_poDS;
1917
0
}
1918
1919
/************************************************************************/
1920
/*                           OGRMVTDataset()                            */
1921
/************************************************************************/
1922
1923
OGRMVTDataset::OGRMVTDataset(GByte *pabyData)
1924
172k
    : m_pabyData(pabyData), m_poSRS(new OGRSpatialReference())
1925
172k
{
1926
172k
    m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1927
1928
172k
    m_bClip = CPLTestBool(CPLGetConfigOption("OGR_MVT_CLIP", "YES"));
1929
1930
    // Default WebMercator tiling scheme
1931
172k
    InitWebMercatorTilingScheme(m_poSRS, m_dfTopXOrigin, m_dfTopYOrigin,
1932
172k
                                m_dfTileDim0);
1933
172k
}
1934
1935
/************************************************************************/
1936
/*                           ~OGRMVTDataset()                           */
1937
/************************************************************************/
1938
1939
OGRMVTDataset::~OGRMVTDataset()
1940
172k
{
1941
172k
    VSIFree(m_pabyData);
1942
172k
    if (!m_osMetadataMemFilename.empty())
1943
0
        VSIUnlink(m_osMetadataMemFilename);
1944
172k
    if (m_poSRS)
1945
172k
        m_poSRS->Release();
1946
172k
}
1947
1948
/************************************************************************/
1949
/*                              GetLayer()                              */
1950
/************************************************************************/
1951
1952
OGRLayer *OGRMVTDataset::GetLayer(int iLayer)
1953
1954
488k
{
1955
488k
    if (iLayer < 0 || iLayer >= GetLayerCount())
1956
0
        return nullptr;
1957
488k
    return m_apoLayers[iLayer].get();
1958
488k
}
1959
1960
/************************************************************************/
1961
/*                             Identify()                               */
1962
/************************************************************************/
1963
1964
static int OGRMVTDriverIdentify(GDALOpenInfo *poOpenInfo)
1965
1966
443k
{
1967
443k
    if (STARTS_WITH_CI(poOpenInfo->pszFilename, "MVT:"))
1968
351k
        return TRUE;
1969
1970
92.1k
    if (STARTS_WITH(poOpenInfo->pszFilename, "/vsicurl"))
1971
5.02k
    {
1972
5.02k
        if (CPLGetValueType(CPLGetFilename(poOpenInfo->pszFilename)) ==
1973
5.02k
            CPL_VALUE_INTEGER)
1974
2.83k
        {
1975
2.83k
            return TRUE;
1976
2.83k
        }
1977
5.02k
    }
1978
1979
89.3k
    if (poOpenInfo->bIsDirectory)
1980
2.42k
    {
1981
2.42k
        if (CPLGetValueType(CPLGetFilename(poOpenInfo->pszFilename)) ==
1982
2.42k
            CPL_VALUE_INTEGER)
1983
0
        {
1984
0
            VSIStatBufL sStat;
1985
0
            CPLString osMetadataFile(CPLFormFilenameSafe(
1986
0
                CPLGetPathSafe(poOpenInfo->pszFilename).c_str(),
1987
0
                "metadata.json", nullptr));
1988
0
            const char *pszMetadataFile = CSLFetchNameValue(
1989
0
                poOpenInfo->papszOpenOptions, "METADATA_FILE");
1990
0
            if (pszMetadataFile)
1991
0
            {
1992
0
                osMetadataFile = pszMetadataFile;
1993
0
            }
1994
0
            if (!osMetadataFile.empty() &&
1995
0
                (STARTS_WITH(osMetadataFile, "http://") ||
1996
0
                 STARTS_WITH(osMetadataFile, "https://") ||
1997
0
                 VSIStatL(osMetadataFile, &sStat) == 0))
1998
0
            {
1999
0
                return TRUE;
2000
0
            }
2001
0
            if (pszMetadataFile == nullptr)
2002
0
            {
2003
                // tileserver-gl metadata file:
2004
                // If opening /path/to/foo/0, try looking for /path/to/foo.json
2005
0
                CPLString osParentDir(CPLGetPathSafe(poOpenInfo->pszFilename));
2006
0
                osMetadataFile =
2007
0
                    CPLFormFilenameSafe(CPLGetPathSafe(osParentDir).c_str(),
2008
0
                                        CPLGetFilename(osParentDir), "json");
2009
0
                if (VSIStatL(osMetadataFile, &sStat) == 0)
2010
0
                {
2011
0
                    return TRUE;
2012
0
                }
2013
0
            }
2014
2015
            // At least 3 files, to include the dummy . and ..
2016
0
            const CPLStringList aosDirContent = StripDummyEntries(
2017
0
                CPLStringList(VSIReadDirEx(poOpenInfo->pszFilename, 3)));
2018
0
            if (!aosDirContent.empty() &&
2019
0
                CPLGetValueType(aosDirContent[0]) == CPL_VALUE_INTEGER)
2020
0
            {
2021
0
                const std::string osSubDir = CPLFormFilenameSafe(
2022
0
                    poOpenInfo->pszFilename, aosDirContent[0], nullptr);
2023
                // At least 3 files, to include the dummy . and ..
2024
0
                const CPLStringList aosSubDirContent = StripDummyEntries(
2025
0
                    CPLStringList(VSIReadDirEx(osSubDir.c_str(), 10)));
2026
0
                const std::string osTileExtension(CSLFetchNameValueDef(
2027
0
                    poOpenInfo->papszOpenOptions, "TILE_EXTENSION", "pbf"));
2028
0
                for (int i = 0; i < aosSubDirContent.Count(); i++)
2029
0
                {
2030
0
                    if (CPLGetValueType(
2031
0
                            CPLGetBasenameSafe(aosSubDirContent[i]).c_str()) ==
2032
0
                        CPL_VALUE_INTEGER)
2033
0
                    {
2034
0
                        const std::string osExtension(
2035
0
                            CPLGetExtensionSafe(aosSubDirContent[i]));
2036
0
                        if (EQUAL(osExtension.c_str(),
2037
0
                                  osTileExtension.c_str()) ||
2038
0
                            EQUAL(osExtension.c_str(), "mvt"))
2039
0
                        {
2040
0
                            return TRUE;
2041
0
                        }
2042
0
                    }
2043
0
                }
2044
0
            }
2045
0
        }
2046
2.42k
        return FALSE;
2047
2.42k
    }
2048
2049
86.9k
    if (poOpenInfo->nHeaderBytes <= 2)
2050
45.1k
        return FALSE;
2051
2052
    // GZip header ?
2053
41.7k
    if (poOpenInfo->pabyHeader[0] == 0x1F && poOpenInfo->pabyHeader[1] == 0x8B)
2054
286
    {
2055
        // Prevent recursion
2056
286
        if (STARTS_WITH(poOpenInfo->pszFilename, "/vsigzip/"))
2057
0
        {
2058
0
            return FALSE;
2059
0
        }
2060
286
        CPLConfigOptionSetter oSetter("CPL_VSIL_GZIP_WRITE_PROPERTIES", "NO",
2061
286
                                      false);
2062
286
        GDALOpenInfo oOpenInfo(
2063
286
            (CPLString("/vsigzip/") + poOpenInfo->pszFilename).c_str(),
2064
286
            GA_ReadOnly);
2065
286
        return OGRMVTDriverIdentify(&oOpenInfo);
2066
286
    }
2067
2068
    // The GPB macros assume that the buffer is nul terminated,
2069
    // which is the case
2070
41.4k
    const GByte *pabyData = reinterpret_cast<GByte *>(poOpenInfo->pabyHeader);
2071
41.4k
    const GByte *const pabyDataStart = pabyData;
2072
41.4k
    const GByte *pabyLayerStart;
2073
41.4k
    const GByte *const pabyDataLimit = pabyData + poOpenInfo->nHeaderBytes;
2074
41.4k
    const GByte *pabyLayerEnd = pabyDataLimit;
2075
41.4k
    int nKey = 0;
2076
41.4k
    unsigned int nLayerLength = 0;
2077
41.4k
    bool bLayerNameFound = false;
2078
41.4k
    bool bKeyFound = false;
2079
41.4k
    bool bFeatureFound = false;
2080
41.4k
    bool bVersionFound = false;
2081
2082
41.4k
    try
2083
41.4k
    {
2084
41.4k
        READ_FIELD_KEY(nKey);
2085
41.4k
        if (nKey != MAKE_KEY(knLAYER, WT_DATA))
2086
36.2k
            return FALSE;
2087
5.15k
        READ_VARUINT32(pabyData, pabyDataLimit, nLayerLength);
2088
5.15k
        pabyLayerStart = pabyData;
2089
2090
        // Sanity check on layer length
2091
5.15k
        if (nLayerLength < static_cast<unsigned>(poOpenInfo->nHeaderBytes -
2092
5.15k
                                                 (pabyData - pabyDataStart)))
2093
3.21k
        {
2094
3.21k
            if (pabyData[nLayerLength] != MAKE_KEY(knLAYER, WT_DATA))
2095
6
                return FALSE;
2096
3.21k
            pabyLayerEnd = pabyData + nLayerLength;
2097
3.21k
        }
2098
1.93k
        else if (nLayerLength > 10 * 1024 * 1024)
2099
26
        {
2100
26
            return FALSE;
2101
26
        }
2102
2103
        // Quick scan on partial layer content to see if it seems to conform to
2104
        // the proto
2105
65.8k
        while (pabyData < pabyLayerEnd)
2106
65.0k
        {
2107
65.0k
            READ_VARUINT32(pabyData, pabyLayerEnd, nKey);
2108
64.9k
            auto nFieldNumber = GET_FIELDNUMBER(nKey);
2109
64.9k
            auto nWireType = GET_WIRETYPE(nKey);
2110
64.9k
            if (nFieldNumber == knLAYER_NAME)
2111
7.78k
            {
2112
7.78k
                if (nWireType != WT_DATA)
2113
3.24k
                {
2114
3.24k
                    CPLDebug("MVT", "Invalid wire type for layer_name field");
2115
3.24k
                }
2116
7.78k
                char *pszLayerName = nullptr;
2117
7.78k
                unsigned int nTextSize = 0;
2118
7.78k
                READ_TEXT_WITH_SIZE(pabyData, pabyLayerEnd, pszLayerName,
2119
7.78k
                                    nTextSize);
2120
7.46k
                if (nTextSize == 0 || !CPLIsUTF8(pszLayerName, nTextSize))
2121
35
                {
2122
35
                    CPLFree(pszLayerName);
2123
35
                    CPLDebug("MVT", "Protobuf error: line %d", __LINE__);
2124
35
                    return FALSE;
2125
35
                }
2126
7.43k
                CPLFree(pszLayerName);
2127
7.43k
                bLayerNameFound = true;
2128
7.43k
            }
2129
57.2k
            else if (nFieldNumber == knLAYER_FEATURES)
2130
4.43k
            {
2131
4.43k
                if (nWireType != WT_DATA)
2132
624
                {
2133
624
                    CPLDebug("MVT",
2134
624
                             "Invalid wire type for layer_features field");
2135
624
                }
2136
4.43k
                unsigned int nFeatureLength = 0;
2137
4.43k
                unsigned int nGeomType = 0;
2138
4.43k
                READ_VARUINT32(pabyData, pabyLayerEnd, nFeatureLength);
2139
4.42k
                if (nFeatureLength > nLayerLength - (pabyData - pabyLayerStart))
2140
23
                {
2141
23
                    CPLDebug("MVT", "Protobuf error: line %d", __LINE__);
2142
23
                    return FALSE;
2143
23
                }
2144
4.40k
                bFeatureFound = true;
2145
2146
4.40k
                const GByte *const pabyDataFeatureStart = pabyData;
2147
4.40k
                const GByte *const pabyDataFeatureEnd =
2148
4.40k
                    pabyDataStart +
2149
4.40k
                    std::min(static_cast<int>(pabyData + nFeatureLength -
2150
4.40k
                                              pabyDataStart),
2151
4.40k
                             poOpenInfo->nHeaderBytes);
2152
179k
                while (pabyData < pabyDataFeatureEnd)
2153
175k
                {
2154
175k
                    READ_VARUINT32(pabyData, pabyDataFeatureEnd, nKey);
2155
175k
                    nFieldNumber = GET_FIELDNUMBER(nKey);
2156
175k
                    nWireType = GET_WIRETYPE(nKey);
2157
175k
                    if (nFieldNumber == knFEATURE_TYPE)
2158
335
                    {
2159
335
                        if (nWireType != WT_VARINT)
2160
7
                        {
2161
7
                            CPLDebug(
2162
7
                                "MVT",
2163
7
                                "Invalid wire type for feature_type field");
2164
7
                            return FALSE;
2165
7
                        }
2166
328
                        READ_VARUINT32(pabyData, pabyDataFeatureEnd, nGeomType);
2167
325
                        if (nGeomType > knGEOM_TYPE_POLYGON)
2168
14
                        {
2169
14
                            CPLDebug("MVT", "Protobuf error: line %d",
2170
14
                                     __LINE__);
2171
14
                            return FALSE;
2172
14
                        }
2173
325
                    }
2174
175k
                    else if (nFieldNumber == knFEATURE_TAGS)
2175
362
                    {
2176
362
                        if (nWireType != WT_DATA)
2177
7
                        {
2178
7
                            CPLDebug(
2179
7
                                "MVT",
2180
7
                                "Invalid wire type for feature_tags field");
2181
7
                            return FALSE;
2182
7
                        }
2183
355
                        unsigned int nTagsSize = 0;
2184
355
                        READ_VARUINT32(pabyData, pabyDataFeatureEnd, nTagsSize);
2185
341
                        if (nTagsSize == 0 ||
2186
341
                            nTagsSize > nFeatureLength -
2187
336
                                            (pabyData - pabyDataFeatureStart))
2188
15
                        {
2189
15
                            CPLDebug("MVT", "Protobuf error: line %d",
2190
15
                                     __LINE__);
2191
15
                            return FALSE;
2192
15
                        }
2193
326
                        const GByte *const pabyDataTagsEnd =
2194
326
                            pabyDataStart +
2195
326
                            std::min(static_cast<int>(pabyData + nTagsSize -
2196
326
                                                      pabyDataStart),
2197
326
                                     poOpenInfo->nHeaderBytes);
2198
32.0k
                        while (pabyData < pabyDataTagsEnd)
2199
31.9k
                        {
2200
31.9k
                            unsigned int nKeyIdx = 0;
2201
31.9k
                            unsigned int nValIdx = 0;
2202
31.9k
                            READ_VARUINT32(pabyData, pabyDataTagsEnd, nKeyIdx);
2203
31.8k
                            READ_VARUINT32(pabyData, pabyDataTagsEnd, nValIdx);
2204
31.8k
                            if (nKeyIdx > 10 * 1024 * 1024 ||
2205
31.8k
                                nValIdx > 10 * 1024 * 1024)
2206
39
                            {
2207
39
                                CPLDebug("MVT", "Protobuf error: line %d",
2208
39
                                         __LINE__);
2209
39
                                return FALSE;
2210
39
                            }
2211
31.8k
                        }
2212
326
                    }
2213
175k
                    else if (nFieldNumber == knFEATURE_GEOMETRY &&
2214
175k
                             nWireType != WT_DATA)
2215
9
                    {
2216
9
                        CPLDebug(
2217
9
                            "MVT",
2218
9
                            "Invalid wire type for feature_geometry field");
2219
9
                        return FALSE;
2220
9
                    }
2221
175k
                    else if (nKey == MAKE_KEY(knFEATURE_GEOMETRY, WT_DATA) &&
2222
175k
                             nGeomType >= knGEOM_TYPE_POINT &&
2223
175k
                             nGeomType <= knGEOM_TYPE_POLYGON)
2224
158
                    {
2225
158
                        unsigned int nGeometrySize = 0;
2226
158
                        READ_VARUINT32(pabyData, pabyDataFeatureEnd,
2227
158
                                       nGeometrySize);
2228
155
                        if (nGeometrySize == 0 ||
2229
155
                            nGeometrySize >
2230
154
                                nFeatureLength -
2231
154
                                    (pabyData - pabyDataFeatureStart))
2232
8
                        {
2233
8
                            CPLDebug("MVT", "Protobuf error: line %d",
2234
8
                                     __LINE__);
2235
8
                            return FALSE;
2236
8
                        }
2237
147
                        const GByte *const pabyDataGeometryEnd =
2238
147
                            pabyDataStart +
2239
147
                            std::min(static_cast<int>(pabyData + nGeometrySize -
2240
147
                                                      pabyDataStart),
2241
147
                                     poOpenInfo->nHeaderBytes);
2242
2243
147
                        if (nGeomType == knGEOM_TYPE_POINT)
2244
65
                        {
2245
65
                            unsigned int nCmdCountCombined = 0;
2246
65
                            unsigned int nCount;
2247
65
                            READ_VARUINT32(pabyData, pabyDataGeometryEnd,
2248
65
                                           nCmdCountCombined);
2249
63
                            nCount = GetCmdCount(nCmdCountCombined);
2250
63
                            if (GetCmdId(nCmdCountCombined) != knCMD_MOVETO ||
2251
63
                                nCount == 0 || nCount > 10 * 1024 * 1024)
2252
2
                            {
2253
2
                                CPLDebug("MVT", "Protobuf error: line %d",
2254
2
                                         __LINE__);
2255
2
                                return FALSE;
2256
2
                            }
2257
822
                            for (unsigned i = 0; i < 2 * nCount; i++)
2258
818
                            {
2259
818
                                SKIP_VARINT(pabyData, pabyDataGeometryEnd);
2260
761
                            }
2261
61
                        }
2262
82
                        else if (nGeomType == knGEOM_TYPE_LINESTRING)
2263
37
                        {
2264
46
                            while (pabyData < pabyDataGeometryEnd)
2265
46
                            {
2266
46
                                unsigned int nCmdCountCombined = 0;
2267
46
                                unsigned int nLineToCount;
2268
                                // Should be a moveto
2269
46
                                READ_VARUINT32(pabyData, pabyDataGeometryEnd,
2270
46
                                               nCmdCountCombined);
2271
44
                                if (GetCmdId(nCmdCountCombined) !=
2272
44
                                        knCMD_MOVETO ||
2273
44
                                    GetCmdCount(nCmdCountCombined) != 1)
2274
6
                                {
2275
6
                                    CPLDebug("MVT", "Protobuf error: line %d",
2276
6
                                             __LINE__);
2277
6
                                    return FALSE;
2278
6
                                }
2279
38
                                SKIP_VARINT(pabyData, pabyDataGeometryEnd);
2280
33
                                SKIP_VARINT(pabyData, pabyDataGeometryEnd);
2281
29
                                READ_VARUINT32(pabyData, pabyDataGeometryEnd,
2282
29
                                               nCmdCountCombined);
2283
27
                                if (GetCmdId(nCmdCountCombined) != knCMD_LINETO)
2284
5
                                {
2285
5
                                    CPLDebug("MVT", "Protobuf error: line %d",
2286
5
                                             __LINE__);
2287
5
                                    return FALSE;
2288
5
                                }
2289
22
                                nLineToCount = GetCmdCount(nCmdCountCombined);
2290
136
                                for (unsigned i = 0; i < 2 * nLineToCount; i++)
2291
127
                                {
2292
127
                                    SKIP_VARINT(pabyData, pabyDataGeometryEnd);
2293
114
                                }
2294
22
                            }
2295
37
                        }
2296
45
                        else /* if( nGeomType == knGEOM_TYPE_POLYGON ) */
2297
45
                        {
2298
45
                            while (pabyData < pabyDataGeometryEnd)
2299
42
                            {
2300
42
                                unsigned int nCmdCountCombined = 0;
2301
42
                                unsigned int nLineToCount;
2302
                                // Should be a moveto
2303
42
                                READ_VARUINT32(pabyData, pabyDataGeometryEnd,
2304
42
                                               nCmdCountCombined);
2305
39
                                if (GetCmdId(nCmdCountCombined) !=
2306
39
                                        knCMD_MOVETO ||
2307
39
                                    GetCmdCount(nCmdCountCombined) != 1)
2308
4
                                {
2309
4
                                    CPLDebug("MVT", "Protobuf error: line %d",
2310
4
                                             __LINE__);
2311
4
                                    return FALSE;
2312
4
                                }
2313
35
                                SKIP_VARINT(pabyData, pabyDataGeometryEnd);
2314
30
                                SKIP_VARINT(pabyData, pabyDataGeometryEnd);
2315
21
                                READ_VARUINT32(pabyData, pabyDataGeometryEnd,
2316
21
                                               nCmdCountCombined);
2317
16
                                if (GetCmdId(nCmdCountCombined) != knCMD_LINETO)
2318
2
                                {
2319
2
                                    CPLDebug("MVT", "Protobuf error: line %d",
2320
2
                                             __LINE__);
2321
2
                                    return FALSE;
2322
2
                                }
2323
14
                                nLineToCount = GetCmdCount(nCmdCountCombined);
2324
763
                                for (unsigned i = 0; i < 2 * nLineToCount; i++)
2325
761
                                {
2326
761
                                    SKIP_VARINT(pabyData, pabyDataGeometryEnd);
2327
749
                                }
2328
                                // Should be a closepath
2329
2
                                READ_VARUINT32(pabyData, pabyDataGeometryEnd,
2330
2
                                               nCmdCountCombined);
2331
2
                                if (GetCmdId(nCmdCountCombined) !=
2332
2
                                        knCMD_CLOSEPATH ||
2333
2
                                    GetCmdCount(nCmdCountCombined) != 1)
2334
2
                                {
2335
2
                                    CPLDebug("MVT", "Protobuf error: line %d",
2336
2
                                             __LINE__);
2337
2
                                    return FALSE;
2338
2
                                }
2339
2
                            }
2340
45
                        }
2341
2342
7
                        pabyData = pabyDataGeometryEnd;
2343
7
                    }
2344
175k
                    else
2345
175k
                    {
2346
175k
                        SKIP_UNKNOWN_FIELD(pabyData, pabyDataFeatureEnd, FALSE);
2347
174k
                    }
2348
175k
                }
2349
2350
3.11k
                pabyData = pabyDataFeatureEnd;
2351
3.11k
            }
2352
52.7k
            else if (nFieldNumber == knLAYER_KEYS)
2353
4.72k
            {
2354
4.72k
                if (nWireType != WT_DATA)
2355
7
                {
2356
7
                    CPLDebug("MVT", "Invalid wire type for keys field");
2357
7
                    return FALSE;
2358
7
                }
2359
4.71k
                char *pszKey = nullptr;
2360
4.71k
                unsigned int nTextSize = 0;
2361
4.71k
                READ_TEXT_WITH_SIZE(pabyData, pabyLayerEnd, pszKey, nTextSize);
2362
4.30k
                if (!CPLIsUTF8(pszKey, nTextSize))
2363
14
                {
2364
14
                    CPLDebug("MVT", "Protobuf error: line %d", __LINE__);
2365
14
                    CPLFree(pszKey);
2366
14
                    return FALSE;
2367
14
                }
2368
4.28k
                CPLFree(pszKey);
2369
4.28k
                bKeyFound = true;
2370
4.28k
            }
2371
48.0k
            else if (nFieldNumber == knLAYER_VALUES)
2372
1.28k
            {
2373
1.28k
                if (nWireType != WT_DATA)
2374
13
                {
2375
13
                    CPLDebug("MVT", "Invalid wire type for values field");
2376
13
                    return FALSE;
2377
13
                }
2378
1.27k
                unsigned int nValueLength = 0;
2379
1.27k
                READ_VARUINT32(pabyData, pabyLayerEnd, nValueLength);
2380
1.26k
                if (nValueLength == 0 ||
2381
1.26k
                    nValueLength > nLayerLength - (pabyData - pabyLayerStart))
2382
17
                {
2383
17
                    CPLDebug("MVT", "Protobuf error: line %d", __LINE__);
2384
17
                    return FALSE;
2385
17
                }
2386
1.25k
                pabyData += nValueLength;
2387
1.25k
            }
2388
46.7k
            else if (GET_FIELDNUMBER(nKey) == knLAYER_EXTENT &&
2389
46.7k
                     GET_WIRETYPE(nKey) != WT_VARINT)
2390
12
            {
2391
12
                CPLDebug("MVT", "Invalid wire type for extent field");
2392
12
                return FALSE;
2393
12
            }
2394
#if 0
2395
            // The check on extent is too fragile. Values of 65536 can be found
2396
            else if( nKey == MAKE_KEY(knLAYER_EXTENT, WT_VARINT) )
2397
            {
2398
                unsigned int nExtent = 0;
2399
                READ_VARUINT32(pabyData, pabyLayerEnd, nExtent);
2400
                if( nExtent < 128 || nExtent > 16834 )
2401
                {
2402
                    CPLDebug("MVT", "Invalid extent: %u", nExtent);
2403
                    return FALSE;
2404
                }
2405
            }
2406
#endif
2407
46.7k
            else if (nFieldNumber == knLAYER_VERSION)
2408
399
            {
2409
399
                if (nWireType != WT_VARINT)
2410
8
                {
2411
8
                    CPLDebug("MVT", "Invalid wire type for version field");
2412
8
                    return FALSE;
2413
8
                }
2414
391
                unsigned int nVersion = 0;
2415
391
                READ_VARUINT32(pabyData, pabyLayerEnd, nVersion);
2416
386
                if (nVersion != 1 && nVersion != 2)
2417
13
                {
2418
13
                    CPLDebug("MVT", "Invalid version: %u", nVersion);
2419
13
                    return FALSE;
2420
13
                }
2421
373
                bVersionFound = true;
2422
373
            }
2423
46.3k
            else
2424
46.3k
            {
2425
46.3k
                SKIP_UNKNOWN_FIELD(pabyData, pabyLayerEnd, FALSE);
2426
44.2k
            }
2427
64.9k
        }
2428
5.11k
    }
2429
41.4k
    catch (const GPBException &)
2430
41.4k
    {
2431
4.02k
    }
2432
2433
4.87k
    return bLayerNameFound && (bKeyFound || bFeatureFound || bVersionFound);
2434
41.4k
}
2435
2436
/************************************************************************/
2437
/*                     LongLatToSphericalMercator()                     */
2438
/************************************************************************/
2439
2440
static void LongLatToSphericalMercator(double *x, double *y)
2441
0
{
2442
0
    double X = kmSPHERICAL_RADIUS * (*x) / 180 * M_PI;
2443
0
    double Y =
2444
0
        kmSPHERICAL_RADIUS * log(tan(M_PI / 4 + 0.5 * (*y) / 180 * M_PI));
2445
0
    *x = X;
2446
0
    *y = Y;
2447
0
}
2448
2449
/************************************************************************/
2450
/*                          LoadMetadata()                              */
2451
/************************************************************************/
2452
2453
static bool LoadMetadata(const CPLString &osMetadataFile,
2454
                         const CPLString &osMetadataContent,
2455
                         CPLJSONArray &oVectorLayers,
2456
                         CPLJSONArray &oTileStatLayers, CPLJSONObject &oBounds,
2457
                         OGRSpatialReference *poSRS, double &dfTopX,
2458
                         double &dfTopY, double &dfTileDim0,
2459
                         int &nTileMatrixWidth0, int &nTileMatrixHeight0,
2460
                         const CPLString &osMetadataMemFilename)
2461
2462
108k
{
2463
108k
    CPLJSONDocument oDoc;
2464
2465
108k
    bool bLoadOK;
2466
108k
    if (!osMetadataContent.empty())
2467
0
    {
2468
0
        bLoadOK = oDoc.LoadMemory(osMetadataContent);
2469
0
    }
2470
108k
    else if (STARTS_WITH(osMetadataFile, "http://") ||
2471
108k
             STARTS_WITH(osMetadataFile, "https://"))
2472
0
    {
2473
0
        bLoadOK = oDoc.LoadUrl(osMetadataFile, nullptr);
2474
0
    }
2475
108k
    else
2476
108k
    {
2477
108k
        bLoadOK = oDoc.Load(osMetadataFile);
2478
108k
    }
2479
108k
    if (!bLoadOK)
2480
0
        return false;
2481
2482
108k
    const CPLJSONObject oCrs(oDoc.GetRoot().GetObj("crs"));
2483
108k
    const CPLJSONObject oTopX(
2484
108k
        oDoc.GetRoot().GetObj("tile_origin_upper_left_x"));
2485
108k
    const CPLJSONObject oTopY(
2486
108k
        oDoc.GetRoot().GetObj("tile_origin_upper_left_y"));
2487
108k
    const CPLJSONObject oTileDim0(
2488
108k
        oDoc.GetRoot().GetObj("tile_dimension_zoom_0"));
2489
108k
    nTileMatrixWidth0 = 1;
2490
108k
    nTileMatrixHeight0 = 1;
2491
108k
    if (oCrs.IsValid() && oTopX.IsValid() && oTopY.IsValid() &&
2492
108k
        oTileDim0.IsValid())
2493
0
    {
2494
0
        poSRS->SetFromUserInput(oCrs.ToString().c_str());
2495
0
        dfTopX = oTopX.ToDouble();
2496
0
        dfTopY = oTopY.ToDouble();
2497
0
        dfTileDim0 = oTileDim0.ToDouble();
2498
0
        const CPLJSONObject oTMWidth0(
2499
0
            oDoc.GetRoot().GetObj("tile_matrix_width_zoom_0"));
2500
0
        if (oTMWidth0.GetType() == CPLJSONObject::Type::Integer)
2501
0
            nTileMatrixWidth0 = std::max(1, oTMWidth0.ToInteger());
2502
2503
0
        const CPLJSONObject oTMHeight0(
2504
0
            oDoc.GetRoot().GetObj("tile_matrix_height_zoom_0"));
2505
0
        if (oTMHeight0.GetType() == CPLJSONObject::Type::Integer)
2506
0
            nTileMatrixHeight0 = std::max(1, oTMHeight0.ToInteger());
2507
2508
        // Assumes WorldCRS84Quad with 2 tiles in width
2509
        // cf https://github.com/OSGeo/gdal/issues/11749
2510
0
        if (!oTMWidth0.IsValid() && dfTopX == -180 && dfTileDim0 == 180)
2511
0
            nTileMatrixWidth0 = 2;
2512
0
    }
2513
2514
108k
    oVectorLayers.Deinit();
2515
108k
    oTileStatLayers.Deinit();
2516
2517
108k
    CPLJSONObject oJson = oDoc.GetRoot().GetObj("json");
2518
108k
    if (!(oJson.IsValid() && oJson.GetType() == CPLJSONObject::Type::String))
2519
0
    {
2520
0
        oVectorLayers = oDoc.GetRoot().GetArray("vector_layers");
2521
2522
0
        oTileStatLayers = oDoc.GetRoot().GetArray("tilestats/layers");
2523
0
    }
2524
108k
    else
2525
108k
    {
2526
108k
        CPLJSONDocument oJsonDoc;
2527
108k
        if (!oJsonDoc.LoadMemory(oJson.ToString()))
2528
0
        {
2529
0
            return false;
2530
0
        }
2531
2532
108k
        oVectorLayers = oJsonDoc.GetRoot().GetArray("vector_layers");
2533
2534
108k
        oTileStatLayers = oJsonDoc.GetRoot().GetArray("tilestats/layers");
2535
108k
    }
2536
2537
108k
    oBounds = oDoc.GetRoot().GetObj("bounds");
2538
2539
108k
    if (!osMetadataMemFilename.empty())
2540
0
    {
2541
0
        oDoc.Save(osMetadataMemFilename);
2542
0
    }
2543
2544
108k
    return oVectorLayers.IsValid();
2545
108k
}
2546
2547
/************************************************************************/
2548
/*                       ConvertFromWGS84()                             */
2549
/************************************************************************/
2550
2551
static void ConvertFromWGS84(OGRSpatialReference *poTargetSRS, double &dfX0,
2552
                             double &dfY0, double &dfX1, double &dfY1)
2553
0
{
2554
0
    OGRSpatialReference oSRS_EPSG3857;
2555
0
    oSRS_EPSG3857.SetFromUserInput(SRS_EPSG_3857);
2556
2557
0
    if (poTargetSRS->IsSame(&oSRS_EPSG3857))
2558
0
    {
2559
0
        LongLatToSphericalMercator(&dfX0, &dfY0);
2560
0
        LongLatToSphericalMercator(&dfX1, &dfY1);
2561
0
    }
2562
0
    else
2563
0
    {
2564
0
        OGRSpatialReference oSRS_EPSG4326;
2565
0
        oSRS_EPSG4326.SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
2566
0
        oSRS_EPSG4326.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2567
0
        OGRCoordinateTransformation *poCT =
2568
0
            OGRCreateCoordinateTransformation(&oSRS_EPSG4326, poTargetSRS);
2569
0
        if (poCT)
2570
0
        {
2571
0
            poCT->Transform(1, &dfX0, &dfY0);
2572
0
            poCT->Transform(1, &dfX1, &dfY1);
2573
0
            delete poCT;
2574
0
        }
2575
0
    }
2576
0
}
2577
2578
/************************************************************************/
2579
/*                         OpenDirectory()                              */
2580
/************************************************************************/
2581
2582
GDALDataset *OGRMVTDataset::OpenDirectory(GDALOpenInfo *poOpenInfo)
2583
2584
1.50k
{
2585
1.50k
    const CPLString osZ(CPLGetFilename(poOpenInfo->pszFilename));
2586
1.50k
    if (CPLGetValueType(osZ) != CPL_VALUE_INTEGER)
2587
53
        return nullptr;
2588
2589
1.44k
    const int nZ = atoi(osZ);
2590
1.44k
    if (nZ < 0 || nZ > 30)
2591
87
        return nullptr;
2592
2593
1.36k
    CPLString osMetadataFile(
2594
1.36k
        CPLFormFilenameSafe(CPLGetPathSafe(poOpenInfo->pszFilename).c_str(),
2595
1.36k
                            "metadata.json", nullptr));
2596
1.36k
    const char *pszMetadataFile =
2597
1.36k
        CSLFetchNameValue(poOpenInfo->papszOpenOptions, "METADATA_FILE");
2598
1.36k
    if (pszMetadataFile)
2599
0
    {
2600
0
        osMetadataFile = pszMetadataFile;
2601
0
    }
2602
2603
1.36k
    CPLString osTileExtension(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
2604
1.36k
                                                   "TILE_EXTENSION", "pbf"));
2605
1.36k
    bool bJsonField =
2606
1.36k
        CPLFetchBool(poOpenInfo->papszOpenOptions, "JSON_FIELD", false);
2607
1.36k
    VSIStatBufL sStat;
2608
2609
1.36k
    bool bMetadataFileExists = false;
2610
1.36k
    CPLString osMetadataContent;
2611
1.36k
    if (STARTS_WITH(osMetadataFile, "http://") ||
2612
1.36k
        STARTS_WITH(osMetadataFile, "https://"))
2613
0
    {
2614
0
        for (int i = 0; i < 2; i++)
2615
0
        {
2616
0
            if (pszMetadataFile == nullptr)
2617
0
                CPLPushErrorHandler(CPLQuietErrorHandler);
2618
0
            CPLHTTPResult *psResult = CPLHTTPFetch(osMetadataFile, nullptr);
2619
0
            if (pszMetadataFile == nullptr)
2620
0
                CPLPopErrorHandler();
2621
0
            if (psResult == nullptr)
2622
0
            {
2623
0
                osMetadataFile.clear();
2624
0
            }
2625
0
            else if (psResult->pszErrBuf != nullptr ||
2626
0
                     psResult->pabyData == nullptr)
2627
0
            {
2628
0
                CPLHTTPDestroyResult(psResult);
2629
0
                osMetadataFile.clear();
2630
2631
0
                if (i == 0 && pszMetadataFile == nullptr)
2632
0
                {
2633
                    // tileserver-gl metadata file:
2634
                    // If opening /path/to/foo/0, try looking for
2635
                    // /path/to/foo.json
2636
0
                    CPLString osParentDir(
2637
0
                        CPLGetPathSafe(poOpenInfo->pszFilename));
2638
0
                    osMetadataFile = CPLFormFilenameSafe(
2639
0
                        CPLGetPathSafe(osParentDir).c_str(),
2640
0
                        CPLGetFilename(osParentDir), "json");
2641
0
                    continue;
2642
0
                }
2643
0
            }
2644
0
            else
2645
0
            {
2646
0
                bMetadataFileExists = true;
2647
0
                osMetadataContent =
2648
0
                    reinterpret_cast<const char *>(psResult->pabyData);
2649
0
                CPLHTTPDestroyResult(psResult);
2650
0
            }
2651
0
            break;
2652
0
        }
2653
0
    }
2654
1.36k
    else if (!osMetadataFile.empty())
2655
1.36k
    {
2656
1.36k
        bMetadataFileExists = (VSIStatL(osMetadataFile, &sStat) == 0);
2657
1.36k
        if (!bMetadataFileExists && pszMetadataFile == nullptr)
2658
1.36k
        {
2659
            // tileserver-gl metadata file:
2660
            // If opening /path/to/foo/0, try looking for /path/to/foo.json
2661
1.36k
            CPLString osParentDir(CPLGetPathSafe(poOpenInfo->pszFilename));
2662
1.36k
            osMetadataFile =
2663
1.36k
                CPLFormFilenameSafe(CPLGetPathSafe(osParentDir).c_str(),
2664
1.36k
                                    CPLGetFilename(osParentDir), "json");
2665
1.36k
            bMetadataFileExists = (VSIStatL(osMetadataFile, &sStat) == 0);
2666
1.36k
        }
2667
1.36k
    }
2668
2669
1.36k
    if (!bMetadataFileExists)
2670
1.36k
    {
2671
        // If we don't have a metadata file, iterate through all tiles to
2672
        // establish the layer definitions.
2673
1.36k
        OGRMVTDataset *poDS = nullptr;
2674
1.36k
        bool bTryToListDir =
2675
1.36k
            !STARTS_WITH(poOpenInfo->pszFilename, "/vsicurl/") &&
2676
1.36k
            !STARTS_WITH(poOpenInfo->pszFilename, "/vsicurl_streaming/") &&
2677
1.36k
            !STARTS_WITH(poOpenInfo->pszFilename, "/vsicurl?") &&
2678
1.36k
            !STARTS_WITH(poOpenInfo->pszFilename, "http://") &&
2679
1.36k
            !STARTS_WITH(poOpenInfo->pszFilename, "https://");
2680
1.36k
        CPLStringList aosDirContent;
2681
1.36k
        if (bTryToListDir)
2682
74
        {
2683
74
            aosDirContent = VSIReadDir(poOpenInfo->pszFilename);
2684
74
            aosDirContent = StripDummyEntries(aosDirContent);
2685
74
        }
2686
1.36k
        const int nMaxTiles = atoi(CSLFetchNameValueDef(
2687
1.36k
            poOpenInfo->papszOpenOptions,
2688
1.36k
            "TILE_COUNT_TO_ESTABLISH_FEATURE_DEFN", "1000"));
2689
1.36k
        int nCountTiles = 0;
2690
1.36k
        int nFailedAttempts = 0;
2691
2.75k
        for (int i = 0; i < (bTryToListDir ? aosDirContent.Count() : (1 << nZ));
2692
1.39k
             i++)
2693
2.27k
        {
2694
2.27k
            if (bTryToListDir)
2695
0
            {
2696
0
                if (CPLGetValueType(aosDirContent[i]) != CPL_VALUE_INTEGER)
2697
0
                {
2698
0
                    continue;
2699
0
                }
2700
0
            }
2701
2.27k
            CPLString osSubDir = CPLFormFilenameSafe(
2702
2.27k
                poOpenInfo->pszFilename,
2703
2.27k
                bTryToListDir ? aosDirContent[i] : CPLSPrintf("%d", i),
2704
2.27k
                nullptr);
2705
2.27k
            CPLStringList aosSubDirContent;
2706
2.27k
            if (bTryToListDir)
2707
0
            {
2708
0
                aosSubDirContent = VSIReadDir(osSubDir);
2709
0
                aosSubDirContent = StripDummyEntries(aosSubDirContent);
2710
0
            }
2711
2.27k
            for (int j = 0;
2712
10.9k
                 j < (bTryToListDir ? aosSubDirContent.Count() : (1 << nZ));
2713
8.62k
                 j++)
2714
9.51k
            {
2715
9.51k
                if (bTryToListDir)
2716
0
                {
2717
0
                    if (CPLGetValueType(
2718
0
                            CPLGetBasenameSafe(aosSubDirContent[j]).c_str()) !=
2719
0
                        CPL_VALUE_INTEGER)
2720
0
                    {
2721
0
                        continue;
2722
0
                    }
2723
0
                }
2724
9.51k
                const std::string osFilename(CPLFormFilenameSafe(
2725
9.51k
                    osSubDir,
2726
9.51k
                    bTryToListDir
2727
9.51k
                        ? aosSubDirContent[j]
2728
9.51k
                        : CPLSPrintf("%d.%s", j, osTileExtension.c_str()),
2729
9.51k
                    nullptr));
2730
9.51k
                GDALOpenInfo oOpenInfo(("MVT:" + osFilename).c_str(),
2731
9.51k
                                       GA_ReadOnly);
2732
9.51k
                oOpenInfo.papszOpenOptions =
2733
9.51k
                    CSLSetNameValue(nullptr, "METADATA_FILE", "");
2734
9.51k
                oOpenInfo.papszOpenOptions =
2735
9.51k
                    CSLSetNameValue(oOpenInfo.papszOpenOptions,
2736
9.51k
                                    "DO_NOT_ERROR_ON_MISSING_TILE", "YES");
2737
9.51k
                auto poTileDS = OGRMVTDataset::Open(
2738
9.51k
                    &oOpenInfo, /* bRecurseAllowed = */ false);
2739
9.51k
                if (poTileDS)
2740
64
                {
2741
64
                    if (poDS == nullptr)
2742
4
                    {
2743
4
                        poDS = new OGRMVTDataset(nullptr);
2744
4
                        poDS->m_osTileExtension = osTileExtension;
2745
4
                        poDS->SetDescription(poOpenInfo->pszFilename);
2746
4
                        poDS->m_bClip =
2747
4
                            CPLFetchBool(poOpenInfo->papszOpenOptions, "CLIP",
2748
4
                                         poDS->m_bClip);
2749
4
                    }
2750
2751
64
                    for (int k = 0; k < poTileDS->GetLayerCount(); k++)
2752
0
                    {
2753
0
                        OGRLayer *poTileLayer = poTileDS->GetLayer(k);
2754
0
                        OGRFeatureDefn *poTileLDefn =
2755
0
                            poTileLayer->GetLayerDefn();
2756
0
                        OGRwkbGeometryType eTileGeomType =
2757
0
                            poTileLDefn->GetGeomType();
2758
0
                        OGRwkbGeometryType eTileGeomTypeColl =
2759
0
                            OGR_GT_GetCollection(eTileGeomType);
2760
0
                        if (eTileGeomTypeColl != wkbUnknown &&
2761
0
                            eTileGeomTypeColl != eTileGeomType)
2762
0
                        {
2763
0
                            eTileGeomType = eTileGeomTypeColl;
2764
0
                        }
2765
2766
0
                        OGRLayer *poLayer =
2767
0
                            poDS->GetLayerByName(poTileLayer->GetName());
2768
0
                        OGRFeatureDefn *poLDefn;
2769
0
                        if (poLayer == nullptr)
2770
0
                        {
2771
0
                            CPLJSONObject oFields;
2772
0
                            oFields.Deinit();
2773
0
                            poDS->m_apoLayers.push_back(
2774
0
                                std::unique_ptr<OGRLayer>(
2775
0
                                    new OGRMVTDirectoryLayer(
2776
0
                                        poDS, poTileLayer->GetName(),
2777
0
                                        poOpenInfo->pszFilename, oFields,
2778
0
                                        CPLJSONArray(), bJsonField, wkbUnknown,
2779
0
                                        nullptr)));
2780
0
                            poLayer = poDS->m_apoLayers.back().get();
2781
0
                            poLDefn = poLayer->GetLayerDefn();
2782
0
                            poLDefn->SetGeomType(eTileGeomType);
2783
0
                        }
2784
0
                        else
2785
0
                        {
2786
0
                            poLDefn = poLayer->GetLayerDefn();
2787
0
                            if (poLayer->GetGeomType() != eTileGeomType)
2788
0
                            {
2789
0
                                poLDefn->SetGeomType(wkbUnknown);
2790
0
                            }
2791
0
                        }
2792
2793
0
                        if (!bJsonField)
2794
0
                        {
2795
0
                            for (int l = 1; l < poTileLDefn->GetFieldCount();
2796
0
                                 l++)
2797
0
                            {
2798
0
                                OGRFieldDefn *poTileFDefn =
2799
0
                                    poTileLDefn->GetFieldDefn(l);
2800
0
                                int nFieldIdx = poLDefn->GetFieldIndex(
2801
0
                                    poTileFDefn->GetNameRef());
2802
0
                                if (nFieldIdx < 0)
2803
0
                                {
2804
0
                                    poLDefn->AddFieldDefn(poTileFDefn);
2805
0
                                }
2806
0
                                else
2807
0
                                {
2808
0
                                    MergeFieldDefn(
2809
0
                                        poLDefn->GetFieldDefn(nFieldIdx),
2810
0
                                        poTileFDefn->GetType(),
2811
0
                                        poTileFDefn->GetSubType());
2812
0
                                }
2813
0
                            }
2814
0
                        }
2815
0
                    }
2816
64
                    nCountTiles++;
2817
64
                }
2818
9.44k
                else if (!bTryToListDir)
2819
9.44k
                {
2820
9.44k
                    nFailedAttempts++;
2821
9.44k
                }
2822
9.51k
                delete poTileDS;
2823
9.51k
                CSLDestroy(oOpenInfo.papszOpenOptions);
2824
2825
9.51k
                if (nFailedAttempts == 10)
2826
885
                    break;
2827
8.62k
                if (nMaxTiles > 0 && nCountTiles == nMaxTiles)
2828
0
                    break;
2829
8.62k
            }
2830
2831
2.27k
            if (nFailedAttempts == 10)
2832
885
                break;
2833
1.39k
            if (nMaxTiles > 0 && nCountTiles == nMaxTiles)
2834
0
                break;
2835
1.39k
        }
2836
1.36k
        return poDS;
2837
1.36k
    }
2838
2839
0
    CPLJSONArray oVectorLayers;
2840
0
    CPLJSONArray oTileStatLayers;
2841
0
    CPLJSONObject oBounds;
2842
2843
0
    OGRMVTDataset *poDS = new OGRMVTDataset(nullptr);
2844
2845
0
    CPLString osMetadataMemFilename =
2846
0
        VSIMemGenerateHiddenFilename("mvt_metadata.json");
2847
0
    if (!LoadMetadata(osMetadataFile, osMetadataContent, oVectorLayers,
2848
0
                      oTileStatLayers, oBounds, poDS->m_poSRS,
2849
0
                      poDS->m_dfTopXOrigin, poDS->m_dfTopYOrigin,
2850
0
                      poDS->m_dfTileDim0, poDS->m_nTileMatrixWidth0,
2851
0
                      poDS->m_nTileMatrixHeight0, osMetadataMemFilename))
2852
0
    {
2853
0
        delete poDS;
2854
0
        return nullptr;
2855
0
    }
2856
2857
0
    OGREnvelope sExtent;
2858
0
    bool bExtentValid = false;
2859
0
    if (oBounds.IsValid() && oBounds.GetType() == CPLJSONObject::Type::String)
2860
0
    {
2861
0
        CPLStringList aosTokens(
2862
0
            CSLTokenizeString2(oBounds.ToString().c_str(), ",", 0));
2863
0
        if (aosTokens.Count() == 4)
2864
0
        {
2865
0
            double dfX0 = CPLAtof(aosTokens[0]);
2866
0
            double dfY0 = CPLAtof(aosTokens[1]);
2867
0
            double dfX1 = CPLAtof(aosTokens[2]);
2868
0
            double dfY1 = CPLAtof(aosTokens[3]);
2869
0
            ConvertFromWGS84(poDS->m_poSRS, dfX0, dfY0, dfX1, dfY1);
2870
0
            bExtentValid = true;
2871
0
            sExtent.MinX = dfX0;
2872
0
            sExtent.MinY = dfY0;
2873
0
            sExtent.MaxX = dfX1;
2874
0
            sExtent.MaxY = dfY1;
2875
0
        }
2876
0
    }
2877
0
    else if (oBounds.IsValid() &&
2878
0
             oBounds.GetType() == CPLJSONObject::Type::Array)
2879
0
    {
2880
        // Cf https://free.tilehosting.com/data/v3.json?key=THE_KEY
2881
0
        CPLJSONArray oBoundArray = oBounds.ToArray();
2882
0
        if (oBoundArray.Size() == 4)
2883
0
        {
2884
0
            bExtentValid = true;
2885
0
            sExtent.MinX = oBoundArray[0].ToDouble();
2886
0
            sExtent.MinY = oBoundArray[1].ToDouble();
2887
0
            sExtent.MaxX = oBoundArray[2].ToDouble();
2888
0
            sExtent.MaxY = oBoundArray[3].ToDouble();
2889
0
            ConvertFromWGS84(poDS->m_poSRS, sExtent.MinX, sExtent.MinY,
2890
0
                             sExtent.MaxX, sExtent.MaxY);
2891
0
        }
2892
0
    }
2893
2894
0
    poDS->SetDescription(poOpenInfo->pszFilename);
2895
0
    poDS->m_bClip =
2896
0
        CPLFetchBool(poOpenInfo->papszOpenOptions, "CLIP", poDS->m_bClip);
2897
0
    poDS->m_osTileExtension = std::move(osTileExtension);
2898
0
    poDS->m_osMetadataMemFilename = std::move(osMetadataMemFilename);
2899
0
    for (int i = 0; i < oVectorLayers.Size(); i++)
2900
0
    {
2901
0
        CPLJSONObject oId = oVectorLayers[i].GetObj("id");
2902
0
        if (oId.IsValid() && oId.GetType() == CPLJSONObject::Type::String)
2903
0
        {
2904
0
            OGRwkbGeometryType eGeomType = wkbUnknown;
2905
0
            if (oTileStatLayers.IsValid())
2906
0
            {
2907
0
                eGeomType = OGRMVTFindGeomTypeFromTileStat(
2908
0
                    oTileStatLayers, oId.ToString().c_str());
2909
0
            }
2910
2911
0
            CPLJSONObject oFields = oVectorLayers[i].GetObj("fields");
2912
0
            CPLJSONArray oAttributesFromTileStats =
2913
0
                OGRMVTFindAttributesFromTileStat(oTileStatLayers,
2914
0
                                                 oId.ToString().c_str());
2915
2916
0
            poDS->m_apoLayers.push_back(
2917
0
                std::unique_ptr<OGRLayer>(new OGRMVTDirectoryLayer(
2918
0
                    poDS, oId.ToString().c_str(), poOpenInfo->pszFilename,
2919
0
                    oFields, oAttributesFromTileStats, bJsonField, eGeomType,
2920
0
                    (bExtentValid) ? &sExtent : nullptr)));
2921
0
        }
2922
0
    }
2923
2924
0
    return poDS;
2925
0
}
2926
2927
/************************************************************************/
2928
/*                                Open()                                */
2929
/************************************************************************/
2930
2931
GDALDataset *OGRMVTDataset::Open(GDALOpenInfo *poOpenInfo)
2932
174k
{
2933
174k
    return Open(poOpenInfo, true);
2934
174k
}
2935
2936
GDALDataset *OGRMVTDataset::Open(GDALOpenInfo *poOpenInfo, bool bRecurseAllowed)
2937
2938
184k
{
2939
184k
    if (!OGRMVTDriverIdentify(poOpenInfo) || poOpenInfo->eAccess == GA_Update)
2940
0
        return nullptr;
2941
2942
184k
    VSILFILE *fp = poOpenInfo->fpL;
2943
184k
    CPLString osFilename(poOpenInfo->pszFilename);
2944
184k
    if (STARTS_WITH_CI(poOpenInfo->pszFilename, "MVT:"))
2945
180k
    {
2946
180k
        osFilename = poOpenInfo->pszFilename + strlen("MVT:");
2947
180k
        if (STARTS_WITH(osFilename, "/vsigzip/http://") ||
2948
180k
            STARTS_WITH(osFilename, "/vsigzip/https://"))
2949
187
        {
2950
187
            osFilename = osFilename.substr(strlen("/vsigzip/"));
2951
187
        }
2952
2953
        // If the filename has no extension and is a directory, consider
2954
        // we open a directory
2955
180k
        VSIStatBufL sStat;
2956
180k
        if (bRecurseAllowed && !STARTS_WITH(osFilename, "/vsigzip/") &&
2957
180k
            strchr((CPLGetFilename(osFilename)), '.') == nullptr &&
2958
180k
            VSIStatL(osFilename, &sStat) == 0 && VSI_ISDIR(sStat.st_mode))
2959
53
        {
2960
53
            GDALOpenInfo oOpenInfo(osFilename, GA_ReadOnly);
2961
53
            oOpenInfo.papszOpenOptions = poOpenInfo->papszOpenOptions;
2962
53
            GDALDataset *poDS = OpenDirectory(&oOpenInfo);
2963
53
            if (poDS)
2964
0
                poDS->SetDescription(poOpenInfo->pszFilename);
2965
53
            return poDS;
2966
53
        }
2967
2968
        // For a network resource, if the filename is an integer, consider it
2969
        // is a directory and open as such
2970
180k
        if (bRecurseAllowed &&
2971
180k
            (STARTS_WITH(osFilename, "/vsicurl") ||
2972
171k
             STARTS_WITH(osFilename, "http://") ||
2973
171k
             STARTS_WITH(osFilename, "https://")) &&
2974
180k
            CPLGetValueType(CPLGetFilename(osFilename)) == CPL_VALUE_INTEGER)
2975
30
        {
2976
30
            GDALOpenInfo oOpenInfo(osFilename, GA_ReadOnly);
2977
30
            oOpenInfo.papszOpenOptions = poOpenInfo->papszOpenOptions;
2978
30
            GDALDataset *poDS = OpenDirectory(&oOpenInfo);
2979
30
            if (poDS)
2980
0
                poDS->SetDescription(poOpenInfo->pszFilename);
2981
30
            return poDS;
2982
30
        }
2983
2984
180k
        if (!STARTS_WITH(osFilename, "http://") &&
2985
180k
            !STARTS_WITH(osFilename, "https://"))
2986
180k
        {
2987
180k
            CPLConfigOptionSetter oSetter("CPL_VSIL_GZIP_WRITE_PROPERTIES",
2988
180k
                                          "NO", false);
2989
180k
            CPLConfigOptionSetter oSetter2("CPL_VSIL_GZIP_SAVE_INFO", "NO",
2990
180k
                                           false);
2991
180k
            fp = VSIFOpenL(osFilename, "rb");
2992
            // Is it a gzipped file ?
2993
180k
            if (fp && !STARTS_WITH(osFilename, "/vsigzip/"))
2994
170k
            {
2995
170k
                GByte abyHeaderBytes[2] = {0, 0};
2996
170k
                VSIFReadL(abyHeaderBytes, 2, 1, fp);
2997
170k
                if (abyHeaderBytes[0] == 0x1F && abyHeaderBytes[1] == 0x8B)
2998
159k
                {
2999
159k
                    VSIFCloseL(fp);
3000
159k
                    fp = VSIFOpenL(("/vsigzip/" + osFilename).c_str(), "rb");
3001
159k
                }
3002
170k
            }
3003
180k
        }
3004
180k
    }
3005
3.67k
    else if (bRecurseAllowed &&
3006
3.67k
             (poOpenInfo->bIsDirectory ||
3007
3.67k
              (STARTS_WITH(poOpenInfo->pszFilename, "/vsicurl") &&
3008
3.67k
               CPLGetValueType(CPLGetFilename(poOpenInfo->pszFilename)) ==
3009
1.41k
                   CPL_VALUE_INTEGER)))
3010
1.41k
    {
3011
1.41k
        return OpenDirectory(poOpenInfo);
3012
1.41k
    }
3013
    // Is it a gzipped file ?
3014
2.25k
    else if (poOpenInfo->nHeaderBytes >= 2 &&
3015
2.25k
             poOpenInfo->pabyHeader[0] == 0x1F &&
3016
2.25k
             poOpenInfo->pabyHeader[1] == 0x8B)
3017
4
    {
3018
4
        CPLConfigOptionSetter oSetter("CPL_VSIL_GZIP_WRITE_PROPERTIES", "NO",
3019
4
                                      false);
3020
4
        fp = VSIFOpenL(("/vsigzip/" + osFilename).c_str(), "rb");
3021
4
    }
3022
2.25k
    else
3023
2.25k
    {
3024
2.25k
        poOpenInfo->fpL = nullptr;
3025
2.25k
    }
3026
182k
    if (fp == nullptr && !STARTS_WITH(osFilename, "http://") &&
3027
182k
        !STARTS_WITH(osFilename, "https://"))
3028
9.96k
    {
3029
9.96k
        return nullptr;
3030
9.96k
    }
3031
3032
172k
    CPLString osY = CPLGetBasenameSafe(osFilename);
3033
172k
    CPLString osX = CPLGetBasenameSafe(CPLGetPathSafe(osFilename).c_str());
3034
172k
    CPLString osZ = CPLGetBasenameSafe(
3035
172k
        CPLGetPathSafe(CPLGetPathSafe(osFilename).c_str()).c_str());
3036
172k
    size_t nPos = osY.find('.');
3037
172k
    if (nPos != std::string::npos)
3038
650
        osY.resize(nPos);
3039
3040
172k
    CPLString osMetadataFile;
3041
172k
    if (CSLFetchNameValue(poOpenInfo->papszOpenOptions, "METADATA_FILE"))
3042
169k
    {
3043
169k
        osMetadataFile =
3044
169k
            CSLFetchNameValue(poOpenInfo->papszOpenOptions, "METADATA_FILE");
3045
169k
    }
3046
2.88k
    else if (CPLGetValueType(osX) == CPL_VALUE_INTEGER &&
3047
2.88k
             CPLGetValueType(osY) == CPL_VALUE_INTEGER &&
3048
2.88k
             CPLGetValueType(osZ) == CPL_VALUE_INTEGER)
3049
0
    {
3050
0
        osMetadataFile = CPLFormFilenameSafe(
3051
0
            CPLGetPathSafe(
3052
0
                CPLGetPathSafe(CPLGetPathSafe(osFilename).c_str()).c_str())
3053
0
                .c_str(),
3054
0
            "metadata.json", nullptr);
3055
0
        if (osMetadataFile.find("/vsigzip/") == 0)
3056
0
        {
3057
0
            osMetadataFile = osMetadataFile.substr(strlen("/vsigzip/"));
3058
0
        }
3059
0
        VSIStatBufL sStat;
3060
0
        if (osMetadataFile.empty() || VSIStatL(osMetadataFile, &sStat) != 0)
3061
0
        {
3062
0
            osMetadataFile.clear();
3063
0
        }
3064
0
    }
3065
3066
172k
    if (CSLFetchNameValue(poOpenInfo->papszOpenOptions, "X") &&
3067
172k
        CSLFetchNameValue(poOpenInfo->papszOpenOptions, "Y") &&
3068
172k
        CSLFetchNameValue(poOpenInfo->papszOpenOptions, "Z"))
3069
169k
    {
3070
169k
        osX = CSLFetchNameValue(poOpenInfo->papszOpenOptions, "X");
3071
169k
        osY = CSLFetchNameValue(poOpenInfo->papszOpenOptions, "Y");
3072
169k
        osZ = CSLFetchNameValue(poOpenInfo->papszOpenOptions, "Z");
3073
169k
    }
3074
3075
172k
    GByte *pabyDataMod;
3076
172k
    size_t nFileSize;
3077
3078
172k
    if (fp == nullptr)
3079
235
    {
3080
235
        bool bSilenceErrors =
3081
235
            CPLFetchBool(poOpenInfo->papszOpenOptions,
3082
235
                         "DO_NOT_ERROR_ON_MISSING_TILE", false);
3083
235
        if (bSilenceErrors)
3084
0
            CPLPushErrorHandler(CPLQuietErrorHandler);
3085
235
        CPLHTTPResult *psResult = CPLHTTPFetch(osFilename, nullptr);
3086
235
        if (bSilenceErrors)
3087
0
            CPLPopErrorHandler();
3088
235
        if (psResult == nullptr)
3089
0
            return nullptr;
3090
235
        if (psResult->pszErrBuf != nullptr)
3091
235
        {
3092
235
            CPLHTTPDestroyResult(psResult);
3093
235
            return nullptr;
3094
235
        }
3095
0
        pabyDataMod = psResult->pabyData;
3096
0
        if (pabyDataMod == nullptr)
3097
0
        {
3098
0
            CPLHTTPDestroyResult(psResult);
3099
0
            return nullptr;
3100
0
        }
3101
0
        nFileSize = psResult->nDataLen;
3102
0
        psResult->pabyData = nullptr;
3103
0
        CPLHTTPDestroyResult(psResult);
3104
3105
        // zlib decompress if needed
3106
0
        if (nFileSize > 2 && pabyDataMod[0] == 0x1F && pabyDataMod[1] == 0x8B)
3107
0
        {
3108
0
            size_t nOutBytes = 0;
3109
0
            void *pUncompressed =
3110
0
                CPLZLibInflate(pabyDataMod, nFileSize, nullptr, 0, &nOutBytes);
3111
0
            CPLFree(pabyDataMod);
3112
0
            if (pUncompressed == nullptr)
3113
0
            {
3114
0
                return nullptr;
3115
0
            }
3116
0
            pabyDataMod = static_cast<GByte *>(pUncompressed);
3117
0
            nFileSize = nOutBytes;
3118
0
        }
3119
0
    }
3120
172k
    else
3121
172k
    {
3122
        // Check file size and ingest into memory
3123
172k
        VSIFSeekL(fp, 0, SEEK_END);
3124
172k
        vsi_l_offset nFileSizeL = VSIFTellL(fp);
3125
172k
        if (nFileSizeL > 10 * 1024 * 1024)
3126
0
        {
3127
0
            VSIFCloseL(fp);
3128
0
            return nullptr;
3129
0
        }
3130
172k
        nFileSize = static_cast<size_t>(nFileSizeL);
3131
172k
        pabyDataMod = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nFileSize + 1));
3132
172k
        if (pabyDataMod == nullptr)
3133
0
        {
3134
0
            VSIFCloseL(fp);
3135
0
            return nullptr;
3136
0
        }
3137
172k
        VSIFSeekL(fp, 0, SEEK_SET);
3138
172k
        VSIFReadL(pabyDataMod, 1, nFileSize, fp);
3139
172k
        pabyDataMod[nFileSize] = 0;
3140
172k
        VSIFCloseL(fp);
3141
172k
    }
3142
3143
172k
    const GByte *pabyData = pabyDataMod;
3144
3145
    // First scan to browse through layers
3146
172k
    const GByte *pabyDataLimit = pabyData + nFileSize;
3147
172k
    int nKey = 0;
3148
172k
    OGRMVTDataset *poDS = new OGRMVTDataset(pabyDataMod);
3149
172k
    poDS->SetDescription(poOpenInfo->pszFilename);
3150
172k
    poDS->m_bClip =
3151
172k
        CPLFetchBool(poOpenInfo->papszOpenOptions, "CLIP", poDS->m_bClip);
3152
3153
172k
    if (!(CPLGetValueType(osX) == CPL_VALUE_INTEGER &&
3154
172k
          CPLGetValueType(osY) == CPL_VALUE_INTEGER &&
3155
172k
          CPLGetValueType(osZ) == CPL_VALUE_INTEGER))
3156
2.64k
    {
3157
        // See
3158
        // https://github.com/mapbox/mvt-fixtures/tree/master/real-world/compressed
3159
2.64k
        int nX = 0;
3160
2.64k
        int nY = 0;
3161
2.64k
        int nZ = 0;
3162
2.64k
        CPLString osBasename(
3163
2.64k
            CPLGetBasenameSafe(CPLGetBasenameSafe(osFilename).c_str()));
3164
2.64k
        if (sscanf(osBasename, "%d-%d-%d", &nZ, &nX, &nY) == 3 ||
3165
2.64k
            sscanf(osBasename, "%d_%d_%d", &nZ, &nX, &nY) == 3)
3166
0
        {
3167
0
            osX = CPLSPrintf("%d", nX);
3168
0
            osY = CPLSPrintf("%d", nY);
3169
0
            osZ = CPLSPrintf("%d", nZ);
3170
0
        }
3171
2.64k
    }
3172
3173
172k
    CPLJSONArray oVectorLayers;
3174
172k
    oVectorLayers.Deinit();
3175
3176
172k
    CPLJSONArray oTileStatLayers;
3177
172k
    oTileStatLayers.Deinit();
3178
3179
172k
    if (!osMetadataFile.empty())
3180
108k
    {
3181
108k
        CPLJSONObject oBounds;
3182
108k
        LoadMetadata(osMetadataFile, CPLString(), oVectorLayers,
3183
108k
                     oTileStatLayers, oBounds, poDS->m_poSRS,
3184
108k
                     poDS->m_dfTopXOrigin, poDS->m_dfTopYOrigin,
3185
108k
                     poDS->m_dfTileDim0, poDS->m_nTileMatrixWidth0,
3186
108k
                     poDS->m_nTileMatrixHeight0, CPLString());
3187
108k
    }
3188
3189
172k
    const char *pszGeorefTopX =
3190
172k
        CSLFetchNameValue(poOpenInfo->papszOpenOptions, "GEOREF_TOPX");
3191
172k
    const char *pszGeorefTopY =
3192
172k
        CSLFetchNameValue(poOpenInfo->papszOpenOptions, "GEOREF_TOPY");
3193
172k
    const char *pszGeorefTileDimX =
3194
172k
        CSLFetchNameValue(poOpenInfo->papszOpenOptions, "GEOREF_TILEDIMX");
3195
172k
    const char *pszGeorefTileDimY =
3196
172k
        CSLFetchNameValue(poOpenInfo->papszOpenOptions, "GEOREF_TILEDIMY");
3197
172k
    if (pszGeorefTopX && pszGeorefTopY && pszGeorefTileDimX &&
3198
172k
        pszGeorefTileDimY)
3199
0
    {
3200
0
        poDS->m_bGeoreferenced = true;
3201
0
        poDS->m_dfTileDimX = CPLAtof(pszGeorefTileDimX);
3202
0
        poDS->m_dfTileDimY = CPLAtof(pszGeorefTileDimY);
3203
0
        poDS->m_dfTopX = CPLAtof(pszGeorefTopX);
3204
0
        poDS->m_dfTopY = CPLAtof(pszGeorefTopY);
3205
0
        poDS->m_poSRS->Release();
3206
0
        poDS->m_poSRS = nullptr;
3207
0
    }
3208
172k
    else if (CPLGetValueType(osX) == CPL_VALUE_INTEGER &&
3209
172k
             CPLGetValueType(osY) == CPL_VALUE_INTEGER &&
3210
172k
             CPLGetValueType(osZ) == CPL_VALUE_INTEGER)
3211
169k
    {
3212
169k
        int nX = atoi(osX);
3213
169k
        int nY = atoi(osY);
3214
169k
        int nZ = atoi(osZ);
3215
169k
        if (nZ >= 0 && nZ < 30 && nX >= 0 &&
3216
169k
            nX < (static_cast<int64_t>(1) << nZ) * poDS->m_nTileMatrixWidth0 &&
3217
169k
            nY >= 0 &&
3218
169k
            nY < (static_cast<int64_t>(1) << nZ) * poDS->m_nTileMatrixHeight0)
3219
169k
        {
3220
169k
            poDS->m_bGeoreferenced = true;
3221
169k
            poDS->m_dfTileDimX = poDS->m_dfTileDim0 / (1 << nZ);
3222
169k
            poDS->m_dfTileDimY = poDS->m_dfTileDimX;
3223
169k
            poDS->m_dfTopX = poDS->m_dfTopXOrigin + nX * poDS->m_dfTileDimX;
3224
169k
            poDS->m_dfTopY = poDS->m_dfTopYOrigin - nY * poDS->m_dfTileDimY;
3225
169k
        }
3226
169k
    }
3227
3228
172k
    try
3229
172k
    {
3230
18.5M
        while (pabyData < pabyDataLimit)
3231
18.3M
        {
3232
18.3M
            READ_FIELD_KEY(nKey);
3233
18.3M
            if (nKey == MAKE_KEY(knLAYER, WT_DATA))
3234
658k
            {
3235
658k
                unsigned int nLayerSize = 0;
3236
658k
                READ_SIZE(pabyData, pabyDataLimit, nLayerSize);
3237
636k
                const GByte *pabyDataLayer = pabyData;
3238
636k
                const GByte *pabyDataLimitLayer = pabyData + nLayerSize;
3239
2.57M
                while (pabyData < pabyDataLimitLayer)
3240
2.38M
                {
3241
2.38M
                    READ_VARINT32(pabyData, pabyDataLimitLayer, nKey);
3242
2.38M
                    if (nKey == MAKE_KEY(knLAYER_NAME, WT_DATA))
3243
446k
                    {
3244
446k
                        char *pszLayerName = nullptr;
3245
446k
                        READ_TEXT(pabyData, pabyDataLimitLayer, pszLayerName);
3246
3247
446k
                        CPLJSONObject oFields;
3248
446k
                        oFields.Deinit();
3249
446k
                        if (oVectorLayers.IsValid())
3250
151k
                        {
3251
606k
                            for (int i = 0; i < oVectorLayers.Size(); i++)
3252
594k
                            {
3253
594k
                                CPLJSONObject oId =
3254
594k
                                    oVectorLayers[i].GetObj("id");
3255
594k
                                if (oId.IsValid() &&
3256
594k
                                    oId.GetType() ==
3257
507k
                                        CPLJSONObject::Type::String)
3258
506k
                                {
3259
506k
                                    if (oId.ToString() == pszLayerName)
3260
139k
                                    {
3261
139k
                                        oFields =
3262
139k
                                            oVectorLayers[i].GetObj("fields");
3263
139k
                                        break;
3264
139k
                                    }
3265
506k
                                }
3266
594k
                            }
3267
151k
                        }
3268
3269
446k
                        OGRwkbGeometryType eGeomType = wkbUnknown;
3270
446k
                        if (oTileStatLayers.IsValid())
3271
20.3k
                        {
3272
20.3k
                            eGeomType = OGRMVTFindGeomTypeFromTileStat(
3273
20.3k
                                oTileStatLayers, pszLayerName);
3274
20.3k
                        }
3275
446k
                        CPLJSONArray oAttributesFromTileStats =
3276
446k
                            OGRMVTFindAttributesFromTileStat(oTileStatLayers,
3277
446k
                                                             pszLayerName);
3278
3279
446k
                        poDS->m_apoLayers.push_back(
3280
446k
                            std::unique_ptr<OGRLayer>(new OGRMVTLayer(
3281
446k
                                poDS, pszLayerName, pabyDataLayer, nLayerSize,
3282
446k
                                oFields, oAttributesFromTileStats, eGeomType)));
3283
446k
                        CPLFree(pszLayerName);
3284
446k
                        break;
3285
446k
                    }
3286
1.94M
                    else
3287
1.94M
                    {
3288
1.94M
                        SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimitLayer, FALSE);
3289
1.93M
                    }
3290
2.38M
                }
3291
633k
                pabyData = pabyDataLimitLayer;
3292
633k
            }
3293
17.7M
            else
3294
17.7M
            {
3295
17.7M
                SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, FALSE);
3296
17.7M
            }
3297
18.3M
        }
3298
3299
125k
        return poDS;
3300
172k
    }
3301
172k
    catch (const GPBException &e)
3302
172k
    {
3303
46.8k
        CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
3304
46.8k
        delete poDS;
3305
46.8k
        return nullptr;
3306
46.8k
    }
3307
172k
}
3308
3309
#ifdef HAVE_MVT_WRITE_SUPPORT
3310
3311
/************************************************************************/
3312
/*                           OGRMVTWriterDataset                        */
3313
/************************************************************************/
3314
3315
class OGRMVTWriterLayer;
3316
3317
struct OGRMVTFeatureContent
3318
{
3319
    std::vector<std::pair<std::string, MVTTileLayerValue>> oValues;
3320
    GIntBig nFID;
3321
};
3322
3323
class OGRMVTWriterDataset final : public GDALDataset
3324
{
3325
    class MVTFieldProperties
3326
    {
3327
      public:
3328
        CPLString m_osName;
3329
        std::set<MVTTileLayerValue> m_oSetValues;
3330
        std::set<MVTTileLayerValue> m_oSetAllValues;
3331
        double m_dfMinVal = 0;
3332
        double m_dfMaxVal = 0;
3333
        bool m_bAllInt = false;
3334
        MVTTileLayerValue::ValueType m_eType =
3335
            MVTTileLayerValue::ValueType::NONE;
3336
    };
3337
3338
    class MVTLayerProperties
3339
    {
3340
      public:
3341
        int m_nMinZoom = 0;
3342
        int m_nMaxZoom = 0;
3343
        std::map<MVTTileLayerFeature::GeomType, GIntBig> m_oCountGeomType;
3344
        std::map<CPLString, size_t> m_oMapFieldNameToIdx;
3345
        std::vector<MVTFieldProperties> m_aoFields;
3346
        std::set<CPLString> m_oSetFields;
3347
    };
3348
3349
    std::vector<std::unique_ptr<OGRMVTWriterLayer>> m_apoLayers;
3350
    CPLString m_osTempDB;
3351
    mutable std::mutex m_oDBMutex;
3352
    mutable bool m_bWriteFeatureError = false;
3353
    sqlite3_vfs *m_pMyVFS = nullptr;
3354
    sqlite3 *m_hDB = nullptr;
3355
    sqlite3_stmt *m_hInsertStmt = nullptr;
3356
    int m_nMinZoom = 0;
3357
    int m_nMaxZoom = 5;
3358
    double m_dfSimplification = 0.0;
3359
    double m_dfSimplificationMaxZoom = 0.0;
3360
    CPLJSONDocument m_oConf;
3361
    unsigned m_nExtent = knDEFAULT_EXTENT;
3362
    int m_nMetadataVersion = 2;
3363
    int m_nMVTVersion = 2;
3364
    int m_nBuffer = 5 * knDEFAULT_EXTENT / 256;
3365
    bool m_bGZip = true;
3366
    mutable CPLWorkerThreadPool m_oThreadPool;
3367
    bool m_bThreadPoolOK = false;
3368
    mutable GIntBig m_nTempTiles = 0;
3369
    CPLString m_osName;
3370
    CPLString m_osDescription;
3371
    CPLString m_osType{"overlay"};
3372
    sqlite3 *m_hDBMBTILES = nullptr;
3373
    OGREnvelope m_oEnvelope;
3374
    bool m_bMaxTileSizeOptSpecified = false;
3375
    bool m_bMaxFeaturesOptSpecified = false;
3376
    unsigned m_nMaxTileSize = 500000;
3377
    unsigned m_nMaxFeatures = 200000;
3378
    std::map<std::string, std::string> m_oMapLayerNameToDesc;
3379
    std::map<std::string, GIntBig> m_oMapLayerNameToFeatureCount;
3380
    CPLString m_osBounds;
3381
    CPLString m_osCenter;
3382
    CPLString m_osExtension{"pbf"};
3383
    OGRSpatialReference *m_poSRS = nullptr;
3384
    double m_dfTopX = 0.0;
3385
    double m_dfTopY = 0.0;
3386
    double m_dfTileDim0 = 0.0;
3387
    int m_nTileMatrixWidth0 =
3388
        1;  // Number of tiles along X axis at zoom level 0
3389
    int m_nTileMatrixHeight0 =
3390
        1;  // Number of tiles along Y axis at zoom level 0
3391
    bool m_bReuseTempFile = false;  // debug only
3392
3393
    OGRErr PreGenerateForTile(
3394
        int nZ, int nX, int nY, const CPLString &osTargetName,
3395
        bool bIsMaxZoomForLayer,
3396
        const std::shared_ptr<OGRMVTFeatureContent> &poFeatureContent,
3397
        GIntBig nSerial, const std::shared_ptr<OGRGeometry> &poGeom,
3398
        const OGREnvelope &sEnvelope) const;
3399
3400
    static void WriterTaskFunc(void *pParam);
3401
3402
    OGRErr PreGenerateForTileReal(int nZ, int nX, int nY,
3403
                                  const CPLString &osTargetName,
3404
                                  bool bIsMaxZoomForLayer,
3405
                                  const OGRMVTFeatureContent *poFeatureContent,
3406
                                  GIntBig nSerial, const OGRGeometry *poGeom,
3407
                                  const OGREnvelope &sEnvelope) const;
3408
3409
    void ConvertToTileCoords(double dfX, double dfY, int &nX, int &nY,
3410
                             double dfTopX, double dfTopY,
3411
                             double dfTileDim) const;
3412
    bool EncodeLineString(MVTTileLayerFeature *poGPBFeature,
3413
                          const OGRLineString *poLS, OGRLineString *poOutLS,
3414
                          bool bWriteLastPoint, bool bReverseOrder,
3415
                          GUInt32 nMinLineTo, double dfTopX, double dfTopY,
3416
                          double dfTileDim, int &nLastX, int &nLastY) const;
3417
    bool EncodePolygon(MVTTileLayerFeature *poGPBFeature,
3418
                       const OGRPolygon *poPoly, OGRPolygon *poOutPoly,
3419
                       double dfTopX, double dfTopY, double dfTileDim,
3420
                       int &nLastX, int &nLastY, double &dfArea) const;
3421
#ifdef notdef
3422
    bool EncodeRepairedOuterRing(MVTTileLayerFeature *poGPBFeature,
3423
                                 OGRPolygon &oOutPoly, int &nLastX,
3424
                                 int &nLastY) const;
3425
#endif
3426
3427
    static void UpdateLayerProperties(MVTLayerProperties *poLayerProperties,
3428
                                      const std::string &osKey,
3429
                                      const MVTTileLayerValue &oValue);
3430
3431
    void EncodeFeature(const void *pabyBlob, int nBlobSize,
3432
                       std::shared_ptr<MVTTileLayer> &poTargetLayer,
3433
                       std::map<CPLString, GUInt32> &oMapKeyToIdx,
3434
                       std::map<MVTTileLayerValue, GUInt32> &oMapValueToIdx,
3435
                       MVTLayerProperties *poLayerProperties, GUInt32 nExtent,
3436
                       unsigned &nFeaturesInTile);
3437
3438
    std::string
3439
    EncodeTile(int nZ, int nX, int nY, sqlite3_stmt *hStmtLayer,
3440
               sqlite3_stmt *hStmtRows,
3441
               std::map<CPLString, MVTLayerProperties> &oMapLayerProps,
3442
               std::set<CPLString> &oSetLayers, GIntBig &nTempTilesRead);
3443
3444
    std::string RecodeTileLowerResolution(int nZ, int nX, int nY, int nExtent,
3445
                                          sqlite3_stmt *hStmtLayer,
3446
                                          sqlite3_stmt *hStmtRows);
3447
3448
    bool CreateOutput();
3449
3450
    bool GenerateMetadata(size_t nLayers,
3451
                          const std::map<CPLString, MVTLayerProperties> &oMap);
3452
3453
  public:
3454
    OGRMVTWriterDataset();
3455
    ~OGRMVTWriterDataset();
3456
3457
    CPLErr Close() override;
3458
3459
    OGRLayer *ICreateLayer(const char *pszName,
3460
                           const OGRGeomFieldDefn *poGeomFieldDefn,
3461
                           CSLConstList papszOptions) override;
3462
3463
    int TestCapability(const char *) override;
3464
3465
    OGRErr WriteFeature(OGRMVTWriterLayer *poLayer, OGRFeature *poFeature,
3466
                        GIntBig nSerial, OGRGeometry *poGeom);
3467
3468
    static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
3469
                               int nBandsIn, GDALDataType eDT,
3470
                               char **papszOptions);
3471
3472
    OGRSpatialReference *GetSRS()
3473
    {
3474
        return m_poSRS;
3475
    }
3476
};
3477
3478
/************************************************************************/
3479
/*                           OGRMVTWriterLayer                          */
3480
/************************************************************************/
3481
3482
class OGRMVTWriterLayer final : public OGRLayer
3483
{
3484
    friend class OGRMVTWriterDataset;
3485
3486
    OGRMVTWriterDataset *m_poDS = nullptr;
3487
    OGRFeatureDefn *m_poFeatureDefn = nullptr;
3488
    OGRCoordinateTransformation *m_poCT = nullptr;
3489
    GIntBig m_nSerial = 0;
3490
    int m_nMinZoom = 0;
3491
    int m_nMaxZoom = 5;
3492
    CPLString m_osTargetName;
3493
3494
  public:
3495
    OGRMVTWriterLayer(OGRMVTWriterDataset *poDS, const char *pszLayerName,
3496
                      OGRSpatialReference *poSRS);
3497
    ~OGRMVTWriterLayer();
3498
3499
    void ResetReading() override
3500
    {
3501
    }
3502
3503
    OGRFeature *GetNextFeature() override
3504
    {
3505
        return nullptr;
3506
    }
3507
3508
    OGRFeatureDefn *GetLayerDefn() override
3509
    {
3510
        return m_poFeatureDefn;
3511
    }
3512
3513
    int TestCapability(const char *) override;
3514
    OGRErr ICreateFeature(OGRFeature *) override;
3515
    OGRErr CreateField(const OGRFieldDefn *, int) override;
3516
3517
    GDALDataset *GetDataset() override
3518
    {
3519
        return m_poDS;
3520
    }
3521
};
3522
3523
/************************************************************************/
3524
/*                          OGRMVTWriterLayer()                         */
3525
/************************************************************************/
3526
3527
OGRMVTWriterLayer::OGRMVTWriterLayer(OGRMVTWriterDataset *poDS,
3528
                                     const char *pszLayerName,
3529
                                     OGRSpatialReference *poSRSIn)
3530
{
3531
    m_poDS = poDS;
3532
    m_poFeatureDefn = new OGRFeatureDefn(pszLayerName);
3533
    SetDescription(m_poFeatureDefn->GetName());
3534
    m_poFeatureDefn->Reference();
3535
3536
    m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poDS->GetSRS());
3537
3538
    if (poSRSIn != nullptr && !poDS->GetSRS()->IsSame(poSRSIn))
3539
    {
3540
        m_poCT = OGRCreateCoordinateTransformation(poSRSIn, poDS->GetSRS());
3541
        if (m_poCT == nullptr)
3542
        {
3543
            // If we can't create a transformation, issue a warning - but
3544
            // continue the transformation.
3545
            CPLError(CE_Warning, CPLE_AppDefined,
3546
                     "Failed to create coordinate transformation between the "
3547
                     "input and target coordinate systems.");
3548
        }
3549
    }
3550
}
3551
3552
/************************************************************************/
3553
/*                          ~OGRMVTWriterLayer()                        */
3554
/************************************************************************/
3555
3556
OGRMVTWriterLayer::~OGRMVTWriterLayer()
3557
{
3558
    m_poFeatureDefn->Release();
3559
    delete m_poCT;
3560
}
3561
3562
/************************************************************************/
3563
/*                            TestCapability()                          */
3564
/************************************************************************/
3565
3566
int OGRMVTWriterLayer::TestCapability(const char *pszCap)
3567
{
3568
3569
    if (EQUAL(pszCap, OLCSequentialWrite) || EQUAL(pszCap, OLCCreateField))
3570
        return true;
3571
    return false;
3572
}
3573
3574
/************************************************************************/
3575
/*                            CreateField()                             */
3576
/************************************************************************/
3577
3578
OGRErr OGRMVTWriterLayer::CreateField(const OGRFieldDefn *poFieldDefn, int)
3579
{
3580
    m_poFeatureDefn->AddFieldDefn(poFieldDefn);
3581
    return OGRERR_NONE;
3582
}
3583
3584
/************************************************************************/
3585
/*                            ICreateFeature()                          */
3586
/************************************************************************/
3587
3588
OGRErr OGRMVTWriterLayer::ICreateFeature(OGRFeature *poFeature)
3589
{
3590
    OGRGeometry *poGeom = poFeature->GetGeometryRef();
3591
    if (poGeom == nullptr || poGeom->IsEmpty())
3592
        return OGRERR_NONE;
3593
    if (m_poCT)
3594
    {
3595
        poGeom->transform(m_poCT);
3596
    }
3597
    m_nSerial++;
3598
    return m_poDS->WriteFeature(this, poFeature, m_nSerial, poGeom);
3599
}
3600
3601
/************************************************************************/
3602
/*                          OGRMVTWriterDataset()                       */
3603
/************************************************************************/
3604
3605
OGRMVTWriterDataset::OGRMVTWriterDataset()
3606
{
3607
    // Default WebMercator tiling scheme
3608
    m_poSRS = new OGRSpatialReference();
3609
    m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
3610
3611
    InitWebMercatorTilingScheme(m_poSRS, m_dfTopX, m_dfTopY, m_dfTileDim0);
3612
}
3613
3614
/************************************************************************/
3615
/*                         ~OGRMVTWriterDataset()                       */
3616
/************************************************************************/
3617
3618
OGRMVTWriterDataset::~OGRMVTWriterDataset()
3619
{
3620
    OGRMVTWriterDataset::Close();
3621
3622
    if (m_pMyVFS)
3623
    {
3624
        sqlite3_vfs_unregister(m_pMyVFS);
3625
        CPLFree(m_pMyVFS->pAppData);
3626
        CPLFree(m_pMyVFS);
3627
    }
3628
3629
    m_poSRS->Release();
3630
}
3631
3632
/************************************************************************/
3633
/*                              Close()                                 */
3634
/************************************************************************/
3635
3636
CPLErr OGRMVTWriterDataset::Close()
3637
{
3638
    CPLErr eErr = CE_None;
3639
    if (nOpenFlags != OPEN_FLAGS_CLOSED)
3640
    {
3641
        if (GetDescription()[0] != '\0')
3642
        {
3643
            if (!CreateOutput())
3644
                eErr = CE_Failure;
3645
        }
3646
        if (m_hInsertStmt != nullptr)
3647
        {
3648
            sqlite3_finalize(m_hInsertStmt);
3649
        }
3650
        if (m_hDB)
3651
        {
3652
            sqlite3_close(m_hDB);
3653
        }
3654
        if (m_hDBMBTILES)
3655
        {
3656
            sqlite3_close(m_hDBMBTILES);
3657
        }
3658
        if (!m_osTempDB.empty() && !m_bReuseTempFile &&
3659
            CPLTestBool(CPLGetConfigOption("OGR_MVT_REMOVE_TEMP_FILE", "YES")))
3660
        {
3661
            VSIUnlink(m_osTempDB);
3662
        }
3663
3664
        if (GDALDataset::Close() != CE_None)
3665
            eErr = CE_Failure;
3666
    }
3667
    return eErr;
3668
}
3669
3670
/************************************************************************/
3671
/*                        ConvertToTileCoords()                     */
3672
/************************************************************************/
3673
3674
void OGRMVTWriterDataset::ConvertToTileCoords(double dfX, double dfY, int &nX,
3675
                                              int &nY, double dfTopX,
3676
                                              double dfTopY,
3677
                                              double dfTileDim) const
3678
{
3679
    if (dfTileDim == 0)
3680
    {
3681
        nX = static_cast<int>(dfX);
3682
        nY = static_cast<int>(dfY);
3683
    }
3684
    else
3685
    {
3686
        nX = static_cast<int>(
3687
            std::round((dfX - dfTopX) * m_nExtent / dfTileDim));
3688
        nY = static_cast<int>(
3689
            std::round((dfTopY - dfY) * m_nExtent / dfTileDim));
3690
    }
3691
}
3692
3693
/************************************************************************/
3694
/*                       GetCmdCountCombined()                          */
3695
/************************************************************************/
3696
3697
static unsigned GetCmdCountCombined(unsigned int nCmdId, unsigned int nCmdCount)
3698
{
3699
    return (nCmdId | (nCmdCount << 3));
3700
}
3701
3702
/************************************************************************/
3703
/*                          EncodeLineString()                          */
3704
/************************************************************************/
3705
3706
bool OGRMVTWriterDataset::EncodeLineString(
3707
    MVTTileLayerFeature *poGPBFeature, const OGRLineString *poLS,
3708
    OGRLineString *poOutLS, bool bWriteLastPoint, bool bReverseOrder,
3709
    GUInt32 nMinLineTo, double dfTopX, double dfTopY, double dfTileDim,
3710
    int &nLastX, int &nLastY) const
3711
{
3712
    const GUInt32 nInitialSize = poGPBFeature->getGeometryCount();
3713
    const int nLastXOri = nLastX;
3714
    const int nLastYOri = nLastY;
3715
    GUInt32 nLineToCount = 0;
3716
    const int nPoints = poLS->getNumPoints() - (bWriteLastPoint ? 0 : 1);
3717
    if (poOutLS)
3718
        poOutLS->setNumPoints(nPoints);
3719
    int nFirstX = 0;
3720
    int nFirstY = 0;
3721
    int nLastXValid = nLastX;
3722
    int nLastYValid = nLastY;
3723
    for (int i = 0; i < nPoints; i++)
3724
    {
3725
        int nX, nY;
3726
        int nSrcIdx = bReverseOrder ? poLS->getNumPoints() - 1 - i : i;
3727
        double dfX = poLS->getX(nSrcIdx);
3728
        double dfY = poLS->getY(nSrcIdx);
3729
        ConvertToTileCoords(dfX, dfY, nX, nY, dfTopX, dfTopY, dfTileDim);
3730
        int nDiffX = nX - nLastX;
3731
        int nDiffY = nY - nLastY;
3732
        if (i == 0 || nDiffX != 0 || nDiffY != 0)
3733
        {
3734
            if (i > 0)
3735
            {
3736
                nLineToCount++;
3737
                if (nLineToCount == 1)
3738
                {
3739
                    poGPBFeature->addGeometry(
3740
                        GetCmdCountCombined(knCMD_MOVETO, 1));
3741
                    const int nLastDiffX = nLastX - nLastXOri;
3742
                    const int nLastDiffY = nLastY - nLastYOri;
3743
                    poGPBFeature->addGeometry(EncodeSInt(nLastDiffX));
3744
                    poGPBFeature->addGeometry(EncodeSInt(nLastDiffY));
3745
                    if (poOutLS)
3746
                        poOutLS->setPoint(0, nLastX, nLastY);
3747
3748
                    // To be modified later
3749
                    poGPBFeature->addGeometry(
3750
                        GetCmdCountCombined(knCMD_LINETO, 0));
3751
                }
3752
3753
                poGPBFeature->addGeometry(EncodeSInt(nDiffX));
3754
                poGPBFeature->addGeometry(EncodeSInt(nDiffY));
3755
                if (poOutLS)
3756
                    poOutLS->setPoint(nLineToCount, nX, nY);
3757
            }
3758
            else
3759
            {
3760
                nFirstX = nX;
3761
                nFirstY = nY;
3762
            }
3763
            nLastXValid = nLastX;
3764
            nLastYValid = nLastY;
3765
            nLastX = nX;
3766
            nLastY = nY;
3767
        }
3768
    }
3769
3770
    // If last point of ring is identical to first one, discard it
3771
    if (nMinLineTo == 2 && nLineToCount > 0 && nFirstX == nLastX &&
3772
        nFirstY == nLastY)
3773
    {
3774
        poGPBFeature->resizeGeometryArray(poGPBFeature->getGeometryCount() - 2);
3775
        nLineToCount--;
3776
        nLastX = nLastXValid;
3777
        nLastY = nLastYValid;
3778
    }
3779
3780
    if (nLineToCount >= nMinLineTo)
3781
    {
3782
        if (poOutLS)
3783
            poOutLS->setNumPoints(1 + nLineToCount);
3784
        // Patch actual number of points in LINETO command
3785
        poGPBFeature->setGeometry(
3786
            nInitialSize + 3, GetCmdCountCombined(knCMD_LINETO, nLineToCount));
3787
        return true;
3788
    }
3789
    else
3790
    {
3791
        poGPBFeature->resizeGeometryArray(nInitialSize);
3792
        nLastX = nLastXOri;
3793
        nLastY = nLastYOri;
3794
        return false;
3795
    }
3796
}
3797
3798
#ifdef notdef
3799
/************************************************************************/
3800
/*                     EncodeRepairedOuterRing()                        */
3801
/************************************************************************/
3802
3803
bool OGRMVTWriterDataset::EncodeRepairedOuterRing(
3804
    MVTTileLayerFeature *poGPBFeature, OGRPolygon &oInPoly, int &nLastX,
3805
    int &nLastY) const
3806
{
3807
    std::unique_ptr<OGRGeometry> poFixedGeom(oInPoly.Buffer(0));
3808
    if (!poFixedGeom.get() || poFixedGeom->IsEmpty())
3809
    {
3810
        return false;
3811
    }
3812
3813
    OGRPolygon *poPoly = nullptr;
3814
    if (wkbFlatten(poFixedGeom->getGeometryType()) == wkbMultiPolygon)
3815
    {
3816
        OGRMultiPolygon *poMP = poFixedGeom.get()->toMultiPolygon();
3817
        poPoly = poMP->getGeometryRef(0)->toPolygon();
3818
    }
3819
    else if (wkbFlatten(poFixedGeom->getGeometryType()) == wkbPolygon)
3820
    {
3821
        poPoly = poFixedGeom.get()->toPolygon();
3822
    }
3823
    if (!poPoly)
3824
        return false;
3825
3826
    OGRLinearRing *poRing = poPoly->getExteriorRing();
3827
    const bool bReverseOrder = !poRing->isClockwise();
3828
3829
    const GUInt32 nInitialSize = poGPBFeature->getGeometryCount();
3830
    const int nLastXOri = nLastX;
3831
    const int nLastYOri = nLastY;
3832
    GUInt32 nLineToCount = 0;
3833
    const int nPoints = poRing->getNumPoints() - 1;
3834
    auto poOutLinearRing = std::make_unique<OGRLinearRing>();
3835
    poOutLinearRing->setNumPoints(nPoints);
3836
    for (int i = 0; i < nPoints; i++)
3837
    {
3838
        int nSrcIdx = bReverseOrder ? poRing->getNumPoints() - 1 - i : i;
3839
        double dfX = poRing->getX(nSrcIdx);
3840
        double dfY = poRing->getY(nSrcIdx);
3841
        int nX = static_cast<int>(std::round(dfX));
3842
        int nY = static_cast<int>(std::round(dfY));
3843
        if (nX != dfX || nY != dfY)
3844
            continue;
3845
        int nDiffX = nX - nLastX;
3846
        int nDiffY = nY - nLastY;
3847
        if (i == 0 || nDiffX != 0 || nDiffY != 0)
3848
        {
3849
            if (i > 0)
3850
            {
3851
                nLineToCount++;
3852
                if (nLineToCount == 1)
3853
                {
3854
                    poGPBFeature->addGeometry(
3855
                        GetCmdCountCombined(knCMD_MOVETO, 1));
3856
                    const int nLastDiffX = nLastX - nLastXOri;
3857
                    const int nLastDiffY = nLastY - nLastYOri;
3858
                    poGPBFeature->addGeometry(EncodeSInt(nLastDiffX));
3859
                    poGPBFeature->addGeometry(EncodeSInt(nLastDiffY));
3860
                    poOutLinearRing->setPoint(0, nLastX, nLastY);
3861
3862
                    // To be modified later
3863
                    poGPBFeature->addGeometry(
3864
                        GetCmdCountCombined(knCMD_LINETO, 0));
3865
                }
3866
3867
                poGPBFeature->addGeometry(EncodeSInt(nDiffX));
3868
                poGPBFeature->addGeometry(EncodeSInt(nDiffY));
3869
                poOutLinearRing->setPoint(nLineToCount, nX, nY);
3870
            }
3871
            nLastX = nX;
3872
            nLastY = nY;
3873
        }
3874
    }
3875
    if (nLineToCount >= 2)
3876
    {
3877
        poOutLinearRing->setNumPoints(1 + nLineToCount);
3878
        OGRPolygon oOutPoly;
3879
        oOutPoly.addRingDirectly(poOutLinearRing.release());
3880
        int bIsValid;
3881
        {
3882
            CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
3883
            bIsValid = oOutPoly.IsValid();
3884
        }
3885
        if (bIsValid)
3886
        {
3887
            // Patch actual number of points in LINETO command
3888
            poGPBFeature->setGeometry(
3889
                nInitialSize + 3,
3890
                GetCmdCountCombined(knCMD_LINETO, nLineToCount));
3891
            poGPBFeature->addGeometry(GetCmdCountCombined(knCMD_CLOSEPATH, 1));
3892
            return true;
3893
        }
3894
    }
3895
3896
    poGPBFeature->resizeGeometryArray(nInitialSize);
3897
    nLastX = nLastXOri;
3898
    nLastY = nLastYOri;
3899
    return false;
3900
}
3901
#endif
3902
3903
/************************************************************************/
3904
/*                          EncodePolygon()                             */
3905
/************************************************************************/
3906
3907
bool OGRMVTWriterDataset::EncodePolygon(MVTTileLayerFeature *poGPBFeature,
3908
                                        const OGRPolygon *poPoly,
3909
                                        OGRPolygon *poOutPoly, double dfTopX,
3910
                                        double dfTopY, double dfTileDim,
3911
                                        int &nLastX, int &nLastY,
3912
                                        double &dfArea) const
3913
{
3914
    dfArea = 0;
3915
    auto poOutOuterRing = std::make_unique<OGRLinearRing>();
3916
    for (int i = 0; i < 1 + poPoly->getNumInteriorRings(); i++)
3917
    {
3918
        const OGRLinearRing *poRing = (i == 0) ? poPoly->getExteriorRing()
3919
                                               : poPoly->getInteriorRing(i - 1);
3920
        if (poRing->getNumPoints() < 4 ||
3921
            poRing->getX(0) != poRing->getX(poRing->getNumPoints() - 1) ||
3922
            poRing->getY(0) != poRing->getY(poRing->getNumPoints() - 1))
3923
        {
3924
            if (i == 0)
3925
                return false;
3926
            continue;
3927
        }
3928
        const bool bWriteLastPoint = false;
3929
        // If dealing with input geometry in CRS units, exterior rings must
3930
        // be clockwise oriented.
3931
        // But if re-encoding a geometry already in tile coordinates
3932
        // (dfTileDim == 0), this is the reverse.
3933
        const bool bReverseOrder = dfTileDim != 0
3934
                                       ? ((i == 0 && !poRing->isClockwise()) ||
3935
                                          (i > 0 && poRing->isClockwise()))
3936
                                       : ((i == 0 && poRing->isClockwise()) ||
3937
                                          (i > 0 && !poRing->isClockwise()));
3938
        const GUInt32 nMinLineTo = 2;
3939
        std::unique_ptr<OGRLinearRing> poOutInnerRing;
3940
        if (i > 0)
3941
            poOutInnerRing = std::make_unique<OGRLinearRing>();
3942
        OGRLinearRing *poOutRing =
3943
            poOutInnerRing.get() ? poOutInnerRing.get() : poOutOuterRing.get();
3944
3945
        bool bSuccess = EncodeLineString(
3946
            poGPBFeature, poRing, poOutRing, bWriteLastPoint, bReverseOrder,
3947
            nMinLineTo, dfTopX, dfTopY, dfTileDim, nLastX, nLastY);
3948
        if (!bSuccess)
3949
        {
3950
            if (i == 0)
3951
                return false;
3952
            continue;
3953
        }
3954
3955
        if (poOutPoly == nullptr)
3956
        {
3957
            poGPBFeature->addGeometry(GetCmdCountCombined(knCMD_CLOSEPATH, 1));
3958
            continue;
3959
        }
3960
3961
        poOutRing->closeRings();
3962
3963
        poOutPoly->addRing(poOutRing);
3964
        if (i > 0)
3965
            dfArea -= poOutRing->get_Area();
3966
        else
3967
            dfArea = poOutRing->get_Area();
3968
3969
        poGPBFeature->addGeometry(GetCmdCountCombined(knCMD_CLOSEPATH, 1));
3970
    }
3971
3972
    return true;
3973
}
3974
3975
/************************************************************************/
3976
/*                          PreGenerateForTile()                        */
3977
/************************************************************************/
3978
3979
OGRErr OGRMVTWriterDataset::PreGenerateForTileReal(
3980
    int nZ, int nTileX, int nTileY, const CPLString &osTargetName,
3981
    bool bIsMaxZoomForLayer, const OGRMVTFeatureContent *poFeatureContent,
3982
    GIntBig nSerial, const OGRGeometry *poGeom,
3983
    const OGREnvelope &sEnvelope) const
3984
{
3985
    double dfTileDim = m_dfTileDim0 / (1 << nZ);
3986
    double dfBuffer = dfTileDim * m_nBuffer / m_nExtent;
3987
    double dfTopX = m_dfTopX + nTileX * dfTileDim;
3988
    double dfTopY = m_dfTopY - nTileY * dfTileDim;
3989
    double dfBottomRightX = dfTopX + dfTileDim;
3990
    double dfBottomRightY = dfTopY - dfTileDim;
3991
    double dfIntersectTopX = dfTopX - dfBuffer;
3992
    double dfIntersectTopY = dfTopY + dfBuffer;
3993
    double dfIntersectBottomRightX = dfBottomRightX + dfBuffer;
3994
    double dfIntersectBottomRightY = dfBottomRightY - dfBuffer;
3995
3996
    const OGRGeometry *poIntersection;
3997
    std::unique_ptr<OGRGeometry> poIntersectionHolder;  // keep in that scope
3998
    if (sEnvelope.MinX >= dfIntersectTopX &&
3999
        sEnvelope.MinY >= dfIntersectBottomRightY &&
4000
        sEnvelope.MaxX <= dfIntersectBottomRightX &&
4001
        sEnvelope.MaxY <= dfIntersectTopY)
4002
    {
4003
        poIntersection = poGeom;
4004
    }
4005
    else
4006
    {
4007
        OGRLinearRing *poLR = new OGRLinearRing();
4008
        poLR->addPoint(dfIntersectTopX, dfIntersectTopY);
4009
        poLR->addPoint(dfIntersectTopX, dfIntersectBottomRightY);
4010
        poLR->addPoint(dfIntersectBottomRightX, dfIntersectBottomRightY);
4011
        poLR->addPoint(dfIntersectBottomRightX, dfIntersectTopY);
4012
        poLR->addPoint(dfIntersectTopX, dfIntersectTopY);
4013
        OGRPolygon oPoly;
4014
        oPoly.addRingDirectly(poLR);
4015
4016
        CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
4017
        auto poTmp = poGeom->Intersection(&oPoly);
4018
        poIntersection = poTmp;
4019
        poIntersectionHolder.reset(poTmp);
4020
        if (poIntersection == nullptr || poIntersection->IsEmpty())
4021
        {
4022
            return OGRERR_NONE;
4023
        }
4024
    }
4025
4026
    // Create a layer with a single feature in it
4027
    std::shared_ptr<MVTTileLayer> poLayer =
4028
        std::shared_ptr<MVTTileLayer>(new MVTTileLayer());
4029
    std::shared_ptr<MVTTileLayerFeature> poGPBFeature =
4030
        std::shared_ptr<MVTTileLayerFeature>(new MVTTileLayerFeature());
4031
    poLayer->addFeature(poGPBFeature);
4032
4033
    OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType());
4034
    if (eGeomType == wkbPoint || eGeomType == wkbMultiPoint)
4035
        poGPBFeature->setType(MVTTileLayerFeature::GeomType::POINT);
4036
    else if (eGeomType == wkbLineString || eGeomType == wkbMultiLineString)
4037
        poGPBFeature->setType(MVTTileLayerFeature::GeomType::LINESTRING);
4038
    else if (eGeomType == wkbPolygon || eGeomType == wkbMultiPolygon)
4039
        poGPBFeature->setType(MVTTileLayerFeature::GeomType::POLYGON);
4040
    else
4041
    {
4042
        CPLError(CE_Failure, CPLE_NotSupported, "Unsupported geometry type");
4043
        return OGRERR_NONE;
4044
    }
4045
4046
    OGRwkbGeometryType eGeomToEncodeType =
4047
        wkbFlatten(poIntersection->getGeometryType());
4048
4049
    // Simplify contour if requested by user
4050
    const OGRGeometry *poGeomToEncode = poIntersection;
4051
    std::unique_ptr<OGRGeometry> poGeomSimplified;
4052
    const double dfSimplification =
4053
        bIsMaxZoomForLayer ? m_dfSimplificationMaxZoom : m_dfSimplification;
4054
    if (dfSimplification > 0 &&
4055
        (eGeomType == wkbLineString || eGeomType == wkbMultiLineString ||
4056
         eGeomType == wkbPolygon || eGeomType == wkbMultiPolygon))
4057
    {
4058
        const double dfTol = dfTileDim / m_nExtent;
4059
        poGeomSimplified = std::unique_ptr<OGRGeometry>(
4060
            poIntersection->SimplifyPreserveTopology(dfTol * dfSimplification));
4061
        if (poGeomSimplified.get())
4062
        {
4063
            poGeomToEncode = poGeomSimplified.get();
4064
            eGeomToEncodeType = wkbFlatten(poGeomSimplified->getGeometryType());
4065
        }
4066
    }
4067
4068
    bool bGeomOK = false;
4069
    double dfAreaOrLength = 0.0;
4070
4071
    const auto EmitValidPolygon =
4072
        [this, &bGeomOK, &dfAreaOrLength,
4073
         &poGPBFeature](const OGRGeometry *poValidGeom)
4074
    {
4075
        bGeomOK = false;
4076
        dfAreaOrLength = 0;
4077
        int nLastX = 0;
4078
        int nLastY = 0;
4079
4080
        if (wkbFlatten(poValidGeom->getGeometryType()) == wkbPolygon)
4081
        {
4082
            const OGRPolygon *poPoly = poValidGeom->toPolygon();
4083
            double dfPartArea = 0.0;
4084
            bGeomOK = EncodePolygon(poGPBFeature.get(), poPoly, nullptr, 0, 0,
4085
                                    0, nLastX, nLastY, dfPartArea);
4086
            dfAreaOrLength = dfPartArea;
4087
        }
4088
        else if (OGR_GT_IsSubClassOf(poValidGeom->getGeometryType(),
4089
                                     wkbGeometryCollection))
4090
        {
4091
            for (auto &&poSubGeom : poValidGeom->toGeometryCollection())
4092
            {
4093
                if (wkbFlatten(poSubGeom->getGeometryType()) == wkbPolygon)
4094
                {
4095
                    const OGRPolygon *poPoly = poSubGeom->toPolygon();
4096
                    double dfPartArea = 0.0;
4097
                    bGeomOK |=
4098
                        EncodePolygon(poGPBFeature.get(), poPoly, nullptr, 0, 0,
4099
                                      0, nLastX, nLastY, dfPartArea);
4100
                    dfAreaOrLength += dfPartArea;
4101
                }
4102
                else if (wkbFlatten(poSubGeom->getGeometryType()) ==
4103
                         wkbMultiPolygon)
4104
                {
4105
                    const OGRMultiPolygon *poMPoly =
4106
                        poSubGeom->toMultiPolygon();
4107
                    for (const auto *poPoly : poMPoly)
4108
                    {
4109
                        double dfPartArea = 0.0;
4110
                        bGeomOK |=
4111
                            EncodePolygon(poGPBFeature.get(), poPoly, nullptr,
4112
                                          0, 0, 0, nLastX, nLastY, dfPartArea);
4113
                        dfAreaOrLength += dfPartArea;
4114
                    }
4115
                }
4116
            }
4117
        }
4118
    };
4119
4120
    if (eGeomType == wkbPoint || eGeomType == wkbMultiPoint)
4121
    {
4122
        if (eGeomToEncodeType == wkbPoint)
4123
        {
4124
            const OGRPoint *poPoint = poIntersection->toPoint();
4125
            int nX, nY;
4126
            double dfX = poPoint->getX();
4127
            double dfY = poPoint->getY();
4128
            bGeomOK = true;
4129
            ConvertToTileCoords(dfX, dfY, nX, nY, dfTopX, dfTopY, dfTileDim);
4130
            poGPBFeature->addGeometry(GetCmdCountCombined(knCMD_MOVETO, 1));
4131
            poGPBFeature->addGeometry(EncodeSInt(nX));
4132
            poGPBFeature->addGeometry(EncodeSInt(nY));
4133
        }
4134
        else if (eGeomToEncodeType == wkbMultiPoint ||
4135
                 eGeomToEncodeType == wkbGeometryCollection)
4136
        {
4137
            const OGRGeometryCollection *poGC =
4138
                poIntersection->toGeometryCollection();
4139
            std::set<std::pair<int, int>> oSetUniqueCoords;
4140
            poGPBFeature->addGeometry(
4141
                GetCmdCountCombined(knCMD_MOVETO, 0));  // To be modified later
4142
            int nLastX = 0;
4143
            int nLastY = 0;
4144
            for (auto &&poSubGeom : poGC)
4145
            {
4146
                if (wkbFlatten(poSubGeom->getGeometryType()) == wkbPoint)
4147
                {
4148
                    const OGRPoint *poPoint = poSubGeom->toPoint();
4149
                    int nX, nY;
4150
                    double dfX = poPoint->getX();
4151
                    double dfY = poPoint->getY();
4152
                    ConvertToTileCoords(dfX, dfY, nX, nY, dfTopX, dfTopY,
4153
                                        dfTileDim);
4154
                    if (oSetUniqueCoords.find(std::pair<int, int>(nX, nY)) ==
4155
                        oSetUniqueCoords.end())
4156
                    {
4157
                        oSetUniqueCoords.insert(std::pair<int, int>(nX, nY));
4158
4159
                        int nDiffX = nX - nLastX;
4160
                        int nDiffY = nY - nLastY;
4161
                        poGPBFeature->addGeometry(EncodeSInt(nDiffX));
4162
                        poGPBFeature->addGeometry(EncodeSInt(nDiffY));
4163
                        nLastX = nX;
4164
                        nLastY = nY;
4165
                    }
4166
                }
4167
            }
4168
            GUInt32 nPoints = static_cast<GUInt32>(oSetUniqueCoords.size());
4169
            bGeomOK = nPoints > 0;
4170
            poGPBFeature->setGeometry(
4171
                0, GetCmdCountCombined(knCMD_MOVETO, nPoints));
4172
        }
4173
    }
4174
    else if (eGeomType == wkbLineString || eGeomType == wkbMultiLineString)
4175
    {
4176
        const bool bWriteLastPoint = true;
4177
        const bool bReverseOrder = false;
4178
        const GUInt32 nMinLineTo = 1;
4179
4180
        if (eGeomToEncodeType == wkbLineString)
4181
        {
4182
            const OGRLineString *poLS = poGeomToEncode->toLineString();
4183
            int nLastX = 0;
4184
            int nLastY = 0;
4185
            OGRLineString oOutLS;
4186
            bGeomOK =
4187
                EncodeLineString(poGPBFeature.get(), poLS, &oOutLS,
4188
                                 bWriteLastPoint, bReverseOrder, nMinLineTo,
4189
                                 dfTopX, dfTopY, dfTileDim, nLastX, nLastY);
4190
            dfAreaOrLength = oOutLS.get_Length();
4191
        }
4192
        else if (eGeomToEncodeType == wkbMultiLineString ||
4193
                 eGeomToEncodeType == wkbGeometryCollection)
4194
        {
4195
            const OGRGeometryCollection *poGC =
4196
                poGeomToEncode->toGeometryCollection();
4197
            int nLastX = 0;
4198
            int nLastY = 0;
4199
            for (auto &&poSubGeom : poGC)
4200
            {
4201
                if (wkbFlatten(poSubGeom->getGeometryType()) == wkbLineString)
4202
                {
4203
                    const OGRLineString *poLS = poSubGeom->toLineString();
4204
                    OGRLineString oOutLS;
4205
                    bool bSubGeomOK = EncodeLineString(
4206
                        poGPBFeature.get(), poLS, &oOutLS, bWriteLastPoint,
4207
                        bReverseOrder, nMinLineTo, dfTopX, dfTopY, dfTileDim,
4208
                        nLastX, nLastY);
4209
                    if (bSubGeomOK)
4210
                        dfAreaOrLength += oOutLS.get_Length();
4211
                    bGeomOK |= bSubGeomOK;
4212
                }
4213
            }
4214
        }
4215
    }
4216
    else if (eGeomType == wkbPolygon || eGeomType == wkbMultiPolygon)
4217
    {
4218
        if (eGeomToEncodeType == wkbPolygon)
4219
        {
4220
            const OGRPolygon *poPoly = poGeomToEncode->toPolygon();
4221
            int nLastX = 0;
4222
            int nLastY = 0;
4223
            OGRPolygon oOutPoly;
4224
            const GUInt32 nInitialSize = poGPBFeature->getGeometryCount();
4225
            CPL_IGNORE_RET_VAL(nInitialSize);
4226
            bGeomOK = EncodePolygon(poGPBFeature.get(), poPoly, &oOutPoly,
4227
                                    dfTopX, dfTopY, dfTileDim, nLastX, nLastY,
4228
                                    dfAreaOrLength);
4229
            int bIsValid;
4230
            {
4231
                CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
4232
                bIsValid = oOutPoly.IsValid();
4233
            }
4234
            if (!bIsValid)
4235
            {
4236
                // Build a valid geometry from the initial MVT geometry and emit
4237
                // it
4238
                std::unique_ptr<OGRGeometry> poPolyValid(oOutPoly.MakeValid());
4239
                if (poPolyValid)
4240
                {
4241
                    poGPBFeature->resizeGeometryArray(nInitialSize);
4242
                    EmitValidPolygon(poPolyValid.get());
4243
                }
4244
            }
4245
        }
4246
        else if (eGeomToEncodeType == wkbMultiPolygon ||
4247
                 eGeomToEncodeType == wkbGeometryCollection)
4248
        {
4249
            const OGRGeometryCollection *poGC =
4250
                poGeomToEncode->toGeometryCollection();
4251
            int nLastX = 0;
4252
            int nLastY = 0;
4253
            OGRMultiPolygon oOutMP;
4254
            const GUInt32 nInitialSize = poGPBFeature->getGeometryCount();
4255
            CPL_IGNORE_RET_VAL(nInitialSize);
4256
            for (auto &&poSubGeom : poGC)
4257
            {
4258
                if (wkbFlatten(poSubGeom->getGeometryType()) == wkbPolygon)
4259
                {
4260
                    const OGRPolygon *poPoly = poSubGeom->toPolygon();
4261
                    double dfPartArea = 0.0;
4262
                    auto poOutPoly = std::make_unique<OGRPolygon>();
4263
                    bGeomOK |= EncodePolygon(
4264
                        poGPBFeature.get(), poPoly, poOutPoly.get(), dfTopX,
4265
                        dfTopY, dfTileDim, nLastX, nLastY, dfPartArea);
4266
                    dfAreaOrLength += dfPartArea;
4267
                    oOutMP.addGeometryDirectly(poOutPoly.release());
4268
                }
4269
            }
4270
            int bIsValid;
4271
            {
4272
                CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
4273
                bIsValid = oOutMP.IsValid();
4274
            }
4275
            if (!bIsValid)
4276
            {
4277
                // Build a valid geometry from the initial MVT geometry and emit
4278
                // it
4279
                std::unique_ptr<OGRGeometry> poMPValid(oOutMP.MakeValid());
4280
                if (poMPValid)
4281
                {
4282
                    poGPBFeature->resizeGeometryArray(nInitialSize);
4283
                    EmitValidPolygon(poMPValid.get());
4284
                }
4285
            }
4286
        }
4287
    }
4288
    if (!bGeomOK)
4289
        return OGRERR_NONE;
4290
4291
    for (const auto &pair : poFeatureContent->oValues)
4292
    {
4293
        GUInt32 nKey = poLayer->addKey(pair.first);
4294
        GUInt32 nVal = poLayer->addValue(pair.second);
4295
        poGPBFeature->addTag(nKey);
4296
        poGPBFeature->addTag(nVal);
4297
    }
4298
    if (poFeatureContent->nFID >= 0)
4299
    {
4300
        poGPBFeature->setId(poFeatureContent->nFID);
4301
    }
4302
4303
#ifdef notdef
4304
    {
4305
        MVTTile oTile;
4306
        poLayer->setName("x");
4307
        oTile.addLayer(poLayer);
4308
4309
        CPLString oBuffer(oTile.write());
4310
4311
        VSILFILE *fp = VSIFOpenL(
4312
            CPLSPrintf("/tmp/%d-%d-%d.pbf", nZ, nTileX, nTileY), "wb");
4313
        VSIFWriteL(oBuffer.data(), 1, oBuffer.size(), fp);
4314
        VSIFCloseL(fp);
4315
    }
4316
#endif
4317
4318
    // GPB encode the layer with our single feature
4319
    CPLString oBuffer(poLayer->write());
4320
4321
    // Compress buffer
4322
    size_t nCompressedSize = 0;
4323
    void *pCompressed = CPLZLibDeflate(oBuffer.data(), oBuffer.size(), -1,
4324
                                       nullptr, 0, &nCompressedSize);
4325
    oBuffer.assign(static_cast<char *>(pCompressed), nCompressedSize);
4326
    CPLFree(pCompressed);
4327
4328
    const auto InsertIntoDb = [&]()
4329
    {
4330
        m_nTempTiles++;
4331
        sqlite3_bind_int(m_hInsertStmt, 1, nZ);
4332
        sqlite3_bind_int(m_hInsertStmt, 2, nTileX);
4333
        sqlite3_bind_int(m_hInsertStmt, 3, nTileY);
4334
        sqlite3_bind_text(m_hInsertStmt, 4, osTargetName.c_str(), -1,
4335
                          SQLITE_STATIC);
4336
        sqlite3_bind_int64(m_hInsertStmt, 5, nSerial);
4337
        sqlite3_bind_blob(m_hInsertStmt, 6, oBuffer.data(),
4338
                          static_cast<int>(oBuffer.size()), SQLITE_STATIC);
4339
        sqlite3_bind_int(m_hInsertStmt, 7,
4340
                         static_cast<int>(poGPBFeature->getType()));
4341
        sqlite3_bind_double(m_hInsertStmt, 8, dfAreaOrLength);
4342
        int rc = sqlite3_step(m_hInsertStmt);
4343
        sqlite3_reset(m_hInsertStmt);
4344
        return rc;
4345
    };
4346
4347
    int rc;
4348
    if (m_bThreadPoolOK)
4349
    {
4350
        std::lock_guard<std::mutex> oLock(m_oDBMutex);
4351
        rc = InsertIntoDb();
4352
    }
4353
    else
4354
    {
4355
        rc = InsertIntoDb();
4356
    }
4357
4358
    if (!(rc == SQLITE_OK || rc == SQLITE_DONE))
4359
    {
4360
        return OGRERR_FAILURE;
4361
    }
4362
4363
    return OGRERR_NONE;
4364
}
4365
4366
/************************************************************************/
4367
/*                           MVTWriterTask()                            */
4368
/************************************************************************/
4369
4370
class MVTWriterTask
4371
{
4372
  public:
4373
    const OGRMVTWriterDataset *poDS;
4374
    int nZ;
4375
    int nTileX;
4376
    int nTileY;
4377
    CPLString osTargetName;
4378
    bool bIsMaxZoomForLayer;
4379
    std::shared_ptr<OGRMVTFeatureContent> poFeatureContent;
4380
    GIntBig nSerial;
4381
    std::shared_ptr<OGRGeometry> poGeom;
4382
    OGREnvelope sEnvelope;
4383
};
4384
4385
/************************************************************************/
4386
/*                          WriterTaskFunc()                            */
4387
/************************************************************************/
4388
4389
void OGRMVTWriterDataset::WriterTaskFunc(void *pParam)
4390
{
4391
    MVTWriterTask *poTask = static_cast<MVTWriterTask *>(pParam);
4392
    OGRErr eErr = poTask->poDS->PreGenerateForTileReal(
4393
        poTask->nZ, poTask->nTileX, poTask->nTileY, poTask->osTargetName,
4394
        poTask->bIsMaxZoomForLayer, poTask->poFeatureContent.get(),
4395
        poTask->nSerial, poTask->poGeom.get(), poTask->sEnvelope);
4396
    if (eErr != OGRERR_NONE)
4397
    {
4398
        std::lock_guard oLock(poTask->poDS->m_oDBMutex);
4399
        poTask->poDS->m_bWriteFeatureError = true;
4400
    }
4401
    delete poTask;
4402
}
4403
4404
/************************************************************************/
4405
/*                         PreGenerateForTile()                         */
4406
/************************************************************************/
4407
4408
OGRErr OGRMVTWriterDataset::PreGenerateForTile(
4409
    int nZ, int nTileX, int nTileY, const CPLString &osTargetName,
4410
    bool bIsMaxZoomForLayer,
4411
    const std::shared_ptr<OGRMVTFeatureContent> &poFeatureContent,
4412
    GIntBig nSerial, const std::shared_ptr<OGRGeometry> &poGeom,
4413
    const OGREnvelope &sEnvelope) const
4414
{
4415
    if (!m_bThreadPoolOK)
4416
    {
4417
        return PreGenerateForTileReal(
4418
            nZ, nTileX, nTileY, osTargetName, bIsMaxZoomForLayer,
4419
            poFeatureContent.get(), nSerial, poGeom.get(), sEnvelope);
4420
    }
4421
    else
4422
    {
4423
        MVTWriterTask *poTask = new MVTWriterTask;
4424
        poTask->poDS = this;
4425
        poTask->nZ = nZ;
4426
        poTask->nTileX = nTileX;
4427
        poTask->nTileY = nTileY;
4428
        poTask->osTargetName = osTargetName;
4429
        poTask->bIsMaxZoomForLayer = bIsMaxZoomForLayer;
4430
        poTask->poFeatureContent = poFeatureContent;
4431
        poTask->nSerial = nSerial;
4432
        poTask->poGeom = poGeom;
4433
        poTask->sEnvelope = sEnvelope;
4434
        m_oThreadPool.SubmitJob(OGRMVTWriterDataset::WriterTaskFunc, poTask);
4435
        // Do not queue more than 1000 jobs to avoid memory exhaustion
4436
        m_oThreadPool.WaitCompletion(1000);
4437
4438
        std::lock_guard oLock(m_oDBMutex);
4439
        return m_bWriteFeatureError ? OGRERR_FAILURE : OGRERR_NONE;
4440
    }
4441
}
4442
4443
/************************************************************************/
4444
/*                        UpdateLayerProperties()                       */
4445
/************************************************************************/
4446
4447
void OGRMVTWriterDataset::UpdateLayerProperties(
4448
    MVTLayerProperties *poLayerProperties, const std::string &osKey,
4449
    const MVTTileLayerValue &oValue)
4450
{
4451
    auto oFieldIter = poLayerProperties->m_oMapFieldNameToIdx.find(osKey);
4452
    MVTFieldProperties *poFieldProps = nullptr;
4453
    if (oFieldIter == poLayerProperties->m_oMapFieldNameToIdx.end())
4454
    {
4455
        if (poLayerProperties->m_oSetFields.size() < knMAX_COUNT_FIELDS)
4456
        {
4457
            poLayerProperties->m_oSetFields.insert(osKey);
4458
            if (poLayerProperties->m_oMapFieldNameToIdx.size() <
4459
                knMAX_REPORT_FIELDS)
4460
            {
4461
                MVTFieldProperties oFieldProps;
4462
                oFieldProps.m_osName = osKey;
4463
                if (oValue.isNumeric())
4464
                {
4465
                    oFieldProps.m_dfMinVal = oValue.getNumericValue();
4466
                    oFieldProps.m_dfMaxVal = oValue.getNumericValue();
4467
                    oFieldProps.m_bAllInt = true;  // overridden just below
4468
                }
4469
                oFieldProps.m_eType =
4470
                    oValue.isNumeric()  ? MVTTileLayerValue::ValueType::DOUBLE
4471
                    : oValue.isString() ? MVTTileLayerValue::ValueType::STRING
4472
                                        : MVTTileLayerValue::ValueType::BOOL;
4473
4474
                poLayerProperties->m_oMapFieldNameToIdx[osKey] =
4475
                    poLayerProperties->m_aoFields.size();
4476
                poLayerProperties->m_aoFields.push_back(std::move(oFieldProps));
4477
                poFieldProps = &(poLayerProperties->m_aoFields.back());
4478
            }
4479
        }
4480
    }
4481
    else
4482
    {
4483
        poFieldProps = &(poLayerProperties->m_aoFields[oFieldIter->second]);
4484
    }
4485
4486
    if (poFieldProps)
4487
    {
4488
        if (oValue.getType() == MVTTileLayerValue::ValueType::BOOL)
4489
        {
4490
            MVTTileLayerValue oUniqVal;
4491
            oUniqVal.setBoolValue(oValue.getBoolValue());
4492
            poFieldProps->m_oSetAllValues.insert(oUniqVal);
4493
            poFieldProps->m_oSetValues.insert(oUniqVal);
4494
        }
4495
        else if (oValue.isNumeric())
4496
        {
4497
            if (poFieldProps->m_bAllInt)
4498
            {
4499
                poFieldProps->m_bAllInt =
4500
                    oValue.getType() == MVTTileLayerValue::ValueType::INT ||
4501
                    oValue.getType() == MVTTileLayerValue::ValueType::SINT ||
4502
                    (oValue.getType() == MVTTileLayerValue::ValueType::UINT &&
4503
                     oValue.getUIntValue() < GINT64_MAX);
4504
            }
4505
            double dfVal = oValue.getNumericValue();
4506
            poFieldProps->m_dfMinVal =
4507
                std::min(poFieldProps->m_dfMinVal, dfVal);
4508
            poFieldProps->m_dfMaxVal =
4509
                std::max(poFieldProps->m_dfMaxVal, dfVal);
4510
            if (poFieldProps->m_oSetAllValues.size() < knMAX_COUNT_VALUES)
4511
            {
4512
                MVTTileLayerValue oUniqVal;
4513
                oUniqVal.setDoubleValue(dfVal);
4514
                poFieldProps->m_oSetAllValues.insert(oUniqVal);
4515
                if (poFieldProps->m_oSetValues.size() < knMAX_REPORT_VALUES)
4516
                {
4517
                    poFieldProps->m_oSetValues.insert(oUniqVal);
4518
                }
4519
            }
4520
        }
4521
        else if (oValue.isString() &&
4522
                 poFieldProps->m_oSetAllValues.size() < knMAX_COUNT_VALUES)
4523
        {
4524
            auto osVal = oValue.getStringValue();
4525
            MVTTileLayerValue oUniqVal;
4526
            oUniqVal.setStringValue(osVal);
4527
            poFieldProps->m_oSetAllValues.insert(oUniqVal);
4528
            if (osVal.size() <= knMAX_STRING_VALUE_LENGTH &&
4529
                poFieldProps->m_oSetValues.size() < knMAX_REPORT_VALUES)
4530
            {
4531
                poFieldProps->m_oSetValues.insert(oUniqVal);
4532
            }
4533
        }
4534
    }
4535
}
4536
4537
/************************************************************************/
4538
/*                           GZIPCompress()                             */
4539
/************************************************************************/
4540
4541
static void GZIPCompress(std::string &oTileBuffer)
4542
{
4543
    if (!oTileBuffer.empty())
4544
    {
4545
        const CPLString osTmpFilename(
4546
            VSIMemGenerateHiddenFilename("mvt_temp.gz"));
4547
        CPLString osTmpGZipFilename("/vsigzip/" + osTmpFilename);
4548
        VSILFILE *fpGZip = VSIFOpenL(osTmpGZipFilename, "wb");
4549
        if (fpGZip)
4550
        {
4551
            VSIFWriteL(oTileBuffer.data(), 1, oTileBuffer.size(), fpGZip);
4552
            VSIFCloseL(fpGZip);
4553
4554
            vsi_l_offset nCompressedSize = 0;
4555
            GByte *pabyCompressed =
4556
                VSIGetMemFileBuffer(osTmpFilename, &nCompressedSize, false);
4557
            oTileBuffer.assign(reinterpret_cast<char *>(pabyCompressed),
4558
                               static_cast<size_t>(nCompressedSize));
4559
        }
4560
        VSIUnlink(osTmpFilename);
4561
    }
4562
}
4563
4564
/************************************************************************/
4565
/*                     GetReducedPrecisionGeometry()                    */
4566
/************************************************************************/
4567
4568
static std::vector<GUInt32>
4569
GetReducedPrecisionGeometry(MVTTileLayerFeature::GeomType eGeomType,
4570
                            const std::vector<GUInt32> &anSrcGeometry,
4571
                            GUInt32 nSrcExtent, GUInt32 nDstExtent)
4572
{
4573
    std::vector<GUInt32> anDstGeometry;
4574
    size_t nLastMoveToIdx = 0;
4575
    int nX = 0;
4576
    int nY = 0;
4577
    int nFirstReducedX = 0;
4578
    int nFirstReducedY = 0;
4579
    int nLastReducedX = 0;
4580
    int nLastReducedY = 0;
4581
    int nLastReducedXValid = 0;
4582
    int nLastReducedYValid = 0;
4583
    std::unique_ptr<OGRLinearRing> poInRing;
4584
    std::unique_ptr<OGRLinearRing> poOutRing;
4585
    std::unique_ptr<OGRLinearRing> poOutOuterRing;
4586
    bool bDiscardInnerRings = false;
4587
    const bool bIsPoly = eGeomType == MVTTileLayerFeature::GeomType::POLYGON;
4588
    for (size_t iSrc = 0; iSrc < anSrcGeometry.size();)
4589
    {
4590
        const unsigned nCount = GetCmdCount(anSrcGeometry[iSrc]);
4591
        switch (GetCmdId(anSrcGeometry[iSrc]))
4592
        {
4593
            case knCMD_MOVETO:
4594
            {
4595
                nLastMoveToIdx = anDstGeometry.size();
4596
4597
                anDstGeometry.push_back(anSrcGeometry[iSrc]);
4598
                iSrc++;
4599
4600
                unsigned nDstPoints = 0;
4601
                for (unsigned j = 0;
4602
                     iSrc + 1 < anSrcGeometry.size() && j < nCount;
4603
                     j++, iSrc += 2)
4604
                {
4605
                    nX += DecodeSInt(anSrcGeometry[iSrc]);
4606
                    nY += DecodeSInt(anSrcGeometry[iSrc + 1]);
4607
4608
                    int nReducedX = static_cast<int>(static_cast<GIntBig>(nX) *
4609
                                                     nDstExtent / nSrcExtent);
4610
                    int nReducedY = static_cast<int>(static_cast<GIntBig>(nY) *
4611
                                                     nDstExtent / nSrcExtent);
4612
                    int nDiffX = nReducedX - nLastReducedX;
4613
                    int nDiffY = nReducedY - nLastReducedY;
4614
                    if (j == 0)
4615
                    {
4616
                        if (bIsPoly)
4617
                        {
4618
                            poInRing = std::unique_ptr<OGRLinearRing>(
4619
                                new OGRLinearRing());
4620
                            poOutRing = std::unique_ptr<OGRLinearRing>(
4621
                                new OGRLinearRing());
4622
                        }
4623
                        nFirstReducedX = nReducedX;
4624
                        nFirstReducedY = nReducedY;
4625
                    }
4626
                    if (j == 0 || nDiffX != 0 || nDiffY != 0)
4627
                    {
4628
                        if (bIsPoly)
4629
                        {
4630
                            poInRing->addPoint(nX, nY);
4631
                            poOutRing->addPoint(nReducedX, nReducedY);
4632
                        }
4633
                        nDstPoints++;
4634
                        anDstGeometry.push_back(EncodeSInt(nDiffX));
4635
                        anDstGeometry.push_back(EncodeSInt(nDiffY));
4636
                        nLastReducedX = nReducedX;
4637
                        nLastReducedY = nReducedY;
4638
                    }
4639
                }
4640
                // Patch count of MOVETO
4641
                anDstGeometry[nLastMoveToIdx] = GetCmdCountCombined(
4642
                    GetCmdId(anDstGeometry[nLastMoveToIdx]), nDstPoints);
4643
                break;
4644
            }
4645
            case knCMD_LINETO:
4646
            {
4647
                size_t nIdxToPatch = anDstGeometry.size();
4648
                anDstGeometry.push_back(anSrcGeometry[iSrc]);
4649
                iSrc++;
4650
                unsigned nDstPoints = 0;
4651
                int nLastReducedXBefore = nLastReducedX;
4652
                int nLastReducedYBefore = nLastReducedY;
4653
                for (unsigned j = 0;
4654
                     iSrc + 1 < anSrcGeometry.size() && j < nCount;
4655
                     j++, iSrc += 2)
4656
                {
4657
                    nX += DecodeSInt(anSrcGeometry[iSrc]);
4658
                    nY += DecodeSInt(anSrcGeometry[iSrc + 1]);
4659
4660
                    int nReducedX = static_cast<int>(static_cast<GIntBig>(nX) *
4661
                                                     nDstExtent / nSrcExtent);
4662
                    int nReducedY = static_cast<int>(static_cast<GIntBig>(nY) *
4663
                                                     nDstExtent / nSrcExtent);
4664
                    int nDiffX = nReducedX - nLastReducedX;
4665
                    int nDiffY = nReducedY - nLastReducedY;
4666
                    if (nDiffX != 0 || nDiffY != 0)
4667
                    {
4668
                        if (bIsPoly)
4669
                        {
4670
                            CPLAssert(poInRing);
4671
                            CPLAssert(poOutRing);
4672
                            poInRing->addPoint(nX, nY);
4673
                            poOutRing->addPoint(nReducedX, nReducedY);
4674
                        }
4675
                        nDstPoints++;
4676
                        anDstGeometry.push_back(EncodeSInt(nDiffX));
4677
                        anDstGeometry.push_back(EncodeSInt(nDiffY));
4678
                        nLastReducedXBefore = nLastReducedX;
4679
                        nLastReducedYBefore = nLastReducedY;
4680
                        nLastReducedX = nReducedX;
4681
                        nLastReducedY = nReducedY;
4682
                    }
4683
                }
4684
4685
                // If last point of ring is identical to first one, discard it
4686
                if (nDstPoints > 0 && bIsPoly &&
4687
                    nLastReducedX == nFirstReducedX &&
4688
                    nLastReducedY == nFirstReducedY)
4689
                {
4690
                    nLastReducedX = nLastReducedXBefore;
4691
                    nLastReducedY = nLastReducedYBefore;
4692
                    nDstPoints -= 1;
4693
                    anDstGeometry.resize(anDstGeometry.size() - 2);
4694
                    poOutRing->setNumPoints(poOutRing->getNumPoints() - 1);
4695
                }
4696
4697
                // Patch count of LINETO
4698
                anDstGeometry[nIdxToPatch] = GetCmdCountCombined(
4699
                    GetCmdId(anDstGeometry[nIdxToPatch]), nDstPoints);
4700
4701
                // A valid linestring should have at least one MOVETO +
4702
                // one coord pair + one LINETO + one coord pair
4703
                if (eGeomType == MVTTileLayerFeature::GeomType::LINESTRING)
4704
                {
4705
                    if (anDstGeometry.size() < nLastMoveToIdx + 1 + 2 + 1 + 2)
4706
                    {
4707
                        // Remove last linestring
4708
                        nLastReducedX = nLastReducedXValid;
4709
                        nLastReducedY = nLastReducedYValid;
4710
                        anDstGeometry.resize(nLastMoveToIdx);
4711
                    }
4712
                    else
4713
                    {
4714
                        nLastReducedXValid = nLastReducedX;
4715
                        nLastReducedYValid = nLastReducedY;
4716
                    }
4717
                }
4718
4719
                break;
4720
            }
4721
            case knCMD_CLOSEPATH:
4722
            {
4723
                CPLAssert(bIsPoly);
4724
                CPLAssert(poInRing);
4725
                CPLAssert(poOutRing);
4726
                int bIsValid = true;
4727
4728
                // A valid ring should have at least one MOVETO + one
4729
                // coord pair + one LINETO + two coord pairs
4730
                if (anDstGeometry.size() < nLastMoveToIdx + 1 + 2 + 1 + 2 * 2)
4731
                {
4732
                    // Remove ring. Normally if we remove an outer ring,
4733
                    // its inner rings should also be removed, given they are
4734
                    // smaller than the outer ring.
4735
                    bIsValid = false;
4736
                }
4737
                else
4738
                {
4739
                    poInRing->closeRings();
4740
                    poOutRing->closeRings();
4741
                    bool bIsOuterRing = !poInRing->isClockwise();
4742
                    // Normally the first ring of a polygon geometry should
4743
                    // be a outer ring, except when it is degenerate enough
4744
                    // in which case poOutOuterRing might be null.
4745
                    if (bIsOuterRing)
4746
                    {
4747
                        // if the outer ring turned out to be a inner ring
4748
                        // once reduced
4749
                        if (poOutRing->isClockwise())
4750
                        {
4751
                            bIsValid = false;
4752
                            bDiscardInnerRings = true;
4753
                        }
4754
                        else
4755
                        {
4756
                            OGRPolygon oPoly;
4757
                            oPoly.addRing(poOutRing.get());
4758
                            poOutOuterRing = std::unique_ptr<OGRLinearRing>(
4759
                                poOutRing.release());
4760
                            {
4761
                                CPLErrorStateBackuper oErrorStateBackuper(
4762
                                    CPLQuietErrorHandler);
4763
                                bIsValid = oPoly.IsValid();
4764
                            }
4765
                            bDiscardInnerRings = !bIsValid;
4766
                        }
4767
                    }
4768
                    else if (bDiscardInnerRings ||
4769
                             poOutOuterRing.get() == nullptr ||
4770
                             // if the inner ring turned out to be a outer ring
4771
                             // once reduced
4772
                             !poOutRing->isClockwise())
4773
                    {
4774
                        bIsValid = false;
4775
                    }
4776
                    else
4777
                    {
4778
                        OGRPolygon oPoly;
4779
                        oPoly.addRing(poOutOuterRing.get());
4780
                        oPoly.addRingDirectly(poOutRing.release());
4781
                        {
4782
                            CPLErrorStateBackuper oErrorStateBackuper(
4783
                                CPLQuietErrorHandler);
4784
                            bIsValid = oPoly.IsValid();
4785
                        }
4786
                    }
4787
                }
4788
4789
                if (bIsValid)
4790
                {
4791
                    nLastReducedXValid = nLastReducedX;
4792
                    nLastReducedYValid = nLastReducedY;
4793
                    anDstGeometry.push_back(anSrcGeometry[iSrc]);
4794
                }
4795
                else
4796
                {
4797
                    // Remove this ring
4798
                    nLastReducedX = nLastReducedXValid;
4799
                    nLastReducedY = nLastReducedYValid;
4800
                    anDstGeometry.resize(nLastMoveToIdx);
4801
                }
4802
4803
                iSrc++;
4804
                break;
4805
            }
4806
            default:
4807
            {
4808
                CPLAssert(false);
4809
                break;
4810
            }
4811
        }
4812
    }
4813
4814
    return anDstGeometry;
4815
}
4816
4817
/************************************************************************/
4818
/*                          EncodeFeature()                             */
4819
/************************************************************************/
4820
4821
void OGRMVTWriterDataset::EncodeFeature(
4822
    const void *pabyBlob, int nBlobSize,
4823
    std::shared_ptr<MVTTileLayer> &poTargetLayer,
4824
    std::map<CPLString, GUInt32> &oMapKeyToIdx,
4825
    std::map<MVTTileLayerValue, GUInt32> &oMapValueToIdx,
4826
    MVTLayerProperties *poLayerProperties, GUInt32 nExtent,
4827
    unsigned &nFeaturesInTile)
4828
{
4829
    size_t nUncompressedSize = 0;
4830
    void *pCompressed =
4831
        CPLZLibInflate(pabyBlob, nBlobSize, nullptr, 0, &nUncompressedSize);
4832
    GByte *pabyUncompressed = static_cast<GByte *>(pCompressed);
4833
4834
    MVTTileLayer oSrcTileLayer;
4835
    if (nUncompressedSize &&
4836
        oSrcTileLayer.read(pabyUncompressed,
4837
                           pabyUncompressed + nUncompressedSize))
4838
    {
4839
        const auto &srcFeatures = oSrcTileLayer.getFeatures();
4840
        if (srcFeatures.size() == 1)  // should always be true !
4841
        {
4842
            const auto &poSrcFeature = srcFeatures[0];
4843
            std::shared_ptr<MVTTileLayerFeature> poFeature(
4844
                new MVTTileLayerFeature());
4845
4846
            if (poSrcFeature->hasId())
4847
                poFeature->setId(poSrcFeature->getId());
4848
            poFeature->setType(poSrcFeature->getType());
4849
            if (poLayerProperties)
4850
            {
4851
                poLayerProperties->m_oCountGeomType[poSrcFeature->getType()]++;
4852
            }
4853
            bool bOK = true;
4854
            if (nExtent < m_nExtent)
4855
            {
4856
#ifdef for_debugging
4857
                const auto &srcKeys = oSrcTileLayer.getKeys();
4858
                const auto &srcValues = oSrcTileLayer.getValues();
4859
                const auto &anSrcTags = poSrcFeature->getTags();
4860
                for (size_t i = 0; i + 1 < anSrcTags.size(); i += 2)
4861
                {
4862
                    GUInt32 nSrcIdxKey = anSrcTags[i];
4863
                    GUInt32 nSrcIdxValue = anSrcTags[i + 1];
4864
                    if (nSrcIdxKey < srcKeys.size() &&
4865
                        nSrcIdxValue < srcValues.size())
4866
                    {
4867
                        auto &osKey = srcKeys[nSrcIdxKey];
4868
                        auto &oValue = srcValues[nSrcIdxValue];
4869
                        if (osKey == "tunnus" &&
4870
                            oValue.getUIntValue() == 28799760)
4871
                        {
4872
                            printf("foo\n"); /* ok */
4873
                            break;
4874
                        }
4875
                    }
4876
                }
4877
#endif
4878
4879
                poFeature->setGeometry(GetReducedPrecisionGeometry(
4880
                    poSrcFeature->getType(), poSrcFeature->getGeometry(),
4881
                    m_nExtent, nExtent));
4882
                if (poFeature->getGeometry().empty())
4883
                {
4884
                    bOK = false;
4885
                }
4886
            }
4887
            else
4888
            {
4889
                poFeature->setGeometry(poSrcFeature->getGeometry());
4890
            }
4891
            if (bOK)
4892
            {
4893
                const auto &srcKeys = oSrcTileLayer.getKeys();
4894
                for (const auto &osKey : srcKeys)
4895
                {
4896
                    auto oIter = oMapKeyToIdx.find(osKey);
4897
                    if (oIter == oMapKeyToIdx.end())
4898
                    {
4899
                        oMapKeyToIdx[osKey] = poTargetLayer->addKey(osKey);
4900
                    }
4901
                }
4902
4903
                const auto &srcValues = oSrcTileLayer.getValues();
4904
                for (const auto &oValue : srcValues)
4905
                {
4906
                    auto oIter = oMapValueToIdx.find(oValue);
4907
                    if (oIter == oMapValueToIdx.end())
4908
                    {
4909
                        oMapValueToIdx[oValue] =
4910
                            poTargetLayer->addValue(oValue);
4911
                    }
4912
                }
4913
4914
                const auto &anSrcTags = poSrcFeature->getTags();
4915
                for (size_t i = 0; i + 1 < anSrcTags.size(); i += 2)
4916
                {
4917
                    GUInt32 nSrcIdxKey = anSrcTags[i];
4918
                    GUInt32 nSrcIdxValue = anSrcTags[i + 1];
4919
                    if (nSrcIdxKey < srcKeys.size() &&
4920
                        nSrcIdxValue < srcValues.size())
4921
                    {
4922
                        const auto &osKey = srcKeys[nSrcIdxKey];
4923
                        const auto &oValue = srcValues[nSrcIdxValue];
4924
4925
                        if (poLayerProperties)
4926
                        {
4927
                            UpdateLayerProperties(poLayerProperties, osKey,
4928
                                                  oValue);
4929
                        }
4930
4931
                        poFeature->addTag(oMapKeyToIdx[osKey]);
4932
                        poFeature->addTag(oMapValueToIdx[oValue]);
4933
                    }
4934
                }
4935
4936
                nFeaturesInTile++;
4937
                poTargetLayer->addFeature(std::move(poFeature));
4938
            }
4939
        }
4940
    }
4941
    else
4942
    {
4943
        // Shouldn't fail
4944
        CPLError(CE_Failure, CPLE_AppDefined, "Deserialization failure");
4945
    }
4946
4947
    CPLFree(pabyUncompressed);
4948
}
4949
4950
/************************************************************************/
4951
/*                            EncodeTile()                              */
4952
/************************************************************************/
4953
4954
std::string OGRMVTWriterDataset::EncodeTile(
4955
    int nZ, int nX, int nY, sqlite3_stmt *hStmtLayer, sqlite3_stmt *hStmtRows,
4956
    std::map<CPLString, MVTLayerProperties> &oMapLayerProps,
4957
    std::set<CPLString> &oSetLayers, GIntBig &nTempTilesRead)
4958
{
4959
    MVTTile oTargetTile;
4960
4961
    sqlite3_bind_int(hStmtLayer, 1, nZ);
4962
    sqlite3_bind_int(hStmtLayer, 2, nX);
4963
    sqlite3_bind_int(hStmtLayer, 3, nY);
4964
4965
    unsigned nFeaturesInTile = 0;
4966
    const GIntBig nProgressStep =
4967
        std::max(static_cast<GIntBig>(1), m_nTempTiles / 10);
4968
4969
    while (nFeaturesInTile < m_nMaxFeatures &&
4970
           sqlite3_step(hStmtLayer) == SQLITE_ROW)
4971
    {
4972
        const char *pszLayerName =
4973
            reinterpret_cast<const char *>(sqlite3_column_text(hStmtLayer, 0));
4974
        sqlite3_bind_int(hStmtRows, 1, nZ);
4975
        sqlite3_bind_int(hStmtRows, 2, nX);
4976
        sqlite3_bind_int(hStmtRows, 3, nY);
4977
        sqlite3_bind_text(hStmtRows, 4, pszLayerName, -1, SQLITE_STATIC);
4978
4979
        auto oIterMapLayerProps = oMapLayerProps.find(pszLayerName);
4980
        MVTLayerProperties *poLayerProperties = nullptr;
4981
        if (oIterMapLayerProps == oMapLayerProps.end())
4982
        {
4983
            if (oSetLayers.size() < knMAX_COUNT_LAYERS)
4984
            {
4985
                oSetLayers.insert(pszLayerName);
4986
                if (oMapLayerProps.size() < knMAX_REPORT_LAYERS)
4987
                {
4988
                    MVTLayerProperties props;
4989
                    props.m_nMinZoom = nZ;
4990
                    props.m_nMaxZoom = nZ;
4991
                    oMapLayerProps[pszLayerName] = std::move(props);
4992
                    poLayerProperties = &(oMapLayerProps[pszLayerName]);
4993
                }
4994
            }
4995
        }
4996
        else
4997
        {
4998
            poLayerProperties = &(oIterMapLayerProps->second);
4999
        }
5000
        if (poLayerProperties)
5001
        {
5002
            poLayerProperties->m_nMinZoom =
5003
                std::min(nZ, poLayerProperties->m_nMinZoom);
5004
            poLayerProperties->m_nMaxZoom =
5005
                std::max(nZ, poLayerProperties->m_nMaxZoom);
5006
        }
5007
5008
        std::shared_ptr<MVTTileLayer> poTargetLayer(new MVTTileLayer());
5009
        oTargetTile.addLayer(poTargetLayer);
5010
        poTargetLayer->setName(pszLayerName);
5011
        poTargetLayer->setVersion(m_nMVTVersion);
5012
        poTargetLayer->setExtent(m_nExtent);
5013
5014
        std::map<CPLString, GUInt32> oMapKeyToIdx;
5015
        std::map<MVTTileLayerValue, GUInt32> oMapValueToIdx;
5016
5017
        while (nFeaturesInTile < m_nMaxFeatures &&
5018
               sqlite3_step(hStmtRows) == SQLITE_ROW)
5019
        {
5020
            int nBlobSize = sqlite3_column_bytes(hStmtRows, 0);
5021
            const void *pabyBlob = sqlite3_column_blob(hStmtRows, 0);
5022
5023
            EncodeFeature(pabyBlob, nBlobSize, poTargetLayer, oMapKeyToIdx,
5024
                          oMapValueToIdx, poLayerProperties, m_nExtent,
5025
                          nFeaturesInTile);
5026
5027
            nTempTilesRead++;
5028
            if (nTempTilesRead == m_nTempTiles ||
5029
                (nTempTilesRead % nProgressStep) == 0)
5030
            {
5031
                const int nPct =
5032
                    static_cast<int>((100 * nTempTilesRead) / m_nTempTiles);
5033
                CPLDebug("MVT", "%d%%...", nPct);
5034
            }
5035
        }
5036
        sqlite3_reset(hStmtRows);
5037
    }
5038
5039
    sqlite3_reset(hStmtLayer);
5040
5041
    std::string oTileBuffer(oTargetTile.write());
5042
    size_t nSizeBefore = oTileBuffer.size();
5043
    if (m_bGZip)
5044
        GZIPCompress(oTileBuffer);
5045
    const size_t nSizeAfter = oTileBuffer.size();
5046
    const double dfCompressionRatio =
5047
        static_cast<double>(nSizeAfter) / nSizeBefore;
5048
5049
    const bool bTooManyFeatures = nFeaturesInTile >= m_nMaxFeatures;
5050
    if (bTooManyFeatures && !m_bMaxFeaturesOptSpecified)
5051
    {
5052
        m_bMaxFeaturesOptSpecified = true;
5053
        CPLError(CE_Warning, CPLE_AppDefined,
5054
                 "At least one tile exceeded the default maximum number of "
5055
                 "features per tile (%u) and was truncated to satisfy it.",
5056
                 m_nMaxFeatures);
5057
    }
5058
5059
    // If the tile size is above the allowed values or there are too many
5060
    // features, then sort by descending area / length until we get to the
5061
    // limit.
5062
    bool bTooBigTile = oTileBuffer.size() > m_nMaxTileSize;
5063
    if (bTooBigTile && !m_bMaxTileSizeOptSpecified)
5064
    {
5065
        m_bMaxTileSizeOptSpecified = true;
5066
        CPLError(CE_Warning, CPLE_AppDefined,
5067
                 "At least one tile exceeded the default maximum tile size of "
5068
                 "%u bytes and was encoded at lower resolution",
5069
                 m_nMaxTileSize);
5070
    }
5071
5072
    GUInt32 nExtent = m_nExtent;
5073
    while (bTooBigTile && !bTooManyFeatures && nExtent >= 256)
5074
    {
5075
        nExtent /= 2;
5076
        nSizeBefore = oTileBuffer.size();
5077
        oTileBuffer = RecodeTileLowerResolution(nZ, nX, nY, nExtent, hStmtLayer,
5078
                                                hStmtRows);
5079
        bTooBigTile = oTileBuffer.size() > m_nMaxTileSize;
5080
        CPLDebug("MVT",
5081
                 "Recoding tile %d/%d/%d with extent = %u. "
5082
                 "From %u to %u bytes",
5083
                 nZ, nX, nY, nExtent, static_cast<unsigned>(nSizeBefore),
5084
                 static_cast<unsigned>(oTileBuffer.size()));
5085
    }
5086
5087
    if (bTooBigTile || bTooManyFeatures)
5088
    {
5089
        if (bTooBigTile)
5090
        {
5091
            CPLDebug("MVT", "For tile %d/%d/%d, tile size is %u > %u", nZ, nX,
5092
                     nY, static_cast<unsigned>(oTileBuffer.size()),
5093
                     m_nMaxTileSize);
5094
        }
5095
        if (bTooManyFeatures)
5096
        {
5097
            CPLDebug("MVT",
5098
                     "For tile %d/%d/%d, feature count limit of %u is reached",
5099
                     nZ, nX, nY, m_nMaxFeatures);
5100
        }
5101
5102
        oTargetTile.clear();
5103
5104
        const unsigned nTotalFeaturesInTile =
5105
            std::min(m_nMaxFeatures, nFeaturesInTile);
5106
        char *pszSQL =
5107
            sqlite3_mprintf("SELECT layer, feature FROM temp "
5108
                            "WHERE z = %d AND x = %d AND y = %d ORDER BY "
5109
                            "area_or_length DESC LIMIT %d",
5110
                            nZ, nX, nY, nTotalFeaturesInTile);
5111
        sqlite3_stmt *hTmpStmt = nullptr;
5112
        CPL_IGNORE_RET_VAL(
5113
            sqlite3_prepare_v2(m_hDB, pszSQL, -1, &hTmpStmt, nullptr));
5114
        sqlite3_free(pszSQL);
5115
        if (!hTmpStmt)
5116
            return std::string();
5117
5118
        class TargetTileLayerProps
5119
        {
5120
          public:
5121
            std::shared_ptr<MVTTileLayer> m_poLayer;
5122
            std::map<CPLString, GUInt32> m_oMapKeyToIdx;
5123
            std::map<MVTTileLayerValue, GUInt32> m_oMapValueToIdx;
5124
        };
5125
5126
        std::map<std::string, TargetTileLayerProps> oMapLayerNameToTargetLayer;
5127
5128
        nFeaturesInTile = 0;
5129
        const unsigned nCheckStep = std::max(1U, nTotalFeaturesInTile / 100);
5130
        while (sqlite3_step(hTmpStmt) == SQLITE_ROW)
5131
        {
5132
            const char *pszLayerName = reinterpret_cast<const char *>(
5133
                sqlite3_column_text(hTmpStmt, 0));
5134
            int nBlobSize = sqlite3_column_bytes(hTmpStmt, 1);
5135
            const void *pabyBlob = sqlite3_column_blob(hTmpStmt, 1);
5136
5137
            std::shared_ptr<MVTTileLayer> poTargetLayer;
5138
            std::map<CPLString, GUInt32> *poMapKeyToIdx;
5139
            std::map<MVTTileLayerValue, GUInt32> *poMapValueToIdx;
5140
            auto oIter = oMapLayerNameToTargetLayer.find(pszLayerName);
5141
            if (oIter == oMapLayerNameToTargetLayer.end())
5142
            {
5143
                poTargetLayer =
5144
                    std::shared_ptr<MVTTileLayer>(new MVTTileLayer());
5145
                TargetTileLayerProps props;
5146
                props.m_poLayer = poTargetLayer;
5147
                oTargetTile.addLayer(poTargetLayer);
5148
                poTargetLayer->setName(pszLayerName);
5149
                poTargetLayer->setVersion(m_nMVTVersion);
5150
                poTargetLayer->setExtent(nExtent);
5151
                oMapLayerNameToTargetLayer[pszLayerName] = std::move(props);
5152
                poMapKeyToIdx =
5153
                    &oMapLayerNameToTargetLayer[pszLayerName].m_oMapKeyToIdx;
5154
                poMapValueToIdx =
5155
                    &oMapLayerNameToTargetLayer[pszLayerName].m_oMapValueToIdx;
5156
            }
5157
            else
5158
            {
5159
                poTargetLayer = oIter->second.m_poLayer;
5160
                poMapKeyToIdx = &oIter->second.m_oMapKeyToIdx;
5161
                poMapValueToIdx = &oIter->second.m_oMapValueToIdx;
5162
            }
5163
5164
            EncodeFeature(pabyBlob, nBlobSize, poTargetLayer, *poMapKeyToIdx,
5165
                          *poMapValueToIdx, nullptr, nExtent, nFeaturesInTile);
5166
5167
            if (nFeaturesInTile == nTotalFeaturesInTile ||
5168
                (bTooBigTile && (nFeaturesInTile % nCheckStep == 0)))
5169
            {
5170
                if (oTargetTile.getSize() * dfCompressionRatio > m_nMaxTileSize)
5171
                {
5172
                    break;
5173
                }
5174
            }
5175
        }
5176
5177
        oTileBuffer = oTargetTile.write();
5178
        if (m_bGZip)
5179
            GZIPCompress(oTileBuffer);
5180
5181
        if (bTooBigTile)
5182
        {
5183
            CPLDebug("MVT", "For tile %d/%d/%d, final tile size is %u", nZ, nX,
5184
                     nY, static_cast<unsigned>(oTileBuffer.size()));
5185
        }
5186
5187
        sqlite3_finalize(hTmpStmt);
5188
    }
5189
5190
    return oTileBuffer;
5191
}
5192
5193
/************************************************************************/
5194
/*                    RecodeTileLowerResolution()                       */
5195
/************************************************************************/
5196
5197
std::string OGRMVTWriterDataset::RecodeTileLowerResolution(
5198
    int nZ, int nX, int nY, int nExtent, sqlite3_stmt *hStmtLayer,
5199
    sqlite3_stmt *hStmtRows)
5200
{
5201
    MVTTile oTargetTile;
5202
5203
    sqlite3_bind_int(hStmtLayer, 1, nZ);
5204
    sqlite3_bind_int(hStmtLayer, 2, nX);
5205
    sqlite3_bind_int(hStmtLayer, 3, nY);
5206
5207
    unsigned nFeaturesInTile = 0;
5208
    while (nFeaturesInTile < m_nMaxFeatures &&
5209
           sqlite3_step(hStmtLayer) == SQLITE_ROW)
5210
    {
5211
        const char *pszLayerName =
5212
            reinterpret_cast<const char *>(sqlite3_column_text(hStmtLayer, 0));
5213
        sqlite3_bind_int(hStmtRows, 1, nZ);
5214
        sqlite3_bind_int(hStmtRows, 2, nX);
5215
        sqlite3_bind_int(hStmtRows, 3, nY);
5216
        sqlite3_bind_text(hStmtRows, 4, pszLayerName, -1, SQLITE_STATIC);
5217
5218
        std::shared_ptr<MVTTileLayer> poTargetLayer(new MVTTileLayer());
5219
        oTargetTile.addLayer(poTargetLayer);
5220
        poTargetLayer->setName(pszLayerName);
5221
        poTargetLayer->setVersion(m_nMVTVersion);
5222
        poTargetLayer->setExtent(nExtent);
5223
5224
        std::map<CPLString, GUInt32> oMapKeyToIdx;
5225
        std::map<MVTTileLayerValue, GUInt32> oMapValueToIdx;
5226
5227
        while (nFeaturesInTile < m_nMaxFeatures &&
5228
               sqlite3_step(hStmtRows) == SQLITE_ROW)
5229
        {
5230
            int nBlobSize = sqlite3_column_bytes(hStmtRows, 0);
5231
            const void *pabyBlob = sqlite3_column_blob(hStmtRows, 0);
5232
5233
            EncodeFeature(pabyBlob, nBlobSize, poTargetLayer, oMapKeyToIdx,
5234
                          oMapValueToIdx, nullptr, nExtent, nFeaturesInTile);
5235
        }
5236
        sqlite3_reset(hStmtRows);
5237
    }
5238
5239
    sqlite3_reset(hStmtLayer);
5240
5241
    std::string oTileBuffer(oTargetTile.write());
5242
    if (m_bGZip)
5243
        GZIPCompress(oTileBuffer);
5244
5245
    return oTileBuffer;
5246
}
5247
5248
/************************************************************************/
5249
/*                            CreateOutput()                            */
5250
/************************************************************************/
5251
5252
bool OGRMVTWriterDataset::CreateOutput()
5253
{
5254
    if (m_bThreadPoolOK)
5255
        m_oThreadPool.WaitCompletion();
5256
5257
    std::map<CPLString, MVTLayerProperties> oMapLayerProps;
5258
    std::set<CPLString> oSetLayers;
5259
5260
    if (!m_oEnvelope.IsInit())
5261
    {
5262
        return GenerateMetadata(0, oMapLayerProps);
5263
    }
5264
5265
    CPLDebug("MVT", "Building output file from temporary database...");
5266
5267
    sqlite3_stmt *hStmtZXY = nullptr;
5268
    CPL_IGNORE_RET_VAL(sqlite3_prepare_v2(
5269
        m_hDB, "SELECT DISTINCT z, x, y FROM temp ORDER BY z, x, y", -1,
5270
        &hStmtZXY, nullptr));
5271
    if (hStmtZXY == nullptr)
5272
    {
5273
        CPLError(CE_Failure, CPLE_AppDefined, "Prepared statement failed");
5274
        return false;
5275
    }
5276
5277
    sqlite3_stmt *hStmtLayer = nullptr;
5278
    CPL_IGNORE_RET_VAL(
5279
        sqlite3_prepare_v2(m_hDB,
5280
                           "SELECT DISTINCT layer FROM temp "
5281
                           "WHERE z = ? AND x = ? AND y = ? ORDER BY layer",
5282
                           -1, &hStmtLayer, nullptr));
5283
    if (hStmtLayer == nullptr)
5284
    {
5285
        CPLError(CE_Failure, CPLE_AppDefined, "Prepared statement failed");
5286
        sqlite3_finalize(hStmtZXY);
5287
        return false;
5288
    }
5289
    sqlite3_stmt *hStmtRows = nullptr;
5290
    CPL_IGNORE_RET_VAL(sqlite3_prepare_v2(
5291
        m_hDB,
5292
        "SELECT feature FROM temp "
5293
        "WHERE z = ? AND x = ? AND y = ? AND layer = ? ORDER BY idx",
5294
        -1, &hStmtRows, nullptr));
5295
    if (hStmtRows == nullptr)
5296
    {
5297
        CPLError(CE_Failure, CPLE_AppDefined, "Prepared statement failed");
5298
        sqlite3_finalize(hStmtZXY);
5299
        sqlite3_finalize(hStmtLayer);
5300
        return false;
5301
    }
5302
5303
    sqlite3_stmt *hInsertStmt = nullptr;
5304
    if (m_hDBMBTILES)
5305
    {
5306
        CPL_IGNORE_RET_VAL(sqlite3_prepare_v2(
5307
            m_hDBMBTILES,
5308
            "INSERT INTO tiles(zoom_level, tile_column, tile_row, "
5309
            "tile_data) VALUES (?,?,?,?)",
5310
            -1, &hInsertStmt, nullptr));
5311
        if (hInsertStmt == nullptr)
5312
        {
5313
            CPLError(CE_Failure, CPLE_AppDefined, "Prepared statement failed");
5314
            sqlite3_finalize(hStmtZXY);
5315
            sqlite3_finalize(hStmtLayer);
5316
            sqlite3_finalize(hStmtRows);
5317
            return false;
5318
        }
5319
    }
5320
5321
    int nLastZ = -1;
5322
    int nLastX = -1;
5323
    bool bRet = true;
5324
    GIntBig nTempTilesRead = 0;
5325
5326
    while (sqlite3_step(hStmtZXY) == SQLITE_ROW)
5327
    {
5328
        int nZ = sqlite3_column_int(hStmtZXY, 0);
5329
        int nX = sqlite3_column_int(hStmtZXY, 1);
5330
        int nY = sqlite3_column_int(hStmtZXY, 2);
5331
5332
        std::string oTileBuffer(EncodeTile(nZ, nX, nY, hStmtLayer, hStmtRows,
5333
                                           oMapLayerProps, oSetLayers,
5334
                                           nTempTilesRead));
5335
5336
        if (oTileBuffer.empty())
5337
        {
5338
            bRet = false;
5339
        }
5340
        else if (hInsertStmt)
5341
        {
5342
            sqlite3_bind_int(hInsertStmt, 1, nZ);
5343
            sqlite3_bind_int(hInsertStmt, 2, nX);
5344
            sqlite3_bind_int(hInsertStmt, 3, (1 << nZ) - 1 - nY);
5345
            sqlite3_bind_blob(hInsertStmt, 4, oTileBuffer.data(),
5346
                              static_cast<int>(oTileBuffer.size()),
5347
                              SQLITE_STATIC);
5348
            const int rc = sqlite3_step(hInsertStmt);
5349
            bRet = (rc == SQLITE_OK || rc == SQLITE_DONE);
5350
            sqlite3_reset(hInsertStmt);
5351
        }
5352
        else
5353
        {
5354
            const std::string osZDirname(CPLFormFilenameSafe(
5355
                GetDescription(), CPLSPrintf("%d", nZ), nullptr));
5356
            const std::string osXDirname(CPLFormFilenameSafe(
5357
                osZDirname.c_str(), CPLSPrintf("%d", nX), nullptr));
5358
            if (nZ != nLastZ)
5359
            {
5360
                VSIMkdir(osZDirname.c_str(), 0755);
5361
                nLastZ = nZ;
5362
                nLastX = -1;
5363
            }
5364
            if (nX != nLastX)
5365
            {
5366
                VSIMkdir(osXDirname.c_str(), 0755);
5367
                nLastX = nX;
5368
            }
5369
            const std::string osTileFilename(
5370
                CPLFormFilenameSafe(osXDirname.c_str(), CPLSPrintf("%d", nY),
5371
                                    m_osExtension.c_str()));
5372
            VSILFILE *fpOut = VSIFOpenL(osTileFilename.c_str(), "wb");
5373
            if (fpOut)
5374
            {
5375
                const size_t nRet = VSIFWriteL(oTileBuffer.data(), 1,
5376
                                               oTileBuffer.size(), fpOut);
5377
                bRet = (nRet == oTileBuffer.size());
5378
                VSIFCloseL(fpOut);
5379
            }
5380
            else
5381
            {
5382
                bRet = false;
5383
            }
5384
        }
5385
5386
        if (!bRet)
5387
        {
5388
            CPLError(CE_Failure, CPLE_AppDefined,
5389
                     "Error while writing tile %d/%d/%d", nZ, nX, nY);
5390
            break;
5391
        }
5392
    }
5393
    sqlite3_finalize(hStmtZXY);
5394
    sqlite3_finalize(hStmtLayer);
5395
    sqlite3_finalize(hStmtRows);
5396
    if (hInsertStmt)
5397
        sqlite3_finalize(hInsertStmt);
5398
5399
    bRet &= GenerateMetadata(oSetLayers.size(), oMapLayerProps);
5400
5401
    return bRet;
5402
}
5403
5404
/************************************************************************/
5405
/*                     SphericalMercatorToLongLat()                     */
5406
/************************************************************************/
5407
5408
static void SphericalMercatorToLongLat(double *x, double *y)
5409
{
5410
    double lng = *x / kmSPHERICAL_RADIUS / M_PI * 180;
5411
    double lat =
5412
        2 * (atan(exp(*y / kmSPHERICAL_RADIUS)) - M_PI / 4) / M_PI * 180;
5413
    *x = lng;
5414
    *y = lat;
5415
}
5416
5417
/************************************************************************/
5418
/*                          WriteMetadataItem()                         */
5419
/************************************************************************/
5420
5421
template <class T>
5422
static bool WriteMetadataItemT(const char *pszKey, T value,
5423
                               const char *pszValueFormat, sqlite3 *hDBMBTILES,
5424
                               CPLJSONObject &oRoot)
5425
{
5426
    if (hDBMBTILES)
5427
    {
5428
        char *pszSQL;
5429
5430
        pszSQL = sqlite3_mprintf(
5431
            CPLSPrintf("INSERT INTO metadata(name, value) VALUES('%%q', '%s')",
5432
                       pszValueFormat),
5433
            pszKey, value);
5434
        OGRErr eErr = SQLCommand(hDBMBTILES, pszSQL);
5435
        sqlite3_free(pszSQL);
5436
        return eErr == OGRERR_NONE;
5437
    }
5438
    else
5439
    {
5440
        oRoot.Add(pszKey, value);
5441
        return true;
5442
    }
5443
}
5444
5445
/************************************************************************/
5446
/*                          WriteMetadataItem()                         */
5447
/************************************************************************/
5448
5449
static bool WriteMetadataItem(const char *pszKey, const char *pszValue,
5450
                              sqlite3 *hDBMBTILES, CPLJSONObject &oRoot)
5451
{
5452
    return WriteMetadataItemT(pszKey, pszValue, "%q", hDBMBTILES, oRoot);
5453
}
5454
5455
/************************************************************************/
5456
/*                          WriteMetadataItem()                         */
5457
/************************************************************************/
5458
5459
static bool WriteMetadataItem(const char *pszKey, int nValue,
5460
                              sqlite3 *hDBMBTILES, CPLJSONObject &oRoot)
5461
{
5462
    return WriteMetadataItemT(pszKey, nValue, "%d", hDBMBTILES, oRoot);
5463
}
5464
5465
/************************************************************************/
5466
/*                          WriteMetadataItem()                         */
5467
/************************************************************************/
5468
5469
static bool WriteMetadataItem(const char *pszKey, double dfValue,
5470
                              sqlite3 *hDBMBTILES, CPLJSONObject &oRoot)
5471
{
5472
    return WriteMetadataItemT(pszKey, dfValue, "%.17g", hDBMBTILES, oRoot);
5473
}
5474
5475
/************************************************************************/
5476
/*                          GenerateMetadata()                          */
5477
/************************************************************************/
5478
5479
bool OGRMVTWriterDataset::GenerateMetadata(
5480
    size_t nLayers, const std::map<CPLString, MVTLayerProperties> &oMap)
5481
{
5482
    CPLJSONDocument oDoc;
5483
    CPLJSONObject oRoot = oDoc.GetRoot();
5484
5485
    OGRSpatialReference oSRS_EPSG3857;
5486
    double dfTopXWebMercator;
5487
    double dfTopYWebMercator;
5488
    double dfTileDim0WebMercator;
5489
    InitWebMercatorTilingScheme(&oSRS_EPSG3857, dfTopXWebMercator,
5490
                                dfTopYWebMercator, dfTileDim0WebMercator);
5491
    const bool bIsStandardTilingScheme =
5492
        m_poSRS->IsSame(&oSRS_EPSG3857) && m_dfTopX == dfTopXWebMercator &&
5493
        m_dfTopY == dfTopYWebMercator && m_dfTileDim0 == dfTileDim0WebMercator;
5494
    if (bIsStandardTilingScheme)
5495
    {
5496
        SphericalMercatorToLongLat(&(m_oEnvelope.MinX), &(m_oEnvelope.MinY));
5497
        SphericalMercatorToLongLat(&(m_oEnvelope.MaxX), &(m_oEnvelope.MaxY));
5498
        m_oEnvelope.MinY = std::max(-85.0, m_oEnvelope.MinY);
5499
        m_oEnvelope.MaxY = std::min(85.0, m_oEnvelope.MaxY);
5500
    }
5501
    else
5502
    {
5503
        OGRSpatialReference oSRS_EPSG4326;
5504
        oSRS_EPSG4326.SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
5505
        oSRS_EPSG4326.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
5506
        OGRCoordinateTransformation *poCT =
5507
            OGRCreateCoordinateTransformation(m_poSRS, &oSRS_EPSG4326);
5508
        if (poCT)
5509
        {
5510
            OGRPoint oPoint1(m_oEnvelope.MinX, m_oEnvelope.MinY);
5511
            oPoint1.transform(poCT);
5512
            OGRPoint oPoint2(m_oEnvelope.MinX, m_oEnvelope.MaxY);
5513
            oPoint2.transform(poCT);
5514
            OGRPoint oPoint3(m_oEnvelope.MaxX, m_oEnvelope.MaxY);
5515
            oPoint3.transform(poCT);
5516
            OGRPoint oPoint4(m_oEnvelope.MaxX, m_oEnvelope.MinY);
5517
            oPoint4.transform(poCT);
5518
            m_oEnvelope.MinX =
5519
                std::min(std::min(oPoint1.getX(), oPoint2.getX()),
5520
                         std::min(oPoint3.getX(), oPoint4.getX()));
5521
            m_oEnvelope.MinY =
5522
                std::min(std::min(oPoint1.getY(), oPoint2.getY()),
5523
                         std::min(oPoint3.getY(), oPoint4.getY()));
5524
            m_oEnvelope.MaxX =
5525
                std::max(std::max(oPoint1.getX(), oPoint2.getX()),
5526
                         std::max(oPoint3.getX(), oPoint4.getX()));
5527
            m_oEnvelope.MaxY =
5528
                std::max(std::max(oPoint1.getY(), oPoint2.getY()),
5529
                         std::max(oPoint3.getY(), oPoint4.getY()));
5530
            delete poCT;
5531
        }
5532
    }
5533
    const double dfCenterX = (m_oEnvelope.MinX + m_oEnvelope.MaxX) / 2;
5534
    const double dfCenterY = (m_oEnvelope.MinY + m_oEnvelope.MaxY) / 2;
5535
    CPLString osCenter(
5536
        CPLSPrintf("%.7f,%.7f,%d", dfCenterX, dfCenterY, m_nMinZoom));
5537
    CPLString osBounds(CPLSPrintf("%.7f,%.7f,%.7f,%.7f", m_oEnvelope.MinX,
5538
                                  m_oEnvelope.MinY, m_oEnvelope.MaxX,
5539
                                  m_oEnvelope.MaxY));
5540
5541
    WriteMetadataItem("name", m_osName, m_hDBMBTILES, oRoot);
5542
    WriteMetadataItem("description", m_osDescription, m_hDBMBTILES, oRoot);
5543
    WriteMetadataItem("version", m_nMetadataVersion, m_hDBMBTILES, oRoot);
5544
    WriteMetadataItem("minzoom", m_nMinZoom, m_hDBMBTILES, oRoot);
5545
    WriteMetadataItem("maxzoom", m_nMaxZoom, m_hDBMBTILES, oRoot);
5546
    WriteMetadataItem("center", !m_osCenter.empty() ? m_osCenter : osCenter,
5547
                      m_hDBMBTILES, oRoot);
5548
    WriteMetadataItem("bounds", !m_osBounds.empty() ? m_osBounds : osBounds,
5549
                      m_hDBMBTILES, oRoot);
5550
    WriteMetadataItem("type", m_osType, m_hDBMBTILES, oRoot);
5551
    WriteMetadataItem("format", "pbf", m_hDBMBTILES, oRoot);
5552
    if (m_hDBMBTILES)
5553
    {
5554
        WriteMetadataItem("scheme", "tms", m_hDBMBTILES, oRoot);
5555
    }
5556
5557
    // GDAL extension for custom tiling schemes
5558
    if (!bIsStandardTilingScheme)
5559
    {
5560
        const char *pszAuthName = m_poSRS->GetAuthorityName(nullptr);
5561
        const char *pszAuthCode = m_poSRS->GetAuthorityCode(nullptr);
5562
        if (pszAuthName && pszAuthCode)
5563
        {
5564
            WriteMetadataItem("crs",
5565
                              CPLSPrintf("%s:%s", pszAuthName, pszAuthCode),
5566
                              m_hDBMBTILES, oRoot);
5567
        }
5568
        else
5569
        {
5570
            char *pszWKT = nullptr;
5571
            m_poSRS->exportToWkt(&pszWKT);
5572
            WriteMetadataItem("crs", pszWKT, m_hDBMBTILES, oRoot);
5573
            CPLFree(pszWKT);
5574
        }
5575
        WriteMetadataItem("tile_origin_upper_left_x", m_dfTopX, m_hDBMBTILES,
5576
                          oRoot);
5577
        WriteMetadataItem("tile_origin_upper_left_y", m_dfTopY, m_hDBMBTILES,
5578
                          oRoot);
5579
        WriteMetadataItem("tile_dimension_zoom_0", m_dfTileDim0, m_hDBMBTILES,
5580
                          oRoot);
5581
        WriteMetadataItem("tile_matrix_width_zoom_0", m_nTileMatrixWidth0,
5582
                          m_hDBMBTILES, oRoot);
5583
        WriteMetadataItem("tile_matrix_height_zoom_0", m_nTileMatrixHeight0,
5584
                          m_hDBMBTILES, oRoot);
5585
    }
5586
5587
    CPLJSONDocument oJsonDoc;
5588
    CPLJSONObject oJsonRoot = oJsonDoc.GetRoot();
5589
5590
    CPLJSONArray oVectorLayers;
5591
    oJsonRoot.Add("vector_layers", oVectorLayers);
5592
    std::set<std::string> oAlreadyVisited;
5593
    for (const auto &poLayer : m_apoLayers)
5594
    {
5595
        auto oIter = oMap.find(poLayer->m_osTargetName);
5596
        if (oIter != oMap.end() &&
5597
            oAlreadyVisited.find(poLayer->m_osTargetName) ==
5598
                oAlreadyVisited.end())
5599
        {
5600
            oAlreadyVisited.insert(poLayer->m_osTargetName);
5601
5602
            CPLJSONObject oLayerObj;
5603
            oLayerObj.Add("id", poLayer->m_osTargetName);
5604
            oLayerObj.Add("description",
5605
                          m_oMapLayerNameToDesc[poLayer->m_osTargetName]);
5606
            oLayerObj.Add("minzoom", oIter->second.m_nMinZoom);
5607
            oLayerObj.Add("maxzoom", oIter->second.m_nMaxZoom);
5608
5609
            CPLJSONObject oFields;
5610
            oLayerObj.Add("fields", oFields);
5611
            auto poFDefn = poLayer->GetLayerDefn();
5612
            for (int i = 0; i < poFDefn->GetFieldCount(); i++)
5613
            {
5614
                auto poFieldDefn = poFDefn->GetFieldDefn(i);
5615
                auto eType = poFieldDefn->GetType();
5616
                if (eType == OFTInteger &&
5617
                    poFieldDefn->GetSubType() == OFSTBoolean)
5618
                {
5619
                    oFields.Add(poFieldDefn->GetNameRef(), "Boolean");
5620
                }
5621
                else if (eType == OFTInteger || eType == OFTInteger64 ||
5622
                         eType == OFTReal)
5623
                {
5624
                    oFields.Add(poFieldDefn->GetNameRef(), "Number");
5625
                }
5626
                else
5627
                {
5628
                    oFields.Add(poFieldDefn->GetNameRef(), "String");
5629
                }
5630
            }
5631
5632
            oVectorLayers.Add(oLayerObj);
5633
        }
5634
    }
5635
5636
    CPLJSONObject oTileStats;
5637
    oJsonRoot.Add("tilestats", oTileStats);
5638
    oTileStats.Add("layerCount", static_cast<int>(nLayers));
5639
    CPLJSONArray oTileStatsLayers;
5640
    oTileStats.Add("layers", oTileStatsLayers);
5641
    oAlreadyVisited.clear();
5642
    for (const auto &poLayer : m_apoLayers)
5643
    {
5644
        auto oIter = oMap.find(poLayer->m_osTargetName);
5645
        if (oIter != oMap.end() &&
5646
            oAlreadyVisited.find(poLayer->m_osTargetName) ==
5647
                oAlreadyVisited.end())
5648
        {
5649
            oAlreadyVisited.insert(poLayer->m_osTargetName);
5650
            auto &oLayerProps = oIter->second;
5651
            CPLJSONObject oLayerObj;
5652
5653
            std::string osName(poLayer->m_osTargetName);
5654
            osName.resize(std::min(knMAX_LAYER_NAME_LENGTH, osName.size()));
5655
            oLayerObj.Add("layer", osName);
5656
            oLayerObj.Add(
5657
                "count",
5658
                m_oMapLayerNameToFeatureCount[poLayer->m_osTargetName]);
5659
5660
            // Find majority geometry type
5661
            MVTTileLayerFeature::GeomType eMaxGeomType =
5662
                MVTTileLayerFeature::GeomType::UNKNOWN;
5663
            GIntBig nMaxCountGeom = 0;
5664
            for (int i = static_cast<int>(MVTTileLayerFeature::GeomType::POINT);
5665
                 i <= static_cast<int>(MVTTileLayerFeature::GeomType::POLYGON);
5666
                 i++)
5667
            {
5668
                MVTTileLayerFeature::GeomType eGeomType =
5669
                    static_cast<MVTTileLayerFeature::GeomType>(i);
5670
                auto oIterCountGeom =
5671
                    oLayerProps.m_oCountGeomType.find(eGeomType);
5672
                if (oIterCountGeom != oLayerProps.m_oCountGeomType.end())
5673
                {
5674
                    if (oIterCountGeom->second >= nMaxCountGeom)
5675
                    {
5676
                        eMaxGeomType = eGeomType;
5677
                        nMaxCountGeom = oIterCountGeom->second;
5678
                    }
5679
                }
5680
            }
5681
            if (eMaxGeomType == MVTTileLayerFeature::GeomType::POINT)
5682
                oLayerObj.Add("geometry", "Point");
5683
            else if (eMaxGeomType == MVTTileLayerFeature::GeomType::LINESTRING)
5684
                oLayerObj.Add("geometry", "LineString");
5685
            else if (eMaxGeomType == MVTTileLayerFeature::GeomType::POLYGON)
5686
                oLayerObj.Add("geometry", "Polygon");
5687
5688
            oLayerObj.Add("attributeCount",
5689
                          static_cast<int>(oLayerProps.m_oSetFields.size()));
5690
            CPLJSONArray oAttributes;
5691
            oLayerObj.Add("attributes", oAttributes);
5692
            for (const auto &oFieldProps : oLayerProps.m_aoFields)
5693
            {
5694
                CPLJSONObject oFieldObj;
5695
                oAttributes.Add(oFieldObj);
5696
                std::string osFieldNameTruncated(oFieldProps.m_osName);
5697
                osFieldNameTruncated.resize(std::min(
5698
                    knMAX_FIELD_NAME_LENGTH, osFieldNameTruncated.size()));
5699
                oFieldObj.Add("attribute", osFieldNameTruncated);
5700
                oFieldObj.Add("count", static_cast<int>(
5701
                                           oFieldProps.m_oSetAllValues.size()));
5702
                oFieldObj.Add("type",
5703
                              oFieldProps.m_eType ==
5704
                                      MVTTileLayerValue::ValueType::DOUBLE
5705
                                  ? "number"
5706
                              : oFieldProps.m_eType ==
5707
                                      MVTTileLayerValue::ValueType::STRING
5708
                                  ? "string"
5709
                                  : "boolean");
5710
5711
                CPLJSONArray oValues;
5712
                oFieldObj.Add("values", oValues);
5713
                for (const auto &oIterValue : oFieldProps.m_oSetValues)
5714
                {
5715
                    if (oIterValue.getType() ==
5716
                        MVTTileLayerValue::ValueType::BOOL)
5717
                    {
5718
                        oValues.Add(oIterValue.getBoolValue());
5719
                    }
5720
                    else if (oIterValue.isNumeric())
5721
                    {
5722
                        if (oFieldProps.m_bAllInt)
5723
                        {
5724
                            oValues.Add(static_cast<GInt64>(
5725
                                oIterValue.getNumericValue()));
5726
                        }
5727
                        else
5728
                        {
5729
                            oValues.Add(oIterValue.getNumericValue());
5730
                        }
5731
                    }
5732
                    else if (oIterValue.isString())
5733
                    {
5734
                        oValues.Add(oIterValue.getStringValue());
5735
                    }
5736
                }
5737
5738
                if (oFieldProps.m_eType == MVTTileLayerValue::ValueType::DOUBLE)
5739
                {
5740
                    if (oFieldProps.m_bAllInt)
5741
                    {
5742
                        oFieldObj.Add(
5743
                            "min", static_cast<GInt64>(oFieldProps.m_dfMinVal));
5744
                        oFieldObj.Add(
5745
                            "max", static_cast<GInt64>(oFieldProps.m_dfMaxVal));
5746
                    }
5747
                    else
5748
                    {
5749
                        oFieldObj.Add("min", oFieldProps.m_dfMinVal);
5750
                        oFieldObj.Add("max", oFieldProps.m_dfMaxVal);
5751
                    }
5752
                }
5753
            }
5754
5755
            oTileStatsLayers.Add(oLayerObj);
5756
        }
5757
    }
5758
5759
    WriteMetadataItem("json", oJsonDoc.SaveAsString().c_str(), m_hDBMBTILES,
5760
                      oRoot);
5761
5762
    if (m_hDBMBTILES)
5763
    {
5764
        return true;
5765
    }
5766
5767
    return oDoc.Save(
5768
        CPLFormFilenameSafe(GetDescription(), "metadata.json", nullptr));
5769
}
5770
5771
/************************************************************************/
5772
/*                            WriteFeature()                            */
5773
/************************************************************************/
5774
5775
OGRErr OGRMVTWriterDataset::WriteFeature(OGRMVTWriterLayer *poLayer,
5776
                                         OGRFeature *poFeature, GIntBig nSerial,
5777
                                         OGRGeometry *poGeom)
5778
{
5779
    if (poFeature->GetGeometryRef() == poGeom)
5780
    {
5781
        m_oMapLayerNameToFeatureCount[poLayer->m_osTargetName]++;
5782
    }
5783
5784
    OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType());
5785
    if (eGeomType == wkbGeometryCollection)
5786
    {
5787
        OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
5788
        for (int i = 0; i < poGC->getNumGeometries(); i++)
5789
        {
5790
            if (WriteFeature(poLayer, poFeature, nSerial,
5791
                             poGC->getGeometryRef(i)) != OGRERR_NONE)
5792
            {
5793
                return OGRERR_FAILURE;
5794
            }
5795
        }
5796
        return OGRERR_NONE;
5797
    }
5798
5799
    OGREnvelope sExtent;
5800
    poGeom->getEnvelope(&sExtent);
5801
5802
    if (!m_oEnvelope.IsInit())
5803
    {
5804
        CPLDebug("MVT", "Creating temporary database...");
5805
    }
5806
5807
    m_oEnvelope.Merge(sExtent);
5808
5809
    if (!m_bReuseTempFile)
5810
    {
5811
        auto poFeatureContent =
5812
            std::shared_ptr<OGRMVTFeatureContent>(new OGRMVTFeatureContent());
5813
        auto poSharedGeom = std::shared_ptr<OGRGeometry>(poGeom->clone());
5814
5815
        poFeatureContent->nFID = poFeature->GetFID();
5816
5817
        const OGRFeatureDefn *poFDefn = poFeature->GetDefnRef();
5818
        for (int i = 0; i < poFeature->GetFieldCount(); i++)
5819
        {
5820
            if (poFeature->IsFieldSetAndNotNull(i))
5821
            {
5822
                MVTTileLayerValue oValue;
5823
                const OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn(i);
5824
                OGRFieldType eFieldType = poFieldDefn->GetType();
5825
                if (eFieldType == OFTInteger || eFieldType == OFTInteger64)
5826
                {
5827
                    if (poFieldDefn->GetSubType() == OFSTBoolean)
5828
                    {
5829
                        oValue.setBoolValue(poFeature->GetFieldAsInteger(i) !=
5830
                                            0);
5831
                    }
5832
                    else
5833
                    {
5834
                        oValue.setValue(poFeature->GetFieldAsInteger64(i));
5835
                    }
5836
                }
5837
                else if (eFieldType == OFTReal)
5838
                {
5839
                    oValue.setValue(poFeature->GetFieldAsDouble(i));
5840
                }
5841
                else if (eFieldType == OFTDate || eFieldType == OFTDateTime)
5842
                {
5843
                    int nYear, nMonth, nDay, nHour, nMin, nTZ;
5844
                    float fSec;
5845
                    poFeature->GetFieldAsDateTime(i, &nYear, &nMonth, &nDay,
5846
                                                  &nHour, &nMin, &fSec, &nTZ);
5847
                    CPLString osFormatted;
5848
                    if (eFieldType == OFTDate)
5849
                    {
5850
                        osFormatted.Printf("%04d-%02d-%02d", nYear, nMonth,
5851
                                           nDay);
5852
                    }
5853
                    else
5854
                    {
5855
                        char *pszFormatted =
5856
                            OGRGetXMLDateTime(poFeature->GetRawFieldRef(i));
5857
                        osFormatted = pszFormatted;
5858
                        CPLFree(pszFormatted);
5859
                    }
5860
                    oValue.setStringValue(osFormatted);
5861
                }
5862
                else
5863
                {
5864
                    oValue.setStringValue(
5865
                        std::string(poFeature->GetFieldAsString(i)));
5866
                }
5867
5868
                poFeatureContent->oValues.emplace_back(
5869
                    std::pair<std::string, MVTTileLayerValue>(
5870
                        poFieldDefn->GetNameRef(), oValue));
5871
            }
5872
        }
5873
5874
        for (int nZ = poLayer->m_nMinZoom; nZ <= poLayer->m_nMaxZoom; nZ++)
5875
        {
5876
            double dfTileDim = m_dfTileDim0 / (1 << nZ);
5877
            double dfBuffer = dfTileDim * m_nBuffer / m_nExtent;
5878
            const int nTileMinX = std::max(
5879
                0, static_cast<int>((sExtent.MinX - m_dfTopX - dfBuffer) /
5880
                                    dfTileDim));
5881
            const int nTileMinY = std::max(
5882
                0, static_cast<int>((m_dfTopY - sExtent.MaxY - dfBuffer) /
5883
                                    dfTileDim));
5884
            const int nTileMaxX =
5885
                std::min(static_cast<int>((sExtent.MaxX - m_dfTopX + dfBuffer) /
5886
                                          dfTileDim),
5887
                         static_cast<int>(std::min<int64_t>(
5888
                             INT_MAX, (static_cast<int64_t>(1) << nZ) *
5889
                                              m_nTileMatrixWidth0 -
5890
                                          1)));
5891
            const int nTileMaxY =
5892
                std::min(static_cast<int>((m_dfTopY - sExtent.MinY + dfBuffer) /
5893
                                          dfTileDim),
5894
                         static_cast<int>(std::min<int64_t>(
5895
                             INT_MAX, (static_cast<int64_t>(1) << nZ) *
5896
                                              m_nTileMatrixHeight0 -
5897
                                          1)));
5898
            for (int iX = nTileMinX; iX <= nTileMaxX; iX++)
5899
            {
5900
                for (int iY = nTileMinY; iY <= nTileMaxY; iY++)
5901
                {
5902
                    if (PreGenerateForTile(
5903
                            nZ, iX, iY, poLayer->m_osTargetName,
5904
                            (nZ == poLayer->m_nMaxZoom), poFeatureContent,
5905
                            nSerial, poSharedGeom, sExtent) != OGRERR_NONE)
5906
                    {
5907
                        return OGRERR_FAILURE;
5908
                    }
5909
                }
5910
            }
5911
        }
5912
    }
5913
5914
    return OGRERR_NONE;
5915
}
5916
5917
/************************************************************************/
5918
/*                            TestCapability()                          */
5919
/************************************************************************/
5920
5921
int OGRMVTWriterDataset::TestCapability(const char *pszCap)
5922
{
5923
    if (EQUAL(pszCap, ODsCCreateLayer))
5924
        return true;
5925
    return false;
5926
}
5927
5928
/************************************************************************/
5929
/*                         ValidateMinMaxZoom()                         */
5930
/************************************************************************/
5931
5932
static bool ValidateMinMaxZoom(int nMinZoom, int nMaxZoom)
5933
{
5934
    if (nMinZoom < 0 || nMinZoom > 22)
5935
    {
5936
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid MINZOOM");
5937
        return false;
5938
    }
5939
    if (nMaxZoom < 0 || nMaxZoom > 22)
5940
    {
5941
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid MAXZOOM");
5942
        return false;
5943
    }
5944
    if (nMaxZoom < nMinZoom)
5945
    {
5946
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid MAXZOOM < MINZOOM");
5947
        return false;
5948
    }
5949
    return true;
5950
}
5951
5952
/************************************************************************/
5953
/*                           ICreateLayer()                             */
5954
/************************************************************************/
5955
5956
OGRLayer *
5957
OGRMVTWriterDataset::ICreateLayer(const char *pszLayerName,
5958
                                  const OGRGeomFieldDefn *poGeomFieldDefn,
5959
                                  CSLConstList papszOptions)
5960
{
5961
    OGRSpatialReference *poSRSClone = nullptr;
5962
    const auto poSRS =
5963
        poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
5964
    if (poSRS)
5965
    {
5966
        poSRSClone = poSRS->Clone();
5967
        poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
5968
    }
5969
    OGRMVTWriterLayer *poLayer =
5970
        new OGRMVTWriterLayer(this, pszLayerName, poSRSClone);
5971
    if (poSRSClone)
5972
        poSRSClone->Release();
5973
    poLayer->m_nMinZoom = m_nMinZoom;
5974
    poLayer->m_nMaxZoom = m_nMaxZoom;
5975
    poLayer->m_osTargetName = pszLayerName;
5976
5977
    /*
5978
5979
            {
5980
                "src_layer":
5981
                    { "target_name": "",
5982
                      "description": "",
5983
                      "minzoom": 0,
5984
                      "maxzoom": 0
5985
                    }
5986
            }
5987
    */
5988
5989
    CPLJSONObject oObj = m_oConf.GetRoot().GetObj(pszLayerName);
5990
    CPLString osDescription;
5991
    if (oObj.IsValid())
5992
    {
5993
        std::string osTargetName = oObj.GetString("target_name");
5994
        if (!osTargetName.empty())
5995
            poLayer->m_osTargetName = std::move(osTargetName);
5996
        int nMinZoom = oObj.GetInteger("minzoom", -1);
5997
        if (nMinZoom >= 0)
5998
            poLayer->m_nMinZoom = nMinZoom;
5999
        int nMaxZoom = oObj.GetInteger("maxzoom", -1);
6000
        if (nMaxZoom >= 0)
6001
            poLayer->m_nMaxZoom = nMaxZoom;
6002
        osDescription = oObj.GetString("description");
6003
    }
6004
6005
    poLayer->m_nMinZoom = atoi(CSLFetchNameValueDef(
6006
        papszOptions, "MINZOOM", CPLSPrintf("%d", poLayer->m_nMinZoom)));
6007
    poLayer->m_nMaxZoom = atoi(CSLFetchNameValueDef(
6008
        papszOptions, "MAXZOOM", CPLSPrintf("%d", poLayer->m_nMaxZoom)));
6009
    if (!ValidateMinMaxZoom(poLayer->m_nMinZoom, poLayer->m_nMaxZoom))
6010
    {
6011
        delete poLayer;
6012
        return nullptr;
6013
    }
6014
    poLayer->m_osTargetName = CSLFetchNameValueDef(
6015
        papszOptions, "NAME", poLayer->m_osTargetName.c_str());
6016
    osDescription =
6017
        CSLFetchNameValueDef(papszOptions, "DESCRIPTION", osDescription);
6018
    if (!osDescription.empty())
6019
        m_oMapLayerNameToDesc[poLayer->m_osTargetName] =
6020
            std::move(osDescription);
6021
6022
    m_apoLayers.push_back(std::unique_ptr<OGRMVTWriterLayer>(poLayer));
6023
    return m_apoLayers.back().get();
6024
}
6025
6026
/************************************************************************/
6027
/*                                Create()                              */
6028
/************************************************************************/
6029
6030
GDALDataset *OGRMVTWriterDataset::Create(const char *pszFilename, int nXSize,
6031
                                         int nYSize, int nBandsIn,
6032
                                         GDALDataType eDT, char **papszOptions)
6033
{
6034
    if (nXSize != 0 || nYSize != 0 || nBandsIn != 0 || eDT != GDT_Unknown)
6035
    {
6036
        CPLError(CE_Failure, CPLE_NotSupported,
6037
                 "Only vector creation supported");
6038
        return nullptr;
6039
    }
6040
6041
    const char *pszFormat = CSLFetchNameValue(papszOptions, "FORMAT");
6042
    const bool bMBTILESExt =
6043
        EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "mbtiles");
6044
    if (pszFormat == nullptr && bMBTILESExt)
6045
    {
6046
        pszFormat = "MBTILES";
6047
    }
6048
    const bool bMBTILES = pszFormat != nullptr && EQUAL(pszFormat, "MBTILES");
6049
6050
    // For debug only
6051
    bool bReuseTempFile =
6052
        CPLTestBool(CPLGetConfigOption("OGR_MVT_REUSE_TEMP_FILE", "NO"));
6053
6054
    if (bMBTILES)
6055
    {
6056
        if (!bMBTILESExt)
6057
        {
6058
            CPLError(CE_Failure, CPLE_FileIO,
6059
                     "%s should have mbtiles extension", pszFilename);
6060
            return nullptr;
6061
        }
6062
6063
        VSIUnlink(pszFilename);
6064
    }
6065
    else
6066
    {
6067
        VSIStatBufL sStat;
6068
        if (VSIStatL(pszFilename, &sStat) == 0)
6069
        {
6070
            CPLError(CE_Failure, CPLE_FileIO, "%s already exists", pszFilename);
6071
            return nullptr;
6072
        }
6073
6074
        if (VSIMkdir(pszFilename, 0755) != 0)
6075
        {
6076
            CPLError(CE_Failure, CPLE_FileIO, "Cannot create directory %s",
6077
                     pszFilename);
6078
            return nullptr;
6079
        }
6080
    }
6081
6082
    OGRMVTWriterDataset *poDS = new OGRMVTWriterDataset();
6083
    poDS->m_pMyVFS = OGRSQLiteCreateVFS(nullptr, poDS);
6084
    sqlite3_vfs_register(poDS->m_pMyVFS, 0);
6085
6086
    CPLString osTempDBDefault = CPLString(pszFilename) + ".temp.db";
6087
    if (STARTS_WITH(osTempDBDefault, "/vsizip/"))
6088
    {
6089
        osTempDBDefault =
6090
            CPLString(pszFilename + strlen("/vsizip/")) + ".temp.db";
6091
    }
6092
    CPLString osTempDB = CSLFetchNameValueDef(papszOptions, "TEMPORARY_DB",
6093
                                              osTempDBDefault.c_str());
6094
    if (!bReuseTempFile)
6095
        VSIUnlink(osTempDB);
6096
6097
    sqlite3 *hDB = nullptr;
6098
    if (sqlite3_open_v2(osTempDB, &hDB,
6099
                        SQLITE_OPEN_READWRITE |
6100
                            (bReuseTempFile ? 0 : SQLITE_OPEN_CREATE) |
6101
                            SQLITE_OPEN_NOMUTEX,
6102
                        poDS->m_pMyVFS->zName) != SQLITE_OK ||
6103
        hDB == nullptr)
6104
    {
6105
        CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", osTempDB.c_str());
6106
        delete poDS;
6107
        sqlite3_close(hDB);
6108
        return nullptr;
6109
    }
6110
    poDS->m_osTempDB = osTempDB;
6111
    poDS->m_hDB = hDB;
6112
    poDS->m_bReuseTempFile = bReuseTempFile;
6113
6114
    // For Unix
6115
    if (!poDS->m_bReuseTempFile &&
6116
        CPLTestBool(CPLGetConfigOption("OGR_MVT_REMOVE_TEMP_FILE", "YES")))
6117
    {
6118
        VSIUnlink(osTempDB);
6119
    }
6120
6121
    if (poDS->m_bReuseTempFile)
6122
    {
6123
        poDS->m_nTempTiles =
6124
            SQLGetInteger64(hDB, "SELECT COUNT(*) FROM temp", nullptr);
6125
    }
6126
    else
6127
    {
6128
        CPL_IGNORE_RET_VAL(SQLCommand(
6129
            hDB,
6130
            "PRAGMA page_size = 4096;"  // 4096: default since sqlite 3.12
6131
            "PRAGMA synchronous = OFF;"
6132
            "PRAGMA journal_mode = OFF;"
6133
            "PRAGMA temp_store = MEMORY;"
6134
            "CREATE TABLE temp(z INTEGER, x INTEGER, y INTEGER, layer TEXT, "
6135
            "idx INTEGER, feature BLOB, geomtype INTEGER, area_or_length "
6136
            "DOUBLE);"
6137
            "CREATE INDEX temp_index ON temp (z, x, y, layer, idx);"));
6138
    }
6139
6140
    sqlite3_stmt *hInsertStmt = nullptr;
6141
    CPL_IGNORE_RET_VAL(sqlite3_prepare_v2(
6142
        hDB,
6143
        "INSERT INTO temp (z,x,y,layer,idx,feature,geomtype,area_or_length) "
6144
        "VALUES (?,?,?,?,?,?,?,?)",
6145
        -1, &hInsertStmt, nullptr));
6146
    if (hInsertStmt == nullptr)
6147
    {
6148
        delete poDS;
6149
        return nullptr;
6150
    }
6151
    poDS->m_hInsertStmt = hInsertStmt;
6152
6153
    poDS->m_nMinZoom = atoi(CSLFetchNameValueDef(
6154
        papszOptions, "MINZOOM", CPLSPrintf("%d", poDS->m_nMinZoom)));
6155
    poDS->m_nMaxZoom = atoi(CSLFetchNameValueDef(
6156
        papszOptions, "MAXZOOM", CPLSPrintf("%d", poDS->m_nMaxZoom)));
6157
    if (!ValidateMinMaxZoom(poDS->m_nMinZoom, poDS->m_nMaxZoom))
6158
    {
6159
        delete poDS;
6160
        return nullptr;
6161
    }
6162
6163
    const char *pszConf = CSLFetchNameValue(papszOptions, "CONF");
6164
    if (pszConf)
6165
    {
6166
        VSIStatBufL sStat;
6167
        bool bSuccess;
6168
        if (VSIStatL(pszConf, &sStat) == 0)
6169
        {
6170
            bSuccess = poDS->m_oConf.Load(pszConf);
6171
        }
6172
        else
6173
        {
6174
            bSuccess = poDS->m_oConf.LoadMemory(pszConf);
6175
        }
6176
        if (!bSuccess)
6177
        {
6178
            delete poDS;
6179
            return nullptr;
6180
        }
6181
    }
6182
6183
    poDS->m_dfSimplification =
6184
        CPLAtof(CSLFetchNameValueDef(papszOptions, "SIMPLIFICATION", "0"));
6185
    poDS->m_dfSimplificationMaxZoom = CPLAtof(
6186
        CSLFetchNameValueDef(papszOptions, "SIMPLIFICATION_MAX_ZOOM",
6187
                             CPLSPrintf("%g", poDS->m_dfSimplification)));
6188
    poDS->m_nExtent = static_cast<unsigned>(atoi(CSLFetchNameValueDef(
6189
        papszOptions, "EXTENT", CPLSPrintf("%u", poDS->m_nExtent))));
6190
    poDS->m_nBuffer = static_cast<unsigned>(atoi(CSLFetchNameValueDef(
6191
        papszOptions, "BUFFER", CPLSPrintf("%u", 5 * poDS->m_nExtent / 256))));
6192
6193
    {
6194
        const char *pszMaxSize = CSLFetchNameValue(papszOptions, "MAX_SIZE");
6195
        poDS->m_bMaxTileSizeOptSpecified = pszMaxSize != nullptr;
6196
        // This is used by unit tests
6197
        pszMaxSize = CSLFetchNameValueDef(papszOptions, "@MAX_SIZE_FOR_TEST",
6198
                                          pszMaxSize);
6199
        if (pszMaxSize)
6200
        {
6201
            poDS->m_nMaxTileSize =
6202
                std::max(100U, static_cast<unsigned>(atoi(pszMaxSize)));
6203
        }
6204
    }
6205
6206
    {
6207
        const char *pszMaxFeatures =
6208
            CSLFetchNameValue(papszOptions, "MAX_FEATURES");
6209
        poDS->m_bMaxFeaturesOptSpecified = pszMaxFeatures != nullptr;
6210
        pszMaxFeatures = CSLFetchNameValueDef(
6211
            // This is used by unit tests
6212
            papszOptions, "@MAX_FEATURES_FOR_TEST", pszMaxFeatures);
6213
        if (pszMaxFeatures)
6214
        {
6215
            poDS->m_nMaxFeatures =
6216
                std::max(1U, static_cast<unsigned>(atoi(pszMaxFeatures)));
6217
        }
6218
    }
6219
6220
    poDS->m_osName = CSLFetchNameValueDef(
6221
        papszOptions, "NAME", CPLGetBasenameSafe(pszFilename).c_str());
6222
    poDS->m_osDescription = CSLFetchNameValueDef(papszOptions, "DESCRIPTION",
6223
                                                 poDS->m_osDescription.c_str());
6224
    poDS->m_osType =
6225
        CSLFetchNameValueDef(papszOptions, "TYPE", poDS->m_osType.c_str());
6226
    poDS->m_bGZip = CPLFetchBool(papszOptions, "COMPRESS", poDS->m_bGZip);
6227
    poDS->m_osBounds = CSLFetchNameValueDef(papszOptions, "BOUNDS", "");
6228
    poDS->m_osCenter = CSLFetchNameValueDef(papszOptions, "CENTER", "");
6229
    poDS->m_osExtension = CSLFetchNameValueDef(papszOptions, "TILE_EXTENSION",
6230
                                               poDS->m_osExtension);
6231
6232
    const char *pszTilingScheme =
6233
        CSLFetchNameValue(papszOptions, "TILING_SCHEME");
6234
    if (pszTilingScheme)
6235
    {
6236
        if (bMBTILES)
6237
        {
6238
            CPLError(CE_Failure, CPLE_NotSupported,
6239
                     "Custom TILING_SCHEME not supported with MBTILES output");
6240
            delete poDS;
6241
            return nullptr;
6242
        }
6243
6244
        const CPLStringList aoList(CSLTokenizeString2(pszTilingScheme, ",", 0));
6245
        if (aoList.Count() >= 4)
6246
        {
6247
            poDS->m_poSRS->SetFromUserInput(aoList[0]);
6248
            poDS->m_dfTopX = CPLAtof(aoList[1]);
6249
            poDS->m_dfTopY = CPLAtof(aoList[2]);
6250
            poDS->m_dfTileDim0 = CPLAtof(aoList[3]);
6251
            if (aoList.Count() == 6)
6252
            {
6253
                poDS->m_nTileMatrixWidth0 = std::max(1, atoi(aoList[4]));
6254
                poDS->m_nTileMatrixHeight0 = std::max(1, atoi(aoList[5]));
6255
            }
6256
            else if (poDS->m_dfTopX == -180 && poDS->m_dfTileDim0 == 180)
6257
            {
6258
                // Assumes WorldCRS84Quad with 2 tiles in width
6259
                // cf https://github.com/OSGeo/gdal/issues/11749
6260
                poDS->m_nTileMatrixWidth0 = 2;
6261
            }
6262
        }
6263
        else
6264
        {
6265
            CPLError(CE_Failure, CPLE_AppDefined,
6266
                     "Wrong format for TILING_SCHEME. "
6267
                     "Expecting EPSG:XXXX,tile_origin_upper_left_x,"
6268
                     "tile_origin_upper_left_y,tile_dimension_zoom_0[,tile_"
6269
                     "matrix_width_zoom_0,tile_matrix_height_zoom_0]");
6270
            delete poDS;
6271
            return nullptr;
6272
        }
6273
    }
6274
6275
    if (bMBTILES)
6276
    {
6277
        if (sqlite3_open_v2(pszFilename, &poDS->m_hDBMBTILES,
6278
                            SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
6279
                                SQLITE_OPEN_NOMUTEX,
6280
                            poDS->m_pMyVFS->zName) != SQLITE_OK ||
6281
            poDS->m_hDBMBTILES == nullptr)
6282
        {
6283
            CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", pszFilename);
6284
            delete poDS;
6285
            return nullptr;
6286
        }
6287
6288
        if (SQLCommand(
6289
                poDS->m_hDBMBTILES,
6290
                "PRAGMA page_size = 4096;"  // 4096: default since sqlite 3.12
6291
                "PRAGMA synchronous = OFF;"
6292
                "PRAGMA journal_mode = OFF;"
6293
                "PRAGMA temp_store = MEMORY;"
6294
                "CREATE TABLE metadata (name text, value text);"
6295
                "CREATE TABLE tiles (zoom_level integer, tile_column integer, "
6296
                "tile_row integer, tile_data blob, "
6297
                "UNIQUE (zoom_level, tile_column, tile_row))") != OGRERR_NONE)
6298
        {
6299
            delete poDS;
6300
            return nullptr;
6301
        }
6302
    }
6303
6304
    int nThreads = CPLGetNumCPUs();
6305
    const char *pszNumThreads = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
6306
    if (pszNumThreads && CPLGetValueType(pszNumThreads) == CPL_VALUE_INTEGER)
6307
    {
6308
        nThreads = atoi(pszNumThreads);
6309
    }
6310
    if (nThreads > 1)
6311
    {
6312
        poDS->m_bThreadPoolOK =
6313
            poDS->m_oThreadPool.Setup(nThreads, nullptr, nullptr);
6314
    }
6315
6316
    poDS->SetDescription(pszFilename);
6317
    poDS->poDriver = GDALDriver::FromHandle(GDALGetDriverByName("MVT"));
6318
6319
    return poDS;
6320
}
6321
6322
GDALDataset *OGRMVTWriterDatasetCreate(const char *pszFilename, int nXSize,
6323
                                       int nYSize, int nBandsIn,
6324
                                       GDALDataType eDT, char **papszOptions)
6325
{
6326
    return OGRMVTWriterDataset::Create(pszFilename, nXSize, nYSize, nBandsIn,
6327
                                       eDT, papszOptions);
6328
}
6329
6330
#endif  // HAVE_MVT_WRITE_SUPPORT
6331
6332
/************************************************************************/
6333
/*                           RegisterOGRMVT()                           */
6334
/************************************************************************/
6335
6336
void RegisterOGRMVT()
6337
6338
24
{
6339
24
    if (GDALGetDriverByName("MVT") != nullptr)
6340
0
        return;
6341
6342
24
    GDALDriver *poDriver = new GDALDriver();
6343
6344
24
    poDriver->SetDescription("MVT");
6345
24
    poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
6346
24
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Mapbox Vector Tiles");
6347
24
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/mvt.html");
6348
24
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
6349
24
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "mvt mvt.gz pbf");
6350
24
    poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "SQLITE OGRSQL");
6351
6352
24
    poDriver->SetMetadataItem(
6353
24
        GDAL_DMD_OPENOPTIONLIST,
6354
24
        "<OpenOptionList>"
6355
24
        "  <Option name='X' type='int' description='X coordinate of tile'/>"
6356
24
        "  <Option name='Y' type='int' description='Y coordinate of tile'/>"
6357
24
        "  <Option name='Z' type='int' description='Z coordinate of tile'/>"
6358
        //"  <Option name='@GEOREF_TOPX' type='float' description='X coordinate
6359
        // of top-left corner of tile'/>" "  <Option name='@GEOREF_TOPY'
6360
        // type='float' description='Y coordinate of top-left corner of tile'/>"
6361
        //"  <Option name='@GEOREF_TILEDIMX' type='float' description='Tile
6362
        // width in georeferenced units'/>" "  <Option name='@GEOREF_TILEDIMY'
6363
        // type='float' description='Tile height in georeferenced units'/>"
6364
24
        "  <Option name='METADATA_FILE' type='string' "
6365
24
        "description='Path to metadata.json'/>"
6366
24
        "  <Option name='CLIP' type='boolean' "
6367
24
        "description='Whether to clip geometries to tile extent' "
6368
24
        "default='YES'/>"
6369
24
        "  <Option name='TILE_EXTENSION' type='string' default='pbf' "
6370
24
        "description="
6371
24
        "'For tilesets, extension of tiles'/>"
6372
24
        "  <Option name='TILE_COUNT_TO_ESTABLISH_FEATURE_DEFN' type='int' "
6373
24
        "description="
6374
24
        "'For tilesets without metadata file, maximum number of tiles to use "
6375
24
        "to "
6376
24
        "establish the layer schemas' default='1000'/>"
6377
24
        "  <Option name='JSON_FIELD' type='boolean' description='For tilesets, "
6378
24
        "whether to put all attributes as a serialized JSon dictionary'/>"
6379
24
        "</OpenOptionList>");
6380
6381
24
    poDriver->pfnIdentify = OGRMVTDriverIdentify;
6382
24
    poDriver->pfnOpen = OGRMVTDataset::Open;
6383
#ifdef HAVE_MVT_WRITE_SUPPORT
6384
    poDriver->pfnCreate = OGRMVTWriterDataset::Create;
6385
    poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
6386
    poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
6387
    poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
6388
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES,
6389
                              "Integer Integer64 Real String");
6390
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES,
6391
                              "Boolean Float32");
6392
    poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "SQLITE OGRSQL");
6393
6394
    poDriver->SetMetadataItem(GDAL_DS_LAYER_CREATIONOPTIONLIST, MVT_LCO);
6395
6396
    poDriver->SetMetadataItem(
6397
        GDAL_DMD_CREATIONOPTIONLIST,
6398
        "<CreationOptionList>"
6399
        "  <Option name='NAME' type='string' description='Tileset name'/>"
6400
        "  <Option name='DESCRIPTION' type='string' "
6401
        "description='A description of the tileset'/>"
6402
        "  <Option name='TYPE' type='string-select' description='Layer type' "
6403
        "default='overlay'>"
6404
        "    <Value>overlay</Value>"
6405
        "    <Value>baselayer</Value>"
6406
        "  </Option>"
6407
        "  <Option name='FORMAT' type='string-select' description='Format'>"
6408
        "    <Value>DIRECTORY</Value>"
6409
        "    <Value>MBTILES</Value>"
6410
        "  </Option>"
6411
        "  <Option name='TILE_EXTENSION' type='string' default='pbf' "
6412
        "description="
6413
        "'For tilesets as directories of files, extension of "
6414
        "tiles'/>" MVT_MBTILES_COMMON_DSCO
6415
        "  <Option name='BOUNDS' type='string' "
6416
        "description='Override default value for bounds metadata item'/>"
6417
        "  <Option name='CENTER' type='string' "
6418
        "description='Override default value for center metadata item'/>"
6419
        "  <Option name='TILING_SCHEME' type='string' "
6420
        "description='Custom tiling scheme with following format "
6421
        "\"EPSG:XXXX,tile_origin_upper_left_x,tile_origin_upper_left_y,"
6422
        "tile_dimension_zoom_0[,tile_matrix_width_zoom_0,tile_matrix_height_"
6423
        "zoom_0]\"'/>"
6424
        "</CreationOptionList>");
6425
#endif  // HAVE_MVT_WRITE_SUPPORT
6426
6427
24
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
6428
6429
24
    GetGDALDriverManager()->RegisterDriver(poDriver);
6430
24
}