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