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