Coverage Report

Created: 2025-08-11 09:23

/src/gdal/frmts/mbtiles/mbtilesdataset.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  GDAL MBTiles driver
4
 * Purpose:  Implement GDAL MBTiles support using OGR SQLite driver
5
 * Author:   Even Rouault, Even Rouault <even.rouault at spatialys.com>
6
 *
7
 **********************************************************************
8
 * Copyright (c) 2012-2016, Even Rouault <even.rouault at spatialys.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 "gdal_frmts.h"
19
#include "gdal_pam.h"
20
#include "ogr_api.h"
21
#include "cpl_json.h"
22
#include "cpl_vsil_curl_priv.h"
23
#include "gpkgmbtilescommon.h"
24
#include "gdal_utils.h"
25
#include "gdalwarper.h"
26
#include "mvtutils.h"
27
#include "ogrsqlitevfs.h"
28
#include "ogrsqlitebase.h"
29
30
#include "zlib.h"
31
#include "ogrlibjsonutils.h"
32
33
#include <math.h>
34
#include <algorithm>
35
#include <memory>
36
#include <vector>
37
38
static const char *const apszAllowedDrivers[] = {"JPEG", "PNG", "WEBP",
39
                                                 nullptr};
40
41
#define SRS_EPSG_3857                                                          \
42
6.51k
    "PROJCS[\"WGS 84 / Pseudo-Mercator\",GEOGCS[\"WGS "                        \
43
6.51k
    "84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS "                                  \
44
6.51k
    "84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY["      \
45
6.51k
    "\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]]" \
46
6.51k
    ",UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"       \
47
6.51k
    "AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"Mercator_1SP\"],PARAMETER["    \
48
6.51k
    "\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_" \
49
6.51k
    "easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[" \
50
6.51k
    "\"EPSG\",\"9001\"]],AXIS[\"X\",EAST],AXIS[\"Y\",NORTH],EXTENSION["        \
51
6.51k
    "\"PROJ4\",\"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 "     \
52
6.51k
    "+x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext  "                \
53
6.51k
    "+no_defs\"],AUTHORITY[\"EPSG\",\"3857\"]]"
54
55
12.1k
#define SPHERICAL_RADIUS 6378137.0
56
51.1k
#define MAX_GM (SPHERICAL_RADIUS * M_PI)  // 20037508.342789244
57
58
// TileMatrixSet origin : caution this is in GeoPackage / WMTS convention ! That
59
// is upper-left corner
60
656
#define TMS_ORIGIN_X -MAX_GM
61
656
#define TMS_ORIGIN_Y MAX_GM
62
63
#if defined(DEBUG) || defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) ||     \
64
    defined(ALLOW_FORMAT_DUMPS)
65
// Enable accepting a SQL dump (starting with a "-- SQL MBTILES" line) as a
66
// valid file. This makes fuzzer life easier
67
#define ENABLE_SQL_SQLITE_FORMAT
68
#endif
69
70
constexpr int knDEFAULT_BLOCK_SIZE = 256;
71
72
class MBTilesBand;
73
74
/************************************************************************/
75
/*                         MBTILESOpenSQLiteDB()                        */
76
/************************************************************************/
77
78
static GDALDatasetH MBTILESOpenSQLiteDB(const char *pszFilename,
79
                                        GDALAccess eAccess)
80
873
{
81
873
    const char *l_apszAllowedDrivers[] = {"SQLITE", nullptr};
82
873
    return GDALOpenEx((CPLString("SQLITE:") + pszFilename).c_str(),
83
873
                      GDAL_OF_VECTOR | GDAL_OF_INTERNAL |
84
873
                          ((eAccess == GA_Update) ? GDAL_OF_UPDATE : 0),
85
873
                      l_apszAllowedDrivers, nullptr, nullptr);
86
873
}
87
88
/************************************************************************/
89
/* ==================================================================== */
90
/*                              MBTilesDataset                          */
91
/* ==================================================================== */
92
/************************************************************************/
93
94
class MBTilesDataset final : public GDALPamDataset,
95
                             public GDALGPKGMBTilesLikePseudoDataset
96
{
97
    friend class MBTilesBand;
98
    friend class MBTilesVectorLayer;
99
100
  public:
101
    MBTilesDataset();
102
103
    virtual ~MBTilesDataset();
104
105
    virtual CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
106
    virtual CPLErr SetGeoTransform(const GDALGeoTransform &gt) override;
107
    const OGRSpatialReference *GetSpatialRef() const override;
108
    CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
109
110
    virtual char **GetMetadataDomainList() override;
111
    virtual char **GetMetadata(const char *pszDomain = "") override;
112
    virtual const char *GetMetadataItem(const char *pszName,
113
                                        const char *pszDomain = "") override;
114
115
    virtual CPLErr IBuildOverviews(const char *pszResampling, int nOverviews,
116
                                   const int *panOverviewList, int nBandsIn,
117
                                   const int * /* panBandList */,
118
                                   GDALProgressFunc pfnProgress,
119
                                   void *pProgressData,
120
                                   CSLConstList papszOptions) override;
121
122
    virtual int GetLayerCount() override
123
6.29k
    {
124
6.29k
        return static_cast<int>(m_apoLayers.size());
125
6.29k
    }
126
127
    virtual OGRLayer *GetLayer(int) override;
128
129
    static GDALDataset *Open(GDALOpenInfo *);
130
    static int Identify(GDALOpenInfo *);
131
    static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
132
                               int nBandsIn, GDALDataType eDT,
133
                               char **papszOptions);
134
    static GDALDataset *CreateCopy(const char *pszFilename,
135
                                   GDALDataset *poSrcDS, int bStrict,
136
                                   char **papszOptions,
137
                                   GDALProgressFunc pfnProgress,
138
                                   void *pProgressData);
139
140
    char *FindKey(int iPixel, int iLine);
141
142
    bool HasNonEmptyGrids();
143
144
  private:
145
    bool m_bWriteBounds;
146
    CPLString m_osBounds;
147
    CPLString m_osCenter;
148
    bool m_bWriteMinMaxZoom;
149
    MBTilesDataset *poMainDS;
150
    bool m_bGeoTransformValid;
151
    GDALGeoTransform m_gt{};
152
    int m_nMinZoomLevel = 0;
153
    OGRSpatialReference m_oSRS{};
154
155
    int m_nOverviewCount;
156
    MBTilesDataset **m_papoOverviewDS;
157
158
    GDALDatasetH hDS;
159
    sqlite3 *hDB;
160
161
    sqlite3_vfs *pMyVFS;
162
163
    bool bFetchedMetadata;
164
    CPLStringList aosList;
165
166
    int nHasNonEmptyGrids;
167
168
    bool m_bInFlushCache;
169
170
    CPLString m_osMetadataMemFilename;
171
    CPLString m_osClip;
172
    std::vector<std::unique_ptr<OGRLayer>> m_apoLayers;
173
174
    void ParseCompressionOptions(char **papszOptions);
175
    CPLErr FinalizeRasterRegistration();
176
    void ComputeTileAndPixelShifts();
177
    bool InitRaster(MBTilesDataset *poParentDS, int nZoomLevel, int nBandCount,
178
                    int nTileSize, double dfGDALMinX, double dfGDALMinY,
179
                    double dfGDALMaxX, double dfGDALMaxY);
180
181
    bool CreateInternal(const char *pszFilename, int nXSize, int nYSize,
182
                        int nBandsIn, GDALDataType eDT, char **papszOptions);
183
    void InitVector(double dfMinX, double dfMinY, double dfMaxX, double dfMaxY,
184
                    bool bZoomLevelFromSpatialFilter, bool bJsonField);
185
186
  protected:
187
    // Coming from GDALGPKGMBTilesLikePseudoDataset
188
189
    virtual CPLErr IFlushCacheWithErrCode(bool bAtClosing) override;
190
191
    virtual int IGetRasterCount() override
192
229
    {
193
229
        return nBands;
194
229
    }
195
196
    virtual GDALRasterBand *IGetRasterBand(int nBand) override
197
294
    {
198
294
        return GetRasterBand(nBand);
199
294
    }
200
201
    virtual sqlite3 *IGetDB() override
202
61
    {
203
61
        return hDB;
204
61
    }
205
206
    virtual bool IGetUpdate() override
207
2.63k
    {
208
2.63k
        return eAccess == GA_Update;
209
2.63k
    }
210
211
    virtual bool ICanIWriteBlock() override;
212
    virtual OGRErr IStartTransaction() override;
213
    virtual OGRErr ICommitTransaction() override;
214
215
    virtual const char *IGetFilename() override
216
0
    {
217
0
        return GetDescription();
218
0
    }
219
220
    virtual int GetRowFromIntoTopConvention(int nRow) override;
221
};
222
223
/************************************************************************/
224
/* ==================================================================== */
225
/*                          MBTilesVectorLayer                          */
226
/* ==================================================================== */
227
/************************************************************************/
228
229
class MBTilesVectorLayer final : public OGRLayer
230
{
231
    MBTilesDataset *m_poDS = nullptr;
232
    OGRFeatureDefn *m_poFeatureDefn = nullptr;
233
    OGRLayerH m_hTileIteratorLyr = nullptr;
234
    bool m_bEOF = false;
235
    CPLString m_osTmpFilename;
236
    GDALDatasetH m_hTileDS = nullptr;
237
    GIntBig m_nFeatureCount = -1;
238
    int m_nX = 0;
239
    int m_nY = 0;
240
    OGREnvelope m_sExtent;
241
    int m_nFilterMinX = 0;
242
    int m_nFilterMinY = 0;
243
    int m_nFilterMaxX = 0;
244
    int m_nFilterMaxY = 0;
245
    int m_nZoomLevel = 0;
246
    bool m_bZoomLevelAuto = false;
247
    bool m_bJsonField = false;
248
249
    OGRFeature *GetNextRawFeature();
250
    OGRFeature *GetNextSrcFeature();
251
    OGRFeature *CreateFeatureFrom(OGRFeature *poSrcFeature);
252
253
  public:
254
    MBTilesVectorLayer(MBTilesDataset *poDS, const char *pszLayerName,
255
                       const CPLJSONObject &oFields,
256
                       const CPLJSONArray &oAttributesFromTileStats,
257
                       bool bJsonField, double dfMinX, double dfMinY,
258
                       double dfMaxX, double dfMaxY,
259
                       OGRwkbGeometryType eGeomType,
260
                       bool bZoomLevelFromSpatialFilter);
261
    ~MBTilesVectorLayer();
262
263
    virtual void ResetReading() override;
264
    virtual OGRFeature *GetNextFeature() override;
265
266
    virtual OGRFeatureDefn *GetLayerDefn() override
267
240k
    {
268
240k
        return m_poFeatureDefn;
269
240k
    }
270
271
    virtual GIntBig GetFeatureCount(int bForce) override;
272
    virtual int TestCapability(const char *) override;
273
274
    virtual OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
275
                              bool bForce) override;
276
277
    virtual OGRErr ISetSpatialFilter(int iGeomField,
278
                                     const OGRGeometry *poGeom) override;
279
280
    virtual OGRFeature *GetFeature(GIntBig nFID) override;
281
};
282
283
/************************************************************************/
284
/* ==================================================================== */
285
/*                              MBTilesBand                             */
286
/* ==================================================================== */
287
/************************************************************************/
288
289
class MBTilesBand final : public GDALGPKGMBTilesLikeRasterBand
290
{
291
    friend class MBTilesDataset;
292
293
    CPLString osLocationInfo;
294
295
  public:
296
    explicit MBTilesBand(MBTilesDataset *poDS, int nTileSize);
297
298
    virtual int GetOverviewCount() override;
299
    virtual GDALRasterBand *GetOverview(int nLevel) override;
300
301
    virtual char **GetMetadataDomainList() override;
302
    virtual const char *GetMetadataItem(const char *pszName,
303
                                        const char *pszDomain = "") override;
304
};
305
306
/************************************************************************/
307
/*                            MBTilesBand()                             */
308
/************************************************************************/
309
310
MBTilesBand::MBTilesBand(MBTilesDataset *poDSIn, int nTileSize)
311
2.62k
    : GDALGPKGMBTilesLikeRasterBand(poDSIn, nTileSize, nTileSize)
