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