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