312
2.62k
{
313
2.62k
}
314
315
/************************************************************************/
316
/*                           utf8decode()                               */
317
/************************************************************************/
318
319
static unsigned utf8decode(const char *p, const char *end, int *len)
320
0
{
321
0
    unsigned char c = *(unsigned char *)p;
322
0
    if (c < 0x80)
323
0
    {
324
0
        *len = 1;
325
0
        return c;
326
0
    }
327
0
    else if (c < 0xc2)
328
0
    {
329
0
        goto FAIL;
330
0
    }
331
0
    if (p + 1 >= end || (p[1] & 0xc0) != 0x80)
332
0
        goto FAIL;
333
0
    if (c < 0xe0)
334
0
    {
335
0
        *len = 2;
336
0
        return ((p[0] & 0x1f) << 6) + ((p[1] & 0x3f));
337
0
    }
338
0
    else if (c == 0xe0)
339
0
    {
340
0
        if (((unsigned char *)p)[1] < 0xa0)
341
0
            goto FAIL;
342
0
        goto UTF8_3;
343
0
    }
344
#if STRICT_RFC3629
345
    else if (c == 0xed)
346
    {
347
        // RFC 3629 says surrogate chars are illegal.
348
        if (((unsigned char *)p)[1] >= 0xa0)
349
            goto FAIL;
350
        goto UTF8_3;
351
    }
352
    else if (c == 0xef)
353
    {
354
        // 0xfffe and 0xffff are also illegal characters
355
        if (((unsigned char *)p)[1] == 0xbf && ((unsigned char *)p)[2] >= 0xbe)
356
            goto FAIL;
357
        goto UTF8_3;
358
    }
359
#endif
360
0
    else if (c < 0xf0)
361
0
    {
362
0
    UTF8_3:
363
0
        if (p + 2 >= end || (p[2] & 0xc0) != 0x80)
364
0
            goto FAIL;
365
0
        *len = 3;
366
0
        return ((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6) + ((p[2] & 0x3f));
367
0
    }
368
0
    else if (c == 0xf0)
369
0
    {
370
0
        if (((unsigned char *)p)[1] < 0x90)
371
0
            goto FAIL;
372
0
        goto UTF8_4;
373
0
    }
374
0
    else if (c < 0xf4)
375
0
    {
376
0
    UTF8_4:
377
0
        if (p + 3 >= end || (p[2] & 0xc0) != 0x80 || (p[3] & 0xc0) != 0x80)
378
0
            goto FAIL;
379
0
        *len = 4;
380
#if STRICT_RFC3629
381
        // RFC 3629 says all codes ending in fffe or ffff are illegal:
382
        if ((p[1] & 0xf) == 0xf && ((unsigned char *)p)[2] == 0xbf &&
383
            ((unsigned char *)p)[3] >= 0xbe)
384
            goto FAIL;
385
#endif
386
0
        return ((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12) +
387
0
               ((p[2] & 0x3f) << 6) + ((p[3] & 0x3f));
388
0
    }
389
0
    else if (c == 0xf4)
390
0
    {
391
0
        if (((unsigned char *)p)[1] > 0x8f)
392
0
            goto FAIL;  // after 0x10ffff
393
0
        goto UTF8_4;
394
0
    }
395
0
    else
396
0
    {
397
0
    FAIL:
398
0
        *len = 1;
399
0
        return 0xfffd;  // Unicode REPLACEMENT CHARACTER
400
0
    }
401
0
}
402
403
/************************************************************************/
404
/*                          HasNonEmptyGrids()                          */
405
/************************************************************************/
406
407
bool MBTilesDataset::HasNonEmptyGrids()
408
0
{
409
0
    OGRLayerH hSQLLyr;
410
0
    OGRFeatureH hFeat;
411
412
0
    if (poMainDS)
413
0
        return poMainDS->HasNonEmptyGrids();
414
415
0
    if (nHasNonEmptyGrids >= 0)
416
0
        return nHasNonEmptyGrids != FALSE;
417
418
0
    nHasNonEmptyGrids = false;
419
420
0
    if (GDALDatasetGetLayerByName(hDS, "grids") == nullptr)
421
0
        return false;
422
423
0
    const char *pszSQL = "SELECT type FROM sqlite_master WHERE name = 'grids'";
424
0
    CPLDebug("MBTILES", "%s", pszSQL);
425
0
    hSQLLyr = GDALDatasetExecuteSQL(hDS, pszSQL, nullptr, nullptr);
426
0
    if (hSQLLyr == nullptr)
427
0
        return false;
428
429
0
    hFeat = OGR_L_GetNextFeature(hSQLLyr);
430
0
    if (hFeat == nullptr || !OGR_F_IsFieldSetAndNotNull(hFeat, 0))
431
0
    {
432
0
        OGR_F_Destroy(hFeat);
433
0
        GDALDatasetReleaseResultSet(hDS, hSQLLyr);
434
0
        return false;
435
0
    }
436
437
0
    bool bGridsIsView = strcmp(OGR_F_GetFieldAsString(hFeat, 0), "view") == 0;
438
439
0
    OGR_F_Destroy(hFeat);
440
0
    GDALDatasetReleaseResultSet(hDS, hSQLLyr);
441
442
0
    nHasNonEmptyGrids = TRUE;
443
444
    /* In the case 'grids' is a view (and a join between the 'map' and
445
     * 'grid_utfgrid' layers */
446
    /* the cost of evaluating a join is very long, even if grid_utfgrid is empty
447
     */
448
    /* so check it is not empty */
449
0
    if (bGridsIsView)
450
0
    {
451
0
        OGRLayerH hGridUTFGridLyr;
452
0
        hGridUTFGridLyr = GDALDatasetGetLayerByName(hDS, "grid_utfgrid");
453
0
        if (hGridUTFGridLyr != nullptr)
454
0
        {
455
0
            OGR_L_ResetReading(hGridUTFGridLyr);
456
0
            hFeat = OGR_L_GetNextFeature(hGridUTFGridLyr);
457
0
            OGR_F_Destroy(hFeat);
458
459
0
            nHasNonEmptyGrids = hFeat != nullptr;
460
0
        }
461
0
    }
462
463
0
    return nHasNonEmptyGrids != FALSE;
464
0
}
465
466
/************************************************************************/
467
/*                             FindKey()                                */
468
/************************************************************************/
469
470
char *MBTilesDataset::FindKey(int iPixel, int iLine)
471
0
{
472
0
    int nBlockXSize;
473
0
    int nBlockYSize;
474
0
    GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
475
476
    // Compute shift between GDAL origin and TileMatrixSet origin
477
    // Caution this is in GeoPackage / WMTS convention ! That is upper-left
478
    // corner
479
0
    const int nShiftXPixels =
480
0
        (int)floor(0.5 + (m_gt[0] - TMS_ORIGIN_X) / m_gt[1]);
481
0
    const int nShiftYPixelsFromGPKGOrigin =
482
0
        (int)floor(0.5 + (m_gt[3] - TMS_ORIGIN_Y) / m_gt[5]);
483
484
0
    const int iLineFromGPKGOrigin = iLine + nShiftYPixelsFromGPKGOrigin;
485
0
    const int iLineFromMBTilesOrigin =
486
0
        m_nTileMatrixHeight * nBlockYSize - 1 - iLineFromGPKGOrigin;
487
0
    const int iPixelFromMBTilesOrigin = iPixel + nShiftXPixels;
488
489
0
    const int nTileColumn = iPixelFromMBTilesOrigin / nBlockXSize;
490
0
    const int nTileRow = iLineFromMBTilesOrigin / nBlockYSize;
491
0
    int nColInTile = iPixelFromMBTilesOrigin % nBlockXSize;
492
0
    int nRowInTile = nBlockYSize - 1 - (iLineFromMBTilesOrigin % nBlockYSize);
493
494
0
    char *pszKey = nullptr;
495
496
0
    OGRLayerH hSQLLyr;
497
0
    OGRFeatureH hFeat;
498
0
    json_object *poGrid = nullptr;
499
0
    int i;
500
501
    /* See https://github.com/mapbox/utfgrid-spec/blob/master/1.0/utfgrid.md */
502
    /* for the explanation of the following process */
503
0
    const char *pszSQL =
504
0
        CPLSPrintf("SELECT grid FROM grids WHERE "
505
0
                   "zoom_level = %d AND tile_column = %d AND tile_row = %d",
506
0
                   m_nZoomLevel, nTileColumn, nTileRow);
507
0
    CPLDebug("MBTILES", "%s", pszSQL);
508
0
    hSQLLyr = GDALDatasetExecuteSQL(hDS, pszSQL, nullptr, nullptr);
509
0
    if (hSQLLyr == nullptr)
510
0
        return nullptr;
511
512
0
    hFeat = OGR_L_GetNextFeature(hSQLLyr);
513
0
    if (hFeat == nullptr || !OGR_F_IsFieldSetAndNotNull(hFeat, 0))
514
0
    {
515
0
        OGR_F_Destroy(hFeat);
516
0
        GDALDatasetReleaseResultSet(hDS, hSQLLyr);
517
0
        return nullptr;
518
0
    }
519
520
0
    int nDataSize = 0;
521
0
    GByte *pabyData = OGR_F_GetFieldAsBinary(hFeat, 0, &nDataSize);
522
523
0
    int nUncompressedSize = nBlockXSize * nBlockYSize;
524
0
    GByte *pabyUncompressed = (GByte *)VSIMalloc(nUncompressedSize + 1);
525
0
    if (pabyUncompressed == nullptr)
526
0
    {
527
0
        OGR_F_Destroy(hFeat);
528
0
        GDALDatasetReleaseResultSet(hDS, hSQLLyr);
529
0
        return nullptr;
530
0
    }
531
532
0
    z_stream sStream;
533
0
    memset(&sStream, 0, sizeof(sStream));
534
0
    if (inflateInit(&sStream) != Z_OK)
535
0
    {
536
0
        OGR_F_Destroy(hFeat);
537
0
        GDALDatasetReleaseResultSet(hDS, hSQLLyr);
538
0
        CPLFree(pabyUncompressed);
539
0
        return nullptr;
540
0
    }
541
0
    sStream.next_in = pabyData;
542
0
    sStream.avail_in = nDataSize;
543
0
    sStream.next_out = pabyUncompressed;
544
0
    sStream.avail_out = nUncompressedSize;
545
0
    int nStatus = inflate(&sStream, Z_FINISH);
546
0
    inflateEnd(&sStream);
547
0
    if (nStatus != Z_OK && nStatus != Z_STREAM_END)
548
0
    {
549
0
        CPLDebug("MBTILES", "Error unzipping grid");
550
0
        nUncompressedSize = 0;
551
0
        pabyUncompressed[nUncompressedSize] = 0;
552
0
    }
553
0
    else
554
0
    {
555
0
        nUncompressedSize -= sStream.avail_out;
556
0
        pabyUncompressed[nUncompressedSize] = 0;
557
        // CPLDebug("MBTILES", "Grid size = %d", nUncompressedSize);
558
        // CPLDebug("MBTILES", "Grid value = %s", (const
559
        // char*)pabyUncompressed);
560
0
    }
561
562
0
    json_object *jsobj = nullptr;
563
564
0
    if (nUncompressedSize == 0)
565
0
    {
566
0
        goto end;
567
0
    }
568
569
0
    if (!OGRJSonParse((const char *)pabyUncompressed, &jsobj, true))
570
0
    {
571
0
        goto end;
572
0
    }
573
574
0
    if (json_object_is_type(jsobj, json_type_object))
575
0
    {
576
0
        poGrid = CPL_json_object_object_get(jsobj, "grid");
577
0
    }
578
0
    if (poGrid != nullptr && json_object_is_type(poGrid, json_type_array))
579
0
    {
580
0
        int nFactor;
581
0
        json_object *poRow;
582
0
        char *pszRow = nullptr;
583
584
0
        const int nLines = static_cast<int>(json_object_array_length(poGrid));
585
0
        if (nLines == 0)
586
0
            goto end;
587
588
0
        nFactor = nBlockXSize / nLines;
589
0
        nRowInTile /= nFactor;
590
0
        nColInTile /= nFactor;
591
592
0
        poRow = json_object_array_get_idx(poGrid, nRowInTile);
593
594
        /* Extract line of interest in grid */
595
0
        if (poRow != nullptr && json_object_is_type(poRow, json_type_string))
596
0
        {
597
0
            pszRow = CPLStrdup(json_object_get_string(poRow));
598
0
        }
599
600
0
        if (pszRow == nullptr)
601
0
            goto end;
602
603
        /* Unapply JSON encoding */
604
0
        for (i = 0; pszRow[i] != '\0'; i++)
605
0
        {
606
0
            unsigned char c = ((GByte *)pszRow)[i];
607
0
            if (c >= 93)
608
0
                c--;
609
0
            if (c >= 35)
610
0
                c--;
611
0
            if (c < 32)
612
0
            {
613
0
                CPLDebug("MBTILES", "Invalid character at byte %d", i);
614
0
                break;
615
0
            }
616
0
            c -= 32;
617
0
            ((GByte *)pszRow)[i] = c;
618
0
        }
619
620
0
        if (pszRow[i] == '\0')
621
0
        {
622
0
            char *pszEnd = pszRow + i;
623
624
0
            int iCol = 0;
625
0
            i = 0;
626
0
            int nKey = -1;
627
0
            while (pszRow + i < pszEnd)
628
0
            {
629
0
                int len = 0;
630
0
                unsigned int res = utf8decode(pszRow + i, pszEnd, &len);
631
632
                /* Invalid UTF8 ? */
633
0
                if (res > 127 && len == 1)
634
0
                    break;
635
636
0
                if (iCol == nColInTile)
637
0
                {
638
0
                    nKey = (int)res;
639
                    // CPLDebug("MBTILES", "Key index = %d", nKey);
640
0
                    break;
641
0
                }
642
0
                i += len;
643
0
                iCol++;
644
0
            }
645
646
            /* Find key */
647
0
            json_object *poKeys = CPL_json_object_object_get(jsobj, "keys");
648
0
            if (nKey >= 0 && poKeys != nullptr &&
649
0
                json_object_is_type(poKeys, json_type_array) &&
650
0
                nKey < static_cast<int>(json_object_array_length(poKeys)))
651
0
            {
652
0
                json_object *poKey = json_object_array_get_idx(poKeys, nKey);
653
0
                if (poKey != nullptr &&
654
0
                    json_object_is_type(poKey, json_type_string))
655
0
                {
656
0
                    pszKey = CPLStrdup(json_object_get_string(poKey));
657
0
                }
658
0
            }
659
0
        }
660
661
0
        CPLFree(pszRow);
662
0
    }
663
664
0
end:
665
0
    if (jsobj)
666
0
        json_object_put(jsobj);
667
0
    VSIFree(pabyUncompressed);
668
0
    if (hFeat)
669
0
        OGR_F_Destroy(hFeat);
670
0
    if (hSQLLyr)
671
0
        GDALDatasetReleaseResultSet(hDS, hSQLLyr);
672
673
0
    return pszKey;
674
0
}
675
676
/************************************************************************/
677
/*                      GetMetadataDomainList()                         */
678
/************************************************************************/
679
680
char **MBTilesBand::GetMetadataDomainList()
681
0
{
682
0
    return CSLAddString(GDALPamRasterBand::GetMetadataDomainList(),
683
0
                        "LocationInfo");
684
0
}
685
686
/************************************************************************/
687
/*                         GetMetadataItem()                            */
688
/************************************************************************/
689
690
const char *MBTilesBand::GetMetadataItem(const char *pszName,
691
                                         const char *pszDomain)
692
21
{
693
21
    MBTilesDataset *poGDS = (MBTilesDataset *)poDS;
694
695
    /* ==================================================================== */
696
    /*      LocationInfo handling.                                          */
697
    /* ==================================================================== */
698
21
    if (poGDS->hDS != nullptr && pszDomain != nullptr &&
699
21
        EQUAL(pszDomain, "LocationInfo") &&
700
21
        (STARTS_WITH_CI(pszName, "Pixel_") ||
701
0
         STARTS_WITH_CI(pszName, "GeoPixel_")))
702
0
    {
703
0
        int iPixel, iLine;
704
705
0
        if (!poGDS->HasNonEmptyGrids())
706
0
            return nullptr;
707
708
        /* --------------------------------------------------------------------
709
         */
710
        /*      What pixel are we aiming at? */
711
        /* --------------------------------------------------------------------
712
         */
713
0
        if (STARTS_WITH_CI(pszName, "Pixel_"))
714
0
        {
715
0
            if (sscanf(pszName + 6, "%d_%d", &iPixel, &iLine) != 2)
716
0
                return nullptr;
717
0
        }
718
0
        else if (STARTS_WITH_CI(pszName, "GeoPixel_"))
719
0
        {
720
0
            GDALGeoTransform gt;
721
0
            GDALGeoTransform invGT;
722
0
            double dfGeoX, dfGeoY;
723
724
0
            dfGeoX = CPLAtof(pszName + 9);
725
0
            const char *pszUnderscore = strchr(pszName + 9, '_');
726
0
            if (!pszUnderscore)
727
0
                return nullptr;
728
0
            dfGeoY = CPLAtof(pszUnderscore + 1);
729
730
0
            if (GetDataset() == nullptr)
731
0
                return nullptr;
732
733
0
            if (GetDataset()->GetGeoTransform(gt) != CE_None)
734
0
                return nullptr;
735
736
0
            if (!GDALInvGeoTransform(gt.data(), invGT.data()))
737
0
                return nullptr;
738
739
0
            iPixel =
740
0
                (int)floor(invGT[0] + invGT[1] * dfGeoX + invGT[2] * dfGeoY);
741
0
            iLine =
742
0
                (int)floor(invGT[3] + invGT[4] * dfGeoX + invGT[5] * dfGeoY);
743
0
        }
744
0
        else
745
0
            return nullptr;
746
747
0
        if (iPixel < 0 || iLine < 0 || iPixel >= GetXSize() ||
748
0
            iLine >= GetYSize())
749
0
            return nullptr;
750
751
0
        char *pszKey = poGDS->FindKey(iPixel, iLine);
752
753
0
        if (pszKey != nullptr)
754
0
        {
755
            // CPLDebug("MBTILES", "Key = %s", pszKey);
756
757
0
            osLocationInfo = "<LocationInfo>";
758
0
            osLocationInfo += "<Key>";
759
0
            char *pszXMLEscaped =
760
0
                CPLEscapeString(pszKey, -1, CPLES_XML_BUT_QUOTES);
761
0
            osLocationInfo += pszXMLEscaped;
762
0
            CPLFree(pszXMLEscaped);
763
0
            osLocationInfo += "</Key>";
764
765
0
            if (GDALDatasetGetLayerByName(poGDS->hDS, "grid_data") != nullptr &&
766
0
                strchr(pszKey, '\'') == nullptr)
767
0
            {
768
0
                OGRLayerH hSQLLyr;
769
0
                OGRFeatureH hFeat;
770
771
0
                const char *pszSQL =
772
0
                    CPLSPrintf("SELECT key_json FROM keymap WHERE "
773
0
                               "key_name = '%s'",
774
0
                               pszKey);
775
0
                CPLDebug("MBTILES", "%s", pszSQL);
776
0
                hSQLLyr =
777
0
                    GDALDatasetExecuteSQL(poGDS->hDS, pszSQL, nullptr, nullptr);
778
0
                if (hSQLLyr)
779
0
                {
780
0
                    hFeat = OGR_L_GetNextFeature(hSQLLyr);
781
0
                    if (hFeat != nullptr &&
782
0
                        OGR_F_IsFieldSetAndNotNull(hFeat, 0))
783
0
                    {
784
0
                        const char *pszJSon = OGR_F_GetFieldAsString(hFeat, 0);
785
                        // CPLDebug("MBTILES", "JSon = %s", pszJSon);
786
787
0
                        osLocationInfo += "<JSon>";
788
0
#ifdef CPLES_XML_BUT_QUOTES
789
0
                        pszXMLEscaped =
790
0
                            CPLEscapeString(pszJSon, -1, CPLES_XML_BUT_QUOTES);
791
#else
792
                        pszXMLEscaped = CPLEscapeString(pszJSon, -1, CPLES_XML);
793
#endif
794
0
                        osLocationInfo += pszXMLEscaped;
795
0
                        CPLFree(pszXMLEscaped);
796
0
                        osLocationInfo += "</JSon>";
797
0
                    }
798
0
                    OGR_F_Destroy(hFeat);
799
0
                }
800
0
                GDALDatasetReleaseResultSet(poGDS->hDS, hSQLLyr);
801
0
            }
802
803
0
            osLocationInfo += "</LocationInfo>";
804
805
0
            CPLFree(pszKey);
806
807
0
            return osLocationInfo.c_str();
808
0
        }
809
810
0
        return nullptr;
811
0
    }
812
21
    else
813
21
        return GDALPamRasterBand::GetMetadataItem(pszName, pszDomain);
814
21
}
815
816
/************************************************************************/
817
/*                         GetOverviewCount()                           */
818
/************************************************************************/
819
820
int MBTilesBand::GetOverviewCount()
821
21
{
822
21
    MBTilesDataset *poGDS = (MBTilesDataset *)poDS;
823
824
21
    if (poGDS->m_nOverviewCount >= 1)
825
1
        return poGDS->m_nOverviewCount;
826
20
    else
827
20
        return GDALPamRasterBand::GetOverviewCount();
828
21
}
829
830
/************************************************************************/
831
/*                              GetOverview()                           */
832
/************************************************************************/
833
834
GDALRasterBand *MBTilesBand::GetOverview(int nLevel)
835
1
{
836
1
    MBTilesDataset *poGDS = (MBTilesDataset *)poDS;
837
838
1
    if (poGDS->m_nOverviewCount == 0)
839
0
        return GDALPamRasterBand::GetOverview(nLevel);
840
841
1
    if (nLevel < 0 || nLevel >= poGDS->m_nOverviewCount)
842
0
        return nullptr;
843
844
1
    GDALDataset *poOvrDS = poGDS->m_papoOverviewDS[nLevel];
845
1
    if (poOvrDS)
846
1
        return poOvrDS->GetRasterBand(nBand);
847
0
    else
848
0
        return nullptr;
849
1
}
850
851
/************************************************************************/
852
/*                         MBTilesDataset()                          */
853
/************************************************************************/
854
855
MBTilesDataset::MBTilesDataset()
856
656
{
857
656
    m_bWriteBounds = true;
858
656
    m_bWriteMinMaxZoom = true;
859
656
    poMainDS = nullptr;
860
656
    m_nOverviewCount = 0;
861
656
    hDS = nullptr;
862
656
    m_papoOverviewDS = nullptr;
863
656
    bFetchedMetadata = false;
864
656
    nHasNonEmptyGrids = -1;
865
656
    hDB = nullptr;
866
656
    pMyVFS = nullptr;
867
868
656
    m_bGeoTransformValid = false;
869
656
    m_bInFlushCache = false;
870
871
656
    m_osRasterTable = "tiles";
872
656
    m_eTF = GPKG_TF_PNG;
873
874
656
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
875
656
    m_oSRS.importFromEPSG(3857);
876
656
}
877
878
/************************************************************************/
879
/*                        ~MBTilesDataset()                             */
880
/************************************************************************/
881
882
MBTilesDataset::~MBTilesDataset()
883
656
{
884
    // Need to explicitly clear it before close hDS
885
656
    m_apoLayers.clear();
886
887
656
    FlushCache(true);
888
889
656
    if (poMainDS == nullptr)
890
596
    {
891
596
        if (m_papoOverviewDS)
892
60
        {
893
120
            for (int i = 0; i < m_nOverviewCount; i++)
894
60
                delete m_papoOverviewDS[i];
895
60
            CPLFree(m_papoOverviewDS);
896
60
        }
897
898
596
        if (hDS != nullptr)
899
596
        {
900
596
            GDALClose(hDS);
901
596
            hDB = nullptr;
902
596
        }
903
596
        if (hDB != nullptr)
904
0
        {
905
0
            sqlite3_close(hDB);
906
907
0
            if (pMyVFS)
908
0
            {
909
0
                sqlite3_vfs_unregister(pMyVFS);
910
0
                CPLFree(pMyVFS->pAppData);
911
0
                CPLFree(pMyVFS);
912
0
            }
913
0
        }
914
596
    }
915
916
656
    if (!m_osMetadataMemFilename.empty())
917
530
    {
918
530
        VSIUnlink(m_osMetadataMemFilename);
919
530
    }
920
656
}
921
922
/************************************************************************/
923
/*                         IStartTransaction()                          */
924
/************************************************************************/
925
926
OGRErr MBTilesDataset::IStartTransaction()
927
0
{
928
0
    char *pszErrMsg = nullptr;
929
0
    const int rc = sqlite3_exec(hDB, "BEGIN", nullptr, nullptr, &pszErrMsg);
930
0
    if (rc != SQLITE_OK)
931
0
    {
932
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s transaction failed: %s",
933
0
                 "BEGIN", pszErrMsg);
934
0
        sqlite3_free(pszErrMsg);
935
0
        return OGRERR_FAILURE;
936
0
    }
937
938
0
    return OGRERR_NONE;
939
0
}
940
941
/************************************************************************/
942
/*                         ICommitTransaction()                         */
943
/************************************************************************/
944
945
OGRErr MBTilesDataset::ICommitTransaction()
946
0
{
947
0
    char *pszErrMsg = nullptr;
948
0
    const int rc = sqlite3_exec(hDB, "COMMIT", nullptr, nullptr, &pszErrMsg);
949
0
    if (rc != SQLITE_OK)
950
0
    {
951
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s transaction failed: %s",
952
0
                 "COMMIT", pszErrMsg);
953
0
        sqlite3_free(pszErrMsg);
954
0
        return OGRERR_FAILURE;
955
0
    }
956
957
0
    return OGRERR_NONE;
958
0
}
959
960
/************************************************************************/
961
/*                         ICanIWriteBlock()                            */
962
/************************************************************************/
963
964
bool MBTilesDataset::ICanIWriteBlock()
965
0
{
966
0
    if (eAccess != GA_Update)
967
0
    {
968
0
        CPLError(
969
0
            CE_Failure, CPLE_NotSupported,
970
0
            "IWriteBlock() not supported on dataset opened in read-only mode");
971
0
        return false;
972
0
    }
973
974
0
    if (!m_bGeoTransformValid)
975
0
    {
976
0
        CPLError(CE_Failure, CPLE_NotSupported,
977
0
                 "IWriteBlock() not supported if georeferencing not set");
978
0
        return false;
979
0
    }
980
0
    return true;
981
0
}
982
983
/************************************************************************/
984
/*                         IFlushCacheWithErrCode()                     */
985
/************************************************************************/
986
987
CPLErr MBTilesDataset::IFlushCacheWithErrCode(bool bAtClosing)
988
989
13.1k
{
990
13.1k
    if (m_bInFlushCache)
991
10.4k
        return CE_None;
992
2.62k
    m_bInFlushCache = true;
993
    // Short circuit GDALPamDataset to avoid serialization to .aux.xml
994
2.62k
    GDALDataset::FlushCache(bAtClosing);
995
996
2.62k
    CPLErr eErr = FlushTiles();
997
998
2.62k
    m_bInFlushCache = false;
999
2.62k
    return eErr;
1000
13.1k
}
1001
1002
/************************************************************************/
1003
/*                         ICanIWriteBlock()                            */
1004
/************************************************************************/
1005
1006
int MBTilesDataset::GetRowFromIntoTopConvention(int nRow)
1007
61
{
1008
61
    return m_nTileMatrixHeight - 1 - nRow;
1009
61
}
1010
1011
/************************************************************************/
1012
/*                          GetGeoTransform()                           */
1013
/************************************************************************/
1014
1015
CPLErr MBTilesDataset::GetGeoTransform(GDALGeoTransform &gt) const
1016
21
{
1017
21
    gt = m_gt;
1018
21
    return (m_bGeoTransformValid) ? CE_None : CE_Failure;
1019
21
}
1020
1021
/************************************************************************/
1022
/*                     SphericalMercatorToLongLat()                     */
1023
/************************************************************************/
1024
1025
static void SphericalMercatorToLongLat(double *x, double *y)
1026
0
{
1027
0
    double lng = *x / SPHERICAL_RADIUS / M_PI * 180;
1028
0
    double lat = 2 * (atan(exp(*y / SPHERICAL_RADIUS)) - M_PI / 4) / M_PI * 180;
1029
0
    *x = lng;
1030
0
    *y = lat;
1031
0
}
1032
1033
/************************************************************************/
1034
/*                     LongLatToSphericalMercator()                     */
1035
/************************************************************************/
1036
1037
static void LongLatToSphericalMercator(double *x, double *y)
1038
42
{
1039
42
    double X = SPHERICAL_RADIUS * (*x) / 180 * M_PI;
1040
42
    double Y = SPHERICAL_RADIUS * log(tan(M_PI / 4 + 0.5 * (*y) / 180 * M_PI));
1041
42
    *x = X;
1042
42
    *y = Y;
1043
42
}
1044
1045
/************************************************************************/
1046
/*                          SetGeoTransform()                           */
1047
/************************************************************************/
1048
1049
CPLErr MBTilesDataset::SetGeoTransform(const GDALGeoTransform &gt)
1050
0
{
1051
0
    if (eAccess != GA_Update)
1052
0
    {
1053
0
        CPLError(CE_Failure, CPLE_NotSupported,
1054
0
                 "SetGeoTransform() not supported on read-only dataset");
1055
0
        return CE_Failure;
1056
0
    }
1057
0
    if (m_bGeoTransformValid)
1058
0
    {
1059
0
        CPLError(CE_Failure, CPLE_NotSupported,
1060
0
                 "Cannot modify geotransform once set");
1061
0
        return CE_Failure;
1062
0
    }
1063
0
    if (gt[2] != 0.0 || gt[4] != 0 || gt[5] > 0.0)
1064
0
    {
1065
0
        CPLError(CE_Failure, CPLE_NotSupported,
1066
0
                 "Only north-up non rotated geotransform supported");
1067
0
        return CE_Failure;
1068
0
    }
1069
1070
0
    if (m_bWriteBounds)
1071
0
    {
1072
0
        CPLString osBounds(m_osBounds);
1073
0
        if (osBounds.empty())
1074
0
        {
1075
0
            double minx = gt[0];
1076
0
            double miny = gt[3] + nRasterYSize * gt[5];
1077
0
            double maxx = gt[0] + nRasterXSize * gt[1];
1078
0
            double maxy = gt[3];
1079
1080
0
            SphericalMercatorToLongLat(&minx, &miny);
1081
0
            SphericalMercatorToLongLat(&maxx, &maxy);
1082
0
            if (fabs(minx + 180) < 1e-7)
1083
0
            {
1084
0
                minx = -180.0;
1085
0
            }
1086
0
            if (fabs(maxx - 180) < 1e-7)
1087
0
            {
1088
0
                maxx = 180.0;
1089
0
            }
1090
1091
            // Clamp latitude so that when transformed back to EPSG:3857, we
1092
            // don't have too big northings
1093
0
            double tmpx = 0.0;
1094
0
            double ok_maxy = MAX_GM;
1095
0
            SphericalMercatorToLongLat(&tmpx, &ok_maxy);
1096
0
            if (maxy > ok_maxy)
1097
0
                maxy = ok_maxy;
1098
0
            if (miny < -ok_maxy)
1099
0
                miny = -ok_maxy;
1100
1101
0
            osBounds.Printf("%.17g,%.17g,%.17g,%.17g", minx, miny, maxx, maxy);
1102
0
        }
1103
1104
0
        char *pszSQL = sqlite3_mprintf(
1105
0
            "INSERT INTO metadata (name, value) VALUES ('bounds', '%q')",
1106
0
            osBounds.c_str());
1107
0
        sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr);
1108
0
        sqlite3_free(pszSQL);
1109
1110
0
        if (!m_osCenter.empty())
1111
0
        {
1112
0
            pszSQL = sqlite3_mprintf(
1113
0
                "INSERT INTO metadata (name, value) VALUES ('center', '%q')",
1114
0
                m_osCenter.c_str());
1115
0
            sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr);
1116
0
            sqlite3_free(pszSQL);
1117
0
        }
