Coverage Report

Created: 2025-11-15 08:43

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