Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implementation of PMTiles
5 : * Author: Even Rouault <even.rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2023, Planet Labs
9 : *
10 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : #include "ogr_pmtiles.h"
30 :
31 : #include "mvtutils.h"
32 :
33 : #include <algorithm>
34 : #include <time.h>
35 :
36 : /************************************************************************/
37 : /* OGRPMTilesVectorLayer() */
38 : /************************************************************************/
39 :
40 41 : OGRPMTilesVectorLayer::OGRPMTilesVectorLayer(
41 : OGRPMTilesDataset *poDS, const char *pszLayerName,
42 : const CPLJSONObject &oFields, const CPLJSONArray &oAttributesFromTileStats,
43 : bool bJsonField, double dfMinX, double dfMinY, double dfMaxX, double dfMaxY,
44 : OGRwkbGeometryType eGeomType, int nZoomLevel,
45 41 : bool bZoomLevelFromSpatialFilter)
46 41 : : m_poDS(poDS), m_poFeatureDefn(new OGRFeatureDefn(pszLayerName)),
47 82 : m_bJsonField(bJsonField)
48 : {
49 41 : SetDescription(pszLayerName);
50 41 : m_poFeatureDefn->SetGeomType(eGeomType);
51 41 : OGRSpatialReference *poSRS = new OGRSpatialReference();
52 41 : poSRS->importFromEPSG(3857);
53 41 : m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
54 41 : poSRS->Release();
55 41 : m_poFeatureDefn->Reference();
56 :
57 41 : if (m_bJsonField)
58 : {
59 2 : OGRFieldDefn oFieldDefnId("mvt_id", OFTInteger64);
60 1 : m_poFeatureDefn->AddFieldDefn(&oFieldDefnId);
61 : }
62 : else
63 : {
64 40 : OGRMVTInitFields(m_poFeatureDefn, oFields, oAttributesFromTileStats);
65 : }
66 :
67 41 : m_sExtent.MinX = dfMinX;
68 41 : m_sExtent.MinY = dfMinY;
69 41 : m_sExtent.MaxX = dfMaxX;
70 41 : m_sExtent.MaxY = dfMaxY;
71 :
72 41 : m_nZoomLevel = nZoomLevel;
73 41 : m_bZoomLevelAuto = bZoomLevelFromSpatialFilter;
74 41 : OGRPMTilesVectorLayer::SetSpatialFilter(nullptr);
75 :
76 : // If the metadata contains an empty fields object, this may be a sign
77 : // that it doesn't know the schema. In that case check if a tile has
78 : // attributes, and in that case create a json field.
79 41 : if (!m_bJsonField && oFields.IsValid() && oFields.GetChildren().empty())
80 : {
81 1 : m_bJsonField = true;
82 2 : auto poSrcFeature = GetNextSrcFeature();
83 1 : m_bJsonField = false;
84 :
85 1 : if (poSrcFeature)
86 : {
87 : // There is at least the mvt_id field
88 1 : if (poSrcFeature->GetFieldCount() > 1)
89 : {
90 0 : m_bJsonField = true;
91 : }
92 : }
93 1 : OGRPMTilesVectorLayer::ResetReading();
94 : }
95 :
96 41 : if (m_bJsonField)
97 : {
98 2 : OGRFieldDefn oFieldDefn("json", OFTString);
99 1 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
100 : }
101 41 : }
102 :
103 : /************************************************************************/
104 : /* ~OGRPMTilesVectorLayer() */
105 : /************************************************************************/
106 :
107 82 : OGRPMTilesVectorLayer::~OGRPMTilesVectorLayer()
108 : {
109 41 : m_poFeatureDefn->Release();
110 82 : }
111 :
112 : /************************************************************************/
113 : /* ResetReading() */
114 : /************************************************************************/
115 :
116 393 : void OGRPMTilesVectorLayer::ResetReading()
117 : {
118 393 : m_poTileDS.reset();
119 393 : m_poTileLayer = nullptr;
120 393 : m_poTileIterator.reset();
121 393 : }
122 :
123 : /************************************************************************/
124 : /* GuessGeometryType() */
125 : /************************************************************************/
126 :
127 : /* static */
128 2 : OGRwkbGeometryType OGRPMTilesVectorLayer::GuessGeometryType(
129 : OGRPMTilesDataset *poDS, const char *pszLayerName, int nZoomLevel)
130 : {
131 4 : OGRPMTilesTileIterator oIterator(poDS, nZoomLevel);
132 :
133 2 : const char *const apszAllowedDrivers[] = {"MVT", nullptr};
134 4 : CPLStringList aosOpenOptions;
135 : aosOpenOptions.SetNameValue("METADATA_FILE",
136 2 : poDS->GetMetadataFilename().c_str());
137 4 : std::string osTileData;
138 2 : bool bFirst = true;
139 2 : OGRwkbGeometryType eGeomType = wkbUnknown;
140 : time_t nStart;
141 2 : time(&nStart);
142 : while (true)
143 : {
144 5 : uint32_t nRunLength = 0;
145 5 : const auto sTile = oIterator.GetNextTile(&nRunLength);
146 5 : if (sTile.offset == 0)
147 : {
148 2 : break;
149 : }
150 :
151 3 : const auto *posStr = poDS->ReadTileData(sTile.offset, sTile.length);
152 3 : if (!posStr)
153 : {
154 0 : continue;
155 : }
156 3 : osTileData = *posStr;
157 :
158 : std::string osTmpFilename =
159 3 : CPLSPrintf("/vsimem/mvt_%p_%u_%u.pbf", poDS, sTile.x, sTile.y);
160 3 : VSIFCloseL(VSIFileFromMemBuffer(
161 3 : osTmpFilename.c_str(), reinterpret_cast<GByte *>(&osTileData[0]),
162 3 : osTileData.size(), false));
163 :
164 : auto poTileDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
165 3 : ("MVT:" + osTmpFilename).c_str(), GDAL_OF_VECTOR | GDAL_OF_INTERNAL,
166 6 : apszAllowedDrivers, aosOpenOptions.List(), nullptr));
167 3 : if (poTileDS)
168 : {
169 3 : auto poTileLayer = poTileDS->GetLayerByName(pszLayerName);
170 3 : if (poTileLayer)
171 : {
172 3 : if (bFirst)
173 : {
174 2 : eGeomType = poTileLayer->GetGeomType();
175 2 : if (eGeomType != wkbUnknown)
176 2 : bFirst = false;
177 : }
178 1 : else if (eGeomType != poTileLayer->GetGeomType())
179 : {
180 0 : VSIUnlink(osTmpFilename.c_str());
181 0 : return wkbUnknown;
182 : }
183 3 : if (nRunLength > 1)
184 1 : oIterator.SkipRunLength();
185 : }
186 : }
187 3 : VSIUnlink(osTmpFilename.c_str());
188 :
189 : // Browse through tiles no longer than 1 sec
190 : time_t nNow;
191 3 : time(&nNow);
192 3 : if (nNow - nStart > 1)
193 0 : break;
194 3 : }
195 :
196 2 : return eGeomType;
197 : }
198 :
199 : /************************************************************************/
200 : /* GetTotalFeatureCount() */
201 : /************************************************************************/
202 :
203 8 : GIntBig OGRPMTilesVectorLayer::GetTotalFeatureCount() const
204 : {
205 16 : OGRPMTilesTileIterator oIterator(m_poDS, m_nZoomLevel);
206 :
207 8 : GIntBig nFeatureCount = 0;
208 8 : const char *const apszAllowedDrivers[] = {"MVT", nullptr};
209 16 : CPLStringList aosOpenOptions;
210 : aosOpenOptions.SetNameValue("METADATA_FILE",
211 8 : m_poDS->GetMetadataFilename().c_str());
212 8 : std::string osTileData;
213 : while (true)
214 : {
215 23 : uint32_t nRunLength = 0;
216 23 : const auto sTile = oIterator.GetNextTile(&nRunLength);
217 23 : if (sTile.offset == 0)
218 : {
219 8 : break;
220 : }
221 :
222 15 : const auto *posStr = m_poDS->ReadTileData(sTile.offset, sTile.length);
223 15 : if (!posStr)
224 : {
225 0 : continue;
226 : }
227 15 : osTileData = *posStr;
228 :
229 : std::string osTmpFilename = CPLSPrintf(
230 30 : "/vsimem/mvt_%p_%u_%u_getfeaturecount.pbf", this, sTile.x, sTile.y);
231 15 : VSIFCloseL(VSIFileFromMemBuffer(
232 15 : osTmpFilename.c_str(), reinterpret_cast<GByte *>(&osTileData[0]),
233 15 : osTileData.size(), false));
234 :
235 : auto poTileDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
236 15 : ("MVT:" + osTmpFilename).c_str(), GDAL_OF_VECTOR | GDAL_OF_INTERNAL,
237 45 : apszAllowedDrivers, aosOpenOptions.List(), nullptr));
238 15 : if (poTileDS)
239 : {
240 15 : auto poTileLayer = poTileDS->GetLayerByName(GetDescription());
241 15 : if (poTileLayer)
242 : {
243 : const GIntBig nTileFeatureCount =
244 15 : poTileLayer->GetFeatureCount();
245 15 : nFeatureCount += nRunLength * nTileFeatureCount;
246 15 : if (nRunLength > 1)
247 1 : oIterator.SkipRunLength();
248 : }
249 : }
250 15 : VSIUnlink(osTmpFilename.c_str());
251 15 : }
252 :
253 16 : return nFeatureCount;
254 : }
255 :
256 : /************************************************************************/
257 : /* GetFeatureCount() */
258 : /************************************************************************/
259 :
260 70 : GIntBig OGRPMTilesVectorLayer::GetFeatureCount(int bForce)
261 : {
262 70 : if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
263 : {
264 32 : if (m_nFeatureCount < 0)
265 : {
266 8 : m_nFeatureCount = GetTotalFeatureCount();
267 : }
268 32 : return m_nFeatureCount;
269 : }
270 38 : return OGRLayer::GetFeatureCount(bForce);
271 : }
272 :
273 : /************************************************************************/
274 : /* GetFeature() */
275 : /************************************************************************/
276 :
277 35 : OGRFeature *OGRPMTilesVectorLayer::GetFeature(GIntBig nFID)
278 : {
279 35 : if (nFID < 0)
280 4 : return nullptr;
281 31 : const int nZ = m_nZoomLevel;
282 31 : const int nX = static_cast<int>(nFID & ((1 << nZ) - 1));
283 31 : const int nY = static_cast<int>((nFID >> nZ) & ((1 << nZ) - 1));
284 31 : const GIntBig nTileFID = nFID >> (2 * nZ);
285 :
286 62 : OGRPMTilesTileIterator oIterator(m_poDS, m_nZoomLevel, nX, nY, nX, nY);
287 31 : const auto sTile = oIterator.GetNextTile();
288 31 : if (sTile.offset == 0)
289 : {
290 8 : return nullptr;
291 : }
292 23 : CPLAssert(sTile.z == m_nZoomLevel);
293 23 : CPLAssert(sTile.x == static_cast<uint32_t>(nX));
294 23 : CPLAssert(sTile.y == static_cast<uint32_t>(nY));
295 :
296 23 : const auto *posStr = m_poDS->ReadTileData(sTile.offset, sTile.length);
297 23 : if (!posStr)
298 : {
299 0 : return nullptr;
300 : }
301 46 : std::string osTileData = *posStr;
302 :
303 : std::string osTmpFilename = CPLSPrintf(
304 46 : "/vsimem/mvt_%p_%u_%u_getfeature.pbf", this, sTile.x, sTile.y);
305 23 : VSIFCloseL(VSIFileFromMemBuffer(osTmpFilename.c_str(),
306 23 : reinterpret_cast<GByte *>(&osTileData[0]),
307 23 : osTileData.size(), false));
308 :
309 23 : const char *const apszAllowedDrivers[] = {"MVT", nullptr};
310 46 : CPLStringList aosOpenOptions;
311 23 : aosOpenOptions.SetNameValue("X", CPLSPrintf("%u", sTile.x));
312 23 : aosOpenOptions.SetNameValue("Y", CPLSPrintf("%u", sTile.y));
313 23 : aosOpenOptions.SetNameValue("Z", CPLSPrintf("%d", m_nZoomLevel));
314 : aosOpenOptions.SetNameValue(
315 : "METADATA_FILE",
316 23 : m_bJsonField ? "" : m_poDS->GetMetadataFilename().c_str());
317 23 : if (!m_poDS->GetClipOpenOption().empty())
318 : {
319 : aosOpenOptions.SetNameValue("CLIP",
320 0 : m_poDS->GetClipOpenOption().c_str());
321 : }
322 : auto poTileDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
323 23 : ("MVT:" + osTmpFilename).c_str(), GDAL_OF_VECTOR | GDAL_OF_INTERNAL,
324 69 : apszAllowedDrivers, aosOpenOptions.List(), nullptr));
325 23 : std::unique_ptr<OGRFeature> poFeature;
326 23 : if (poTileDS)
327 : {
328 23 : auto poTileLayer = poTileDS->GetLayerByName(GetDescription());
329 23 : if (poTileLayer)
330 : {
331 : auto poUnderlyingFeature =
332 46 : std::unique_ptr<OGRFeature>(poTileLayer->GetFeature(nTileFID));
333 23 : if (poUnderlyingFeature)
334 : {
335 22 : poFeature = CreateFeatureFrom(poUnderlyingFeature.get());
336 22 : poFeature->SetFID(nFID);
337 : }
338 : }
339 : }
340 23 : VSIUnlink(osTmpFilename.c_str());
341 :
342 23 : return poFeature.release();
343 : }
344 :
345 : /************************************************************************/
346 : /* GetNextSrcFeature() */
347 : /************************************************************************/
348 :
349 1849 : std::unique_ptr<OGRFeature> OGRPMTilesVectorLayer::GetNextSrcFeature()
350 : {
351 1849 : if (!m_poTileIterator)
352 : {
353 : int nMinTileX;
354 : int nMinTileY;
355 : int nMaxTileX;
356 : int nMaxTileY;
357 177 : ExtentToTileExtent(m_sExtent, nMinTileX, nMinTileY, nMaxTileX,
358 : nMaxTileY);
359 :
360 : // Optimization: if the spatial filter is totally out of the extent,
361 : // exit early
362 177 : if (m_nFilterMaxX < nMinTileX || m_nFilterMaxY < nMinTileY ||
363 177 : m_nFilterMinX > nMaxTileX || m_nFilterMinY > nMaxTileY)
364 : {
365 4 : return nullptr;
366 : }
367 :
368 173 : m_poTileIterator = std::make_unique<OGRPMTilesTileIterator>(
369 173 : m_poDS, m_nZoomLevel, m_nFilterMinX, m_nFilterMinY, m_nFilterMaxX,
370 173 : m_nFilterMaxY);
371 : }
372 :
373 1845 : OGRFeature *poTileFeat = nullptr;
374 3517 : if (!m_poTileLayer ||
375 1672 : (poTileFeat = m_poTileLayer->GetNextFeature()) == nullptr)
376 : {
377 758 : const char *const apszAllowedDrivers[] = {"MVT", nullptr};
378 :
379 : while (true)
380 : {
381 758 : const auto sTile = m_poTileIterator->GetNextTile();
382 758 : if (sTile.offset == 0)
383 : {
384 116 : return nullptr;
385 : }
386 :
387 642 : m_nX = sTile.x;
388 642 : m_nY = sTile.y;
389 :
390 642 : if (sTile.offset == m_nLastTileOffset)
391 : {
392 : // In case of run-length encoded tiles, we do not need to
393 : // re-read it from disk
394 : }
395 : else
396 : {
397 202 : m_nLastTileOffset = sTile.offset;
398 202 : CPLDebugOnly("PMTiles", "Opening tile X=%u, Y=%u, Z=%d",
399 : sTile.x, sTile.y, m_nZoomLevel);
400 :
401 : const auto *posStr =
402 202 : m_poDS->ReadTileData(sTile.offset, sTile.length);
403 202 : if (!posStr)
404 : {
405 0 : return nullptr;
406 : }
407 202 : m_osTileData = *posStr;
408 : }
409 :
410 642 : m_poTileDS.reset();
411 : const std::string osTmpFilename =
412 642 : CPLSPrintf("/vsimem/mvt_%p_%u_%u.pbf", this, sTile.x, sTile.y);
413 642 : VSIFCloseL(VSIFileFromMemBuffer(
414 : osTmpFilename.c_str(),
415 642 : reinterpret_cast<GByte *>(&m_osTileData[0]),
416 642 : m_osTileData.size(), false));
417 :
418 642 : CPLStringList aosOpenOptions;
419 642 : aosOpenOptions.SetNameValue("X", CPLSPrintf("%u", sTile.x));
420 642 : aosOpenOptions.SetNameValue("Y", CPLSPrintf("%u", sTile.y));
421 642 : aosOpenOptions.SetNameValue("Z", CPLSPrintf("%d", m_nZoomLevel));
422 : aosOpenOptions.SetNameValue(
423 : "METADATA_FILE",
424 642 : m_bJsonField ? "" : m_poDS->GetMetadataFilename().c_str());
425 642 : if (!m_poDS->GetClipOpenOption().empty())
426 : {
427 : aosOpenOptions.SetNameValue(
428 2 : "CLIP", m_poDS->GetClipOpenOption().c_str());
429 : }
430 642 : m_poTileDS.reset(GDALDataset::Open(
431 1284 : ("MVT:" + osTmpFilename).c_str(),
432 : GDAL_OF_VECTOR | GDAL_OF_INTERNAL, apszAllowedDrivers,
433 642 : aosOpenOptions.List(), nullptr));
434 642 : if (m_poTileDS)
435 : {
436 642 : m_poTileDS->SetDescription(osTmpFilename.c_str());
437 642 : m_poTileDS->MarkSuppressOnClose();
438 642 : m_poTileLayer = m_poTileDS->GetLayerByName(GetDescription());
439 642 : if (m_poTileLayer)
440 : {
441 642 : poTileFeat = m_poTileLayer->GetNextFeature();
442 642 : if (poTileFeat)
443 : {
444 642 : break;
445 : }
446 : }
447 0 : m_poTileDS.reset();
448 0 : m_poTileLayer = nullptr;
449 : }
450 : else
451 : {
452 0 : VSIUnlink(osTmpFilename.c_str());
453 : }
454 0 : }
455 : }
456 :
457 1729 : return std::unique_ptr<OGRFeature>(poTileFeat);
458 : }
459 :
460 : /************************************************************************/
461 : /* CreateFeatureFrom() */
462 : /************************************************************************/
463 :
464 : std::unique_ptr<OGRFeature>
465 1750 : OGRPMTilesVectorLayer::CreateFeatureFrom(OGRFeature *poSrcFeature)
466 : {
467 : return std::unique_ptr<OGRFeature>(OGRMVTCreateFeatureFrom(
468 1750 : poSrcFeature, m_poFeatureDefn, m_bJsonField, GetSpatialRef()));
469 : }
470 :
471 : /************************************************************************/
472 : /* GetNextRawFeature() */
473 : /************************************************************************/
474 :
475 1848 : OGRFeature *OGRPMTilesVectorLayer::GetNextRawFeature()
476 : {
477 3696 : auto poSrcFeat = GetNextSrcFeature();
478 1848 : if (poSrcFeat == nullptr)
479 120 : return nullptr;
480 :
481 1728 : const GIntBig nFIDBase =
482 1728 : (static_cast<GIntBig>(m_nY) << m_nZoomLevel) | m_nX;
483 3456 : auto poFeature = CreateFeatureFrom(poSrcFeat.get());
484 1728 : poFeature->SetFID((poSrcFeat->GetFID() << (2 * m_nZoomLevel)) | nFIDBase);
485 :
486 1728 : return poFeature.release();
487 : }
488 :
489 : /************************************************************************/
490 : /* TestCapability() */
491 : /************************************************************************/
492 :
493 140 : int OGRPMTilesVectorLayer::TestCapability(const char *pszCap)
494 : {
495 140 : if (EQUAL(pszCap, OLCStringsAsUTF8) ||
496 92 : EQUAL(pszCap, OLCFastSpatialFilter) || EQUAL(pszCap, OLCFastGetExtent))
497 : {
498 56 : return TRUE;
499 : }
500 :
501 84 : if (EQUAL(pszCap, OLCFastFeatureCount))
502 0 : return m_nFeatureCount >= 0 && !m_poFilterGeom && !m_poAttrQuery;
503 :
504 84 : return FALSE;
505 : }
506 :
507 : /************************************************************************/
508 : /* GetExtent() */
509 : /************************************************************************/
510 :
511 23 : OGRErr OGRPMTilesVectorLayer::GetExtent(OGREnvelope *psExtent, int)
512 : {
513 23 : *psExtent = m_sExtent;
514 23 : return OGRERR_NONE;
515 : }
516 :
517 : /************************************************************************/
518 : /* ExtentToTileExtent() */
519 : /************************************************************************/
520 :
521 211 : void OGRPMTilesVectorLayer::ExtentToTileExtent(const OGREnvelope &sEnvelope,
522 : int &nTileMinX, int &nTileMinY,
523 : int &nTileMaxX,
524 : int &nTileMaxY) const
525 : {
526 211 : const double dfTileDim = 2 * MAX_GM / (1 << m_nZoomLevel);
527 211 : constexpr double EPS = 1e-5;
528 422 : nTileMinX = std::max(0, static_cast<int>(floor(
529 211 : (sEnvelope.MinX + MAX_GM) / dfTileDim + EPS)));
530 : // PMTiles and MVT uses a Y=MAX_GM as the y=0 tile
531 422 : nTileMinY = std::max(0, static_cast<int>(floor(
532 211 : (MAX_GM - sEnvelope.MaxY) / dfTileDim + EPS)));
533 211 : nTileMaxX = std::min(
534 422 : static_cast<int>(floor((sEnvelope.MaxX + MAX_GM) / dfTileDim + EPS)),
535 211 : (1 << m_nZoomLevel) - 1);
536 211 : nTileMaxY = std::min(
537 422 : static_cast<int>(floor((MAX_GM - sEnvelope.MinY) / dfTileDim + EPS)),
538 211 : (1 << m_nZoomLevel) - 1);
539 211 : }
540 :
541 : /************************************************************************/
542 : /* SetSpatialFilter() */
543 : /************************************************************************/
544 :
545 151 : void OGRPMTilesVectorLayer::SetSpatialFilter(OGRGeometry *poGeomIn)
546 : {
547 151 : OGRLayer::SetSpatialFilter(poGeomIn);
548 :
549 151 : if (m_poFilterGeom != nullptr && m_sFilterEnvelope.MinX <= -MAX_GM &&
550 12 : m_sFilterEnvelope.MinY <= -MAX_GM && m_sFilterEnvelope.MaxX >= MAX_GM &&
551 8 : m_sFilterEnvelope.MaxY >= MAX_GM)
552 : {
553 8 : if (m_bZoomLevelAuto)
554 : {
555 0 : m_nZoomLevel = m_poDS->GetMinZoomLevel();
556 : }
557 8 : m_nFilterMinX = 0;
558 8 : m_nFilterMinY = 0;
559 8 : m_nFilterMaxX = (1 << m_nZoomLevel) - 1;
560 8 : m_nFilterMaxY = (1 << m_nZoomLevel) - 1;
561 : }
562 143 : else if (m_poFilterGeom != nullptr &&
563 34 : m_sFilterEnvelope.MinX >= -10 * MAX_GM &&
564 34 : m_sFilterEnvelope.MinY >= -10 * MAX_GM &&
565 34 : m_sFilterEnvelope.MaxX <= 10 * MAX_GM &&
566 34 : m_sFilterEnvelope.MaxY <= 10 * MAX_GM)
567 : {
568 34 : if (m_bZoomLevelAuto)
569 : {
570 : double dfExtent =
571 4 : std::min(m_sFilterEnvelope.MaxX - m_sFilterEnvelope.MinX,
572 2 : m_sFilterEnvelope.MaxY - m_sFilterEnvelope.MinY);
573 2 : m_nZoomLevel = std::max(
574 2 : m_poDS->GetMinZoomLevel(),
575 4 : std::min(static_cast<int>(0.5 + log(2 * MAX_GM / dfExtent) /
576 : log(2.0)),
577 4 : m_poDS->GetMaxZoomLevel()));
578 2 : CPLDebug("PMTiles", "Zoom level = %d", m_nZoomLevel);
579 : }
580 34 : ExtentToTileExtent(m_sFilterEnvelope, m_nFilterMinX, m_nFilterMinY,
581 34 : m_nFilterMaxX, m_nFilterMaxY);
582 : }
583 : else
584 : {
585 109 : if (m_bZoomLevelAuto)
586 : {
587 2 : m_nZoomLevel = m_poDS->GetMaxZoomLevel();
588 : }
589 109 : m_nFilterMinX = 0;
590 109 : m_nFilterMinY = 0;
591 109 : m_nFilterMaxX = (1 << m_nZoomLevel) - 1;
592 109 : m_nFilterMaxY = (1 << m_nZoomLevel) - 1;
593 : }
594 151 : }
|