1118
0
    }
1119
1120
0
    int nBlockXSize;
1121
0
    int nBlockYSize;
1122
0
    GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
1123
0
    const double dfPixelXSizeZoomLevel0 = 2 * MAX_GM / nBlockXSize;
1124
0
    const double dfPixelYSizeZoomLevel0 = 2 * MAX_GM / nBlockYSize;
1125
0
    for (m_nZoomLevel = 0; m_nZoomLevel < 25; m_nZoomLevel++)
1126
0
    {
1127
0
        double dfExpectedPixelXSize =
1128
0
            dfPixelXSizeZoomLevel0 / (1 << m_nZoomLevel);
1129
0
        double dfExpectedPixelYSize =
1130
0
            dfPixelYSizeZoomLevel0 / (1 << m_nZoomLevel);
1131
0
        if (fabs(gt[1] - dfExpectedPixelXSize) < 1e-8 * dfExpectedPixelXSize &&
1132
0
            fabs(fabs(gt[5]) - dfExpectedPixelYSize) <
1133
0
                1e-8 * dfExpectedPixelYSize)
1134
0
        {
1135
0
            break;
1136
0
        }
1137
0
    }
1138
0
    if (m_nZoomLevel == 25)
1139
0
    {
1140
0
        m_nZoomLevel = -1;
1141
0
        CPLError(CE_Failure, CPLE_NotSupported,
1142
0
                 "Could not find an appropriate zoom level that matches raster "
1143
0
                 "pixel size");
1144
0
        return CE_Failure;
1145
0
    }
1146
1147
0
    m_gt = gt;
1148
0
    m_bGeoTransformValid = true;
1149
1150
0
    return FinalizeRasterRegistration();
1151
0
}
1152
1153
/************************************************************************/
1154
/*                      ComputeTileAndPixelShifts()                     */
1155
/************************************************************************/
1156
1157
void MBTilesDataset::ComputeTileAndPixelShifts()
1158
656
{
1159
656
    int nTileWidth, nTileHeight;
1160
656
    GetRasterBand(1)->GetBlockSize(&nTileWidth, &nTileHeight);
1161
1162
    // Compute shift between GDAL origin and TileMatrixSet origin
1163
    // Caution this is in GeoPackage / WMTS convention ! That is upper-left
1164
    // corner
1165
656
    int nShiftXPixels = (int)floor(0.5 + (m_gt[0] - TMS_ORIGIN_X) / m_gt[1]);
1166
656
    m_nShiftXTiles = (int)floor(1.0 * nShiftXPixels / nTileWidth);
1167
656
    m_nShiftXPixelsMod =
1168
656
        ((nShiftXPixels % nTileWidth) + nTileWidth) % nTileWidth;
1169
656
    int nShiftYPixels = (int)floor(0.5 + (m_gt[3] - TMS_ORIGIN_Y) / m_gt[5]);
1170
656
    m_nShiftYTiles = (int)floor(1.0 * nShiftYPixels / nTileHeight);
1171
656
    m_nShiftYPixelsMod =
1172
656
        ((nShiftYPixels % nTileHeight) + nTileHeight) % nTileHeight;
1173
656
}
1174
1175
/************************************************************************/
1176
/*                      FinalizeRasterRegistration()                    */
1177
/************************************************************************/
1178
1179
CPLErr MBTilesDataset::FinalizeRasterRegistration()
1180
0
{
1181
0
    m_nTileMatrixWidth = (1 << m_nZoomLevel);
1182
0
    m_nTileMatrixHeight = (1 << m_nZoomLevel);
1183
1184
0
    ComputeTileAndPixelShifts();
1185
1186
0
    double dfGDALMinX = m_gt[0];
1187
0
    double dfGDALMinY = m_gt[3] + nRasterYSize * m_gt[5];
1188
0
    double dfGDALMaxX = m_gt[0] + nRasterXSize * m_gt[1];
1189
0
    double dfGDALMaxY = m_gt[3];
1190
1191
0
    m_nOverviewCount = m_nZoomLevel;
1192
0
    m_papoOverviewDS = (MBTilesDataset **)CPLCalloc(sizeof(MBTilesDataset *),
1193
0
                                                    m_nOverviewCount);
1194
1195
0
    if (m_bWriteMinMaxZoom)
1196
0
    {
1197
0
        char *pszSQL = sqlite3_mprintf(
1198
0
            "INSERT INTO metadata (name, value) VALUES ('minzoom', '%d')",
1199
0
            m_nZoomLevel);
1200
0
        sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr);
1201
0
        sqlite3_free(pszSQL);
1202
0
        pszSQL = sqlite3_mprintf(
1203
0
            "INSERT INTO metadata (name, value) VALUES ('maxzoom', '%d')",
1204
0
            m_nZoomLevel);
1205
0
        sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr);
1206
0
        sqlite3_free(pszSQL);
1207
0
    }
1208
1209
0
    for (int i = 0; i < m_nOverviewCount; i++)
1210
0
    {
1211
0
        MBTilesDataset *poOvrDS = new MBTilesDataset();
1212
0
        poOvrDS->ShareLockWithParentDataset(this);
1213
0
        int nBlockSize;
1214
0
        GetRasterBand(1)->GetBlockSize(&nBlockSize, &nBlockSize);
1215
0
        poOvrDS->InitRaster(this, i, nBands, nBlockSize, dfGDALMinX, dfGDALMinY,
1216
0
                            dfGDALMaxX, dfGDALMaxY);
1217
1218
0
        m_papoOverviewDS[m_nZoomLevel - 1 - i] = poOvrDS;
1219
0
    }
1220
1221
0
    return CE_None;
1222
0
}
1223
1224
/************************************************************************/
1225
/*                         InitRaster()                                 */
1226
/************************************************************************/
1227
1228
bool MBTilesDataset::InitRaster(MBTilesDataset *poParentDS, int nZoomLevel,
1229
                                int nBandCount, int nTileSize,
1230
                                double dfGDALMinX, double dfGDALMinY,
1231
                                double dfGDALMaxX, double dfGDALMaxY)
1232
656
{
1233
656
    m_nZoomLevel = nZoomLevel;
1234
656
    m_nTileMatrixWidth = 1 << nZoomLevel;
1235
656
    m_nTileMatrixHeight = 1 << nZoomLevel;
1236
1237
656
    const int nTileWidth = nTileSize;
1238
656
    const int nTileHeight = nTileSize;
1239
656
    const double dfPixelXSize = 2 * MAX_GM / nTileWidth / (1 << nZoomLevel);
1240
656
    const double dfPixelYSize = 2 * MAX_GM / nTileHeight / (1 << nZoomLevel);
1241
1242
656
    m_bGeoTransformValid = true;
1243
656
    m_gt[0] = dfGDALMinX;
1244
656
    m_gt[1] = dfPixelXSize;
1245
656
    m_gt[3] = dfGDALMaxY;
1246
656
    m_gt[5] = -dfPixelYSize;
1247
656
    double dfRasterXSize = 0.5 + (dfGDALMaxX - dfGDALMinX) / dfPixelXSize;
1248
656
    double dfRasterYSize = 0.5 + (dfGDALMaxY - dfGDALMinY) / dfPixelYSize;
1249
656
    if (dfRasterXSize > INT_MAX || dfRasterYSize > INT_MAX)
1250
0
        return false;
1251
656
    nRasterXSize = (int)dfRasterXSize;
1252
656
    nRasterYSize = (int)dfRasterYSize;
1253
1254
656
    m_pabyCachedTiles =
1255
656
        (GByte *)VSI_MALLOC3_VERBOSE(4 * 4, nTileWidth, nTileHeight);
1256
656
    if (m_pabyCachedTiles == nullptr)
1257
0
    {
1258
0
        return false;
1259
0
    }
1260
1261
656
    if (poParentDS)
1262
60
    {
1263
60
        eAccess = poParentDS->eAccess;
1264
60
    }
1265
1266
3.28k
    for (int i = 1; i <= nBandCount; i++)
1267
2.62k
        SetBand(i, new MBTilesBand(this, nTileSize));
1268
1269
656
    ComputeTileAndPixelShifts();
1270
1271
656
    GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
1272
656
    GDALDataset::SetMetadataItem("ZOOM_LEVEL", CPLSPrintf("%d", m_nZoomLevel));
1273
1274
656
    if (poParentDS)
1275
60
    {
1276
60
        m_poParentDS = poParentDS;
1277
60
        poMainDS = poParentDS;
1278
60
        hDS = poParentDS->hDS;
1279
60
        hDB = poParentDS->hDB;
1280
60
        m_eTF = poParentDS->m_eTF;
1281
60
        m_nQuality = poParentDS->m_nQuality;
1282
60
        m_nZLevel = poParentDS->m_nZLevel;
1283
60
        m_bDither = poParentDS->m_bDither;
1284
60
        m_osWHERE = poParentDS->m_osWHERE;
1285
60
        SetDescription(CPLSPrintf("%s - zoom_level=%d",
1286
60
                                  poParentDS->GetDescription(), m_nZoomLevel));
1287
60
    }
1288
1289
656
    return true;
1290
656
}
1291
1292
/************************************************************************/
1293
/*                         GetSpatialRef()                              */
1294
/************************************************************************/
1295
1296
const OGRSpatialReference *MBTilesDataset::GetSpatialRef() const
1297
21
{
1298
21
    return &m_oSRS;
1299
21
}
1300
1301
/************************************************************************/
1302
/*                           SetSpatialRef()                            */
1303
/************************************************************************/
1304
1305
CPLErr MBTilesDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
1306
0
{
1307
0
    if (eAccess != GA_Update)
1308
0
    {
1309
0
        CPLError(CE_Failure, CPLE_NotSupported,
1310
0
                 "SetSpatialRef() not supported on read-only dataset");
1311
0
        return CE_Failure;
1312
0
    }
1313
1314
0
    if (poSRS == nullptr || poSRS->GetAuthorityName(nullptr) == nullptr ||
1315
0
        !EQUAL(poSRS->GetAuthorityName(nullptr), "EPSG") ||
1316
0
        poSRS->GetAuthorityCode(nullptr) == nullptr ||
1317
0
        !EQUAL(poSRS->GetAuthorityCode(nullptr), "3857"))
1318
0
    {
1319
0
        CPLError(CE_Failure, CPLE_NotSupported,
1320
0
                 "Only EPSG:3857 supported on MBTiles dataset");
1321
0
        return CE_Failure;
1322
0
    }
1323
0
    return CE_None;
1324
0
}
1325
1326
/************************************************************************/
1327
/*                      GetMetadataDomainList()                         */
1328
/************************************************************************/
1329
1330
char **MBTilesDataset::GetMetadataDomainList()
1331
0
{
1332
0
    return BuildMetadataDomainList(GDALDataset::GetMetadataDomainList(), TRUE,
1333
0
                                   "", nullptr);
1334
0
}
1335
1336
/************************************************************************/
1337
/*                            GetMetadata()                             */
1338
/************************************************************************/
1339
1340
char **MBTilesDataset::GetMetadata(const char *pszDomain)
1341
659
{
1342
659
    if (hDS == nullptr || (pszDomain != nullptr && !EQUAL(pszDomain, "")))
1343
0
        return GDALPamDataset::GetMetadata(pszDomain);
1344
1345
659
    if (bFetchedMetadata)
1346
63
        return aosList.List();
1347
1348
596
    bFetchedMetadata = true;
1349
596
    aosList = CPLStringList(GDALPamDataset::GetMetadata(), FALSE);
1350
1351
596
    OGRLayerH hSQLLyr = GDALDatasetExecuteSQL(
1352
596
        hDS, "SELECT name, value FROM metadata WHERE name != 'json' LIMIT 1000",
1353
596
        nullptr, nullptr);
1354
596
    if (hSQLLyr == nullptr)
1355
1
        return nullptr;
1356
1357
595
    if (OGR_FD_GetFieldCount(OGR_L_GetLayerDefn(hSQLLyr)) != 2)
1358
0
    {
1359
0
        GDALDatasetReleaseResultSet(hDS, hSQLLyr);
1360
0
        return nullptr;
1361
0
    }
1362
1363
595
    OGRFeatureH hFeat;
1364
17.3k
    while ((hFeat = OGR_L_GetNextFeature(hSQLLyr)) != nullptr)
1365
16.7k
    {
1366
16.7k
        if (OGR_F_IsFieldSetAndNotNull(hFeat, 0) &&
1367
16.7k
            OGR_F_IsFieldSetAndNotNull(hFeat, 1))
1368
16.7k
        {
1369
16.7k
            CPLString osName = OGR_F_GetFieldAsString(hFeat, 0);
1370
16.7k
            CPLString osValue = OGR_F_GetFieldAsString(hFeat, 1);
1371
16.7k
            if (osName[0] != '\0' && !STARTS_WITH(osValue, "function(") &&
1372
16.7k
                strstr(osValue, "<img ") == nullptr &&
1373
16.7k
                strstr(osValue, "<p>") == nullptr &&
1374
16.7k
                strstr(osValue, "</p>") == nullptr &&
1375
16.7k
                strstr(osValue, "<div") == nullptr)
1376
16.3k
            {
1377
16.3k
                aosList.AddNameValue(osName, osValue);
1378
16.3k
            }
1379
16.7k
        }
1380
16.7k
        OGR_F_Destroy(hFeat);
1381
16.7k
    }
1382
595
    GDALDatasetReleaseResultSet(hDS, hSQLLyr);
1383
1384
595
    return aosList.List();
1385
595
}
1386
1387
/************************************************************************/
1388
/*                         GetMetadataItem()                            */
1389
/************************************************************************/
1390
1391
const char *MBTilesDataset::GetMetadataItem(const char *pszName,
1392
                                            const char *pszDomain)
1393
743
{
1394
743
    if (pszDomain == nullptr || EQUAL(pszDomain, ""))
1395
638
    {
1396
638
        const char *pszValue = CSLFetchNameValue(GetMetadata(), pszName);
1397
638
        if (pszValue)
1398
540
            return pszValue;
1399
638
    }
1400
203
    return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
1401
743
}
1402
1403
/************************************************************************/
1404
/*                              GetLayer()                              */
1405
/************************************************************************/
1406
1407
OGRLayer *MBTilesDataset::GetLayer(int iLayer)
1408
1409
2.44k
{
1410
2.44k
    if (iLayer < 0 || iLayer >= GetLayerCount())
1411
0
        return nullptr;
1412
2.44k
    return m_apoLayers[iLayer].get();
1413
2.44k
}
1414
1415
/************************************************************************/
1416
/*                        MBTilesVectorLayer()                          */
1417
/************************************************************************/
1418
1419
MBTilesVectorLayer::MBTilesVectorLayer(
1420
    MBTilesDataset *poDS, const char *pszLayerName,
1421
    const CPLJSONObject &oFields, const CPLJSONArray &oAttributesFromTileStats,
1422
    bool bJsonField, double dfMinX, double dfMinY, double dfMaxX, double dfMaxY,
1423
    OGRwkbGeometryType eGeomType, bool bZoomLevelFromSpatialFilter)
1424
6.51k
    : m_poDS(poDS), m_poFeatureDefn(new OGRFeatureDefn(pszLayerName)),
1425
6.51k
      m_bJsonField(bJsonField)
1426
6.51k
{
1427
6.51k
    SetDescription(pszLayerName);
1428
6.51k
    m_poFeatureDefn->SetGeomType(eGeomType);
1429
6.51k
    OGRSpatialReference *poSRS = new OGRSpatialReference();
1430
6.51k
    poSRS->SetFromUserInput(SRS_EPSG_3857);
1431
6.51k
    m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
1432
6.51k
    poSRS->Release();
1433
6.51k
    m_poFeatureDefn->Reference();
1434
1435
6.51k
    if (m_bJsonField)
1436
0
    {
1437
0
        OGRFieldDefn oFieldDefnId("mvt_id", OFTInteger64);
1438
0
        m_poFeatureDefn->AddFieldDefn(&oFieldDefnId);
1439
0
    }
1440
6.51k
    else
1441
6.51k
    {
1442
6.51k
        OGRMVTInitFields(m_poFeatureDefn, oFields, oAttributesFromTileStats);
1443
6.51k
    }
1444
1445
6.51k
    m_sExtent.MinX = dfMinX;
1446
6.51k
    m_sExtent.MinY = dfMinY;
1447
6.51k
    m_sExtent.MaxX = dfMaxX;
1448
6.51k
    m_sExtent.MaxY = dfMaxY;
1449
1450
6.51k
    m_nZoomLevel = m_poDS->m_nZoomLevel;
1451
6.51k
    m_bZoomLevelAuto = bZoomLevelFromSpatialFilter;
1452
6.51k
    MBTilesVectorLayer::SetSpatialFilter(nullptr);
1453
1454
    // If the metadata contains an empty fields object, this may be a sign
1455
    // that it doesn't know the schema. In that case check if a tile has
1456
    // attributes, and in that case create a json field.
1457
6.51k
    if (!m_bJsonField && oFields.IsValid() && oFields.GetChildren().empty())
1458
3.98k
    {
1459
3.98k
        m_bJsonField = true;
1460
3.98k
        OGRFeature *poSrcFeature = GetNextSrcFeature();
1461
3.98k
        m_bJsonField = false;
1462
1463
3.98k
        if (poSrcFeature)
1464
383
        {
1465
            // There is at least the mvt_id field
1466
383
            if (poSrcFeature->GetFieldCount() > 1)
1467
162
            {
1468
162
                m_bJsonField = true;
1469
162
            }
1470
383
            delete poSrcFeature;
1471
383
        }
1472
3.98k
        MBTilesVectorLayer::ResetReading();
1473
3.98k
    }
1474
1475
6.51k
    if (m_bJsonField)
1476
162
    {
1477
162
        OGRFieldDefn oFieldDefn("json", OFTString);
1478
162
        m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
1479
162
    }
1480
6.51k
}
1481
1482
/************************************************************************/
1483
/*                       ~MBTilesVectorLayer()                          */
1484
/************************************************************************/
1485
1486
MBTilesVectorLayer::~MBTilesVectorLayer()
1487
6.51k
{
1488
6.51k
    m_poFeatureDefn->Release();
1489
6.51k
    if (m_hTileIteratorLyr)
1490
4.99k
        GDALDatasetReleaseResultSet(m_poDS->hDS, m_hTileIteratorLyr);
1491
6.51k
    if (!m_osTmpFilename.empty())
1492
4.88k
    {
1493
4.88k
        VSIUnlink(m_osTmpFilename);
1494
4.88k
    }
1495
6.51k
    if (m_hTileDS)
1496
739
        GDALClose(m_hTileDS);
1497
6.51k
}
1498
1499
/************************************************************************/
1500
/*                           TestCapability()                           */
1501
/************************************************************************/
1502
1503
int MBTilesVectorLayer::TestCapability(const char *pszCap)
1504
0
{
1505
0
    if (EQUAL(pszCap, OLCStringsAsUTF8) ||
1506
0
        EQUAL(pszCap, OLCFastSpatialFilter) || EQUAL(pszCap, OLCFastGetExtent))
1507
0
    {
1508
0
        return TRUE;
1509
0
    }
1510
0
    return FALSE;
1511
0
}
1512
1513
/************************************************************************/
1514
/*                            IGetExtent()                              */
1515
/************************************************************************/
1516
1517
OGRErr MBTilesVectorLayer::IGetExtent(int /* iGeomField */,
1518
                                      OGREnvelope *psExtent, bool /* bForce */)
1519
0
{
1520
0
    *psExtent = m_sExtent;
1521
0
    return OGRERR_NONE;
1522
0
}
1523
1524
/************************************************************************/
1525
/*                          ResetReading()                              */
1526
/************************************************************************/
1527
1528
void MBTilesVectorLayer::ResetReading()
1529
9.00k
{
1530
9.00k
    if (m_hTileDS)
1531
383
        GDALClose(m_hTileDS);
1532
9.00k
    m_hTileDS = nullptr;
1533
9.00k
    m_bEOF = false;
1534
9.00k
    if (m_hTileIteratorLyr)
1535
3.97k
        GDALDatasetReleaseResultSet(m_poDS->hDS, m_hTileIteratorLyr);
1536
9.00k
    CPLString osSQL;
1537
9.00k
    osSQL.Printf("SELECT tile_column, tile_row, tile_data FROM tiles "
1538
9.00k
                 "WHERE zoom_level = %d "
1539
9.00k
                 "AND tile_column BETWEEN %d AND %d "
1540
9.00k
                 "AND tile_row BETWEEN %d AND %d",
1541
9.00k
                 m_nZoomLevel, m_nFilterMinX, m_nFilterMaxX, m_nFilterMinY,
1542
9.00k
                 m_nFilterMaxY);
1543
9.00k
    m_hTileIteratorLyr =
1544
9.00k
        GDALDatasetExecuteSQL(m_poDS->hDS, osSQL.c_str(), nullptr, nullptr);
1545
9.00k
}
1546
1547
/************************************************************************/
1548
/*                        ISetSpatialFilter()                           */
1549
/************************************************************************/
1550
1551
OGRErr MBTilesVectorLayer::ISetSpatialFilter(int iGeomField,
1552
                                             const OGRGeometry *poGeomIn)
