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