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