1553
6.51k
{
1554
6.51k
    OGRErr eErr = OGRLayer::ISetSpatialFilter(iGeomField, poGeomIn);
1555
6.51k
    if (eErr == OGRERR_NONE)
1556
6.51k
    {
1557
6.51k
        if (m_poFilterGeom != nullptr && m_sFilterEnvelope.MinX <= -MAX_GM &&
1558
6.51k
            m_sFilterEnvelope.MinY <= -MAX_GM &&
1559
6.51k
            m_sFilterEnvelope.MaxX >= MAX_GM &&
1560
6.51k
            m_sFilterEnvelope.MaxY >= MAX_GM)
1561
0
        {
1562
0
            if (m_bZoomLevelAuto)
1563
0
            {
1564
0
                m_nZoomLevel = m_poDS->m_nMinZoomLevel;
1565
0
            }
1566
0
            m_nFilterMinX = 0;
1567
0
            m_nFilterMinY = 0;
1568
0
            m_nFilterMaxX = (1 << m_nZoomLevel) - 1;
1569
0
            m_nFilterMaxY = (1 << m_nZoomLevel) - 1;
1570
0
        }
1571
6.51k
        else if (m_poFilterGeom != nullptr &&
1572
6.51k
                 m_sFilterEnvelope.MinX >= -10 * MAX_GM &&
1573
6.51k
                 m_sFilterEnvelope.MinY >= -10 * MAX_GM &&
1574
6.51k
                 m_sFilterEnvelope.MaxX <= 10 * MAX_GM &&
1575
6.51k
                 m_sFilterEnvelope.MaxY <= 10 * MAX_GM)
1576
0
        {
1577
0
            if (m_bZoomLevelAuto)
1578
0
            {
1579
0
                double dfExtent =
1580
0
                    std::min(m_sFilterEnvelope.MaxX - m_sFilterEnvelope.MinX,
1581
0
                             m_sFilterEnvelope.MaxY - m_sFilterEnvelope.MinY);
1582
0
                m_nZoomLevel = std::max(
1583
0
                    m_poDS->m_nMinZoomLevel,
1584
0
                    std::min(static_cast<int>(0.5 + log(2 * MAX_GM / dfExtent) /
1585
0
                                                        log(2.0)),
1586
0
                             m_poDS->m_nZoomLevel));
1587
0
                CPLDebug("MBTILES", "Zoom level = %d", m_nZoomLevel);
1588
0
            }
1589
0
            const double dfTileDim = 2 * MAX_GM / (1 << m_nZoomLevel);
1590
0
            m_nFilterMinX = std::max(
1591
0
                0, static_cast<int>(
1592
0
                       floor((m_sFilterEnvelope.MinX + MAX_GM) / dfTileDim)));
1593
0
            m_nFilterMinY = std::max(
1594
0
                0, static_cast<int>(
1595
0
                       floor((m_sFilterEnvelope.MinY + MAX_GM) / dfTileDim)));
1596
0
            m_nFilterMaxX =
1597
0
                std::min(static_cast<int>(ceil(
1598
0
                             (m_sFilterEnvelope.MaxX + MAX_GM) / dfTileDim)),
1599
0
                         (1 << m_nZoomLevel) - 1);
1600
0
            m_nFilterMaxY =
1601
0
                std::min(static_cast<int>(ceil(
1602
0
                             (m_sFilterEnvelope.MaxY + MAX_GM) / dfTileDim)),
1603
0
                         (1 << m_nZoomLevel) - 1);
1604
0
        }
1605
6.51k
        else
1606
6.51k
        {
1607
6.51k
            if (m_bZoomLevelAuto)
1608
0
            {
1609
0
                m_nZoomLevel = m_poDS->m_nZoomLevel;
1610
0
            }
1611
6.51k
            m_nFilterMinX = 0;
1612
6.51k
            m_nFilterMinY = 0;
1613
6.51k
            m_nFilterMaxX = (1 << m_nZoomLevel) - 1;
1614
6.51k
            m_nFilterMaxY = (1 << m_nZoomLevel) - 1;
1615
6.51k
        }
1616
6.51k
    }
1617
6.51k
    return eErr;
1618
6.51k
}
1619
1620
/************************************************************************/
1621
/*                          GetNextFeature()                            */
1622
/************************************************************************/
1623
1624
OGRFeature *MBTilesVectorLayer::GetNextFeature()
1625
38.6k
{
1626
38.6k
    while (true)
1627
38.6k
    {
1628
38.6k
        OGRFeature *poFeature = GetNextRawFeature();
1629
38.6k
        if (poFeature == nullptr)
1630
2.44k
            return nullptr;
1631
1632
36.2k
        if ((m_poFilterGeom == nullptr ||
1633
36.2k
             FilterGeometry(poFeature->GetGeometryRef())) &&
1634
36.2k
            (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
1635
36.2k
        {
1636
36.2k
            return poFeature;
1637
36.2k
        }
1638
1639
0
        delete poFeature;
1640
0
    }
1641
38.6k
}
1642
1643
/************************************************************************/
1644
/*                         GetFeatureCount()                            */
1645
/************************************************************************/
1646
1647
GIntBig MBTilesVectorLayer::GetFeatureCount(int bForce)
1648
0
{
1649
0
    if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
1650
0
    {
1651
0
        if (m_nFeatureCount < 0)
1652
0
        {
1653
0
            m_nFeatureCount = 0;
1654
0
            ResetReading();
1655
0
            while (m_hTileIteratorLyr != nullptr)
1656
0
            {
1657
0
                OGRFeatureH hFeat = OGR_L_GetNextFeature(m_hTileIteratorLyr);
1658
0
                if (hFeat == nullptr)
1659
0
                {
1660
0
                    break;
1661
0
                }
1662
0
                m_nX = OGR_F_GetFieldAsInteger(hFeat, 0);
1663
                // MBTiles y origin is bottom based, whereas MVT directory
1664
                // is top based
1665
0
                m_nY =
1666
0
                    (1 << m_nZoomLevel) - 1 - OGR_F_GetFieldAsInteger(hFeat, 1);
1667
0
                int nDataSize = 0;
1668
0
                GByte *pabyData = OGR_F_GetFieldAsBinary(hFeat, 2, &nDataSize);
1669
0
                GByte *pabyDataDup = static_cast<GByte *>(CPLMalloc(nDataSize));
1670
0
                memcpy(pabyDataDup, pabyData, nDataSize);
1671
0
                OGR_F_Destroy(hFeat);
1672
1673
0
                if (!m_osTmpFilename.empty())
1674
0
                {
1675
0
                    VSIUnlink(m_osTmpFilename);
1676
0
                }
1677
0
                m_osTmpFilename = VSIMemGenerateHiddenFilename(
1678
0
                    CPLSPrintf("mvt_%d_%d.pbf", m_nX, m_nY));
1679
0
                VSIFCloseL(VSIFileFromMemBuffer(m_osTmpFilename, pabyDataDup,
1680
0
                                                nDataSize, true));
1681
1682
0
                const char *l_apszAllowedDrivers[] = {"MVT", nullptr};
1683
0
                if (m_hTileDS)
1684
0
                    GDALClose(m_hTileDS);
1685
0
                char **papszOpenOptions = nullptr;
1686
0
                papszOpenOptions =
1687
0
                    CSLSetNameValue(papszOpenOptions, "METADATA_FILE",
1688
0
                                    m_poDS->m_osMetadataMemFilename.c_str());
1689
0
                m_hTileDS =
1690
0
                    GDALOpenEx(("MVT:" + m_osTmpFilename).c_str(),
1691
0
                               GDAL_OF_VECTOR | GDAL_OF_INTERNAL,
1692
0
                               l_apszAllowedDrivers, papszOpenOptions, nullptr);
1693
0
                CSLDestroy(papszOpenOptions);
1694
0
                if (m_hTileDS)
1695
0
                {
1696
0
                    OGRLayerH hLayer =
1697
0
                        GDALDatasetGetLayerByName(m_hTileDS, GetName());
1698
0
                    if (hLayer)
1699
0
                    {
1700
0
                        m_nFeatureCount += OGR_L_GetFeatureCount(hLayer, true);
1701
0
                    }
1702
0
                    GDALClose(m_hTileDS);
1703
0
                    m_hTileDS = nullptr;
1704
0
                }
1705
0
            }
1706
0
            ResetReading();
1707
0
        }
1708
0
        return m_nFeatureCount;
1709
0
    }
1710
0
    return OGRLayer::GetFeatureCount(bForce);
1711
0
}
1712
1713
/************************************************************************/
1714
/*                        GetNextSrcFeature()                           */
1715
/************************************************************************/
1716
1717
OGRFeature *MBTilesVectorLayer::GetNextSrcFeature()
1718
42.6k
{
1719
42.6k
    if (m_bEOF)
1720
0
    {
1721
0
        return nullptr;
1722
0
    }
1723
42.6k
    if (m_hTileIteratorLyr == nullptr)
1724
5.02k
    {
1725
5.02k
        ResetReading();
1726
5.02k
        if (m_hTileIteratorLyr == nullptr)
1727
32
        {
1728
32
            return nullptr;
1729
32
        }
1730
5.02k
    }
1731
1732
42.6k
    OGRFeatureH hTileFeat = nullptr;
1733
42.6k
    if (m_hTileDS == nullptr ||
1734
42.6k
        (hTileFeat = OGR_L_GetNextFeature(
1735
36.2k
             GDALDatasetGetLayerByName(m_hTileDS, GetName()))) == nullptr)
1736
37.0k
    {
1737
118k
        while (true)
1738
118k
        {
1739
118k
            OGRFeatureH hFeat = OGR_L_GetNextFeature(m_hTileIteratorLyr);
1740
118k
            if (hFeat == nullptr)
1741
6.01k
            {
1742
6.01k
                m_bEOF = true;
1743
6.01k
                return nullptr;
1744
6.01k
            }
1745
112k
            m_nX = OGR_F_GetFieldAsInteger(hFeat, 0);
1746
            // MBTiles y origin is bottom based, whereas MVT directory
1747
            // is top based
1748
112k
            m_nY = (1 << m_nZoomLevel) - 1 - OGR_F_GetFieldAsInteger(hFeat, 1);
1749
112k
            CPLDebug("MBTiles", "X=%d, Y=%d", m_nX, m_nY);
1750
1751
112k
            int nDataSize = 0;
1752
112k
            GByte *pabyData = OGR_F_GetFieldAsBinary(hFeat, 2, &nDataSize);
1753
112k
            GByte *pabyDataDup = static_cast<GByte *>(CPLMalloc(nDataSize));
1754
112k
            memcpy(pabyDataDup, pabyData, nDataSize);
1755
112k
            OGR_F_Destroy(hFeat);
1756
1757
112k
            if (!m_osTmpFilename.empty())
1758
107k
            {
1759
107k
                VSIUnlink(m_osTmpFilename);
1760
107k
            }
1761
112k
            m_osTmpFilename = VSIMemGenerateHiddenFilename(
1762
112k
                CPLSPrintf("mvt_%d_%d.pbf", m_nX, m_nY));
1763
112k
            VSIFCloseL(VSIFileFromMemBuffer(m_osTmpFilename, pabyDataDup,
1764
112k
                                            nDataSize, true));
1765
1766
112k
            const char *l_apszAllowedDrivers[] = {"MVT", nullptr};
1767
112k
            if (m_hTileDS)
1768
29.9k
                GDALClose(m_hTileDS);
1769
112k
            char **papszOpenOptions = nullptr;
1770
112k
            papszOpenOptions =
1771
112k
                CSLSetNameValue(papszOpenOptions, "X", CPLSPrintf("%d", m_nX));
1772
112k
            papszOpenOptions =
1773
112k
                CSLSetNameValue(papszOpenOptions, "Y", CPLSPrintf("%d", m_nY));
1774
112k
            papszOpenOptions = CSLSetNameValue(papszOpenOptions, "Z",
1775
112k
                                               CPLSPrintf("%d", m_nZoomLevel));
1776
112k
            papszOpenOptions = CSLSetNameValue(
1777
112k
                papszOpenOptions, "METADATA_FILE",
1778
112k
                m_bJsonField ? "" : m_poDS->m_osMetadataMemFilename.c_str());
1779
112k
            if (!m_poDS->m_osClip.empty())
1780
0
            {
1781
0
                papszOpenOptions =
1782
0
                    CSLSetNameValue(papszOpenOptions, "CLIP", m_poDS->m_osClip);
1783
0
            }
1784
112k
            m_hTileDS =
1785
112k
                GDALOpenEx(("MVT:" + m_osTmpFilename).c_str(),
1786
112k
                           GDAL_OF_VECTOR | GDAL_OF_INTERNAL,
1787
112k
                           l_apszAllowedDrivers, papszOpenOptions, nullptr);
1788
112k
            CSLDestroy(papszOpenOptions);
1789
112k
            if (m_hTileDS)
1790
79.2k
            {
1791
79.2k
                if (GDALDatasetGetLayerByName(m_hTileDS, GetName()))
1792
40.4k
                {
1793
40.4k
                    hTileFeat = OGR_L_GetNextFeature(
1794
40.4k
                        GDALDatasetGetLayerByName(m_hTileDS, GetName()));
1795
40.4k
                    if (hTileFeat)
1796
31.0k
                        break;
1797
40.4k
                }
1798
48.2k
                GDALClose(m_hTileDS);
1799
48.2k
                m_hTileDS = nullptr;
1800
48.2k
            }
1801
112k
        }
1802
37.0k
    }
1803
1804
36.6k
    return reinterpret_cast<OGRFeature *>(hTileFeat);
1805
42.6k
}
1806
1807
/************************************************************************/
1808
/*                         CreateFeatureFrom()                          */
1809
/************************************************************************/
1810
1811
OGRFeature *MBTilesVectorLayer::CreateFeatureFrom(OGRFeature *poSrcFeature)
1812
36.2k
{
1813
1814
36.2k
    return OGRMVTCreateFeatureFrom(poSrcFeature, m_poFeatureDefn, m_bJsonField,
1815
36.2k
                                   GetSpatialRef());
1816
36.2k
}
1817
1818
/************************************************************************/
1819
/*                        GetNextRawFeature()                           */
1820
/************************************************************************/
1821
1822
OGRFeature *MBTilesVectorLayer::GetNextRawFeature()
1823
38.6k
{
1824
38.6k
    OGRFeature *poSrcFeat = GetNextSrcFeature();
1825
38.6k
    if (poSrcFeat == nullptr)
1826
2.44k
        return nullptr;
1827
1828
36.2k
    const GIntBig nFIDBase =
1829
36.2k
        (static_cast<GIntBig>(m_nY) << m_nZoomLevel) | m_nX;
1830
36.2k
    OGRFeature *poFeature = CreateFeatureFrom(poSrcFeat);
1831
36.2k
    poFeature->SetFID((poSrcFeat->GetFID() << (2 * m_nZoomLevel)) | nFIDBase);
1832
36.2k
    delete poSrcFeat;
1833
1834
36.2k
    return poFeature;
1835
38.6k
}
1836
1837
/************************************************************************/
1838
/*                           GetFeature()                               */
1839
/************************************************************************/
1840
1841
OGRFeature *MBTilesVectorLayer::GetFeature(GIntBig nFID)
1842
0
{
1843
0
    const int nZ = m_nZoomLevel;
1844
0
    const int nX = static_cast<int>(nFID & ((1 << nZ) - 1));
1845
0
    const int nY = static_cast<int>((nFID >> nZ) & ((1 << nZ) - 1));
1846
0
    const GIntBig nTileFID = nFID >> (2 * nZ);
1847
1848
0
    CPLString osSQL;
1849
0
    osSQL.Printf("SELECT tile_data FROM tiles "
1850
0
                 "WHERE zoom_level = %d AND "
1851
0
                 "tile_column = %d AND tile_row = %d",
1852
0
                 m_nZoomLevel, nX, (1 << nZ) - 1 - nY);
1853
0
    auto hSQLLyr =
1854
0
        GDALDatasetExecuteSQL(m_poDS->hDS, osSQL.c_str(), nullptr, nullptr);
1855
0
    if (hSQLLyr == nullptr)
1856
0
        return nullptr;
1857
0
    auto hFeat = OGR_L_GetNextFeature(hSQLLyr);
1858
0
    if (hFeat == nullptr)
1859
0
    {
1860
0
        GDALDatasetReleaseResultSet(m_poDS->hDS, hSQLLyr);
1861
0
        return nullptr;
1862
0
    }
1863
0
    int nDataSize = 0;
1864
0
    GByte *pabyData = OGR_F_GetFieldAsBinary(hFeat, 0, &nDataSize);
1865
0
    GByte *pabyDataDup = static_cast<GByte *>(CPLMalloc(nDataSize));
1866
0
    memcpy(pabyDataDup, pabyData, nDataSize);
1867
0
    OGR_F_Destroy(hFeat);
1868
0
    GDALDatasetReleaseResultSet(m_poDS->hDS, hSQLLyr);
1869
1870
0
    const CPLString osTmpFilename = VSIMemGenerateHiddenFilename(
1871
0
        CPLSPrintf("mvt_get_feature_%d_%d.pbf", m_nX, m_nY));
1872
0
    VSIFCloseL(
1873
0
        VSIFileFromMemBuffer(osTmpFilename, pabyDataDup, nDataSize, true));
1874
1875
0
    const char *l_apszAllowedDrivers[] = {"MVT", nullptr};
1876
0
    char **papszOpenOptions = nullptr;
1877
0
    papszOpenOptions =
1878
0
        CSLSetNameValue(papszOpenOptions, "X", CPLSPrintf("%d", nX));
1879
0
    papszOpenOptions =
1880
0
        CSLSetNameValue(papszOpenOptions, "Y", CPLSPrintf("%d", nY));
1881
0
    papszOpenOptions =
1882
0
        CSLSetNameValue(papszOpenOptions, "Z", CPLSPrintf("%d", m_nZoomLevel));
1883
0
    papszOpenOptions = CSLSetNameValue(
1884
0
        papszOpenOptions, "METADATA_FILE",
1885
0
        m_bJsonField ? "" : m_poDS->m_osMetadataMemFilename.c_str());
1886
0
    if (!m_poDS->m_osClip.empty())
1887
0
    {
1888
0
        papszOpenOptions =
1889
0
            CSLSetNameValue(papszOpenOptions, "CLIP", m_poDS->m_osClip);
1890
0
    }
1891
0
    auto hTileDS = GDALOpenEx(("MVT:" + osTmpFilename).c_str(),
1892
0
                              GDAL_OF_VECTOR | GDAL_OF_INTERNAL,
1893
0
                              l_apszAllowedDrivers, papszOpenOptions, nullptr);
1894
0
    CSLDestroy(papszOpenOptions);
1895
1896
0
    OGRFeature *poFeature = nullptr;
1897
0
    if (hTileDS)
1898
0
    {
1899
0
        OGRLayerH hLayer = GDALDatasetGetLayerByName(hTileDS, GetName());
1900
0
        if (hLayer)
1901
0
        {
1902
0
            OGRFeature *poUnderlyingFeature = reinterpret_cast<OGRFeature *>(
1903
0
                OGR_L_GetFeature(hLayer, nTileFID));
1904
0
            if (poUnderlyingFeature)
1905
0
            {
1906
0
                poFeature = CreateFeatureFrom(poUnderlyingFeature);
1907
0
                poFeature->SetFID(nFID);
1908
0
            }
1909
0
            delete poUnderlyingFeature;
1910
0
        }
1911
0
    }
1912
0
    GDALClose(hTileDS);
1913
1914
0
    VSIUnlink(osTmpFilename);
1915
1916
0
    return poFeature;
1917
0
}
1918
1919
/************************************************************************/
1920
/*                           InitVector()                               */
1921
/************************************************************************/
1922
1923
void MBTilesDataset::InitVector(double dfMinX, double dfMinY, double dfMaxX,
1924
                                double dfMaxY, bool bZoomLevelFromSpatialFilter,
1925
                                bool bJsonField)
1926
530
{
1927
530
    const char *pszSQL = "SELECT value FROM metadata WHERE name = 'json'";
1928
530
    CPLDebug("MBTILES", "%s", pszSQL);
1929
530
    CPLJSONDocument oJsonDoc;
1930
530
    CPLJSONDocument oDoc;
1931
530
    auto hSQLLyr = GDALDatasetExecuteSQL(hDS, pszSQL, nullptr, nullptr);
1932
530
    if (hSQLLyr)
1933
530
    {
1934
530
        auto hFeat = OGR_L_GetNextFeature(hSQLLyr);
1935
530
        if (hFeat)
1936
521
        {
1937
521
            auto pszJson = OGR_F_GetFieldAsString(hFeat, 0);
1938
521
            oDoc.GetRoot().Add("json", pszJson);
1939
521
            CPL_IGNORE_RET_VAL(
1940
521
                oJsonDoc.LoadMemory(reinterpret_cast<const GByte *>(pszJson)));
1941
521
            OGR_F_Destroy(hFeat);
1942
521
        }
1943
530
        GDALDatasetReleaseResultSet(hDS, hSQLLyr);
1944
530
    }
1945
1946
530
    m_osMetadataMemFilename =
1947
530
        VSIMemGenerateHiddenFilename("mbtiles_metadata.json");
1948
530
    oDoc.Save(m_osMetadataMemFilename);
1949
1950
530
    CPLJSONArray oVectorLayers;
1951
530
    oVectorLayers.Deinit();
1952
1953
530
    CPLJSONArray oTileStatLayers;
1954
530
    oTileStatLayers.Deinit();
1955
1956
530
    oVectorLayers = oJsonDoc.GetRoot().GetArray("vector_layers");
1957
1958
530
    oTileStatLayers = oJsonDoc.GetRoot().GetArray("tilestats/layers");
1959
1960
9.32k
    for (int i = 0; i < oVectorLayers.Size(); i++)
1961
8.79k
    {
1962
8.79k
        CPLJSONObject oId = oVectorLayers[i].GetObj("id");
1963
8.79k
        if (oId.IsValid() && oId.GetType() == CPLJSONObject::Type::String)
1964
6.51k
        {
1965
6.51k
            OGRwkbGeometryType eGeomType = wkbUnknown;
1966
6.51k
            if (oTileStatLayers.IsValid())
1967
273
            {
1968
273
                eGeomType = OGRMVTFindGeomTypeFromTileStat(
1969
273
                    oTileStatLayers, oId.ToString().c_str());
1970
273
            }
1971
1972
6.51k
            CPLJSONObject oFields = oVectorLayers[i].GetObj("fields");
1973
6.51k
            CPLJSONArray oAttributesFromTileStats =
1974
6.51k
                OGRMVTFindAttributesFromTileStat(oTileStatLayers,
1975
6.51k
                                                 oId.ToString().c_str());
1976
6.51k
            m_apoLayers.push_back(
1977
6.51k
                std::unique_ptr<OGRLayer>(new MBTilesVectorLayer(
1978
6.51k
                    this, oId.ToString().c_str(), oFields,
1979
6.51k
                    oAttributesFromTileStats, bJsonField, dfMinX, dfMinY,
1980
6.51k
                    dfMaxX, dfMaxY, eGeomType, bZoomLevelFromSpatialFilter)));
1981
6.51k
        }
1982
8.79k
    }
1983
530
}
1984
1985
/************************************************************************/
1986
/*                             Identify()                               */
1987
/************************************************************************/
1988
1989
int MBTilesDataset::Identify(GDALOpenInfo *poOpenInfo)
1990
668k
{
1991
668k
#ifdef ENABLE_SQL_SQLITE_FORMAT
1992
668k
    if (poOpenInfo->pabyHeader &&
1993
668k
        STARTS_WITH((const char *)poOpenInfo->pabyHeader, "-- SQL MBTILES"))
1994
1.74k
    {
1995
1.74k
        return TRUE;
1996
1.74k
    }
1997
666k
#endif
1998
1999
666k
    if ((poOpenInfo->IsExtensionEqualToCI("MBTILES") ||
2000
         // Allow direct Amazon S3 signed URLs that contains .mbtiles in the
2001
         // middle of the URL
2002
666k
         strstr(poOpenInfo->pszFilename, ".mbtiles") != nullptr) &&
2003
666k
        poOpenInfo->nHeaderBytes >= 1024 && poOpenInfo->pabyHeader &&
2004
666k
        STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader, "SQLite Format 3"))
2005
0
    {
2006
0
        return TRUE;
2007
0
    }
2008
2009
666k
    return FALSE;
2010
666k
}
2011
2012
/************************************************************************/
2013
/*                        MBTilesGetMinMaxZoomLevel()                   */
2014
/************************************************************************/
2015
2016
static int MBTilesGetMinMaxZoomLevel(GDALDatasetH hDS, int bHasMap,
2017
                                     int &nMinLevel, int &nMaxLevel)
2018
619
{
2019
619
    OGRLayerH hSQLLyr;
2020
619
    OGRFeatureH hFeat;
2021
619
    int bHasMinMaxLevel = FALSE;
2022
2023
619
    const char *pszSQL =
2024
619
        "SELECT value FROM metadata WHERE name = 'minzoom' UNION ALL "
2025
619
        "SELECT value FROM metadata WHERE name = 'maxzoom'";
2026
619
    CPLDebug("MBTILES", "%s", pszSQL);
2027
619
    hSQLLyr = GDALDatasetExecuteSQL(hDS, pszSQL, nullptr, nullptr);
2028
619
    if (hSQLLyr)
2029
617
    {
2030
617
        hFeat = OGR_L_GetNextFeature(hSQLLyr);
2031
617
        if (hFeat)
2032
51
        {
2033
51
            int bHasMinLevel = FALSE;
2034
51
            if (OGR_F_IsFieldSetAndNotNull(hFeat, 0))
2035
51
            {
2036
51
                nMinLevel = OGR_F_GetFieldAsInteger(hFeat, 0);
2037
51
                bHasMinLevel = TRUE;
2038
51
            }
2039
51
            OGR_F_Destroy(hFeat);
2040
2041
51
            if (bHasMinLevel)
2042
51
            {
2043
51
                hFeat = OGR_L_GetNextFeature(hSQLLyr);
2044
51
                if (hFeat)
2045
37
                {
2046
37
                    if (OGR_F_IsFieldSetAndNotNull(hFeat, 0))
2047
37
                    {
2048
37
                        nMaxLevel = OGR_F_GetFieldAsInteger(hFeat, 0);
2049
37
                        bHasMinMaxLevel = TRUE;
2050
37
                    }
2051
37
                    OGR_F_Destroy(hFeat);
2052
37
                }
2053
51
            }
2054
51
        }
2055
2056
617
        GDALDatasetReleaseResultSet(hDS, hSQLLyr);
2057
617
    }
2058
2059
619
    if (!bHasMinMaxLevel)
2060
582
    {
2061
582
#define OPTIMIZED_FOR_VSICURL
2062
582
#ifdef OPTIMIZED_FOR_VSICURL
2063
582
        int iLevel;
2064
2.07k
        for (iLevel = 0; nMinLevel < 0 && iLevel <= 32; iLevel++)
2065
1.49k
        {
2066
1.49k
            pszSQL = CPLSPrintf(
2067
1.49k
                "SELECT zoom_level FROM %s WHERE zoom_level = %d LIMIT 1",
2068
1.49k
                (bHasMap) ? "map" : "tiles", iLevel);
2069
1.49k
            CPLDebug("MBTILES", "%s", pszSQL);
2070
1.49k
            hSQLLyr = GDALDatasetExecuteSQL(hDS, pszSQL, nullptr, nullptr);
2071
1.49k
            if (hSQLLyr)
2072
1.46k
            {
2073
1.46k
                hFeat = OGR_L_GetNextFeature(hSQLLyr);
2074
1.46k
                if (hFeat)
2075
563
                {
2076
563
                    nMinLevel = iLevel;
2077
563
                    OGR_F_Destroy(hFeat);
2078
563
                }
2079
1.46k
                GDALDatasetReleaseResultSet(hDS, hSQLLyr);
2080
1.46k
            }
2081
1.49k
        }
2082
2083
582
        if (nMinLevel < 0)
2084
5
            return FALSE;
2085
2086
18.5k
        for (iLevel = 32; nMaxLevel < 0 && iLevel >= nMinLevel; iLevel--)
2087
17.9k
        {
2088
17.9k
            pszSQL = CPLSPrintf(
2089
17.9k
                "SELECT zoom_level FROM %s WHERE zoom_level = %d LIMIT 1",
2090
17.9k
                (bHasMap) ? "map" : "tiles", iLevel);
2091
17.9k
            CPLDebug("MBTILES", "%s", pszSQL);
2092
17.9k
            hSQLLyr = GDALDatasetExecuteSQL(hDS, pszSQL, nullptr, nullptr);
2093
17.9k
            if (hSQLLyr)
2094
17.8k
            {
2095
17.8k
                hFeat = OGR_L_GetNextFeature(hSQLLyr);
2096
17.8k
                if (hFeat)
2097
572
                {
2098
572
                    nMaxLevel = iLevel;
2099
572
                    bHasMinMaxLevel = TRUE;
2100
572
                    OGR_F_Destroy(hFeat);
2101
572
                }
2102
17.8k
                GDALDatasetReleaseResultSet(hDS, hSQLLyr);
2103
17.8k
            }
2104
17.9k
        }
2105
#else
2106
        pszSQL = "SELECT min(zoom_level), max(zoom_level) FROM tiles";
2107
        CPLDebug("MBTILES", "%s", pszSQL);
2108
        hSQLLyr = GDALDatasetExecuteSQL(hDS, pszSQL, NULL, nullptr);
2109
        if (hSQLLyr == NULL)
2110
        {
2111
            return FALSE;
2112
        }
2113
2114
        hFeat = OGR_L_GetNextFeature(hSQLLyr);
2115
        if (hFeat == NULL)
2116
        {
2117
            GDALDatasetReleaseResultSet(hDS, hSQLLyr);
2118
            return FALSE;
2119
        }
2120
2121
        if (OGR_F_IsFieldSetAndNotNull(hFeat, 0) &&
2122
            OGR_F_IsFieldSetAndNotNull(hFeat, 1))
2123
        {
2124
            nMinLevel = OGR_F_GetFieldAsInteger(hFeat, 0);
2125
            nMaxLevel = OGR_F_GetFieldAsInteger(hFeat, 1);
2126
            bHasMinMaxLevel = TRUE;
2127
        }
2128
2129
        OGR_F_Destroy(hFeat);
2130
        GDALDatasetReleaseResultSet(hDS, hSQLLyr);
2131
#endif
2132
577
    }
2133
2134
614
    return bHasMinMaxLevel;
2135
619
}
2136
2137
/************************************************************************/
2138
/*                   MBTilesTileCoordToWorldCoord()                     */
2139
/************************************************************************/
2140
2141
static double MBTilesTileCoordToWorldCoord(double dfTileCoord, int nZoomLevel)
2142
2.30k
{
2143
2.30k
    return -MAX_GM + 2 * MAX_GM * (dfTileCoord / (1 << nZoomLevel));
2144
2.30k
}
2145
2146
/************************************************************************/
2147
/*                   MBTilesWorldCoordToTileCoord()                     */
2148
/************************************************************************/
2149
2150
static double MBTilesWorldCoordToTileCoord(double dfWorldCoord, int nZoomLevel)
2151
2.38k
{
2152
2.38k
    return (dfWorldCoord + MAX_GM) / (2 * MAX_GM) * (1 << nZoomLevel);
2153
2.38k
}
2154
2155
/************************************************************************/
2156
/*                           MBTilesGetBounds()                         */
2157
/************************************************************************/
2158
2159
static bool MBTilesGetBounds(GDALDatasetH hDS, bool bUseBounds, int nMaxLevel,
2160
                             double &minX, double &minY, double &maxX,
2161
                             double &maxY)
2162
602
{
2163
602
    bool bHasBounds = false;
2164
602
    OGRLayerH hSQLLyr;
2165
602
    OGRFeatureH hFeat;
2166
2167
602
    if (bUseBounds)
2168
602
    {
2169
602
        const char *pszSQL = "SELECT value FROM metadata WHERE name = 'bounds'";
2170
602
        CPLDebug("MBTILES", "%s", pszSQL);
2171
602
        hSQLLyr = GDALDatasetExecuteSQL(hDS, pszSQL, nullptr, nullptr);
2172
602
        if (hSQLLyr)
2173
601
        {
2174
601
            hFeat = OGR_L_GetNextFeature(hSQLLyr);
2175
601
            if (hFeat != nullptr)
2176
178
            {
2177
178
                const char *pszBounds = OGR_F_GetFieldAsString(hFeat, 0);
2178
178
                char **papszTokens = CSLTokenizeString2(pszBounds, ",", 0);
2179
178
                if (CSLCount(papszTokens) != 4 ||
2180
178
                    fabs(CPLAtof(papszTokens[0])) > 180 ||
2181
178
                    fabs(CPLAtof(papszTokens[1])) >= 89.99 ||
2182
178
                    fabs(CPLAtof(papszTokens[2])) > 180 ||
2183
178
                    fabs(CPLAtof(papszTokens[3])) >= 89.99 ||
2184
178
                    CPLAtof(papszTokens[0]) > CPLAtof(papszTokens[2]) ||
2185
178
                    CPLAtof(papszTokens[1]) > CPLAtof(papszTokens[3]))
2186
157
                {
2187
157
                    CPLError(CE_Warning, CPLE_AppDefined,
2188
157
                             "Invalid value for 'bounds' metadata. Ignoring it "
2189
157
                             "and fall back to present tile extent");
2190
157
                }
2191
21
                else
2192
21
                {
2193
21
                    minX = CPLAtof(papszTokens[0]);
2194
21
                    minY = CPLAtof(papszTokens[1]);
2195
21
                    maxX = CPLAtof(papszTokens[2]);
2196
21
                    maxY = CPLAtof(papszTokens[3]);
2197
21
                    LongLatToSphericalMercator(&minX, &minY);
2198
21
                    LongLatToSphericalMercator(&maxX, &maxY);
2199
2200
                    // Clamp northings
2201
21
                    if (maxY > MAX_GM)
2202
0
                        maxY = MAX_GM;
2203
21
                    if (minY < -MAX_GM)
2204
0
                        minY = -MAX_GM;
2205
2206
21
                    bHasBounds = true;
2207
21
                }
2208
2209
178
                CSLDestroy(papszTokens);
2210
2211
178
                OGR_F_Destroy(hFeat);
2212
178
            }
2213
601
            GDALDatasetReleaseResultSet(hDS, hSQLLyr);
2214
601
        }
2215
602
    }
2216
2217
602
    if (!bHasBounds)
2218
581
    {
2219
581
        const char *pszSQL =
2220
581
            CPLSPrintf("SELECT min(tile_column), max(tile_column), "
2221
581
                       "min(tile_row), max(tile_row) FROM tiles "
2222
581
                       "WHERE zoom_level = %d",
2223
581
                       nMaxLevel);
2224
581
        CPLDebug("MBTILES", "%s", pszSQL);
2225
581
        hSQLLyr = GDALDatasetExecuteSQL(hDS, pszSQL, nullptr, nullptr);
2226
581
        if (hSQLLyr == nullptr)
2227
2
        {
2228
2
            return false;
2229
2
        }
2230
2231
579
        hFeat = OGR_L_GetNextFeature(hSQLLyr);
2232
579
        if (hFeat == nullptr)
2233
0
        {
2234
0
            GDALDatasetReleaseResultSet(hDS, hSQLLyr);
2235
0
            return false;
2236
0
        }
2237
2238
579
        if (OGR_F_IsFieldSetAndNotNull(hFeat, 0) &&
2239
579
            OGR_F_IsFieldSetAndNotNull(hFeat, 1) &&
2240
579
            OGR_F_IsFieldSetAndNotNull(hFeat, 2) &&
2241
579
            OGR_F_IsFieldSetAndNotNull(hFeat, 3))
2242
575
        {
2243
575
            int nMinTileCol = OGR_F_GetFieldAsInteger(hFeat, 0);
2244
575
            int nMaxTileCol = OGR_F_GetFieldAsInteger(hFeat, 1);
2245
575
            int nMinTileRow = OGR_F_GetFieldAsInteger(hFeat, 2);
2246
575
            int nMaxTileRow = OGR_F_GetFieldAsInteger(hFeat, 3);
2247
575
            if (nMaxTileCol < INT_MAX && nMaxTileRow < INT_MAX)
2248
575
            {
2249
575
                minX = MBTilesTileCoordToWorldCoord(nMinTileCol, nMaxLevel);
2250
575
                minY = MBTilesTileCoordToWorldCoord(nMinTileRow, nMaxLevel);
2251
575
                maxX = MBTilesTileCoordToWorldCoord(nMaxTileCol + 1, nMaxLevel);
2252
575
                maxY = MBTilesTileCoordToWorldCoord(nMaxTileRow + 1, nMaxLevel);
2253
575
                bHasBounds = true;
2254
575
            }
2255
575
        }
2256
2257
579
        OGR_F_Destroy(hFeat);
2258
579
        GDALDatasetReleaseResultSet(hDS, hSQLLyr);
2259
579
    }
2260
2261
600
    return bHasBounds;
2262
602
}
2263
2264
/************************************************************************/
2265
/*                        MBTilesCurlReadCbk()                          */
2266
/************************************************************************/
2267
2268
typedef struct
2269
{
2270
    int nBands;
2271
    int nSize;
2272
} TileProperties;
2273
2274
/* We spy the data received by CURL for the initial request where we try */
2275
/* to get a first tile to see its characteristics. We just need the header */
2276
/* to determine that, so let's make VSICurl stop reading after we have found it
2277
 */
2278
2279
static int MBTilesCurlReadCbk(CPL_UNUSED VSILFILE *fp, void *pabyBuffer,
2280
                              size_t nBufferSize, void *pfnUserData)
2281
0
{
2282
0
    TileProperties *psTP = static_cast<TileProperties *>(pfnUserData);
2283
2284
0
    const GByte abyPNGSig[] = {0x89, 0x50, 0x4E, 0x47,
2285
0
                               0x0D, 0x0A, 0x1A, 0x0A, /* PNG signature */
2286
0
                               0x00, 0x00, 0x00, 0x0D, /* IHDR length */
2287
0
                               0x49, 0x48, 0x44, 0x52 /* IHDR chunk */};
2288
2289
    /* JPEG SOF0 (Start Of Frame 0) marker */
2290
0
    const GByte abyJPEG1CompSig[] = {0xFF, 0xC0, /* marker */
2291
0
                                     0x00, 0x0B, /* data length = 8 + 1 * 3 */
2292
0
                                     0x08, /* depth : 8 bit */};
2293
0
    const GByte abyJPEG3CompSig[] = {0xFF, 0xC0, /* marker */
2294
0
                                     0x00, 0x11, /* data length = 8 + 3 * 3 */
2295
0
                                     0x08, /* depth : 8 bit */};
2296
2297
0
    int i;
2298
0
    for (i = 0; i < (int)nBufferSize - (int)sizeof(abyPNGSig); i++)
2299
0
    {
2300
0
        if (memcmp(((GByte *)pabyBuffer) + i, abyPNGSig, sizeof(abyPNGSig)) ==
2301
0
                0 &&
2302
0
            i + sizeof(abyPNGSig) + 4 + 4 + 1 + 1 < nBufferSize)
2303
0
        {
2304
0
            GByte *ptr = ((GByte *)(pabyBuffer)) + i + (int)sizeof(abyPNGSig);
2305
2306
0
            int nWidth;
2307
0
            memcpy(&nWidth, ptr, 4);
2308
0
            CPL_MSBPTR32(&nWidth);
2309
0
            ptr += 4;
2310
2311
0
            int nHeight;
2312
0
            memcpy(&nHeight, ptr, 4);
2313
0
            CPL_MSBPTR32(&nHeight);
2314
0
            ptr += 4;
2315
2316
0
            GByte nDepth = *ptr;
2317
0
            ptr += 1;
2318
2319
0
            GByte nColorType = *ptr;
2320
0
            CPLDebug("MBTILES",
2321
0
                     "PNG: nWidth=%d nHeight=%d depth=%d nColorType=%d", nWidth,
2322
0
                     nHeight, nDepth, nColorType);
2323
2324
0
            psTP->nBands = -2;
2325
0
            psTP->nSize = nWidth;
2326
0
            if (nWidth == nHeight && nDepth == 8)
2327
0
            {
2328
0
                if (nColorType == 0)
2329
0
                    psTP->nBands = 1; /* Gray */
2330
0
                else if (nColorType == 2)
2331
0
                    psTP->nBands = 3; /* RGB */
2332
0
                else if (nColorType == 3)
2333
0
                {
2334
                    /* This might also be a color table with transparency */
2335
                    /* but we cannot tell ! */
2336
0
                    psTP->nBands = -1;
2337
0
                    return TRUE;
2338
0
                }
2339
0
                else if (nColorType == 4)
2340
0
                    psTP->nBands = 2; /* Gray + alpha */
2341
0
                else if (nColorType == 6)
2342
0
                    psTP->nBands = 4; /* RGB + alpha */
2343
0
            }
2344
2345
0
            return FALSE;
2346
0
        }
2347
0
    }
2348
2349
0
    for (i = 0; i < (int)nBufferSize - ((int)sizeof(abyJPEG1CompSig) + 5); i++)
2350
0
    {
2351
0
        if (memcmp(((GByte *)pabyBuffer) + i, abyJPEG1CompSig,
2352
0
                   sizeof(abyJPEG1CompSig)) == 0 &&
2353
0
            ((GByte *)pabyBuffer)[sizeof(abyJPEG1CompSig) + 4] == 1)
2354
0
        {
2355
0
            GUInt16 nWidth;
2356
0
            memcpy(&nWidth, &(((GByte *)pabyBuffer)[sizeof(abyJPEG1CompSig)]),
2357
0
                   2);
2358
0
            CPL_MSBPTR16(&nWidth);
2359
0
            GUInt16 nHeight;
2360
0
            memcpy(&nHeight,
2361
0
                   &(((GByte *)pabyBuffer)[sizeof(abyJPEG1CompSig) + 2]), 2);
2362
0
            CPL_MSBPTR16(&nHeight);
2363
2364
0
            CPLDebug("MBTILES", "JPEG: nWidth=%d nHeight=%d depth=%d nBands=%d",
2365
0
                     nWidth, nHeight, 8, 1);
2366
2367
0
            psTP->nBands = -2;
2368
0
            if (nWidth == nHeight)
2369
0
            {
2370
0
                psTP->nSize = nWidth;
2371
0
                psTP->nBands = 1;
2372
0
            }
2373
2374
0
            return FALSE;
2375
0
        }
2376
0
        else if (memcmp(((GByte *)pabyBuffer) + i, abyJPEG3CompSig,
2377
0
                        sizeof(abyJPEG3CompSig)) == 0 &&
2378
0
                 ((GByte *)pabyBuffer)[sizeof(abyJPEG3CompSig) + 4] == 3)
2379
0
        {
2380
0
            GUInt16 nWidth;
2381
0
            memcpy(&nWidth, &(((GByte *)pabyBuffer)[sizeof(abyJPEG3CompSig)]),
2382
0
                   2);
2383
0
            CPL_MSBPTR16(&nWidth);
2384
0
            GUInt16 nHeight;
2385
0
            memcpy(&nHeight,
2386
0
                   &(((GByte *)pabyBuffer)[sizeof(abyJPEG3CompSig) + 2]), 2);
2387
0
            CPL_MSBPTR16(&nHeight);
2388
2389
0
            CPLDebug("MBTILES", "JPEG: nWidth=%d nHeight=%d depth=%d nBands=%d",
2390
0
                     nWidth, nHeight, 8, 3);
2391
2392
0
            psTP->nBands = -2;
2393
0
            if (nWidth == nHeight)
2394
0
            {
2395
0
                psTP->nSize = nWidth;
2396
0
                psTP->nBands = 3;
2397
0
            }
2398
2399
0
            return FALSE;
2400
0
        }
2401
0
    }
2402
2403
0
    return TRUE;
2404
0
}
2405
2406
/************************************************************************/
2407
/*                  MBTilesGetBandCountAndTileSize()                    */
2408
/************************************************************************/
2409
2410
static int MBTilesGetBandCountAndTileSize(bool bIsVSICURL, GDALDatasetH &hDS,
2411
                                          int nMaxLevel, int nMinTileRow,
2412
                                          int nMaxTileRow, int nMinTileCol,
2413
                                          int nMaxTileCol, int &nTileSize)
2414
596
{
2415
596
    OGRLayerH hSQLLyr;
2416
596
    OGRFeatureH hFeat;
2417
596
    VSILFILE *fpCURLOGR = nullptr;
2418
596
    int bFirstSelect = TRUE;
2419
2420
596
    int nBands = -1;
2421
596
    nTileSize = 0;
2422
2423
    /* Get the VSILFILE associated with the OGR SQLite DB */
2424
596
    CPLString osDSName(GDALGetDescription(hDS));
2425
596
    if (bIsVSICURL)
2426
0
    {
2427
0
        auto poDS = dynamic_cast<OGRSQLiteBaseDataSource *>(
2428
0
            GDALDataset::FromHandle(hDS));
2429
0
        CPLAssert(poDS);
2430
0
        if (poDS)
2431
0
        {
2432
0
            fpCURLOGR = poDS->GetVSILFILE();
2433
0
        }
2434
0
    }
2435
2436
596
    const char *pszSQL =
2437
596
        CPLSPrintf("SELECT tile_data FROM tiles WHERE "
2438
596
                   "tile_column = %d AND tile_row = %d AND zoom_level = %d",
2439
596
                   nMinTileCol / 2 + nMaxTileCol / 2,
2440
596
                   nMinTileRow / 2 + nMaxTileRow / 2, nMaxLevel);
2441
596
    CPLDebug("MBTILES", "%s", pszSQL);
2442
2443
596
    if (fpCURLOGR)
2444
0
    {
2445
        /* Install a spy on the file connection that will intercept */
2446
        /* PNG or JPEG headers, to interrupt their downloading */
2447
        /* once the header is found. Speeds up dataset opening. */
2448
0
        CPLErrorReset();
2449
0
        TileProperties tp;
2450
0
        tp.nBands = -1;
2451
0
        tp.nSize = 0;
2452
0
        VSICurlInstallReadCbk(fpCURLOGR, MBTilesCurlReadCbk, &tp, TRUE);
2453
0
        nBands = tp.nBands;
2454
0
        nTileSize = tp.nSize;
2455
2456
0
        CPLErrorReset();
2457
0
        CPLPushErrorHandler(CPLQuietErrorHandler);
2458
0
        hSQLLyr = GDALDatasetExecuteSQL(hDS, pszSQL, nullptr, nullptr);
2459
0
        CPLPopErrorHandler();
2460
2461
0
        VSICurlUninstallReadCbk(fpCURLOGR);
2462
2463
        /* Did the spy intercept something interesting ? */
2464
        // cppcheck-suppress knownConditionTrueFalse
2465
0
        if (nBands != -1)
2466
0
        {
2467
0
            CPLErrorReset();
2468
2469
0
            GDALDatasetReleaseResultSet(hDS, hSQLLyr);
2470
0
            hSQLLyr = nullptr;
2471
2472
            // Re-open OGR SQLite DB, because with our spy we have simulated an
2473
            // I/O error that SQLite will have difficulties to recover within
2474
            // the existing connection.  This will be fast because
2475
            // the /vsicurl/ cache has cached the already read blocks.
2476
0
            GDALClose(hDS);
2477
0
            hDS = MBTILESOpenSQLiteDB(osDSName.c_str(), GA_ReadOnly);
2478
0
            if (hDS == nullptr)
2479
0
                return -1;
2480
2481
            /* Unrecognized form of PNG. Error out */
2482
0
            if (nBands <= 0)
2483
0
                return -1;
2484
2485
0
            return nBands;
2486
0
        }
2487
0
        else if (CPLGetLastErrorType() == CE_Failure)
2488
0
        {
2489
0
            CPLError(CE_Failure, CPLGetLastErrorNo(), "%s",
2490
0
                     CPLGetLastErrorMsg());
2491
0
        }
2492
0
    }
2493
596
    else
2494
596
    {
2495
596
        hSQLLyr = GDALDatasetExecuteSQL(hDS, pszSQL, nullptr, nullptr);
2496
596
    }
2497
2498
623
    while (true)
2499
623
    {
2500
623
        if (hSQLLyr == nullptr && bFirstSelect)
2501
59
        {
2502
59
            bFirstSelect = FALSE;
2503
59
            pszSQL = CPLSPrintf("SELECT tile_data FROM tiles WHERE "
2504
59
                                "zoom_level = %d LIMIT 1",
2505
59
                                nMaxLevel);
2506
59
            CPLDebug("MBTILES", "%s", pszSQL);
2507
59
            hSQLLyr = GDALDatasetExecuteSQL(hDS, pszSQL, nullptr, nullptr);
2508
59
            if (hSQLLyr == nullptr)
2509
32
                return -1;
2510
59
        }
2511
2512
591
        hFeat = OGR_L_GetNextFeature(hSQLLyr);
2513
591
        if (hFeat == nullptr)
2514
32
        {
2515
32
            GDALDatasetReleaseResultSet(hDS, hSQLLyr);
2516
32
            hSQLLyr = nullptr;
2517
32
            if (!bFirstSelect)
2518
5
                return -1;
2519
32
        }
2520
559
        else
2521
559
            break;
2522
591
    }
2523
2524
559
    const CPLString osMemFileName(VSIMemGenerateHiddenFilename("mvt_temp.db"));
2525
2526
559
    int nDataSize = 0;
2527
559
    GByte *pabyData = OGR_F_GetFieldAsBinary(hFeat, 0, &nDataSize);
2528
2529
559
    VSIFCloseL(VSIFileFromMemBuffer(osMemFileName.c_str(), pabyData, nDataSize,
2530
559
                                    FALSE));
2531
2532
559
    GDALDatasetH hDSTile = GDALOpenEx(osMemFileName.c_str(), GDAL_OF_RASTER,
2533
559
                                      apszAllowedDrivers, nullptr, nullptr);
2534
559
    if (hDSTile == nullptr)
2535
555
    {
2536
555
        VSIUnlink(osMemFileName.c_str());
2537
555
        OGR_F_Destroy(hFeat);
2538
555
        GDALDatasetReleaseResultSet(hDS, hSQLLyr);
2539
555
        return -1;
2540
555
    }
2541
2542
4
    nBands = GDALGetRasterCount(hDSTile);
2543
2544
4
    if ((nBands != 1 && nBands != 2 && nBands != 3 && nBands != 4) ||
2545
4
        GDALGetRasterXSize(hDSTile) != GDALGetRasterYSize(hDSTile) ||
2546
4
        GDALGetRasterDataType(GDALGetRasterBand(hDSTile, 1)) != GDT_Byte)
2547
0
    {
2548
0
        CPLError(CE_Failure, CPLE_NotSupported,
2549
0
                 "Unsupported tile characteristics");
2550
0
        GDALClose(hDSTile);
2551
0
        VSIUnlink(osMemFileName.c_str());
2552
0
        OGR_F_Destroy(hFeat);
2553
0
        GDALDatasetReleaseResultSet(hDS, hSQLLyr);
2554
0
        return -1;
2555
0
    }
2556
2557
4
    nTileSize = GDALGetRasterXSize(hDSTile);
2558
4
    GDALColorTableH hCT =
2559
4
        GDALGetRasterColorTable(GDALGetRasterBand(hDSTile, 1));
2560
4
    if (nBands == 1 && hCT != nullptr)
2561
0
    {
2562
0
        nBands = 3;
2563
0
        if (GDALGetColorEntryCount(hCT) > 0)
2564
0
        {
2565
            /* Typical of paletted PNG with transparency */
2566
0
            const GDALColorEntry *psEntry = GDALGetColorEntry(hCT, 0);
2567
0
            if (psEntry->c4 == 0)
2568
0
                nBands = 4;
2569
0
        }
2570
0
    }
2571
2572
4
    GDALClose(hDSTile);
2573
4
    VSIUnlink(osMemFileName.c_str());
2574
4
    OGR_F_Destroy(hFeat);
2575
4
    GDALDatasetReleaseResultSet(hDS, hSQLLyr);
2576
2577
4
    return nBands;
2578
4
}
2579
2580
/************************************************************************/
2581
/*                                Open()                                */
2582
/************************************************************************/
2583
2584
GDALDataset *MBTilesDataset::Open(GDALOpenInfo *poOpenInfo)
2585
873
{
2586
873
    CPLString osFileName;
2587
873
    CPLString osTableName;
2588
2589
873
    if (!Identify(poOpenInfo))
2590
0
        return nullptr;
2591
2592
873
    if ((poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0 &&
2593
873
        (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
2594
873
        (poOpenInfo->nOpenFlags & GDAL_OF_UPDATE) != 0)
2595
0
    {
2596
0
        return nullptr;
2597
0
    }
2598
2599
    /* -------------------------------------------------------------------- */
2600
    /*      Open underlying OGR DB                                          */
2601
    /* -------------------------------------------------------------------- */
2602
2603
873
    GDALDatasetH hDS =
2604
873
        MBTILESOpenSQLiteDB(poOpenInfo->pszFilename, poOpenInfo->eAccess);
2605
2606
873
    MBTilesDataset *poDS = nullptr;
2607
2608
873
    if (hDS == nullptr)
2609
0
        goto end;
2610
2611
    /* -------------------------------------------------------------------- */
2612
    /*      Build dataset                                                   */
2613
    /* -------------------------------------------------------------------- */
2614
873
    {
2615
873
        CPLString osMetadataTableName, osRasterTableName;
2616
873
        CPLString osSQL;
2617
873
        OGRLayerH hMetadataLyr, hRasterLyr;
2618
873
        OGRFeatureH hFeat;
2619
873
        int nBands;
2620
873
        OGRLayerH hSQLLyr = nullptr;
2621
873
        int nMinLevel = -1;
2622
873
        int nMaxLevel = -1;
2623
873
        int bHasMinMaxLevel = FALSE;
2624
873
        int bHasMap;
2625
2626
873
        osMetadataTableName = "metadata";
2627
2628
873
        hMetadataLyr =
2629
873
            GDALDatasetGetLayerByName(hDS, osMetadataTableName.c_str());
2630
873
        if (hMetadataLyr == nullptr)
2631
229
            goto end;
2632
2633
644
        osRasterTableName += "tiles";
2634
2635
644
        hRasterLyr = GDALDatasetGetLayerByName(hDS, osRasterTableName.c_str());
2636
644
        if (hRasterLyr == nullptr)
2637
25
            goto end;
2638
2639
619
        bHasMap = GDALDatasetGetLayerByName(hDS, "map") != nullptr;
2640
619
        if (bHasMap)
2641
0
        {
2642
0
            bHasMap = FALSE;
2643
2644
0
            hSQLLyr = GDALDatasetExecuteSQL(
2645
0
                hDS, "SELECT type FROM sqlite_master WHERE name = 'tiles'",
2646
0
                nullptr, nullptr);
2647
0
            if (hSQLLyr != nullptr)
2648
0
            {
2649
0
                hFeat = OGR_L_GetNextFeature(hSQLLyr);
2650
0
                if (hFeat)
2651
0
                {
2652
0
                    if (OGR_F_IsFieldSetAndNotNull(hFeat, 0))
2653
0
                    {
2654
0
                        bHasMap = strcmp(OGR_F_GetFieldAsString(hFeat, 0),
2655
0
                                         "view") == 0;
2656
0
                        if (!bHasMap)
2657
0
                        {
2658
0
                            CPLDebug("MBTILES", "Weird! 'tiles' is not a view, "
2659
0
                                                "but 'map' exists");
2660
0
                        }
2661
0
                    }
2662
0
                    OGR_F_Destroy(hFeat);
2663
0
                }
2664
0
                GDALDatasetReleaseResultSet(hDS, hSQLLyr);
2665
0
            }
2666
0
        }
2667
2668
        /* --------------------------------------------------------------------
2669
         */
2670
        /*      Get minimum and maximum zoom levels */
2671
        /* --------------------------------------------------------------------
2672
         */
2673
2674
619
        bHasMinMaxLevel =
2675
619
            MBTilesGetMinMaxZoomLevel(hDS, bHasMap, nMinLevel, nMaxLevel);
2676
2677
619
        const char *pszZoomLevel =
2678
619
            CSLFetchNameValue(poOpenInfo->papszOpenOptions, "ZOOM_LEVEL");
2679
619
        if (pszZoomLevel != nullptr)
2680
0
            nMaxLevel = atoi(pszZoomLevel);
2681
2682
619
        if (bHasMinMaxLevel && (nMinLevel < 0 || nMinLevel > nMaxLevel))
2683
3
        {
2684
3
            CPLError(CE_Failure, CPLE_AppDefined,
2685
3
                     "Inconsistent values : min(zoom_level) = %d, "
2686
3
                     "max(zoom_level) = %d",
2687
3
                     nMinLevel, nMaxLevel);
2688
3
            goto end;
2689
3
        }
2690
2691
616
        if (bHasMinMaxLevel && nMaxLevel > 22)
2692
4
        {
2693
4
            CPLError(CE_Failure, CPLE_NotSupported,
2694
4
                     "zoom_level > 22 not supported");
2695
4
            goto end;
2696
4
        }
2697
2698
612
        if (!bHasMinMaxLevel)
2699
10
        {
2700
10
            CPLError(CE_Failure, CPLE_AppDefined,
2701
10
                     "Cannot find min and max zoom_level");
2702
10
            goto end;
2703
10
        }
2704
2705
        /* --------------------------------------------------------------------
2706
         */
2707
        /*      Get bounds */
2708
        /* --------------------------------------------------------------------
2709
         */
2710
602
        double dfMinX = 0.0;
2711
602
        double dfMinY = 0.0;
2712
602
        double dfMaxX = 0.0;
2713
602
        double dfMaxY = 0.0;
2714
602
        bool bUseBounds = CPLFetchBool(
2715
602
            const_cast<const char **>(poOpenInfo->papszOpenOptions),
2716
602
            "USE_BOUNDS", true);
2717
602
        const char *pszMinX =
2718
602
            CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MINX");
2719
602
        const char *pszMinY =
2720
602
            CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MINY");
2721
602
        const char *pszMaxX =
2722
602
            CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MAXX");
2723
602
        const char *pszMaxY =
2724
602
            CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MAXY");
2725
602
        bool bHasBounds;
2726
602
        if (pszMinX != nullptr && pszMinY != nullptr && pszMaxX != nullptr &&
2727
602
            pszMaxY != nullptr)
2728
0
        {
2729
0
            bHasBounds = true;
2730
0
        }
2731
602
        else
2732
602
        {
2733
602
            bHasBounds = MBTilesGetBounds(hDS, bUseBounds, nMaxLevel, dfMinX,
2734
602
                                          dfMinY, dfMaxX, dfMaxY);
2735
602
        }
2736
602
        if (!bHasBounds)
2737
6
        {
2738
6
            CPLError(CE_Failure, CPLE_AppDefined,
2739
6
                     "Cannot find min and max tile numbers");
2740
6
            goto end;
2741
6
        }
2742
596
        if (pszMinX != nullptr)
2743
0
            dfMinX = CPLAtof(pszMinX);
2744
596
        if (pszMinY != nullptr)
2745
0
            dfMinY = CPLAtof(pszMinY);
2746
596
        if (pszMaxX != nullptr)
2747
0
            dfMaxX = CPLAtof(pszMaxX);
2748
596
        if (pszMaxY != nullptr)
2749
0
            dfMaxY = CPLAtof(pszMaxY);
2750
2751
        /* --------------------------------------------------------------------
2752
         */
2753
        /*      Get number of bands */
2754
        /* --------------------------------------------------------------------
2755
         */
2756
596
        int nMinTileCol =
2757
596
            static_cast<int>(MBTilesWorldCoordToTileCoord(dfMinX, nMaxLevel));
2758
596
        int nMinTileRow =
2759
596
            static_cast<int>(MBTilesWorldCoordToTileCoord(dfMinY, nMaxLevel));
2760
596
        int nMaxTileCol =
2761
596
            static_cast<int>(MBTilesWorldCoordToTileCoord(dfMaxX, nMaxLevel));
2762
596
        int nMaxTileRow =
2763
596
            static_cast<int>(MBTilesWorldCoordToTileCoord(dfMaxY, nMaxLevel));
2764
596
        int nTileSize = 0;
2765
596
        nBands = MBTilesGetBandCountAndTileSize(
2766
596
            STARTS_WITH_CI(poOpenInfo->pszFilename, "/vsicurl/"), hDS,
2767
596
            nMaxLevel, nMinTileRow, nMaxTileRow, nMinTileCol, nMaxTileCol,
2768
596
            nTileSize);
2769
596
        bool bFoundRasterTile = nBands > 0;
2770
596
        if (!bFoundRasterTile)
2771
592
            nTileSize = knDEFAULT_BLOCK_SIZE;
2772
2773
        // Force 4 bands by default (see #6119)
2774
596
        nBands = 4;
2775
2776
596
        const char *pszBandCount = CSLFetchNameValueDef(
2777
596
            poOpenInfo->papszOpenOptions, "BAND_COUNT",
2778
596
            CPLGetConfigOption("MBTILES_BAND_COUNT", nullptr));
2779
596
        if (pszBandCount)
2780
0
        {
2781
0
            int nTmpBands = atoi(pszBandCount);
2782
0
            if (nTmpBands >= 1 && nTmpBands <= 4)
2783
0
                nBands = nTmpBands;
2784
0
        }
2785
2786
596
        if (poOpenInfo->eAccess == GA_Update)
2787
0
        {
2788
            // So that we can edit all potential overviews
2789
0
            nMinLevel = 0;
2790
0
        }
2791
2792
        /* --------------------------------------------------------------------
2793
         */
2794
        /*      Set dataset attributes */
2795
        /* --------------------------------------------------------------------
2796
         */
2797
2798
596
        poDS = new MBTilesDataset();
2799
596
        poDS->eAccess = poOpenInfo->eAccess;
2800
596
        poDS->hDS = hDS;
2801
596
        poDS->hDB = (sqlite3 *)GDALGetInternalHandle((GDALDatasetH)hDS,
2802
596
                                                     "SQLITE_HANDLE");
2803
596
        CPLAssert(poDS->hDB != nullptr);
2804
2805
        /* poDS will release it from now */
2806
596
        hDS = nullptr;
2807
2808
596
        poDS->m_osClip =
2809
596
            CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "CLIP", "");
2810
596
        poDS->m_nMinZoomLevel = nMinLevel;
2811
596
        bool bRasterOK = poDS->InitRaster(nullptr, nMaxLevel, nBands, nTileSize,
2812
596
                                          dfMinX, dfMinY, dfMaxX, dfMaxY);
2813
2814
596
        const char *pszFormat = poDS->GetMetadataItem("format");
2815
596
        if (pszFormat != nullptr && EQUAL(pszFormat, "pbf"))
2816
532
        {
2817
532
            if ((poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) == 0)
2818
2
            {
2819
2
                CPLDebug("MBTiles", "This files contain vector tiles, "
2820
2
                                    "but driver open in raster-only mode");
2821
2
                delete poDS;
2822
2
                return nullptr;
2823
2
            }
2824
530
            poDS->InitVector(dfMinX, dfMinY, dfMaxX, dfMaxY,
2825
530
                             CPLFetchBool(poOpenInfo->papszOpenOptions,
2826
530
                                          "ZOOM_LEVEL_AUTO",
2827
530
                                          CPLTestBool(CPLGetConfigOption(
2828
530
                                              "MVT_ZOOM_LEVEL_AUTO", "NO"))),
2829
530
                             CPLFetchBool(poOpenInfo->papszOpenOptions,
2830
530
                                          "JSON_FIELD", false));
2831
530
        }
2832
64
        else if ((pszFormat != nullptr && !EQUAL(pszFormat, "pbf")) ||
2833
64
                 bFoundRasterTile)
2834
9
        {
2835
9
            if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0)
2836
3
            {
2837
3
                CPLDebug("MBTiles", "This files contain raster tiles, "
2838
3
                                    "but driver open in vector-only mode");
2839
3
                delete poDS;
2840
3
                return nullptr;
2841
3
            }
2842
9
        }
2843
2844
591
        if ((pszFormat == nullptr || !EQUAL(pszFormat, "pbf")) && !bRasterOK)
2845
0
        {
2846
0
            delete poDS;
2847
0
            return nullptr;
2848
0
        }
2849
2850
591
        if (poDS->eAccess == GA_Update)
2851
0
        {
2852
0
            if (pszFormat != nullptr &&
2853
0
                (EQUAL(pszFormat, "jpg") || EQUAL(pszFormat, "jpeg")))
2854
0
            {
2855
0
                poDS->m_eTF = GPKG_TF_JPEG;
2856
0
            }
2857
0
            else if (pszFormat != nullptr && (EQUAL(pszFormat, "webp")))
2858
0
            {
2859
0
                poDS->m_eTF = GPKG_TF_WEBP;
2860
0
            }
2861
2862
0
            const char *pszTF =
2863
0
                CSLFetchNameValue(poOpenInfo->papszOpenOptions, "TILE_FORMAT");
2864
0
            if (pszTF)
2865
0
            {
2866
0
                poDS->m_eTF = GDALGPKGMBTilesGetTileFormat(pszTF);
2867
0
                if ((pszFormat != nullptr &&
2868
0
                     (EQUAL(pszFormat, "jpg") || EQUAL(pszFormat, "jpeg")) &&
2869
0
                     poDS->m_eTF != GPKG_TF_JPEG) ||
2870
0
                    (pszFormat != nullptr && EQUAL(pszFormat, "webp") &&
2871
0
                     poDS->m_eTF != GPKG_TF_WEBP) ||
2872
0
                    (pszFormat != nullptr && EQUAL(pszFormat, "png") &&
2873
0
                     poDS->m_eTF == GPKG_TF_JPEG))
2874
0
                {
2875
0
                    CPLError(CE_Warning, CPLE_AppDefined,
2876
0
                             "Format metadata = '%s', but TILE_FORMAT='%s'",
2877
0
                             pszFormat, pszTF);
2878
0
                }
2879
0
            }
2880
2881
0
            poDS->ParseCompressionOptions(poOpenInfo->papszOpenOptions);
2882
0
        }
2883
2884
        /* --------------------------------------------------------------------
2885
         */
2886
        /*      Add overview levels as internal datasets */
2887
        /* --------------------------------------------------------------------
2888
         */
2889
601
        for (int iLevel = nMaxLevel - 1; iLevel >= nMinLevel; iLevel--)
2890
60
        {
2891
60
            MBTilesDataset *poOvrDS = new MBTilesDataset();
2892
60
            poOvrDS->ShareLockWithParentDataset(poDS);
2893
60
            poOvrDS->InitRaster(poDS, iLevel, nBands, nTileSize, dfMinX, dfMinY,
2894
60
                                dfMaxX, dfMaxY);
2895
2896
60
            poDS->m_papoOverviewDS = (MBTilesDataset **)CPLRealloc(
2897
60
                poDS->m_papoOverviewDS,
2898
60
                sizeof(MBTilesDataset *) * (poDS->m_nOverviewCount + 1));
2899
60
            poDS->m_papoOverviewDS[poDS->m_nOverviewCount++] = poOvrDS;
2900
2901
60
            if (poOvrDS->GetRasterXSize() < 256 &&
2902
60
                poOvrDS->GetRasterYSize() < 256)
2903
50
            {
2904
50
                break;
2905
50
            }
2906
60
        }
2907
2908
        /* --------------------------------------------------------------------
2909
         */
2910
        /*      Initialize any PAM information. */
2911
        /* --------------------------------------------------------------------
2912
         */
2913
591
        poDS->SetDescription(poOpenInfo->pszFilename);
2914
2915
591
        if (!STARTS_WITH_CI(poOpenInfo->pszFilename, "/vsicurl/"))
2916
591
            poDS->TryLoadXML();
2917
0
        else
2918
0
        {
2919
0
            poDS->SetPamFlags(poDS->GetPamFlags() & ~GPF_DIRTY);
2920
0
        }
2921
591
    }
2922
2923
868
end:
2924
868
    if (hDS)
2925
277
        GDALClose(hDS);
2926
2927
868
    return poDS;
2928
591
}
2929
2930
/************************************************************************/
2931
/*                                Create()                              */
2932
/************************************************************************/
2933
2934
GDALDataset *MBTilesDataset::Create(const char *pszFilename, int nXSize,
2935
                                    int nYSize, int nBandsIn, GDALDataType eDT,
2936
                                    char **papszOptions)
2937
0
{
2938
#ifdef HAVE_MVT_WRITE_SUPPORT
2939
    if (nXSize == 0 && nYSize == 0 && nBandsIn == 0 && eDT == GDT_Unknown)
2940
    {
2941
        char **papszOptionsMod = CSLDuplicate(papszOptions);
2942
        papszOptionsMod = CSLSetNameValue(papszOptionsMod, "FORMAT", "MBTILES");
2943
        GDALDataset *poRet = OGRMVTWriterDatasetCreate(
2944
            pszFilename, nXSize, nYSize, nBandsIn, eDT, papszOptionsMod);
2945
        CSLDestroy(papszOptionsMod);
2946
        return poRet;
2947
    }
2948
#endif
2949
2950
0
    MBTilesDataset *poDS = new MBTilesDataset();
2951
0
    if (!poDS->CreateInternal(pszFilename, nXSize, nYSize, nBandsIn, eDT,
2952
0
                              papszOptions))
2953
0
    {
2954
0
        delete poDS;
2955
0
        poDS = nullptr;
2956
0
    }
2957
0
    return poDS;
2958
0
}
2959
2960
/************************************************************************/
2961
/*                            CreateInternal()                          */
2962
/************************************************************************/
2963
2964
bool MBTilesDataset::CreateInternal(const char *pszFilename, int nXSize,
2965
                                    int nYSize, int nBandsIn, GDALDataType eDT,
2966
                                    char **papszOptions)
2967
0
{
2968
0
    if (eDT != GDT_Byte)
2969
0
    {
2970
0
        CPLError(CE_Failure, CPLE_NotSupported, "Only Byte supported");
2971
0
        return false;
2972
0
    }
2973
0
    if (nBandsIn != 1 && nBandsIn != 2 && nBandsIn != 3 && nBandsIn != 4)
2974
0
    {
2975
0
        CPLError(CE_Failure, CPLE_NotSupported,
2976
0
                 "Only 1 (Grey/ColorTable), 2 (Grey+Alpha), 3 (RGB) or 4 "
2977
0
                 "(RGBA) band dataset supported");
2978
0
        return false;
2979
0
    }
2980
2981
    // for test/debug purposes only. true is the nominal value
2982
0
    m_bPNGSupports2Bands =
2983
0
        CPLTestBool(CPLGetConfigOption("MBTILES_PNG_SUPPORTS_2BANDS", "TRUE"));
2984
0
    m_bPNGSupportsCT =
2985
0
        CPLTestBool(CPLGetConfigOption("MBTILES_PNG_SUPPORTS_CT", "TRUE"));
2986
0
    m_bWriteBounds = CPLFetchBool(const_cast<const char **>(papszOptions),
2987
0
                                  "WRITE_BOUNDS", true);
2988
0
    m_bWriteMinMaxZoom = CPLFetchBool(const_cast<const char **>(papszOptions),
2989
0
                                      "WRITE_MINMAXZOOM", true);
2990
0
    int nBlockSize = std::max(
2991
0
        64, std::min(8192, atoi(CSLFetchNameValueDef(
2992
0
                               papszOptions, "BLOCKSIZE",
2993
0
                               CPLSPrintf("%d", knDEFAULT_BLOCK_SIZE)))));
2994
0
    m_osBounds = CSLFetchNameValueDef(papszOptions, "BOUNDS", "");
2995
0
    m_osCenter = CSLFetchNameValueDef(papszOptions, "CENTER", "");
2996
2997
0
    VSIUnlink(pszFilename);
2998
0
    SetDescription(pszFilename);
2999
3000
0
    int rc;
3001
0
    if (STARTS_WITH(pszFilename, "/vsi"))
3002
0
    {
3003
0
        pMyVFS = OGRSQLiteCreateVFS(nullptr, nullptr);
3004
0
        sqlite3_vfs_register(pMyVFS, 0);
3005
0
        rc = sqlite3_open_v2(pszFilename, &hDB,
3006
0
                             SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
3007
0
                             pMyVFS->zName);
3008
0
    }
3009
0
    else
3010
0
    {
3011
0
        rc = sqlite3_open(pszFilename, &hDB);
3012
0
    }
3013
3014
0
    if (rc != SQLITE_OK)
3015
0
    {
3016
0
        CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", pszFilename);
3017
0
        return false;
3018
0
    }
3019
3020
0
    sqlite3_exec(hDB, "PRAGMA synchronous = OFF", nullptr, nullptr, nullptr);
3021
3022
0
    rc = sqlite3_exec(hDB,
3023
0
                      "CREATE TABLE tiles ("
3024
0
                      "zoom_level INTEGER NOT NULL,"
3025
0
                      "tile_column INTEGER NOT NULL,"
3026
0
                      "tile_row INTEGER NOT NULL,"
3027
0
                      "tile_data BLOB NOT NULL,"
3028
0
                      "UNIQUE (zoom_level, tile_column, tile_row) )",
3029
0
                      nullptr, nullptr, nullptr);
3030
0
    if (rc != SQLITE_OK)
3031
0
    {
3032
0
        CPLError(CE_Failure, CPLE_FileIO, "Cannot create tiles table");
3033
0
        return false;
3034
0
    }
3035
3036
0
    rc = sqlite3_exec(hDB, "CREATE TABLE metadata (name TEXT, value TEXT)",
3037
0
                      nullptr, nullptr, nullptr);
3038
0
    if (rc != SQLITE_OK)
3039
0
    {
3040
0
        CPLError(CE_Failure, CPLE_FileIO, "Cannot create metadata table");
3041
0
        return false;
3042
0
    }
3043
3044
0
    const std::string osName = CSLFetchNameValueDef(
3045
0
        papszOptions, "NAME", CPLGetBasenameSafe(pszFilename).c_str());
3046
0
    char *pszSQL = sqlite3_mprintf(
3047
0
        "INSERT INTO metadata (name, value) VALUES ('name', '%q')",
3048
0
        osName.c_str());
3049
0
    sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr);
3050
0
    sqlite3_free(pszSQL);
3051
3052
0
    const char *pszType = CSLFetchNameValueDef(papszOptions, "TYPE", "overlay");
3053
0
    pszSQL = sqlite3_mprintf(
3054
0
        "INSERT INTO metadata (name, value) VALUES ('type', '%q')", pszType);
3055
0
    sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr);
