Coverage Report

Created: 2025-06-09 07:02

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