Coverage Report

Created: 2026-08-11 08:26

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