3056
0
    sqlite3_free(pszSQL);
3057
3058
0
    const std::string osDescription = CSLFetchNameValueDef(
3059
0
        papszOptions, "DESCRIPTION", CPLGetBasenameSafe(pszFilename).c_str());
3060
0
    pszSQL = sqlite3_mprintf(
3061
0
        "INSERT INTO metadata (name, value) VALUES ('description', '%q')",
3062
0
        osDescription.c_str());
3063
0
    sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr);
3064
0
    sqlite3_free(pszSQL);
3065
3066
0
    const char *pszTF = CSLFetchNameValue(papszOptions, "TILE_FORMAT");
3067
0
    if (pszTF)
3068
0
        m_eTF = GDALGPKGMBTilesGetTileFormat(pszTF);
3069
3070
0
    const char *pszVersion = CSLFetchNameValueDef(
3071
0
        papszOptions, "VERSION", (m_eTF == GPKG_TF_WEBP) ? "1.3" : "1.1");
3072
0
    pszSQL = sqlite3_mprintf(
3073
0
        "INSERT INTO metadata (name, value) VALUES ('version', '%q')",
3074
0
        pszVersion);
3075
0
    sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr);
3076
0
    sqlite3_free(pszSQL);
3077
3078
0
    const char *pszFormat = CSLFetchNameValueDef(
3079
0
        papszOptions, "FORMAT", GDALMBTilesGetTileFormatName(m_eTF));
3080
0
    pszSQL = sqlite3_mprintf(
3081
0
        "INSERT INTO metadata (name, value) VALUES ('format', '%q')",
3082
0
        pszFormat);
3083
0
    sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr);
3084
0
    sqlite3_free(pszSQL);
3085
3086
0
    m_bNew = true;
3087
0
    eAccess = GA_Update;
3088
0
    nRasterXSize = nXSize;
3089
0
    nRasterYSize = nYSize;
3090
3091
0
    m_pabyCachedTiles =
3092
0
        (GByte *)VSI_MALLOC3_VERBOSE(4 * 4, nBlockSize, nBlockSize);
3093
0
    if (m_pabyCachedTiles == nullptr)
3094
0
    {
3095
0
        return false;
3096
0
    }
3097
3098
0
    for (int i = 1; i <= nBandsIn; i++)
3099
0
        SetBand(i, new MBTilesBand(this, nBlockSize));
3100
3101
0
    ParseCompressionOptions(papszOptions);
3102
3103
0
    return true;
3104
0
}
3105
3106
/************************************************************************/
3107
/*                            CreateCopy()                              */
3108
/************************************************************************/
3109
3110
typedef struct
3111
{
3112
    const char *pszName;
3113
    GDALResampleAlg eResampleAlg;
3114
} WarpResamplingAlg;
3115
3116
static const WarpResamplingAlg asResamplingAlg[] = {
3117
    {"NEAREST", GRA_NearestNeighbour},
3118
    {"BILINEAR", GRA_Bilinear},
3119
    {"CUBIC", GRA_Cubic},
3120
    {"CUBICSPLINE", GRA_CubicSpline},
3121
    {"LANCZOS", GRA_Lanczos},
3122
    {"MODE", GRA_Mode},
3123
    {"AVERAGE", GRA_Average},
3124
    {"RMS", GRA_RMS},
3125
};
3126
3127
GDALDataset *MBTilesDataset::CreateCopy(const char *pszFilename,
3128
                                        GDALDataset *poSrcDS, int /*bStrict*/,
3129
                                        char **papszOptions,
3130
                                        GDALProgressFunc pfnProgress,
3131
                                        void *pProgressData)
3132
0
{
3133
3134
0
    int nBands = poSrcDS->GetRasterCount();
3135
0
    if (nBands != 1 && nBands != 2 && nBands != 3 && nBands != 4)
3136
0
    {
3137
0
        CPLError(CE_Failure, CPLE_NotSupported,
3138
0
                 "Only 1 (Grey/ColorTable), 2 (Grey+Alpha), 3 (RGB) or 4 "
3139
0
                 "(RGBA) band dataset supported");
3140
0
        return nullptr;
3141
0
    }
3142
3143
0
    char **papszTO = CSLSetNameValue(nullptr, "DST_SRS", SRS_EPSG_3857);
3144
3145
0
    void *hTransformArg = nullptr;
3146
3147
    // Hack to compensate for GDALSuggestedWarpOutput2() failure (or not
3148
    // ideal suggestion with PROJ 8) when reprojecting latitude = +/- 90 to
3149
    // EPSG:3857.
3150
0
    GDALGeoTransform srcGT;
3151
0
    std::unique_ptr<GDALDataset> poTmpDS;
3152
0
    bool bModifiedMaxLat = false;
3153
0
    bool bModifiedMinLat = false;
3154
0
    const auto poSrcSRS = poSrcDS->GetSpatialRef();
3155
0
    if (poSrcDS->GetGeoTransform(srcGT) == CE_None && srcGT[2] == 0 &&
3156
0
        srcGT[4] == 0 && srcGT[5] < 0)
3157
0
    {
3158
0
        if (poSrcSRS && poSrcSRS->IsGeographic())
3159
0
        {
3160
0
            double maxLat = srcGT[3];
3161
0
            double minLat = srcGT[3] + poSrcDS->GetRasterYSize() * srcGT[5];
3162
            // Corresponds to the latitude of MAX_GM
3163
0
            constexpr double MAX_LAT = 85.0511287798066;
3164
0
            if (maxLat > MAX_LAT)
3165
0
            {
3166
0
                maxLat = MAX_LAT;
3167
0
                bModifiedMaxLat = true;
3168
0
            }
3169
0
            if (minLat < -MAX_LAT)
3170
0
            {
3171
0
                minLat = -MAX_LAT;
3172
0
                bModifiedMinLat = true;
3173
0
            }
3174
0
            if (bModifiedMaxLat || bModifiedMinLat)
3175
0
            {
3176
0
                CPLStringList aosOptions;
3177
0
                aosOptions.AddString("-of");
3178
0
                aosOptions.AddString("VRT");
3179
0
                aosOptions.AddString("-projwin");
3180
0
                aosOptions.AddString(CPLSPrintf("%.17g", srcGT[0]));
3181
0
                aosOptions.AddString(CPLSPrintf("%.17g", maxLat));
3182
0
                aosOptions.AddString(CPLSPrintf(
3183
0
                    "%.17g", srcGT[0] + poSrcDS->GetRasterXSize() * srcGT[1]));
3184
0
                aosOptions.AddString(CPLSPrintf("%.17g", minLat));
3185
0
                auto psOptions =
3186
0
                    GDALTranslateOptionsNew(aosOptions.List(), nullptr);
3187
0
                poTmpDS.reset(GDALDataset::FromHandle(GDALTranslate(
3188
0
                    "", GDALDataset::ToHandle(poSrcDS), psOptions, nullptr)));
3189
0
                GDALTranslateOptionsFree(psOptions);
3190
0
                if (poTmpDS)
3191
0
                {
3192
0
                    hTransformArg = GDALCreateGenImgProjTransformer2(
3193
0
                        GDALDataset::FromHandle(poTmpDS.get()), nullptr,
3194
0
                        papszTO);
3195
0
                }
3196
0
            }
3197
0
        }
3198
0
    }
3199
0
    if (hTransformArg == nullptr)
3200
0
    {
3201
0
        hTransformArg =
3202
0
            GDALCreateGenImgProjTransformer2(poSrcDS, nullptr, papszTO);
3203
0
    }
3204
0
    if (hTransformArg == nullptr)
3205
0
    {
3206
0
        CSLDestroy(papszTO);
3207
0
        return nullptr;
3208
0
    }
3209
3210
0
    GDALTransformerInfo *psInfo = (GDALTransformerInfo *)hTransformArg;
3211
0
    GDALGeoTransform gt;
3212
0
    double adfExtent[4];
3213
0
    int nXSize, nYSize;
3214
3215
0
    if (GDALSuggestedWarpOutput2(poSrcDS, psInfo->pfnTransform, hTransformArg,
3216
0
                                 gt.data(), &nXSize, &nYSize, adfExtent,
3217
0
                                 0) != CE_None)
3218
0
    {
3219
0
        CSLDestroy(papszTO);
3220
0
        GDALDestroyGenImgProjTransformer(hTransformArg);
3221
0
        return nullptr;
3222
0
    }
3223
3224
0
    GDALDestroyGenImgProjTransformer(hTransformArg);
3225
0
    hTransformArg = nullptr;
3226
0
    poTmpDS.reset();
3227
3228
0
    if (bModifiedMaxLat || bModifiedMinLat)
3229
0
    {
3230
0
        if (bModifiedMaxLat)
3231
0
        {
3232
0
            const double maxNorthing = MAX_GM;
3233
0
            gt[3] = maxNorthing;
3234
0
            adfExtent[3] = maxNorthing;
3235
0
        }
3236
0
        if (bModifiedMinLat)
3237
0
        {
3238
0
            const double minNorthing = -MAX_GM;
3239
0
            adfExtent[1] = minNorthing;
3240
0
        }
3241
3242
0
        if (poSrcSRS && poSrcSRS->IsGeographic())
3243
0
        {
3244
0
            if (srcGT[0] + poSrcDS->GetRasterXSize() * srcGT[1] == 180)
3245
0
            {
3246
0
                adfExtent[2] = MAX_GM;
3247
0
            }
3248
0
        }
3249
0
    }
3250
3251
0
    int nZoomLevel;
3252
0
    double dfComputedRes = gt[1];
3253
0
    double dfPrevRes = 0.0;
3254
0
    double dfRes = 0.0;
3255
0
    int nBlockSize = std::max(
3256
0
        64, std::min(8192, atoi(CSLFetchNameValueDef(
3257
0
                               papszOptions, "BLOCKSIZE",
3258
0
                               CPLSPrintf("%d", knDEFAULT_BLOCK_SIZE)))));
3259
0
    const double dfPixelXSizeZoomLevel0 = 2 * MAX_GM / nBlockSize;
3260
0
    for (nZoomLevel = 0; nZoomLevel < 25; nZoomLevel++)
3261
0
    {
3262
0
        dfRes = dfPixelXSizeZoomLevel0 / (1 << nZoomLevel);
3263
0
        if (dfComputedRes > dfRes)
3264
0
            break;
3265
0
        dfPrevRes = dfRes;
3266
0
    }
3267
0
    if (nZoomLevel == 25)
3268
0
    {
3269
0
        CPLError(CE_Failure, CPLE_AppDefined,
3270
0
                 "Could not find an appropriate zoom level");
3271
0
        CSLDestroy(papszTO);
3272
0
        return nullptr;
3273
0
    }
3274
3275
0
    const char *pszZoomLevelStrategy =
3276
0
        CSLFetchNameValueDef(papszOptions, "ZOOM_LEVEL_STRATEGY", "AUTO");
3277
0
    if (fabs(dfComputedRes - dfRes) / dfRes > 1e-8)
3278
0
    {
3279
0
        if (EQUAL(pszZoomLevelStrategy, "LOWER"))
3280
0
        {
3281
0
            if (nZoomLevel > 0)
3282
0
                nZoomLevel--;
3283
0
        }
3284
0
        else if (EQUAL(pszZoomLevelStrategy, "UPPER"))
3285
0
        {
3286
            /* do nothing */
3287
0
        }
3288
0
        else if (nZoomLevel > 0)
3289
0
        {
3290
0
            if (dfPrevRes / dfComputedRes < dfComputedRes / dfRes)
3291
0
                nZoomLevel--;
3292
0
        }
3293
0
    }
3294
3295
0
    dfRes = dfPixelXSizeZoomLevel0 / (1 << nZoomLevel);
3296
3297
0
    double dfMinX = adfExtent[0];
3298
0
    double dfMinY = adfExtent[1];
3299
0
    double dfMaxX = adfExtent[2];
3300
0
    double dfMaxY = adfExtent[3];
3301
3302
0
    nXSize = (int)(0.5 + (dfMaxX - dfMinX) / dfRes);
3303
0
    nYSize = (int)(0.5 + (dfMaxY - dfMinY) / dfRes);
3304
0
    gt[1] = dfRes;
3305
0
    gt[5] = -dfRes;
3306
3307
0
    int nTargetBands = nBands;
3308
    /* For grey level or RGB, if there's reprojection involved, add an alpha */
3309
    /* channel */
3310
0
    if ((nBands == 1 &&
3311
0
         poSrcDS->GetRasterBand(1)->GetColorTable() == nullptr) ||
3312
0
        nBands == 3)
3313
0
    {
3314
0
        OGRSpatialReference oSrcSRS;
3315
0
        oSrcSRS.SetFromUserInput(poSrcDS->GetProjectionRef());
3316
0
        oSrcSRS.AutoIdentifyEPSG();
3317
0
        if (oSrcSRS.GetAuthorityCode(nullptr) == nullptr ||
3318
0
            atoi(oSrcSRS.GetAuthorityCode(nullptr)) != 3857)
3319
0
        {
3320
0
            nTargetBands++;
3321
0
        }
3322
0
    }
3323
3324
0
    GDALResampleAlg eResampleAlg = GRA_Bilinear;
3325
0
    const char *pszResampling = CSLFetchNameValue(papszOptions, "RESAMPLING");
3326
0
    if (pszResampling)
3327
0
    {
3328
0
        for (size_t iAlg = 0;
3329
0
             iAlg < sizeof(asResamplingAlg) / sizeof(asResamplingAlg[0]);
3330
0
             iAlg++)
3331
0
        {
3332
0
            if (EQUAL(pszResampling, asResamplingAlg[iAlg].pszName))
3333
0
            {
3334
0
                eResampleAlg = asResamplingAlg[iAlg].eResampleAlg;
3335
0
                break;
3336
0
            }
3337
0
        }
3338
0
    }
3339
3340
0
    if (nBands == 1 && poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr &&
3341
0
        eResampleAlg != GRA_NearestNeighbour && eResampleAlg != GRA_Mode)
3342
0
    {
3343
0
        CPLError(
3344
0
            CE_Warning, CPLE_AppDefined,
3345
0
            "Input dataset has a color table, which will likely lead to "
3346
0
            "bad results when using a resampling method other than "
3347
0
            "nearest neighbour or mode. Converting the dataset to 24/32 bit "
3348
0
            "(e.g. with gdal_translate -expand rgb/rgba) is advised.");
3349
0
    }
3350
3351
0
    GDALDataset *poDS = Create(pszFilename, nXSize, nYSize, nTargetBands,
3352
0
                               GDT_Byte, papszOptions);
3353
0
    if (poDS == nullptr)
3354
0
    {
3355
0
        CSLDestroy(papszTO);
3356
0
        return nullptr;
3357
0
    }
3358
0
    poDS->SetGeoTransform(gt);
3359
0
    if (nTargetBands == 1 && nBands == 1 &&
3360
0
        poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr)
3361
0
    {
3362
0
        poDS->GetRasterBand(1)->SetColorTable(
3363
0
            poSrcDS->GetRasterBand(1)->GetColorTable());
3364
0
    }
3365
3366
0
    hTransformArg = GDALCreateGenImgProjTransformer2(poSrcDS, poDS, papszTO);
3367
0
    CSLDestroy(papszTO);
3368
0
    if (hTransformArg == nullptr)
3369
0
    {
3370
0
        CPLError(CE_Failure, CPLE_AppDefined,
3371
0
                 "GDALCreateGenImgProjTransformer2 failed");
3372
0
        delete poDS;
3373
0
        return nullptr;
3374
0
    }
3375
3376
    /* -------------------------------------------------------------------- */
3377
    /*      Warp the transformer with a linear approximator                 */
3378
    /* -------------------------------------------------------------------- */
3379
0
    hTransformArg = GDALCreateApproxTransformer(GDALGenImgProjTransform,
3380
0
                                                hTransformArg, 0.125);
3381
0
    GDALApproxTransformerOwnsSubtransformer(hTransformArg, TRUE);
3382
3383
    /* -------------------------------------------------------------------- */
3384
    /*      Setup warp options.                                             */
3385
    /* -------------------------------------------------------------------- */
3386
0
    GDALWarpOptions *psWO = GDALCreateWarpOptions();
3387
3388
0
    psWO->papszWarpOptions = CSLSetNameValue(nullptr, "OPTIMIZE_SIZE", "YES");
3389
0
    psWO->eWorkingDataType = GDT_Byte;
3390
3391
0
    psWO->eResampleAlg = eResampleAlg;
3392
3393
0
    psWO->hSrcDS = poSrcDS;
3394
0
    psWO->hDstDS = poDS;
3395
3396
0
    psWO->pfnTransformer = GDALApproxTransform;
3397
0
    psWO->pTransformerArg = hTransformArg;
3398
3399
0
    psWO->pfnProgress = pfnProgress;
3400
0
    psWO->pProgressArg = pProgressData;
3401
3402
    /* -------------------------------------------------------------------- */
3403
    /*      Setup band mapping.                                             */
3404
    /* -------------------------------------------------------------------- */
3405
3406
0
    if (nBands == 2 || nBands == 4)
3407
0
        psWO->nBandCount = nBands - 1;
3408
0
    else
3409
0
        psWO->nBandCount = nBands;
3410
3411
0
    psWO->panSrcBands = (int *)CPLMalloc(psWO->nBandCount * sizeof(int));
3412
0
    psWO->panDstBands = (int *)CPLMalloc(psWO->nBandCount * sizeof(int));
3413
3414
0
    for (int i = 0; i < psWO->nBandCount; i++)
3415
0
    {
3416
0
        psWO->panSrcBands[i] = i + 1;
3417
0
        psWO->panDstBands[i] = i + 1;
3418
0
    }
3419
3420
0
    if (nBands == 2 || nBands == 4)
3421
0
    {
3422
0
        psWO->nSrcAlphaBand = nBands;
3423
0
    }
3424
0
    if (nTargetBands == 2 || nTargetBands == 4)
3425
0
    {
3426
0
        psWO->nDstAlphaBand = nTargetBands;
3427
0
    }
3428
3429
    /* -------------------------------------------------------------------- */
3430
    /*      Initialize and execute the warp.                                */
3431
    /* -------------------------------------------------------------------- */
3432
0
    GDALWarpOperation oWO;
3433
3434
0
    CPLErr eErr = oWO.Initialize(psWO);
3435
0
    if (eErr == CE_None)
3436
0
    {
3437
        /*if( bMulti )
3438
            eErr = oWO.ChunkAndWarpMulti( 0, 0, nXSize, nYSize );
3439
        else*/
3440
0
        eErr = oWO.ChunkAndWarpImage(0, 0, nXSize, nYSize);
3441
0
    }
3442
0
    if (eErr != CE_None)
3443
0
    {
3444
0
        delete poDS;
3445
0
        poDS = nullptr;
3446
0
    }
3447
3448
0
    GDALDestroyTransformer(hTransformArg);
3449
0
    GDALDestroyWarpOptions(psWO);
3450
3451
0
    return poDS;
3452
0
}
3453
3454
/************************************************************************/
3455
/*                        ParseCompressionOptions()                     */
3456
/************************************************************************/
3457
3458
void MBTilesDataset::ParseCompressionOptions(char **papszOptions)
3459
0
{
3460
0
    const char *pszZLevel = CSLFetchNameValue(papszOptions, "ZLEVEL");
3461
0
    if (pszZLevel)
3462
0
        m_nZLevel = atoi(pszZLevel);
3463
3464
0
    const char *pszQuality = CSLFetchNameValue(papszOptions, "QUALITY");
3465
0
    if (pszQuality)
3466
0
        m_nQuality = atoi(pszQuality);
3467
3468
0
    const char *pszDither = CSLFetchNameValue(papszOptions, "DITHER");
3469
0
    if (pszDither)
3470
0
        m_bDither = CPLTestBool(pszDither);
3471
0
}
3472
3473
/************************************************************************/
3474
/*                          IBuildOverviews()                           */
3475
/************************************************************************/
3476
3477
static int GetFloorPowerOfTwo(int n)
3478
0
{
3479
0
    int p2 = 1;
3480
0
    while ((n = n >> 1) > 0)
3481
0
    {
3482
0
        p2 <<= 1;
3483
0
    }
3484
0
    return p2;
3485
0
}
3486
3487
CPLErr MBTilesDataset::IBuildOverviews(
3488
    const char *pszResampling, int nOverviews, const int *panOverviewList,
3489
    int nBandsIn, const int * /*panBandList*/, GDALProgressFunc pfnProgress,
3490
    void *pProgressData, CSLConstList papszOptions)
3491
0
{
3492
0
    if (GetAccess() != GA_Update)
3493
0
    {
3494
0
        CPLError(CE_Failure, CPLE_NotSupported,
3495
0
                 "Overview building not supported on a database opened in "
3496
0
                 "read-only mode");
3497
0
        return CE_Failure;
3498
0
    }
3499
0
    if (m_poParentDS != nullptr)
3500
0
    {
3501
0
        CPLError(CE_Failure, CPLE_NotSupported,
3502
0
                 "Overview building not supported on overview dataset");
3503
0
        return CE_Failure;
3504
0
    }
3505
3506
0
    if (nOverviews == 0)
3507
0
    {
3508
0
        for (int i = 0; i < m_nOverviewCount; i++)
3509
0
            m_papoOverviewDS[i]->FlushCache(false);
3510
0
        char *pszSQL = sqlite3_mprintf(
3511
0
            "DELETE FROM 'tiles' WHERE zoom_level < %d", m_nZoomLevel);
3512
0
        char *pszErrMsg = nullptr;
3513
0
        int ret = sqlite3_exec(hDB, pszSQL, nullptr, nullptr, &pszErrMsg);
3514
0
        sqlite3_free(pszSQL);
3515
0
        if (ret != SQLITE_OK)
3516
0
        {
3517
0
            CPLError(CE_Failure, CPLE_AppDefined, "Failure: %s",
3518
0
                     pszErrMsg ? pszErrMsg : "");
3519
0
            sqlite3_free(pszErrMsg);
3520
0
            return CE_Failure;
3521
0
        }
3522
3523
0
        int nRows = 0;
3524
0
        int nCols = 0;
3525
0
        char **papszResult = nullptr;
3526
0
        sqlite3_get_table(
3527
0
            hDB, "SELECT * FROM metadata WHERE name = 'minzoom' LIMIT 2",
3528
0
            &papszResult, &nRows, &nCols, nullptr);
3529
0
        sqlite3_free_table(papszResult);
3530
0
        if (nRows == 1)
3531
0
        {
3532
0
            pszSQL = sqlite3_mprintf(
3533
0
                "UPDATE metadata SET value = %d WHERE name = 'minzoom'",
3534
0
                m_nZoomLevel);
3535
0
            sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr);
3536
0
            sqlite3_free(pszSQL);
3537
0
        }
3538
3539
0
        return CE_None;
3540
0
    }
3541
3542
0
    if (nBandsIn != nBands)
3543
0
    {
3544
0
        CPLError(CE_Failure, CPLE_NotSupported,
3545
0
                 "Generation of overviews only"
3546
0
                 "supported when operating on all bands.");
3547
0
        return CE_Failure;
3548
0
    }
3549
3550
0
    if (m_nOverviewCount == 0)
3551
0
    {
3552
0
        CPLError(CE_Failure, CPLE_AppDefined,
3553
0
                 "Image too small to support overviews");
3554
0
        return CE_Failure;
3555
0
    }
3556
3557
0
    FlushCache(false);
3558
3559
0
    const auto GetOverviewIndex = [](int nVal)
3560
0
    {
3561
0
        int iOvr = -1;
3562
0
        while (nVal > 1)
3563
0
        {
3564
0
            nVal >>= 1;
3565
0
            iOvr++;
3566
0
        }
3567
0
        return iOvr;
3568
0
    };
3569
3570
0
    for (int i = 0; i < nOverviews; i++)
3571
0
    {
3572
0
        if (panOverviewList[i] < 2)
3573
0
        {
3574
0
            CPLError(CE_Failure, CPLE_IllegalArg,
3575
0
                     "Overview factor '%d' must be >= 2", panOverviewList[i]);
3576
0
            return CE_Failure;
3577
0
        }
3578
3579
0
        if (GetFloorPowerOfTwo(panOverviewList[i]) != panOverviewList[i])
3580
0
        {
3581
0
            CPLError(CE_Failure, CPLE_IllegalArg,
3582
0
                     "Overview factor '%d' is not a power of 2",
3583
0
                     panOverviewList[i]);
3584
0
            return CE_Failure;
3585
0
        }
3586
0
        const int iOvr = GetOverviewIndex(panOverviewList[i]);
3587
0
        if (iOvr >= m_nOverviewCount)
3588
0
        {
3589
0
            CPLDebug("MBTILES",
3590
0
                     "Requested overview factor %d leads to too small overview "
3591
0
                     "and will be ignored",
3592
0
                     panOverviewList[i]);
3593
0
        }
3594
0
    }
3595
3596
0
    GDALRasterBand ***papapoOverviewBands =
3597
0
        (GDALRasterBand ***)CPLCalloc(sizeof(void *), nBands);
3598
0
    int iCurOverview = 0;
3599
0
    for (int iBand = 0; iBand < nBands; iBand++)
3600
0
    {
3601
0
        papapoOverviewBands[iBand] =
3602
0
            (GDALRasterBand **)CPLCalloc(sizeof(void *), nOverviews);
3603
0
        iCurOverview = 0;
3604
0
        for (int i = 0; i < nOverviews; i++)
3605
0
        {
3606
0
            const int iOvr = GetOverviewIndex(panOverviewList[i]);
3607
0
            if (iOvr < m_nOverviewCount)
3608
0
            {
3609
0
                MBTilesDataset *poODS = m_papoOverviewDS[iOvr];
3610
0
                papapoOverviewBands[iBand][iCurOverview] =
3611
0
                    poODS->GetRasterBand(iBand + 1);
3612
0
                iCurOverview++;
3613
0
            }
3614
0
        }
3615
0
    }
3616
3617
0
    CPLErr eErr = GDALRegenerateOverviewsMultiBand(
3618
0
        nBands, papoBands, iCurOverview, papapoOverviewBands, pszResampling,
3619
0
        pfnProgress, pProgressData, papszOptions);
3620
3621
0
    for (int iBand = 0; iBand < nBands; iBand++)
3622
0
    {
3623
0
        CPLFree(papapoOverviewBands[iBand]);
3624
0
    }
3625
0
    CPLFree(papapoOverviewBands);
3626
3627
0
    if (eErr == CE_None)
3628
0
    {
3629
        // Determine new minzoom value from the existing one and the new
3630
        // requested overview levels
3631
0
        int nMinZoom = m_nZoomLevel;
3632
0
        bool bHasMinZoomMetadata = false;
3633
0
        int nRows = 0;
3634
0
        int nCols = 0;
3635
0
        char **papszResult = nullptr;
3636
0
        sqlite3_get_table(
3637
0
            hDB, "SELECT value FROM metadata WHERE name = 'minzoom' LIMIT 2",
3638
0
            &papszResult, &nRows, &nCols, nullptr);
3639
0
        if (nRows == 1 && nCols == 1 && papszResult[1])
3640
0
        {
3641
0
            bHasMinZoomMetadata = true;
3642
0
            nMinZoom = atoi(papszResult[1]);
3643
0
        }
3644
0
        sqlite3_free_table(papszResult);
3645
0
        if (bHasMinZoomMetadata)
3646
0
        {
3647
0
            for (int i = 0; i < nOverviews; i++)
3648
0
            {
3649
0
                const int iOvr = GetOverviewIndex(panOverviewList[i]);
3650
0
                if (iOvr < m_nOverviewCount)
3651
0
                {
3652
0
                    const MBTilesDataset *poODS = m_papoOverviewDS[iOvr];
3653
0
                    nMinZoom = std::min(nMinZoom, poODS->m_nZoomLevel);
3654
0
                }
3655
0
            }
3656
3657
0
            char *pszSQL = sqlite3_mprintf(
3658
0
                "UPDATE metadata SET value = '%d' WHERE name = 'minzoom'",
3659
0
                nMinZoom);
3660
0
            sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr);
3661
0
            sqlite3_free(pszSQL);
3662
0
        }
3663
0
    }
3664
3665
0
    return eErr;
3666
0
}
3667
3668
/************************************************************************/
3669
/*                       GDALRegister_MBTiles()                         */
3670
/************************************************************************/
3671
3672
void GDALRegister_MBTiles()
3673
3674
24
{
3675
24
    if (!GDAL_CHECK_VERSION("MBTiles driver"))
3676
0
        return;
3677
3678
24
    if (GDALGetDriverByName("MBTiles") != nullptr)
3679
0
        return;
3680
3681
24
    GDALDriver *poDriver = new GDALDriver();
3682
3683
24
    poDriver->SetDescription("MBTiles");
3684
24
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
3685
24
    poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
3686
24
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "MBTiles");
3687
24
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
3688
24
                              "drivers/raster/mbtiles.html");
3689
24
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "mbtiles");
3690
24
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte");
3691
3692
24
#define COMPRESSION_OPTIONS                                                    \
3693
24
    "  <Option name='TILE_FORMAT' scope='raster' type='string-select' "        \
3694
24
    "description='Format to use to create tiles' default='PNG'>"               \
3695
24
    "    <Value>PNG</Value>"                                                   \
3696
24
    "    <Value>PNG8</Value>"                                                  \
3697
24
    "    <Value>JPEG</Value>"                                                  \
3698
24
    "    <Value>WEBP</Value>"                                                  \
3699
24
    "  </Option>"                                                              \
3700
24
    "  <Option name='QUALITY' scope='raster' type='int' min='1' max='100' "    \
3701
24
    "description='Quality for JPEG and WEBP tiles' default='75'/>"             \
3702
24
    "  <Option name='ZLEVEL' scope='raster' type='int' min='1' max='9' "       \
3703
24
    "description='DEFLATE compression level for PNG tiles' default='6'/>"      \
3704
24
    "  <Option name='DITHER' scope='raster' type='boolean' "                   \
3705
24
    "description='Whether to apply Floyd-Steinberg dithering (for "            \
3706
24
    "TILE_FORMAT=PNG8)' default='NO'/>"
3707
3708
24
    poDriver->SetMetadataItem(
3709
24
        GDAL_DMD_OPENOPTIONLIST,
3710
24
        "<OpenOptionList>"
3711
24
        "  <Option name='ZOOM_LEVEL' scope='raster,vector' type='integer' "
3712
24
        "description='Zoom level of full resolution. If not specified, maximum "
3713
24
        "non-empty zoom level'/>"
3714
24
        "  <Option name='BAND_COUNT' scope='raster' type='string-select' "
3715
24
        "description='Number of raster bands' default='AUTO'>"
3716
24
        "    <Value>AUTO</Value>"
3717
24
        "    <Value>1</Value>"
3718
24
        "    <Value>2</Value>"
3719
24
        "    <Value>3</Value>"
3720
24
        "    <Value>4</Value>"
3721
24
        "  </Option>"
3722
24
        "  <Option name='MINX' scope='raster,vector' type='float' "
3723
24
        "description='Minimum X of area of interest'/>"
3724
24
        "  <Option name='MINY' scope='raster,vector' type='float' "
3725
24
        "description='Minimum Y of area of interest'/>"
3726
24
        "  <Option name='MAXX' scope='raster,vector' type='float' "
3727
24
        "description='Maximum X of area of interest'/>"
3728
24
        "  <Option name='MAXY' scope='raster,vector' type='float' "
3729
24
        "description='Maximum Y of area of interest'/>"
3730
24
        "  <Option name='USE_BOUNDS' scope='raster,vector' type='boolean' "
3731
24
        "description='Whether to use the bounds metadata, when available, to "
3732
24
        "determine the AOI' default='YES'/>" COMPRESSION_OPTIONS
3733
24
        "  <Option name='CLIP' scope='vector' type='boolean' "
3734
24
        "description='Whether to clip geometries to tile extent' "
3735
24
        "default='YES'/>"
3736
24
        "  <Option name='ZOOM_LEVEL_AUTO' scope='vector' type='boolean' "
3737
24
        "description='Whether to auto-select the zoom level for vector layers "
3738
24
        "according to spatial filter extent. Only for display purpose' "
3739
24
        "default='NO'/>"
3740
24
        "  <Option name='JSON_FIELD' scope='vector' type='boolean' "
3741
24
        "description='For vector layers, "
3742
24
        "whether to put all attributes as a serialized JSon dictionary'/>"
3743
24
        "</OpenOptionList>");
3744
3745
24
    poDriver->SetMetadataItem(
3746
24
        GDAL_DMD_CREATIONOPTIONLIST,
3747
24
        "<CreationOptionList>"
3748
24
        "  <Option name='NAME' scope='raster,vector' type='string' "
3749
24
        "description='Tileset name'/>"
3750
24
        "  <Option name='DESCRIPTION' scope='raster,vector' type='string' "
3751
24
        "description='A description of the layer'/>"
3752
24
        "  <Option name='TYPE' scope='raster,vector' type='string-select' "
3753
24
        "description='Layer type' default='overlay'>"
3754
24
        "    <Value>overlay</Value>"
3755
24
        "    <Value>baselayer</Value>"
3756
24
        "  </Option>"
3757
24
        "  <Option name='VERSION' scope='raster' type='string' "
3758
24
        "description='The version of the tileset, as a plain number' "
3759
24
        "default='1.1'/>"
3760
24
        "  <Option name='BLOCKSIZE' scope='raster' type='int' "
3761
24
        "description='Block size in pixels' default='256' min='64' "
3762
24
        "max='8192'/>" COMPRESSION_OPTIONS
3763
24
        "  <Option name='ZOOM_LEVEL_STRATEGY' scope='raster' "
3764
24
        "type='string-select' description='Strategy to determine zoom level.' "
3765
24
        "default='AUTO'>"
3766
24
        "    <Value>AUTO</Value>"
3767
24
        "    <Value>LOWER</Value>"
3768
24
        "    <Value>UPPER</Value>"
3769
24
        "  </Option>"
3770
24
        "  <Option name='RESAMPLING' scope='raster' type='string-select' "
3771
24
        "description='Resampling algorithm.' default='BILINEAR'>"
3772
24
        "    <Value>NEAREST</Value>"
3773
24
        "    <Value>BILINEAR</Value>"
3774
24
        "    <Value>CUBIC</Value>"
3775
24
        "    <Value>CUBICSPLINE</Value>"
3776
24
        "    <Value>LANCZOS</Value>"
3777
24
        "    <Value>MODE</Value>"
3778
24
        "    <Value>AVERAGE</Value>"
3779
24
        "  </Option>"
3780
24
        "  <Option name='WRITE_BOUNDS' scope='raster' type='boolean' "
3781
24
        "description='Whether to write the bounds metadata' default='YES'/>"
3782
24
        "  <Option name='WRITE_MINMAXZOOM' scope='raster' type='boolean' "
3783
24
        "description='Whether to write the minzoom and maxzoom metadata' "
3784
24
        "default='YES'/>"
3785
24
        "  <Option name='BOUNDS' scope='raster,vector' type='string' "
3786
24
        "description='Override default value for bounds metadata item'/>"
3787
24
        "  <Option name='CENTER' scope='raster,vector' type='string' "
3788
24
        "description='Override default value for center metadata item'/>"
3789
#ifdef HAVE_MVT_WRITE_SUPPORT
3790
        MVT_MBTILES_COMMON_DSCO
3791
#endif
3792
24
        "</CreationOptionList>");
3793
24
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
3794
3795
#ifdef HAVE_MVT_WRITE_SUPPORT
3796
    poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
3797
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES,
3798
                              "Integer Integer64 Real String");
3799
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES,
3800
                              "Boolean Float32");
3801
3802
    poDriver->SetMetadataItem(GDAL_DS_LAYER_CREATIONOPTIONLIST, MVT_LCO);
3803
#endif
3804
3805
24
#ifdef ENABLE_SQL_SQLITE_FORMAT
3806
24
    poDriver->SetMetadataItem("ENABLE_SQL_SQLITE_FORMAT", "YES");
3807
24
#endif
3808
24
    poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "SQLITE OGRSQL");
3809
3810
24
    poDriver->pfnOpen = MBTilesDataset::Open;
3811
24
    poDriver->pfnIdentify = MBTilesDataset::Identify;
3812
24
    poDriver->pfnCreateCopy = MBTilesDataset::CreateCopy;
3813
24
    poDriver->pfnCreate = MBTilesDataset::Create;
3814
3815
24
    GetGDALDriverManager()->RegisterDriver(poDriver);
3816
24
}