Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: MVT Translator
4 : * Purpose: Mapbox Vector Tile decoder
5 : * Author: Even Rouault, Even Rouault <even dot rouault at spatialys dot com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2018, Even Rouault <even dot rouault at spatialys dot 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 "ogrsf_frmts.h"
19 : #include "cpl_conv.h"
20 : #include "cpl_json.h"
21 : #include "cpl_http.h"
22 : #include "ogr_p.h"
23 :
24 : #include "mvt_tile.h"
25 : #include "mvtutils.h"
26 :
27 : #include "ogr_geos.h"
28 :
29 : #include "gpb.h"
30 :
31 : #include <algorithm>
32 : #include <memory>
33 : #include <vector>
34 : #include <set>
35 :
36 : const char *SRS_EPSG_3857 =
37 : "PROJCS[\"WGS 84 / Pseudo-Mercator\",GEOGCS[\"WGS "
38 : "84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS "
39 : "84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY["
40 : "\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
41 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
42 : "AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"Mercator_1SP\"],PARAMETER["
43 : "\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_"
44 : "easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY["
45 : "\"EPSG\",\"9001\"]],AXIS[\"X\",EAST],AXIS[\"Y\",NORTH],EXTENSION["
46 : "\"PROJ4\",\"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 "
47 : "+x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext "
48 : "+no_defs\"],AUTHORITY[\"EPSG\",\"3857\"]]";
49 :
50 : // WebMercator related constants
51 : constexpr double kmSPHERICAL_RADIUS = 6378137.0;
52 :
53 : constexpr int knMAX_FILES_PER_DIR = 10000;
54 :
55 : #ifdef HAVE_MVT_WRITE_SUPPORT
56 :
57 : #include <sqlite3.h>
58 : #include "../sqlite/ogrsqliteutility.h"
59 :
60 : #include "../sqlite/ogrsqlitevfs.h"
61 :
62 : #include "cpl_worker_thread_pool.h"
63 :
64 : #include <mutex>
65 :
66 : // Limitations from https://github.com/mapbox/mapbox-geostats
67 : constexpr size_t knMAX_COUNT_LAYERS = 1000;
68 : constexpr size_t knMAX_REPORT_LAYERS = 100;
69 : constexpr size_t knMAX_COUNT_FIELDS = 1000;
70 : constexpr size_t knMAX_REPORT_FIELDS = 100;
71 : constexpr size_t knMAX_COUNT_VALUES = 1000;
72 : constexpr size_t knMAX_REPORT_VALUES = 100;
73 : constexpr size_t knMAX_STRING_VALUE_LENGTH = 256;
74 : constexpr size_t knMAX_LAYER_NAME_LENGTH = 256;
75 : constexpr size_t knMAX_FIELD_NAME_LENGTH = 256;
76 :
77 : #undef SQLITE_STATIC
78 : #define SQLITE_STATIC ((sqlite3_destructor_type) nullptr)
79 :
80 : #endif
81 :
82 : /************************************************************************/
83 : /* InitWebMercatorTilingScheme() */
84 : /************************************************************************/
85 :
86 1246 : static void InitWebMercatorTilingScheme(OGRSpatialReference *poSRS,
87 : double &dfTopX, double &dfTopY,
88 : double &dfTileDim0)
89 : {
90 1246 : constexpr double kmMAX_GM =
91 : kmSPHERICAL_RADIUS * M_PI; // 20037508.342789244
92 1246 : poSRS->SetFromUserInput(SRS_EPSG_3857);
93 1246 : dfTopX = -kmMAX_GM;
94 1246 : dfTopY = kmMAX_GM;
95 1246 : dfTileDim0 = 2 * kmMAX_GM;
96 1246 : }
97 :
98 : /************************************************************************/
99 : /* GetCmdId() */
100 : /************************************************************************/
101 :
102 : /* For a drawing instruction combining a command id and a command count,
103 : * return the command id */
104 2032 : static unsigned GetCmdId(unsigned int nCmdCountCombined)
105 : {
106 2032 : return nCmdCountCombined & 0x7;
107 : }
108 :
109 : /************************************************************************/
110 : /* GetCmdCount() */
111 : /************************************************************************/
112 :
113 : /* For a drawing instruction combining a command id and a command count,
114 : * return the command count */
115 6073 : static unsigned GetCmdCount(unsigned int nCmdCountCombined)
116 : {
117 6073 : return nCmdCountCombined >> 3;
118 : }
119 :
120 : /************************************************************************/
121 : /* OGRMVTLayerBase */
122 : /************************************************************************/
123 :
124 : class OGRMVTLayerBase CPL_NON_FINAL
125 : : public OGRLayer,
126 : public OGRGetNextFeatureThroughRaw<OGRMVTLayerBase>
127 : {
128 : virtual OGRFeature *GetNextRawFeature() = 0;
129 :
130 : protected:
131 : OGRFeatureDefn *m_poFeatureDefn = nullptr;
132 :
133 : void InitFields(const CPLJSONObject &oFields,
134 : const CPLJSONArray &oAttributesFromTileStats);
135 :
136 : public:
137 : ~OGRMVTLayerBase() override;
138 :
139 5733 : OGRFeatureDefn *GetLayerDefn() const override
140 : {
141 5733 : return m_poFeatureDefn;
142 : }
143 :
144 2960 : DEFINE_GET_NEXT_FEATURE_THROUGH_RAW(OGRMVTLayerBase)
145 :
146 : int TestCapability(const char *) const override;
147 : };
148 :
149 : /************************************************************************/
150 : /* OGRMVTLayer */
151 : /************************************************************************/
152 :
153 : class OGRMVTDataset;
154 :
155 : class OGRMVTLayer final : public OGRMVTLayerBase
156 : {
157 : OGRMVTDataset *m_poDS;
158 : const GByte *m_pabyDataStart;
159 : const GByte *m_pabyDataEnd;
160 : const GByte *m_pabyDataCur = nullptr;
161 : const GByte *m_pabyDataFeatureStart = nullptr;
162 : bool m_bError = false;
163 : unsigned int m_nExtent = knDEFAULT_EXTENT;
164 : std::vector<CPLString> m_aosKeys;
165 :
166 : typedef struct
167 : {
168 : OGRFieldType eType;
169 : OGRFieldSubType eSubType;
170 : OGRField sValue;
171 : } Value;
172 :
173 : std::vector<Value> m_asValues;
174 : GIntBig m_nFID = 0;
175 : GIntBig m_nFeatureCount = -1;
176 : OGRPolygon m_oClipPoly;
177 : double m_dfTileMinX = 0;
178 : double m_dfTileMinY = 0;
179 : double m_dfTileMaxX = 0;
180 : double m_dfTileMaxY = 0;
181 : bool m_bEnforceExternalIsClockwise = false;
182 :
183 : void Init(const CPLJSONObject &oFields,
184 : const CPLJSONArray &oAttributesFromTileStats);
185 : bool QuickScanFeature(const GByte *pabyData,
186 : const GByte *pabyDataFeatureEnd, bool bScanFields,
187 : bool bScanGeometries, bool &bGeomTypeSet);
188 : void GetXY(int nX, int nY, double &dfX, double &dfY);
189 : std::unique_ptr<OGRGeometry>
190 : ParseGeometry(unsigned int nGeomType, const GByte *pabyDataGeometryEnd);
191 : void SanitizeClippedGeometry(std::unique_ptr<OGRGeometry> &poGeom);
192 :
193 : OGRFeature *GetNextRawFeature() override;
194 :
195 : public:
196 : OGRMVTLayer(OGRMVTDataset *poDS, const char *pszLayerName,
197 : const GByte *pabyData, int nLayerSize,
198 : const CPLJSONObject &oFields,
199 : const CPLJSONArray &oAttributesFromTileStats,
200 : OGRwkbGeometryType eGeomType);
201 : ~OGRMVTLayer() override;
202 :
203 : void ResetReading() override;
204 :
205 : GIntBig GetFeatureCount(int bForce) override;
206 :
207 : GDALDataset *GetDataset() override;
208 : };
209 :
210 : /************************************************************************/
211 : /* OGRMVTDirectoryLayer */
212 : /************************************************************************/
213 :
214 : class OGRMVTDirectoryLayer final : public OGRMVTLayerBase
215 : {
216 : OGRMVTDataset *m_poDS;
217 : int m_nZ = 0;
218 : bool m_bUseReadDir = true;
219 : CPLString m_osDirName;
220 : CPLStringList m_aosDirContent;
221 : CPLString m_aosSubDirName;
222 : CPLStringList m_aosSubDirContent;
223 : bool m_bEOF = false;
224 : int m_nXIndex = 0;
225 : int m_nYIndex = 0;
226 : GDALDataset *m_poCurrentTile = nullptr;
227 : bool m_bJsonField = false;
228 : GIntBig m_nFIDBase = 0;
229 : OGREnvelope m_sExtent;
230 : int m_nFilterMinX = 0;
231 : int m_nFilterMinY = 0;
232 : int m_nFilterMaxX = 0;
233 : int m_nFilterMaxY = 0;
234 :
235 : OGRFeature *GetNextRawFeature() override;
236 : OGRFeature *CreateFeatureFrom(OGRFeature *poSrcFeature);
237 : void ReadNewSubDir();
238 : void OpenTile();
239 : void OpenTileIfNeeded();
240 :
241 : public:
242 : OGRMVTDirectoryLayer(OGRMVTDataset *poDS, const char *pszLayerName,
243 : const char *pszDirectoryName,
244 : const CPLJSONObject &oFields,
245 : const CPLJSONArray &oAttributesFromTileStats,
246 : bool bJsonField, OGRwkbGeometryType eGeomType,
247 : const OGREnvelope *psExtent);
248 : ~OGRMVTDirectoryLayer() override;
249 :
250 : void ResetReading() override;
251 :
252 : GIntBig GetFeatureCount(int bForce) override;
253 : OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
254 : bool bForce) override;
255 :
256 : OGRErr ISetSpatialFilter(int iGeomField,
257 : const OGRGeometry *poGeom) override;
258 :
259 : OGRFeature *GetFeature(GIntBig nFID) override;
260 :
261 : int TestCapability(const char *) const override;
262 :
263 : GDALDataset *GetDataset() override;
264 : };
265 :
266 : /************************************************************************/
267 : /* OGRMVTDataset */
268 : /************************************************************************/
269 :
270 : class OGRMVTDataset final : public GDALDataset
271 : {
272 : friend class OGRMVTLayer;
273 : friend class OGRMVTDirectoryLayer;
274 :
275 : GByte *m_pabyData;
276 : std::vector<std::unique_ptr<OGRLayer>> m_apoLayers;
277 : bool m_bGeoreferenced = false;
278 : double m_dfTileDimX = 0.0;
279 : double m_dfTileDimY = 0.0;
280 : double m_dfTopX = 0.0;
281 : double m_dfTopY = 0.0;
282 : CPLString m_osMetadataMemFilename;
283 : bool m_bClip = true;
284 : CPLString m_osTileExtension{"pbf"};
285 : OGRSpatialReference *m_poSRS = nullptr;
286 : double m_dfTileDim0 =
287 : 0.0; // Extent (in CRS units) of a tile at zoom level 0
288 : double m_dfTopXOrigin = 0.0; // top-left X of tile matrix scheme
289 : double m_dfTopYOrigin = 0.0; // top-left Y of tile matrix scheme
290 : int m_nTileMatrixWidth0 =
291 : 1; // Number of tiles along X axis at zoom level 0
292 : int m_nTileMatrixHeight0 =
293 : 1; // Number of tiles along Y axis at zoom level 0
294 :
295 : static GDALDataset *OpenDirectory(GDALOpenInfo *);
296 :
297 : public:
298 : explicit OGRMVTDataset(GByte *pabyData);
299 : ~OGRMVTDataset() override;
300 :
301 3452 : int GetLayerCount() const override
302 : {
303 3452 : return static_cast<int>(m_apoLayers.size());
304 : }
305 :
306 : const OGRLayer *GetLayer(int) const override;
307 :
308 12 : int TestCapability(const char *) const override
309 : {
310 12 : return FALSE;
311 : }
312 :
313 : static GDALDataset *Open(GDALOpenInfo *);
314 : static GDALDataset *Open(GDALOpenInfo *, bool bRecurseAllowed);
315 :
316 1034 : OGRSpatialReference *GetSRS()
317 : {
318 1034 : return m_poSRS;
319 : }
320 :
321 195 : inline double GetTileDim0() const
322 : {
323 195 : return m_dfTileDim0;
324 : }
325 :
326 78 : inline double GetTopXOrigin() const
327 : {
328 78 : return m_dfTopXOrigin;
329 : }
330 :
331 78 : inline double GetTopYOrigin() const
332 : {
333 78 : return m_dfTopYOrigin;
334 : }
335 :
336 88 : inline int GetTileMatrixWidth0() const
337 : {
338 88 : return m_nTileMatrixWidth0;
339 : }
340 :
341 88 : inline int GetTileMatrixHeight0() const
342 : {
343 88 : return m_nTileMatrixHeight0;
344 : }
345 : };
346 :
347 : /************************************************************************/
348 : /* ~OGRMVTLayerBase() */
349 : /************************************************************************/
350 :
351 1078 : OGRMVTLayerBase::~OGRMVTLayerBase()
352 : {
353 1078 : m_poFeatureDefn->Release();
354 1078 : }
355 :
356 : /************************************************************************/
357 : /* InitFields() */
358 : /************************************************************************/
359 :
360 1077 : void OGRMVTLayerBase::InitFields(const CPLJSONObject &oFields,
361 : const CPLJSONArray &oAttributesFromTileStats)
362 : {
363 1077 : OGRMVTInitFields(m_poFeatureDefn, oFields, oAttributesFromTileStats);
364 1077 : }
365 :
366 : /************************************************************************/
367 : /* TestCapability() */
368 : /************************************************************************/
369 :
370 71 : int OGRMVTLayerBase::TestCapability(const char *pszCap) const
371 : {
372 71 : if (EQUAL(pszCap, OLCStringsAsUTF8) || EQUAL(pszCap, OLCFastSpatialFilter))
373 : {
374 25 : return TRUE;
375 : }
376 46 : return FALSE;
377 : }
378 :
379 : /************************************************************************/
380 : /* OGRMVTLayer() */
381 : /************************************************************************/
382 :
383 1053 : OGRMVTLayer::OGRMVTLayer(OGRMVTDataset *poDS, const char *pszLayerName,
384 : const GByte *pabyData, int nLayerSize,
385 : const CPLJSONObject &oFields,
386 : const CPLJSONArray &oAttributesFromTileStats,
387 1053 : OGRwkbGeometryType eGeomType)
388 : : m_poDS(poDS), m_pabyDataStart(pabyData),
389 1053 : m_pabyDataEnd(pabyData + nLayerSize)
390 : {
391 1053 : m_poFeatureDefn = new OGRFeatureDefn(pszLayerName);
392 1053 : SetDescription(m_poFeatureDefn->GetName());
393 1053 : m_poFeatureDefn->SetGeomType(eGeomType);
394 1053 : m_poFeatureDefn->Reference();
395 :
396 1053 : if (m_poDS->m_bGeoreferenced)
397 : {
398 1009 : m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_poDS->GetSRS());
399 : }
400 :
401 1053 : Init(oFields, oAttributesFromTileStats);
402 :
403 1053 : GetXY(0, 0, m_dfTileMinX, m_dfTileMaxY);
404 1053 : GetXY(m_nExtent, m_nExtent, m_dfTileMaxX, m_dfTileMinY);
405 1053 : OGRLinearRing *poLR = new OGRLinearRing();
406 1053 : poLR->addPoint(m_dfTileMinX, m_dfTileMinY);
407 1053 : poLR->addPoint(m_dfTileMinX, m_dfTileMaxY);
408 1053 : poLR->addPoint(m_dfTileMaxX, m_dfTileMaxY);
409 1053 : poLR->addPoint(m_dfTileMaxX, m_dfTileMinY);
410 1053 : poLR->addPoint(m_dfTileMinX, m_dfTileMinY);
411 1053 : m_oClipPoly.addRingDirectly(poLR);
412 :
413 : // Config option only for tests for now. When set, it ensures that
414 : // the first ring (exterior ring) of a polygon is clockwise oriented,
415 : // as per the MVT spec.
416 : // By default, we are more tolerant and only use reversal of winding order
417 : // to detect inner rings.
418 1053 : m_bEnforceExternalIsClockwise = CPLTestBool(
419 : CPLGetConfigOption("OGR_MVT_ENFORE_EXTERNAL_RING_IS_CLOCKWISE", "NO"));
420 1053 : }
421 :
422 : /************************************************************************/
423 : /* ~OGRMVTLayer() */
424 : /************************************************************************/
425 :
426 2106 : OGRMVTLayer::~OGRMVTLayer()
427 : {
428 22588 : for (auto &sValue : m_asValues)
429 : {
430 21535 : if (sValue.eType == OFTString)
431 : {
432 12208 : CPLFree(sValue.sValue.String);
433 : }
434 : }
435 2106 : }
436 :
437 : /************************************************************************/
438 : /* Init() */
439 : /************************************************************************/
440 :
441 1053 : void OGRMVTLayer::Init(const CPLJSONObject &oFields,
442 : const CPLJSONArray &oAttributesFromTileStats)
443 : {
444 : // First pass to collect keys and values
445 1053 : const GByte *pabyData = m_pabyDataStart;
446 1053 : const GByte *pabyDataLimit = m_pabyDataEnd;
447 1053 : unsigned int nKey = 0;
448 1053 : bool bGeomTypeSet = false;
449 1053 : const bool bScanFields = !oFields.IsValid();
450 1053 : const bool bScanGeometries = m_poFeatureDefn->GetGeomType() == wkbUnknown;
451 1053 : const bool bQuickScanFeature = bScanFields || bScanGeometries;
452 :
453 : try
454 : {
455 45311 : while (pabyData < pabyDataLimit)
456 : {
457 44258 : READ_FIELD_KEY(nKey);
458 44258 : if (nKey == MAKE_KEY(knLAYER_KEYS, WT_DATA))
459 : {
460 16920 : char *pszKey = nullptr;
461 16920 : READ_TEXT(pabyData, pabyDataLimit, pszKey);
462 16920 : m_aosKeys.push_back(pszKey);
463 16920 : CPLFree(pszKey);
464 : }
465 27338 : else if (nKey == MAKE_KEY(knLAYER_VALUES, WT_DATA))
466 : {
467 21536 : unsigned int nValueLength = 0;
468 21536 : READ_SIZE(pabyData, pabyDataLimit, nValueLength);
469 21536 : const GByte *pabyDataValueEnd = pabyData + nValueLength;
470 21536 : READ_VARUINT32(pabyData, pabyDataLimit, nKey);
471 21536 : if (nKey == MAKE_KEY(knVALUE_STRING, WT_DATA))
472 : {
473 12208 : char *pszValue = nullptr;
474 12208 : READ_TEXT(pabyData, pabyDataLimit, pszValue);
475 : Value sValue;
476 12208 : sValue.eType = OFTString;
477 12208 : sValue.eSubType = OFSTNone;
478 12208 : sValue.sValue.String = pszValue;
479 12208 : m_asValues.push_back(sValue);
480 : }
481 9328 : else if (nKey == MAKE_KEY(knVALUE_FLOAT, WT_32BIT))
482 : {
483 : Value sValue;
484 604 : sValue.eType = OFTReal;
485 604 : sValue.eSubType = OFSTFloat32;
486 604 : sValue.sValue.Real = ReadFloat32(&pabyData, pabyDataLimit);
487 604 : m_asValues.push_back(sValue);
488 : }
489 8724 : else if (nKey == MAKE_KEY(knVALUE_DOUBLE, WT_64BIT))
490 : {
491 : Value sValue;
492 693 : sValue.eType = OFTReal;
493 693 : sValue.eSubType = OFSTNone;
494 693 : sValue.sValue.Real = ReadFloat64(&pabyData, pabyDataLimit);
495 693 : m_asValues.push_back(sValue);
496 : }
497 8031 : else if (nKey == MAKE_KEY(knVALUE_INT, WT_VARINT))
498 : {
499 261 : GIntBig nVal = 0;
500 261 : READ_VARINT64(pabyData, pabyDataLimit, nVal);
501 : Value sValue;
502 203 : sValue.eType = (nVal >= INT_MIN && nVal <= INT_MAX)
503 464 : ? OFTInteger
504 : : OFTInteger64;
505 261 : sValue.eSubType = OFSTNone;
506 261 : if (sValue.eType == OFTInteger)
507 140 : sValue.sValue.Integer = static_cast<int>(nVal);
508 : else
509 121 : sValue.sValue.Integer64 = nVal;
510 261 : m_asValues.push_back(sValue);
511 : }
512 7770 : else if (nKey == MAKE_KEY(knVALUE_UINT, WT_VARINT))
513 : {
514 7150 : GUIntBig nVal = 0;
515 7150 : READ_VARUINT64(pabyData, pabyDataLimit, nVal);
516 : Value sValue;
517 7150 : sValue.eType =
518 7150 : (nVal <= INT_MAX) ? OFTInteger : OFTInteger64;
519 7150 : sValue.eSubType = OFSTNone;
520 7150 : if (sValue.eType == OFTInteger)
521 7090 : sValue.sValue.Integer = static_cast<int>(nVal);
522 : else
523 60 : sValue.sValue.Integer64 = static_cast<GIntBig>(nVal);
524 7150 : m_asValues.push_back(sValue);
525 : }
526 620 : else if (nKey == MAKE_KEY(knVALUE_SINT, WT_VARINT))
527 : {
528 490 : GIntBig nVal = 0;
529 490 : READ_VARSINT64(pabyData, pabyDataLimit, nVal);
530 : Value sValue;
531 430 : sValue.eType = (nVal >= INT_MIN && nVal <= INT_MAX)
532 920 : ? OFTInteger
533 : : OFTInteger64;
534 490 : sValue.eSubType = OFSTNone;
535 490 : if (sValue.eType == OFTInteger)
536 372 : sValue.sValue.Integer = static_cast<int>(nVal);
537 : else
538 118 : sValue.sValue.Integer64 = nVal;
539 490 : m_asValues.push_back(sValue);
540 : }
541 130 : else if (nKey == MAKE_KEY(knVALUE_BOOL, WT_VARINT))
542 : {
543 129 : unsigned nVal = 0;
544 129 : READ_VARUINT32(pabyData, pabyDataLimit, nVal);
545 : Value sValue;
546 129 : sValue.eType = OFTInteger;
547 129 : sValue.eSubType = OFSTBoolean;
548 129 : sValue.sValue.Integer = static_cast<int>(nVal);
549 129 : m_asValues.push_back(sValue);
550 : }
551 :
552 21536 : pabyData = pabyDataValueEnd;
553 : }
554 5802 : else if (nKey == MAKE_KEY(knLAYER_EXTENT, WT_VARINT))
555 : {
556 1040 : GUInt32 nExtent = 0;
557 1040 : READ_VARUINT32(pabyData, pabyDataLimit, nExtent);
558 1040 : m_nExtent = std::max(1U, nExtent); // to avoid divide by zero
559 : }
560 : else
561 : {
562 4762 : SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, FALSE);
563 : }
564 : }
565 :
566 1053 : InitFields(oFields, oAttributesFromTileStats);
567 :
568 1053 : m_nFeatureCount = 0;
569 1053 : pabyData = m_pabyDataStart;
570 : // Second pass to iterate over features to figure out the geometry type
571 : // and attribute schema
572 45300 : while (pabyData < pabyDataLimit)
573 : {
574 44250 : const GByte *pabyDataBefore = pabyData;
575 44250 : READ_FIELD_KEY(nKey);
576 44250 : if (nKey == MAKE_KEY(knLAYER_FEATURES, WT_DATA))
577 : {
578 2655 : if (m_pabyDataFeatureStart == nullptr)
579 : {
580 1051 : m_pabyDataFeatureStart = pabyDataBefore;
581 1051 : m_pabyDataCur = pabyDataBefore;
582 : }
583 :
584 2655 : unsigned int nFeatureLength = 0;
585 2655 : READ_SIZE(pabyData, pabyDataLimit, nFeatureLength);
586 2655 : const GByte *pabyDataFeatureEnd = pabyData + nFeatureLength;
587 2655 : if (bQuickScanFeature)
588 : {
589 514 : if (!QuickScanFeature(pabyData, pabyDataFeatureEnd,
590 : bScanFields, bScanGeometries,
591 : bGeomTypeSet))
592 : {
593 3 : return;
594 : }
595 : }
596 2652 : pabyData = pabyDataFeatureEnd;
597 :
598 2652 : m_nFeatureCount++;
599 : }
600 : else
601 : {
602 41595 : SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, FALSE);
603 : }
604 : }
605 : }
606 0 : catch (const GPBException &e)
607 : {
608 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
609 : }
610 : }
611 :
612 : /************************************************************************/
613 : /* MergeFieldDefn() */
614 : /************************************************************************/
615 :
616 11 : static void MergeFieldDefn(OGRFieldDefn *poFieldDefn, OGRFieldType eSrcType,
617 : OGRFieldSubType eSrcSubType)
618 : {
619 11 : if (eSrcType == OFTString)
620 : {
621 4 : poFieldDefn->SetSubType(OFSTNone);
622 4 : poFieldDefn->SetType(OFTString);
623 : }
624 7 : else if (poFieldDefn->GetType() == OFTInteger && eSrcType == OFTInteger64)
625 : {
626 1 : poFieldDefn->SetSubType(OFSTNone);
627 1 : poFieldDefn->SetType(OFTInteger64);
628 : }
629 10 : else if ((poFieldDefn->GetType() == OFTInteger ||
630 10 : poFieldDefn->GetType() == OFTInteger64) &&
631 : eSrcType == OFTReal)
632 : {
633 2 : poFieldDefn->SetSubType(OFSTNone);
634 2 : poFieldDefn->SetType(OFTReal);
635 2 : poFieldDefn->SetSubType(eSrcSubType);
636 : }
637 4 : else if (poFieldDefn->GetType() == OFTReal && eSrcType == OFTReal &&
638 : eSrcSubType == OFSTNone)
639 : {
640 1 : poFieldDefn->SetSubType(OFSTNone);
641 : }
642 3 : else if (poFieldDefn->GetType() == OFTInteger && eSrcType == OFTInteger &&
643 : eSrcSubType == OFSTNone)
644 : {
645 1 : poFieldDefn->SetSubType(OFSTNone);
646 : }
647 11 : }
648 :
649 : /************************************************************************/
650 : /* QuickScanFeature() */
651 : /************************************************************************/
652 :
653 514 : bool OGRMVTLayer::QuickScanFeature(const GByte *pabyData,
654 : const GByte *pabyDataFeatureEnd,
655 : bool bScanFields, bool bScanGeometries,
656 : bool &bGeomTypeSet)
657 : {
658 514 : unsigned int nKey = 0;
659 514 : unsigned int nGeomType = 0;
660 : try
661 : {
662 1972 : while (pabyData < pabyDataFeatureEnd)
663 : {
664 1461 : READ_VARUINT32(pabyData, pabyDataFeatureEnd, nKey);
665 1461 : if (nKey == MAKE_KEY(knFEATURE_TYPE, WT_VARINT))
666 : {
667 495 : READ_VARUINT32(pabyData, pabyDataFeatureEnd, nGeomType);
668 : }
669 966 : else if (nKey == MAKE_KEY(knFEATURE_TAGS, WT_DATA) && bScanFields)
670 : {
671 53 : unsigned int nTagsSize = 0;
672 53 : READ_SIZE(pabyData, pabyDataFeatureEnd, nTagsSize);
673 53 : const GByte *pabyDataTagsEnd = pabyData + nTagsSize;
674 224 : while (pabyData < pabyDataTagsEnd)
675 : {
676 174 : unsigned int nKeyIdx = 0;
677 174 : unsigned int nValIdx = 0;
678 174 : READ_VARUINT32(pabyData, pabyDataTagsEnd, nKeyIdx);
679 174 : READ_VARUINT32(pabyData, pabyDataTagsEnd, nValIdx);
680 173 : if (nKeyIdx >= m_aosKeys.size())
681 : {
682 1 : CPLError(CE_Failure, CPLE_AppDefined,
683 : "Invalid tag key index: %u", nKeyIdx);
684 1 : m_bError = true;
685 1 : return false;
686 : }
687 172 : if (nValIdx >= m_asValues.size())
688 : {
689 1 : CPLError(CE_Failure, CPLE_AppDefined,
690 : "Invalid tag value index: %u", nValIdx);
691 1 : m_bError = true;
692 1 : return false;
693 : }
694 : const int nFieldIdx =
695 171 : m_poFeatureDefn->GetFieldIndex(m_aosKeys[nKeyIdx]);
696 171 : if (nFieldIdx < 0)
697 : {
698 143 : OGRFieldDefn oFieldDefn(m_aosKeys[nKeyIdx],
699 286 : m_asValues[nValIdx].eType);
700 143 : oFieldDefn.SetSubType(m_asValues[nValIdx].eSubType);
701 143 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
702 : }
703 56 : else if (m_poFeatureDefn->GetFieldDefn(nFieldIdx)
704 52 : ->GetType() != m_asValues[nValIdx].eType ||
705 24 : m_poFeatureDefn->GetFieldDefn(nFieldIdx)
706 24 : ->GetSubType() !=
707 24 : m_asValues[nValIdx].eSubType)
708 : {
709 : OGRFieldDefn *poFieldDefn =
710 8 : m_poFeatureDefn->GetFieldDefn(nFieldIdx);
711 8 : OGRFieldType eSrcType(m_asValues[nValIdx].eType);
712 : OGRFieldSubType eSrcSubType(
713 8 : m_asValues[nValIdx].eSubType);
714 8 : MergeFieldDefn(poFieldDefn, eSrcType, eSrcSubType);
715 : }
716 50 : }
717 : }
718 913 : else if (nKey == MAKE_KEY(knFEATURE_GEOMETRY, WT_DATA) &&
719 494 : bScanGeometries && nGeomType >= knGEOM_TYPE_POINT &&
720 : nGeomType <= knGEOM_TYPE_POLYGON)
721 : {
722 493 : unsigned int nGeometrySize = 0;
723 493 : READ_SIZE(pabyData, pabyDataFeatureEnd, nGeometrySize);
724 493 : const GByte *pabyDataGeometryEnd = pabyData + nGeometrySize;
725 493 : OGRwkbGeometryType eType = wkbUnknown;
726 :
727 493 : if (nGeomType == knGEOM_TYPE_POINT)
728 : {
729 79 : eType = wkbPoint;
730 79 : unsigned int nCmdCountCombined = 0;
731 79 : READ_VARUINT32(pabyData, pabyDataGeometryEnd,
732 : nCmdCountCombined);
733 158 : if (GetCmdId(nCmdCountCombined) == knCMD_MOVETO &&
734 79 : GetCmdCount(nCmdCountCombined) > 1)
735 : {
736 0 : eType = wkbMultiPoint;
737 : }
738 : }
739 414 : else if (nGeomType == knGEOM_TYPE_LINESTRING)
740 : {
741 16 : eType = wkbLineString;
742 32 : for (int iIter = 0; pabyData < pabyDataGeometryEnd; iIter++)
743 : {
744 17 : if (iIter == 1)
745 : {
746 1 : eType = wkbMultiLineString;
747 1 : break;
748 : }
749 16 : unsigned int nCmdCountCombined = 0;
750 : unsigned int nLineToCount;
751 : // Should be a moveto
752 16 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
753 16 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
754 16 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
755 16 : READ_VARUINT32(pabyData, pabyDataGeometryEnd,
756 : nCmdCountCombined);
757 16 : nLineToCount = GetCmdCount(nCmdCountCombined);
758 50 : for (unsigned i = 0; i < 2 * nLineToCount; i++)
759 : {
760 34 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
761 : }
762 : }
763 : }
764 : else /* if( nGeomType == knGEOM_TYPE_POLYGON ) */
765 : {
766 398 : eType = wkbPolygon;
767 796 : for (int iIter = 0; pabyData < pabyDataGeometryEnd; iIter++)
768 : {
769 436 : if (iIter == 1)
770 : {
771 38 : eType = wkbMultiPolygon;
772 38 : break;
773 : }
774 398 : unsigned int nCmdCountCombined = 0;
775 : unsigned int nLineToCount;
776 : // Should be a moveto
777 398 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
778 398 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
779 398 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
780 398 : READ_VARUINT32(pabyData, pabyDataGeometryEnd,
781 : nCmdCountCombined);
782 398 : nLineToCount = GetCmdCount(nCmdCountCombined);
783 3472 : for (unsigned i = 0; i < 2 * nLineToCount; i++)
784 : {
785 3074 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
786 : }
787 : // Should be a closepath
788 398 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
789 : }
790 : }
791 :
792 510 : if (bGeomTypeSet && m_poFeatureDefn->GetGeomType() ==
793 17 : OGR_GT_GetCollection(eType))
794 : {
795 : // do nothing
796 : }
797 500 : else if (bGeomTypeSet &&
798 12 : eType == OGR_GT_GetCollection(
799 12 : m_poFeatureDefn->GetGeomType()))
800 : {
801 0 : m_poFeatureDefn->SetGeomType(eType);
802 : }
803 500 : else if (bGeomTypeSet &&
804 12 : m_poFeatureDefn->GetGeomType() != eType)
805 : {
806 0 : m_poFeatureDefn->SetGeomType(wkbUnknown);
807 : }
808 : else
809 : {
810 488 : m_poFeatureDefn->SetGeomType(eType);
811 : }
812 493 : bGeomTypeSet = true;
813 :
814 493 : pabyData = pabyDataGeometryEnd;
815 : }
816 : else
817 : {
818 420 : SKIP_UNKNOWN_FIELD(pabyData, pabyDataFeatureEnd, FALSE);
819 : }
820 : }
821 511 : return true;
822 : }
823 1 : catch (const GPBException &e)
824 : {
825 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
826 1 : return false;
827 : }
828 : }
829 :
830 : /************************************************************************/
831 : /* GetFeatureCount() */
832 : /************************************************************************/
833 :
834 51 : GIntBig OGRMVTLayer::GetFeatureCount(int bForce)
835 : {
836 51 : if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr &&
837 45 : m_nFeatureCount >= 0)
838 : {
839 45 : return m_nFeatureCount;
840 : }
841 6 : return OGRLayer::GetFeatureCount(bForce);
842 : }
843 :
844 : /************************************************************************/
845 : /* ResetReading() */
846 : /************************************************************************/
847 :
848 133 : void OGRMVTLayer::ResetReading()
849 : {
850 133 : m_nFID = 0;
851 133 : m_pabyDataCur = m_pabyDataFeatureStart;
852 133 : }
853 :
854 : /************************************************************************/
855 : /* GetXY() */
856 : /************************************************************************/
857 :
858 370816 : void OGRMVTLayer::GetXY(int nX, int nY, double &dfX, double &dfY)
859 : {
860 370816 : if (m_poDS->m_bGeoreferenced)
861 : {
862 369253 : dfX = m_poDS->m_dfTopX + nX * m_poDS->m_dfTileDimX / m_nExtent;
863 369253 : dfY = m_poDS->m_dfTopY - nY * m_poDS->m_dfTileDimY / m_nExtent;
864 : }
865 : else
866 : {
867 1563 : dfX = nX;
868 1563 : dfY = static_cast<double>(m_nExtent) - nY;
869 : }
870 370816 : }
871 :
872 : /************************************************************************/
873 : /* AddWithOverflowAccepted() */
874 : /************************************************************************/
875 :
876 : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
877 737194 : static int AddWithOverflowAccepted(int a, int b)
878 : {
879 : // In fact in normal situations a+b should not overflow. That can only
880 : // happen with corrupted datasets. But we don't really want to add code
881 : // to detect that situation, so basically this is just a trick to perform
882 : // the addition without the various sanitizers to yell about the overflow.
883 : //
884 : // Assumes complement-to-two signed integer representation and that
885 : // the compiler will safely cast a big unsigned to negative integer.
886 737194 : return static_cast<int>(static_cast<unsigned>(a) +
887 737194 : static_cast<unsigned>(b));
888 : }
889 :
890 : /************************************************************************/
891 : /* ParseGeometry() */
892 : /************************************************************************/
893 :
894 : std::unique_ptr<OGRGeometry>
895 2108 : OGRMVTLayer::ParseGeometry(unsigned int nGeomType,
896 : const GByte *pabyDataGeometryEnd)
897 : {
898 : try
899 : {
900 2108 : if (nGeomType == knGEOM_TYPE_POINT)
901 : {
902 1 : std::unique_ptr<OGRMultiPoint> poMultiPoint;
903 116 : unsigned int nCmdCountCombined = 0;
904 : unsigned int nCount;
905 116 : READ_VARUINT32(m_pabyDataCur, pabyDataGeometryEnd,
906 : nCmdCountCombined);
907 116 : nCount = GetCmdCount(nCmdCountCombined);
908 116 : if (GetCmdId(nCmdCountCombined) == knCMD_MOVETO && nCount == 1)
909 : {
910 114 : int nX = 0;
911 114 : int nY = 0;
912 114 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nX);
913 113 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nY);
914 : double dfX;
915 : double dfY;
916 113 : GetXY(nX, nY, dfX, dfY);
917 226 : auto poPoint = std::make_unique<OGRPoint>(dfX, dfY);
918 113 : if (m_poFeatureDefn->GetGeomType() == wkbMultiPoint)
919 : {
920 8 : poMultiPoint = std::make_unique<OGRMultiPoint>();
921 8 : poMultiPoint->addGeometry(std::move(poPoint));
922 8 : return poMultiPoint;
923 : }
924 : else
925 : {
926 105 : return poPoint;
927 : }
928 : }
929 2 : else if (GetCmdId(nCmdCountCombined) == knCMD_MOVETO && nCount > 1)
930 : {
931 2 : int nX = 0;
932 2 : int nY = 0;
933 2 : poMultiPoint = std::make_unique<OGRMultiPoint>();
934 6 : for (unsigned i = 0; i < nCount; i++)
935 : {
936 4 : int nDX = 0;
937 4 : int nDY = 0;
938 4 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDX);
939 4 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDY);
940 : // if( nDX != 0 || nDY != 0 )
941 : {
942 4 : nX = AddWithOverflowAccepted(nX, nDX);
943 4 : nY = AddWithOverflowAccepted(nY, nDY);
944 : double dfX;
945 : double dfY;
946 4 : GetXY(nX, nY, dfX, dfY);
947 4 : auto poPoint = std::make_unique<OGRPoint>(dfX, dfY);
948 4 : if (i == 0 && nCount == 2 &&
949 2 : m_pabyDataCur == pabyDataGeometryEnd)
950 : {
951 : // Current versions of Mapserver at time of writing
952 : // wrongly encode a point with nCount = 2
953 : static bool bWarned = false;
954 0 : if (!bWarned)
955 : {
956 0 : CPLDebug(
957 : "MVT",
958 : "Reading likely a broken point as "
959 : "produced by some versions of Mapserver");
960 0 : bWarned = true;
961 : }
962 0 : return poPoint;
963 : }
964 4 : poMultiPoint->addGeometry(std::move(poPoint));
965 : }
966 : }
967 2 : return poMultiPoint;
968 : }
969 : }
970 1992 : else if (nGeomType == knGEOM_TYPE_LINESTRING)
971 : {
972 23 : std::unique_ptr<OGRMultiLineString> poMultiLS;
973 23 : std::unique_ptr<OGRLineString> poLine;
974 23 : int nX = 0;
975 23 : int nY = 0;
976 23 : bool bFirstLine = true;
977 51 : while (m_pabyDataCur < pabyDataGeometryEnd)
978 : {
979 28 : unsigned int nCmdCountCombined = 0;
980 : unsigned int nLineToCount;
981 : // Should be a moveto
982 28 : SKIP_VARINT(m_pabyDataCur, pabyDataGeometryEnd);
983 28 : int nDX = 0;
984 28 : int nDY = 0;
985 28 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDX);
986 28 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDY);
987 28 : nX = AddWithOverflowAccepted(nX, nDX);
988 28 : nY = AddWithOverflowAccepted(nY, nDY);
989 : double dfX;
990 : double dfY;
991 28 : GetXY(nX, nY, dfX, dfY);
992 51 : if (!bFirstLine ||
993 23 : m_poFeatureDefn->GetGeomType() == wkbMultiLineString)
994 : {
995 22 : if (!poMultiLS)
996 17 : poMultiLS = std::make_unique<OGRMultiLineString>();
997 22 : if (poLine)
998 0 : poMultiLS->addGeometry(std::move(poLine));
999 : }
1000 28 : poLine = std::make_unique<OGRLineString>();
1001 28 : poLine->addPoint(dfX, dfY);
1002 28 : READ_VARUINT32(m_pabyDataCur, pabyDataGeometryEnd,
1003 : nCmdCountCombined);
1004 28 : nLineToCount = GetCmdCount(nCmdCountCombined);
1005 59 : for (unsigned i = 0; i < nLineToCount; i++)
1006 : {
1007 31 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDX);
1008 31 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDY);
1009 : // if( nDX != 0 || nDY != 0 )
1010 : {
1011 31 : nX = AddWithOverflowAccepted(nX, nDX);
1012 31 : nY = AddWithOverflowAccepted(nY, nDY);
1013 31 : GetXY(nX, nY, dfX, dfY);
1014 31 : poLine->addPoint(dfX, dfY);
1015 : }
1016 : }
1017 28 : if (poMultiLS)
1018 22 : poMultiLS->addGeometry(std::move(poLine));
1019 28 : bFirstLine = false;
1020 : }
1021 23 : if (poMultiLS)
1022 : {
1023 17 : return poMultiLS;
1024 : }
1025 : else
1026 : {
1027 6 : return poLine;
1028 : }
1029 : }
1030 1969 : else if (nGeomType == knGEOM_TYPE_POLYGON)
1031 : {
1032 1969 : std::unique_ptr<OGRMultiPolygon> poMultiPoly;
1033 1969 : std::unique_ptr<OGRPolygon> poPoly;
1034 1969 : int externalIsClockwise = 0;
1035 1969 : int nX = 0;
1036 1969 : int nY = 0;
1037 1969 : OGREnvelope sExteriorRingEnvelope;
1038 5868 : while (m_pabyDataCur < pabyDataGeometryEnd)
1039 : {
1040 3899 : unsigned int nCmdCountCombined = 0;
1041 : unsigned int nLineToCount;
1042 : // Should be a moveto
1043 3899 : SKIP_VARINT(m_pabyDataCur, pabyDataGeometryEnd);
1044 3899 : int nDX = 0;
1045 3899 : int nDY = 0;
1046 3899 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDX);
1047 3899 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDY);
1048 3899 : nX = AddWithOverflowAccepted(nX, nDX);
1049 3899 : nY = AddWithOverflowAccepted(nY, nDY);
1050 : double dfX;
1051 : double dfY;
1052 3899 : GetXY(nX, nY, dfX, dfY);
1053 3899 : auto poRing = std::make_unique<OGRLinearRing>();
1054 3899 : poRing->addPoint(dfX, dfY);
1055 3899 : READ_VARUINT32(m_pabyDataCur, pabyDataGeometryEnd,
1056 : nCmdCountCombined);
1057 3899 : nLineToCount = GetCmdCount(nCmdCountCombined);
1058 368534 : for (unsigned i = 0; i < nLineToCount; i++)
1059 : {
1060 364635 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDX);
1061 364635 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDY);
1062 : // if( nDX != 0 || nDY != 0 )
1063 : {
1064 364635 : nX = AddWithOverflowAccepted(nX, nDX);
1065 364635 : nY = AddWithOverflowAccepted(nY, nDY);
1066 364635 : GetXY(nX, nY, dfX, dfY);
1067 364635 : poRing->addPoint(dfX, dfY);
1068 : }
1069 : }
1070 : // Should be a closepath
1071 3899 : SKIP_VARINT(m_pabyDataCur, pabyDataGeometryEnd);
1072 3899 : poRing->closeRings();
1073 3899 : if (!poPoly)
1074 : {
1075 1969 : poPoly = std::make_unique<OGRPolygon>();
1076 1969 : externalIsClockwise = poRing->isClockwise();
1077 1969 : if (!externalIsClockwise)
1078 : {
1079 743 : if (m_bEnforceExternalIsClockwise)
1080 : {
1081 0 : CPLError(CE_Failure, CPLE_AppDefined,
1082 : "Bad ring orientation detected");
1083 0 : return nullptr;
1084 : }
1085 : else
1086 : {
1087 743 : CPLDebugOnce(
1088 : "MVT",
1089 : "Bad ring orientation detected. Auto-fixing");
1090 : }
1091 : }
1092 1969 : poPoly->addRing(std::move(poRing));
1093 : }
1094 : else
1095 : {
1096 : // Detect change of winding order to figure out if this is
1097 : // an interior or exterior ring
1098 1930 : if (externalIsClockwise != poRing->isClockwise())
1099 : {
1100 312 : poPoly->addRing(std::move(poRing));
1101 : }
1102 : else
1103 : {
1104 : #ifdef HAVE_GEOS
1105 : {
1106 : // This block is just to deal with potential bad
1107 : // oriented rings
1108 : // Such as those produced by GDAL < 3.12 in some
1109 : // situations like
1110 : // https://github.com/OSGeo/gdal/issues/13305
1111 1618 : if (!sExteriorRingEnvelope.IsInit())
1112 1617 : poPoly->getEnvelope(&sExteriorRingEnvelope);
1113 1618 : OGREnvelope sCurRingEnvelope;
1114 1618 : poRing->getEnvelope(&sCurRingEnvelope);
1115 : // Cheap heuristics to detect potentially inner
1116 : // rings
1117 1618 : if (sExteriorRingEnvelope.Contains(
1118 1618 : sCurRingEnvelope))
1119 : {
1120 : // Now do the real check
1121 253 : OGRLineString oLS(*poRing);
1122 253 : if (poPoly->Contains(&oLS))
1123 : {
1124 2 : CPLDebugOnce("MVT",
1125 : "Bad ring orientation "
1126 : "detected. Auto-fixing");
1127 2 : poPoly->addRing(std::move(poRing));
1128 2 : continue;
1129 : }
1130 : }
1131 : }
1132 : #endif
1133 :
1134 1616 : if (!poMultiPoly)
1135 : {
1136 746 : poMultiPoly = std::make_unique<OGRMultiPolygon>();
1137 : }
1138 1616 : poMultiPoly->addGeometry(std::move(poPoly));
1139 :
1140 1616 : poPoly = std::make_unique<OGRPolygon>();
1141 1616 : poPoly->addRing(std::move(poRing));
1142 1616 : sExteriorRingEnvelope = OGREnvelope();
1143 : }
1144 : }
1145 : }
1146 1969 : if (poMultiPoly)
1147 : {
1148 746 : CPLAssert(poPoly);
1149 746 : poMultiPoly->addGeometry(std::move(poPoly));
1150 : }
1151 2446 : else if (poPoly &&
1152 2446 : m_poFeatureDefn->GetGeomType() == wkbMultiPolygon)
1153 : {
1154 869 : poMultiPoly = std::make_unique<OGRMultiPolygon>();
1155 869 : poMultiPoly->addGeometry(std::move(poPoly));
1156 : }
1157 1969 : if (poMultiPoly)
1158 : {
1159 1615 : return poMultiPoly;
1160 : }
1161 : else
1162 : {
1163 354 : return poPoly;
1164 : }
1165 : }
1166 : }
1167 2 : catch (const GPBException &e)
1168 : {
1169 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1170 : }
1171 1 : return nullptr;
1172 : }
1173 :
1174 : /************************************************************************/
1175 : /* SanitizeClippedGeometry() */
1176 : /************************************************************************/
1177 :
1178 663 : void OGRMVTLayer::SanitizeClippedGeometry(std::unique_ptr<OGRGeometry> &poGeom)
1179 : {
1180 663 : OGRwkbGeometryType eInGeomType = wkbFlatten(poGeom->getGeometryType());
1181 663 : const OGRwkbGeometryType eLayerGeomType = GetGeomType();
1182 663 : if (eLayerGeomType == wkbUnknown)
1183 : {
1184 0 : return;
1185 : }
1186 :
1187 : // GEOS intersection may return a mix of polygon and linestrings when
1188 : // intersection a multipolygon and a polygon
1189 663 : if (eInGeomType == wkbGeometryCollection)
1190 : {
1191 118 : std::unique_ptr<OGRGeometryCollection> poTargetGC;
1192 118 : const OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
1193 0 : std::unique_ptr<OGRGeometry> poTargetSingleGeom;
1194 : OGRwkbGeometryType ePartGeom;
1195 118 : if (eLayerGeomType == wkbPoint || eLayerGeomType == wkbMultiPoint)
1196 : {
1197 0 : ePartGeom = wkbPoint;
1198 : }
1199 118 : else if (eLayerGeomType == wkbLineString ||
1200 : eLayerGeomType == wkbMultiLineString)
1201 : {
1202 0 : ePartGeom = wkbLineString;
1203 : }
1204 : else
1205 : {
1206 118 : ePartGeom = wkbPolygon;
1207 : }
1208 405 : for (const auto *poSubGeom : poGC)
1209 : {
1210 287 : if (wkbFlatten(poSubGeom->getGeometryType()) == ePartGeom)
1211 : {
1212 169 : if (poTargetSingleGeom)
1213 : {
1214 51 : if (!poTargetGC)
1215 : {
1216 51 : poTargetGC.reset(OGRGeometryFactory::createGeometry(
1217 : OGR_GT_GetCollection(ePartGeom))
1218 : ->toGeometryCollection());
1219 : // cppcheck-suppress nullPointerRedundantCheck
1220 51 : poTargetGC->addGeometry(std::move(poTargetSingleGeom));
1221 : }
1222 :
1223 51 : poTargetGC->addGeometry(poSubGeom);
1224 : }
1225 : else
1226 : {
1227 118 : poTargetSingleGeom.reset(poSubGeom->clone());
1228 : }
1229 : }
1230 : }
1231 118 : if (poTargetGC)
1232 51 : poGeom = std::move(poTargetGC);
1233 67 : else if (poTargetSingleGeom)
1234 67 : poGeom = std::move(poTargetSingleGeom);
1235 118 : eInGeomType = wkbFlatten(poGeom->getGeometryType());
1236 : }
1237 :
1238 : // Wrap single into multi if requested by the layer geometry type
1239 663 : if (OGR_GT_GetCollection(eInGeomType) == eLayerGeomType)
1240 : {
1241 : auto poGC = std::unique_ptr<OGRGeometryCollection>(
1242 : OGRGeometryFactory::createGeometry(eLayerGeomType)
1243 800 : ->toGeometryCollection());
1244 400 : poGC->addGeometry(std::move(poGeom));
1245 400 : poGeom = std::move(poGC);
1246 : }
1247 : }
1248 :
1249 : /************************************************************************/
1250 : /* GetNextRawFeature() */
1251 : /************************************************************************/
1252 :
1253 2890 : OGRFeature *OGRMVTLayer::GetNextRawFeature()
1254 : {
1255 2890 : if (m_pabyDataCur == nullptr || m_pabyDataCur >= m_pabyDataEnd || m_bError)
1256 : {
1257 120 : return nullptr;
1258 : }
1259 :
1260 2770 : unsigned int nKey = 0;
1261 2770 : const GByte *pabyDataLimit = m_pabyDataEnd;
1262 2770 : std::unique_ptr<OGRFeature> poFeature;
1263 2770 : unsigned int nFeatureLength = 0;
1264 2770 : unsigned int nGeomType = 0;
1265 :
1266 : try
1267 : {
1268 : while (true)
1269 : {
1270 2775 : bool bOK = true;
1271 :
1272 34648 : while (m_pabyDataCur < pabyDataLimit)
1273 : {
1274 33989 : READ_VARUINT32(m_pabyDataCur, pabyDataLimit, nKey);
1275 33989 : if (nKey == MAKE_KEY(knLAYER_FEATURES, WT_DATA))
1276 : {
1277 2116 : poFeature = std::make_unique<OGRFeature>(m_poFeatureDefn);
1278 2116 : break;
1279 : }
1280 : else
1281 : {
1282 31873 : SKIP_UNKNOWN_FIELD(m_pabyDataCur, pabyDataLimit, FALSE);
1283 : }
1284 : }
1285 :
1286 2775 : if (poFeature == nullptr)
1287 659 : return nullptr;
1288 :
1289 2116 : READ_SIZE(m_pabyDataCur, pabyDataLimit, nFeatureLength);
1290 2116 : const GByte *pabyDataFeatureEnd = m_pabyDataCur + nFeatureLength;
1291 8401 : while (m_pabyDataCur < pabyDataFeatureEnd)
1292 : {
1293 6285 : READ_VARUINT32(m_pabyDataCur, pabyDataFeatureEnd, nKey);
1294 6285 : if (nKey == MAKE_KEY(knFEATURE_ID, WT_VARINT))
1295 : {
1296 14 : GUIntBig nID = 0;
1297 14 : READ_VARUINT64(m_pabyDataCur, pabyDataFeatureEnd, nID);
1298 14 : poFeature->SetField("mvt_id", static_cast<GIntBig>(nID));
1299 : }
1300 6271 : else if (nKey == MAKE_KEY(knFEATURE_TYPE, WT_VARINT))
1301 : {
1302 2110 : READ_VARUINT32(m_pabyDataCur, pabyDataFeatureEnd,
1303 : nGeomType);
1304 : }
1305 4161 : else if (nKey == MAKE_KEY(knFEATURE_TAGS, WT_DATA))
1306 : {
1307 2051 : unsigned int nTagsSize = 0;
1308 2051 : READ_SIZE(m_pabyDataCur, pabyDataFeatureEnd, nTagsSize);
1309 2051 : const GByte *pabyDataTagsEnd = m_pabyDataCur + nTagsSize;
1310 62984 : while (m_pabyDataCur < pabyDataTagsEnd)
1311 : {
1312 60933 : unsigned int nKeyIdx = 0;
1313 60933 : unsigned int nValIdx = 0;
1314 60933 : READ_VARUINT32(m_pabyDataCur, pabyDataTagsEnd, nKeyIdx);
1315 60933 : READ_VARUINT32(m_pabyDataCur, pabyDataTagsEnd, nValIdx);
1316 121866 : if (nKeyIdx < m_aosKeys.size() &&
1317 60933 : nValIdx < m_asValues.size())
1318 : {
1319 : const int nFieldIdx =
1320 60933 : m_poFeatureDefn->GetFieldIndex(
1321 60933 : m_aosKeys[nKeyIdx]);
1322 60933 : if (nFieldIdx >= 0)
1323 : {
1324 60933 : if (m_asValues[nValIdx].eType == OFTString)
1325 : {
1326 33101 : poFeature->SetField(
1327 : nFieldIdx,
1328 33101 : m_asValues[nValIdx].sValue.String);
1329 : }
1330 27832 : else if (m_asValues[nValIdx].eType ==
1331 : OFTInteger)
1332 : {
1333 52608 : poFeature->SetField(
1334 : nFieldIdx,
1335 26304 : m_asValues[nValIdx].sValue.Integer);
1336 : }
1337 1528 : else if (m_asValues[nValIdx].eType ==
1338 : OFTInteger64)
1339 : {
1340 912 : poFeature->SetField(
1341 : nFieldIdx,
1342 456 : m_asValues[nValIdx].sValue.Integer64);
1343 : }
1344 1072 : else if (m_asValues[nValIdx].eType == OFTReal)
1345 : {
1346 2144 : poFeature->SetField(
1347 : nFieldIdx,
1348 1072 : m_asValues[nValIdx].sValue.Real);
1349 : }
1350 : }
1351 : }
1352 : }
1353 : }
1354 2110 : else if (nKey == MAKE_KEY(knFEATURE_GEOMETRY, WT_DATA) &&
1355 2108 : nGeomType >= 1 && nGeomType <= 3)
1356 : {
1357 2108 : unsigned int nGeometrySize = 0;
1358 2108 : READ_SIZE(m_pabyDataCur, pabyDataFeatureEnd, nGeometrySize);
1359 2108 : const GByte *pabyDataGeometryEnd =
1360 2108 : m_pabyDataCur + nGeometrySize;
1361 2108 : auto poGeom = ParseGeometry(nGeomType, pabyDataGeometryEnd);
1362 2108 : if (poGeom)
1363 : {
1364 : // Clip geometry to tile extent if requested
1365 2107 : if (m_poDS->m_bClip && OGRGeometryFactory::haveGEOS())
1366 : {
1367 2102 : OGREnvelope sEnvelope;
1368 2102 : poGeom->getEnvelope(&sEnvelope);
1369 2102 : if (sEnvelope.MinX >= m_dfTileMinX &&
1370 1861 : sEnvelope.MinY >= m_dfTileMinY &&
1371 1700 : sEnvelope.MaxX <= m_dfTileMaxX &&
1372 1480 : sEnvelope.MaxY <= m_dfTileMaxY)
1373 : {
1374 : // do nothing
1375 : }
1376 668 : else if (sEnvelope.MinX < m_dfTileMaxX &&
1377 664 : sEnvelope.MinY < m_dfTileMaxY &&
1378 664 : sEnvelope.MaxX > m_dfTileMinX &&
1379 664 : sEnvelope.MaxY > m_dfTileMinY)
1380 : {
1381 : auto poClipped = std::unique_ptr<OGRGeometry>(
1382 1326 : poGeom->Intersection(&m_oClipPoly));
1383 663 : if (poClipped)
1384 : {
1385 663 : SanitizeClippedGeometry(poClipped);
1386 663 : if (poClipped->IsEmpty())
1387 : {
1388 0 : bOK = false;
1389 : }
1390 : else
1391 : {
1392 1326 : poClipped->assignSpatialReference(
1393 663 : GetSpatialRef());
1394 1326 : poFeature->SetGeometry(
1395 663 : std::move(poClipped));
1396 663 : poGeom.reset();
1397 : }
1398 663 : }
1399 : }
1400 : else
1401 : {
1402 5 : bOK = false;
1403 : }
1404 : }
1405 :
1406 2107 : if (poGeom)
1407 : {
1408 1444 : poGeom->assignSpatialReference(GetSpatialRef());
1409 1444 : poFeature->SetGeometry(std::move(poGeom));
1410 : }
1411 : }
1412 :
1413 2108 : m_pabyDataCur = pabyDataGeometryEnd;
1414 : }
1415 : else
1416 : {
1417 2 : SKIP_UNKNOWN_FIELD(m_pabyDataCur, pabyDataFeatureEnd,
1418 : FALSE);
1419 : }
1420 : }
1421 2116 : m_pabyDataCur = pabyDataFeatureEnd;
1422 :
1423 2116 : if (bOK)
1424 : {
1425 2111 : poFeature->SetFID(m_nFID);
1426 2111 : m_nFID++;
1427 2111 : return poFeature.release();
1428 : }
1429 : else
1430 : {
1431 5 : poFeature.reset();
1432 : }
1433 5 : }
1434 : }
1435 0 : catch (const GPBException &e)
1436 : {
1437 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1438 0 : return nullptr;
1439 : }
1440 : }
1441 :
1442 : /************************************************************************/
1443 : /* GetDataset() */
1444 : /************************************************************************/
1445 :
1446 1 : GDALDataset *OGRMVTLayer::GetDataset()
1447 : {
1448 1 : return m_poDS;
1449 : }
1450 :
1451 : /************************************************************************/
1452 : /* StripDummyEntries() */
1453 : /************************************************************************/
1454 :
1455 111 : static CPLStringList StripDummyEntries(const CPLStringList &aosInput)
1456 : {
1457 222 : CPLStringList aosOutput;
1458 416 : for (int i = 0; i < aosInput.Count(); i++)
1459 : {
1460 743 : if (aosInput[i] != CPLString(".") && aosInput[i] != CPLString("..") &&
1461 438 : CPLString(aosInput[i]).find(".properties") == std::string::npos)
1462 : {
1463 133 : aosOutput.AddString(aosInput[i]);
1464 : }
1465 : }
1466 222 : return aosOutput.Sort();
1467 : }
1468 :
1469 : /************************************************************************/
1470 : /* OGRMVTDirectoryLayer() */
1471 : /************************************************************************/
1472 :
1473 25 : OGRMVTDirectoryLayer::OGRMVTDirectoryLayer(
1474 : OGRMVTDataset *poDS, const char *pszLayerName, const char *pszDirectoryName,
1475 : const CPLJSONObject &oFields, const CPLJSONArray &oAttributesFromTileStats,
1476 25 : bool bJsonField, OGRwkbGeometryType eGeomType, const OGREnvelope *psExtent)
1477 25 : : m_poDS(poDS), m_osDirName(pszDirectoryName), m_bJsonField(bJsonField)
1478 : {
1479 25 : m_poFeatureDefn = new OGRFeatureDefn(pszLayerName);
1480 25 : SetDescription(m_poFeatureDefn->GetName());
1481 25 : m_poFeatureDefn->SetGeomType(eGeomType);
1482 25 : m_poFeatureDefn->Reference();
1483 :
1484 25 : m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poDS->GetSRS());
1485 :
1486 25 : if (m_bJsonField)
1487 : {
1488 2 : OGRFieldDefn oFieldDefnId("mvt_id", OFTInteger64);
1489 1 : m_poFeatureDefn->AddFieldDefn(&oFieldDefnId);
1490 : }
1491 : else
1492 : {
1493 24 : InitFields(oFields, oAttributesFromTileStats);
1494 : }
1495 :
1496 25 : m_nZ = atoi(CPLGetFilename(m_osDirName));
1497 25 : SetMetadataItem("ZOOM_LEVEL", CPLSPrintf("%d", m_nZ));
1498 25 : m_bUseReadDir = CPLTestBool(CPLGetConfigOption(
1499 25 : "MVT_USE_READDIR", (!STARTS_WITH(m_osDirName, "/vsicurl") &&
1500 25 : !STARTS_WITH(m_osDirName, "http://") &&
1501 21 : !STARTS_WITH(m_osDirName, "https://"))
1502 : ? "YES"
1503 : : "NO"));
1504 25 : if (m_bUseReadDir)
1505 : {
1506 20 : m_aosDirContent = VSIReadDirEx(m_osDirName, knMAX_FILES_PER_DIR);
1507 20 : if (m_aosDirContent.Count() >= knMAX_FILES_PER_DIR)
1508 : {
1509 0 : CPLDebug("MVT", "Disabling readdir");
1510 0 : m_aosDirContent.Clear();
1511 0 : m_bUseReadDir = false;
1512 : }
1513 20 : m_aosDirContent = StripDummyEntries(m_aosDirContent);
1514 : }
1515 25 : OGRMVTDirectoryLayer::ResetReading();
1516 :
1517 25 : if (psExtent)
1518 : {
1519 20 : m_sExtent = *psExtent;
1520 : }
1521 :
1522 25 : OGRMVTDirectoryLayer::SetSpatialFilter(nullptr);
1523 :
1524 : // If the metadata contains an empty fields object, this may be a sign
1525 : // that it doesn't know the schema. In that case check if a tile has
1526 : // attributes, and in that case create a json field.
1527 25 : if (!m_bJsonField && oFields.IsValid() && oFields.GetChildren().empty())
1528 : {
1529 14 : m_bJsonField = true;
1530 14 : OpenTileIfNeeded();
1531 14 : m_bJsonField = false;
1532 :
1533 14 : if (m_poCurrentTile)
1534 : {
1535 : OGRLayer *poUnderlyingLayer =
1536 12 : m_poCurrentTile->GetLayerByName(GetName());
1537 : // There is at least the mvt_id field
1538 12 : if (poUnderlyingLayer->GetLayerDefn()->GetFieldCount() > 1)
1539 : {
1540 0 : m_bJsonField = true;
1541 : }
1542 : }
1543 14 : OGRMVTDirectoryLayer::ResetReading();
1544 : }
1545 :
1546 25 : if (m_bJsonField)
1547 : {
1548 2 : OGRFieldDefn oFieldDefn("json", OFTString);
1549 1 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
1550 : }
1551 25 : }
1552 :
1553 : /************************************************************************/
1554 : /* ~OGRMVTDirectoryLayer() */
1555 : /************************************************************************/
1556 :
1557 50 : OGRMVTDirectoryLayer::~OGRMVTDirectoryLayer()
1558 : {
1559 25 : delete m_poCurrentTile;
1560 50 : }
1561 :
1562 : /************************************************************************/
1563 : /* ResetReading() */
1564 : /************************************************************************/
1565 :
1566 144 : void OGRMVTDirectoryLayer::ResetReading()
1567 : {
1568 144 : m_bEOF = false;
1569 144 : m_nXIndex = -1;
1570 144 : m_nYIndex = -1;
1571 144 : delete m_poCurrentTile;
1572 144 : m_poCurrentTile = nullptr;
1573 144 : }
1574 :
1575 : /************************************************************************/
1576 : /* IsBetween() */
1577 : /************************************************************************/
1578 :
1579 147 : static bool IsBetween(int nVal, int nMin, int nMax)
1580 : {
1581 147 : return nVal >= nMin && nVal <= nMax;
1582 : }
1583 :
1584 : /************************************************************************/
1585 : /* ReadNewSubDir() */
1586 : /************************************************************************/
1587 :
1588 121 : void OGRMVTDirectoryLayer::ReadNewSubDir()
1589 : {
1590 121 : delete m_poCurrentTile;
1591 121 : m_poCurrentTile = nullptr;
1592 121 : if (m_bUseReadDir || !m_aosDirContent.empty())
1593 : {
1594 2 : while (
1595 184 : m_nXIndex < m_aosDirContent.Count() &&
1596 74 : (CPLGetValueType(m_aosDirContent[m_nXIndex]) != CPL_VALUE_INTEGER ||
1597 74 : !IsBetween(atoi(m_aosDirContent[m_nXIndex]), m_nFilterMinX,
1598 : m_nFilterMaxX)))
1599 : {
1600 2 : m_nXIndex++;
1601 : }
1602 : }
1603 : else
1604 : {
1605 13 : if (m_nXIndex < m_nFilterMinX)
1606 0 : m_nXIndex = m_nFilterMinX;
1607 13 : else if (m_nXIndex > m_nFilterMaxX)
1608 4 : m_nXIndex = (1 << m_nZ);
1609 : }
1610 134 : if (m_nXIndex < ((m_bUseReadDir || !m_aosDirContent.empty())
1611 121 : ? m_aosDirContent.Count()
1612 13 : : (1 << m_nZ)))
1613 : {
1614 : m_aosSubDirName =
1615 81 : CPLFormFilenameSafe(m_osDirName,
1616 81 : (m_bUseReadDir || !m_aosDirContent.empty())
1617 72 : ? m_aosDirContent[m_nXIndex]
1618 9 : : CPLSPrintf("%d", m_nXIndex),
1619 81 : nullptr);
1620 81 : if (m_bUseReadDir)
1621 : {
1622 : m_aosSubDirContent =
1623 72 : VSIReadDirEx(m_aosSubDirName, knMAX_FILES_PER_DIR);
1624 72 : if (m_aosSubDirContent.Count() >= knMAX_FILES_PER_DIR)
1625 : {
1626 0 : CPLDebug("MVT", "Disabling readdir");
1627 0 : m_aosSubDirContent.Clear();
1628 0 : m_bUseReadDir = false;
1629 : }
1630 72 : m_aosSubDirContent = StripDummyEntries(m_aosSubDirContent);
1631 : }
1632 81 : m_nYIndex = -1;
1633 81 : OpenTileIfNeeded();
1634 : }
1635 : else
1636 : {
1637 40 : m_bEOF = true;
1638 : }
1639 121 : }
1640 :
1641 : /************************************************************************/
1642 : /* OpenTile() */
1643 : /************************************************************************/
1644 :
1645 82 : void OGRMVTDirectoryLayer::OpenTile()
1646 : {
1647 82 : delete m_poCurrentTile;
1648 82 : m_poCurrentTile = nullptr;
1649 82 : if (m_nYIndex < (m_bUseReadDir ? m_aosSubDirContent.Count() : (1 << m_nZ)))
1650 : {
1651 82 : CPLString osFilename = CPLFormFilenameSafe(
1652 : m_aosSubDirName,
1653 82 : m_bUseReadDir ? m_aosSubDirContent[m_nYIndex]
1654 9 : : CPLSPrintf("%d.%s", m_nYIndex,
1655 9 : m_poDS->m_osTileExtension.c_str()),
1656 164 : nullptr);
1657 82 : GDALOpenInfo oOpenInfo(("MVT:" + osFilename).c_str(), GA_ReadOnly);
1658 82 : oOpenInfo.papszOpenOptions = CSLSetNameValue(
1659 : nullptr, "METADATA_FILE",
1660 82 : m_bJsonField ? "" : m_poDS->m_osMetadataMemFilename.c_str());
1661 82 : oOpenInfo.papszOpenOptions = CSLSetNameValue(
1662 : oOpenInfo.papszOpenOptions, "DO_NOT_ERROR_ON_MISSING_TILE", "YES");
1663 82 : m_poCurrentTile =
1664 82 : OGRMVTDataset::Open(&oOpenInfo, /* bRecurseAllowed = */ false);
1665 82 : CSLDestroy(oOpenInfo.papszOpenOptions);
1666 :
1667 9 : int nX = (m_bUseReadDir || !m_aosDirContent.empty())
1668 91 : ? atoi(m_aosDirContent[m_nXIndex])
1669 82 : : m_nXIndex;
1670 : int nY =
1671 82 : m_bUseReadDir ? atoi(m_aosSubDirContent[m_nYIndex]) : m_nYIndex;
1672 82 : m_nFIDBase = (static_cast<GIntBig>(nX) << m_nZ) | nY;
1673 : }
1674 82 : }
1675 :
1676 : /************************************************************************/
1677 : /* OpenTileIfNeeded() */
1678 : /************************************************************************/
1679 :
1680 235 : void OGRMVTDirectoryLayer::OpenTileIfNeeded()
1681 : {
1682 235 : if (m_nXIndex < 0)
1683 : {
1684 80 : m_nXIndex = 0;
1685 80 : ReadNewSubDir();
1686 : }
1687 593 : while ((m_poCurrentTile == nullptr && !m_bEOF) ||
1688 235 : (m_poCurrentTile != nullptr &&
1689 188 : m_poCurrentTile->GetLayerByName(GetName()) == nullptr))
1690 : {
1691 123 : m_nYIndex++;
1692 123 : if (m_bUseReadDir)
1693 : {
1694 183 : while (m_nYIndex < m_aosSubDirContent.Count() &&
1695 73 : (CPLGetValueType(
1696 183 : CPLGetBasenameSafe(m_aosSubDirContent[m_nYIndex])
1697 73 : .c_str()) != CPL_VALUE_INTEGER ||
1698 73 : !IsBetween(atoi(m_aosSubDirContent[m_nYIndex]),
1699 : m_nFilterMinY, m_nFilterMaxY)))
1700 : {
1701 0 : m_nYIndex++;
1702 : }
1703 : }
1704 : else
1705 : {
1706 13 : if (m_nYIndex < m_nFilterMinY)
1707 0 : m_nYIndex = m_nFilterMinY;
1708 13 : else if (m_nYIndex > m_nFilterMaxY)
1709 4 : m_nYIndex = (1 << m_nZ);
1710 : }
1711 123 : if (m_nYIndex ==
1712 123 : (m_bUseReadDir ? m_aosSubDirContent.Count() : (1 << m_nZ)))
1713 : {
1714 41 : m_nXIndex++;
1715 41 : ReadNewSubDir();
1716 : }
1717 : else
1718 : {
1719 82 : OpenTile();
1720 : }
1721 : }
1722 235 : }
1723 :
1724 : /************************************************************************/
1725 : /* GetFeatureCount() */
1726 : /************************************************************************/
1727 :
1728 16 : GIntBig OGRMVTDirectoryLayer::GetFeatureCount(int bForce)
1729 : {
1730 16 : if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
1731 : {
1732 10 : GIntBig nFeatureCount = 0;
1733 10 : ResetReading();
1734 : while (true)
1735 : {
1736 21 : OpenTileIfNeeded();
1737 21 : if (m_poCurrentTile == nullptr)
1738 10 : break;
1739 : OGRLayer *poUnderlyingLayer =
1740 11 : m_poCurrentTile->GetLayerByName(GetName());
1741 11 : nFeatureCount += poUnderlyingLayer->GetFeatureCount(bForce);
1742 11 : delete m_poCurrentTile;
1743 11 : m_poCurrentTile = nullptr;
1744 11 : }
1745 10 : ResetReading();
1746 10 : return nFeatureCount;
1747 : }
1748 6 : return OGRLayer::GetFeatureCount(bForce);
1749 : }
1750 :
1751 : /************************************************************************/
1752 : /* ISetSpatialFilter() */
1753 : /************************************************************************/
1754 :
1755 49 : OGRErr OGRMVTDirectoryLayer::ISetSpatialFilter(int iGeomField,
1756 : const OGRGeometry *poGeomIn)
1757 : {
1758 49 : OGRLayer::ISetSpatialFilter(iGeomField, poGeomIn);
1759 :
1760 49 : OGREnvelope sEnvelope;
1761 49 : if (m_poFilterGeom != nullptr)
1762 7 : sEnvelope = m_sFilterEnvelope;
1763 49 : if (m_sExtent.IsInit())
1764 : {
1765 44 : if (sEnvelope.IsInit())
1766 7 : sEnvelope.Intersect(m_sExtent);
1767 : else
1768 37 : sEnvelope = m_sExtent;
1769 : }
1770 :
1771 88 : if (sEnvelope.IsInit() && sEnvelope.MinX >= -10 * m_poDS->GetTileDim0() &&
1772 39 : sEnvelope.MinY >= -10 * m_poDS->GetTileDim0() &&
1773 39 : sEnvelope.MaxX <=
1774 127 : 10 * m_poDS->GetTileDim0() * m_poDS->GetTileMatrixWidth0() &&
1775 39 : sEnvelope.MaxY <=
1776 39 : 10 * m_poDS->GetTileDim0() * m_poDS->GetTileMatrixHeight0())
1777 : {
1778 39 : const double dfTileDim = m_poDS->GetTileDim0() / (1 << m_nZ);
1779 39 : m_nFilterMinX = std::max(
1780 78 : 0, static_cast<int>(floor(
1781 39 : (sEnvelope.MinX - m_poDS->GetTopXOrigin()) / dfTileDim)));
1782 39 : m_nFilterMinY = std::max(
1783 78 : 0, static_cast<int>(floor(
1784 39 : (m_poDS->GetTopYOrigin() - sEnvelope.MaxY) / dfTileDim)));
1785 39 : m_nFilterMaxX = std::min(
1786 78 : static_cast<int>(
1787 78 : ceil((sEnvelope.MaxX - m_poDS->GetTopXOrigin()) / dfTileDim)),
1788 39 : static_cast<int>(std::min<int64_t>(
1789 78 : INT_MAX, (static_cast<int64_t>(1) << m_nZ) *
1790 39 : m_poDS->GetTileMatrixWidth0() -
1791 78 : 1)));
1792 39 : m_nFilterMaxY = std::min(
1793 78 : static_cast<int>(
1794 78 : ceil((m_poDS->GetTopYOrigin() - sEnvelope.MinY) / dfTileDim)),
1795 39 : static_cast<int>(std::min<int64_t>(
1796 78 : INT_MAX, (static_cast<int64_t>(1) << m_nZ) *
1797 78 : m_poDS->GetTileMatrixHeight0() -
1798 78 : 1)));
1799 : }
1800 : else
1801 : {
1802 10 : m_nFilterMinX = 0;
1803 10 : m_nFilterMinY = 0;
1804 10 : m_nFilterMaxX = static_cast<int>(
1805 20 : std::min<int64_t>(INT_MAX, (static_cast<int64_t>(1) << m_nZ) *
1806 10 : m_poDS->GetTileMatrixWidth0() -
1807 10 : 1));
1808 10 : m_nFilterMaxY = static_cast<int>(
1809 20 : std::min<int64_t>(INT_MAX, (static_cast<int64_t>(1) << m_nZ) *
1810 20 : m_poDS->GetTileMatrixHeight0() -
1811 10 : 1));
1812 : }
1813 :
1814 49 : return OGRERR_NONE;
1815 : }
1816 :
1817 : /************************************************************************/
1818 : /* TestCapability() */
1819 : /************************************************************************/
1820 :
1821 36 : int OGRMVTDirectoryLayer::TestCapability(const char *pszCap) const
1822 : {
1823 36 : if (EQUAL(pszCap, OLCFastGetExtent))
1824 : {
1825 2 : return TRUE;
1826 : }
1827 34 : return OGRMVTLayerBase::TestCapability(pszCap);
1828 : }
1829 :
1830 : /************************************************************************/
1831 : /* IGetExtent() */
1832 : /************************************************************************/
1833 :
1834 4 : OGRErr OGRMVTDirectoryLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
1835 : bool bForce)
1836 : {
1837 4 : if (m_sExtent.IsInit())
1838 : {
1839 4 : *psExtent = m_sExtent;
1840 4 : return OGRERR_NONE;
1841 : }
1842 0 : return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
1843 : }
1844 :
1845 : /************************************************************************/
1846 : /* CreateFeatureFrom() */
1847 : /************************************************************************/
1848 :
1849 63 : OGRFeature *OGRMVTDirectoryLayer::CreateFeatureFrom(OGRFeature *poSrcFeature)
1850 : {
1851 :
1852 63 : return OGRMVTCreateFeatureFrom(poSrcFeature, m_poFeatureDefn, m_bJsonField,
1853 126 : GetSpatialRef());
1854 : }
1855 :
1856 : /************************************************************************/
1857 : /* GetNextRawFeature() */
1858 : /************************************************************************/
1859 :
1860 119 : OGRFeature *OGRMVTDirectoryLayer::GetNextRawFeature()
1861 : {
1862 : while (true)
1863 : {
1864 119 : OpenTileIfNeeded();
1865 119 : if (m_poCurrentTile == nullptr)
1866 31 : return nullptr;
1867 : OGRLayer *poUnderlyingLayer =
1868 88 : m_poCurrentTile->GetLayerByName(GetName());
1869 88 : OGRFeature *poUnderlyingFeature = poUnderlyingLayer->GetNextFeature();
1870 88 : if (poUnderlyingFeature != nullptr)
1871 : {
1872 61 : OGRFeature *poFeature = CreateFeatureFrom(poUnderlyingFeature);
1873 122 : poFeature->SetFID(m_nFIDBase +
1874 61 : (poUnderlyingFeature->GetFID() << (2 * m_nZ)));
1875 61 : delete poUnderlyingFeature;
1876 61 : return poFeature;
1877 : }
1878 : else
1879 : {
1880 27 : delete m_poCurrentTile;
1881 27 : m_poCurrentTile = nullptr;
1882 : }
1883 27 : }
1884 : }
1885 :
1886 : /************************************************************************/
1887 : /* GetFeature() */
1888 : /************************************************************************/
1889 :
1890 5 : OGRFeature *OGRMVTDirectoryLayer::GetFeature(GIntBig nFID)
1891 : {
1892 5 : const int nX = static_cast<int>(nFID & ((1 << m_nZ) - 1));
1893 5 : const int nY = static_cast<int>((nFID >> m_nZ) & ((1 << m_nZ) - 1));
1894 5 : const GIntBig nTileFID = nFID >> (2 * m_nZ);
1895 15 : const CPLString osFilename = CPLFormFilenameSafe(
1896 5 : CPLFormFilenameSafe(m_osDirName, CPLSPrintf("%d", nX), nullptr).c_str(),
1897 15 : CPLSPrintf("%d.%s", nY, m_poDS->m_osTileExtension.c_str()), nullptr);
1898 5 : GDALOpenInfo oOpenInfo(("MVT:" + osFilename).c_str(), GA_ReadOnly);
1899 5 : oOpenInfo.papszOpenOptions = CSLSetNameValue(
1900 : nullptr, "METADATA_FILE",
1901 5 : m_bJsonField ? "" : m_poDS->m_osMetadataMemFilename.c_str());
1902 5 : oOpenInfo.papszOpenOptions = CSLSetNameValue(
1903 : oOpenInfo.papszOpenOptions, "DO_NOT_ERROR_ON_MISSING_TILE", "YES");
1904 : GDALDataset *poTile =
1905 5 : OGRMVTDataset::Open(&oOpenInfo, /* bRecurseAllowed = */ false);
1906 5 : CSLDestroy(oOpenInfo.papszOpenOptions);
1907 5 : OGRFeature *poFeature = nullptr;
1908 5 : if (poTile)
1909 : {
1910 5 : OGRLayer *poLayer = poTile->GetLayerByName(GetName());
1911 5 : if (poLayer)
1912 : {
1913 5 : OGRFeature *poUnderlyingFeature = poLayer->GetFeature(nTileFID);
1914 5 : if (poUnderlyingFeature)
1915 : {
1916 2 : poFeature = CreateFeatureFrom(poUnderlyingFeature);
1917 2 : poFeature->SetFID(nFID);
1918 : }
1919 5 : delete poUnderlyingFeature;
1920 : }
1921 : }
1922 5 : delete poTile;
1923 10 : return poFeature;
1924 : }
1925 :
1926 : /************************************************************************/
1927 : /* GetDataset() */
1928 : /************************************************************************/
1929 :
1930 1 : GDALDataset *OGRMVTDirectoryLayer::GetDataset()
1931 : {
1932 1 : return m_poDS;
1933 : }
1934 :
1935 : /************************************************************************/
1936 : /* OGRMVTDataset() */
1937 : /************************************************************************/
1938 :
1939 992 : OGRMVTDataset::OGRMVTDataset(GByte *pabyData)
1940 992 : : m_pabyData(pabyData), m_poSRS(new OGRSpatialReference())
1941 : {
1942 992 : m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1943 :
1944 992 : m_bClip = CPLTestBool(CPLGetConfigOption("OGR_MVT_CLIP", "YES"));
1945 :
1946 : // Default WebMercator tiling scheme
1947 992 : InitWebMercatorTilingScheme(m_poSRS, m_dfTopXOrigin, m_dfTopYOrigin,
1948 992 : m_dfTileDim0);
1949 992 : }
1950 :
1951 : /************************************************************************/
1952 : /* ~OGRMVTDataset() */
1953 : /************************************************************************/
1954 :
1955 1984 : OGRMVTDataset::~OGRMVTDataset()
1956 : {
1957 992 : VSIFree(m_pabyData);
1958 992 : if (!m_osMetadataMemFilename.empty())
1959 18 : VSIUnlink(m_osMetadataMemFilename);
1960 992 : if (m_poSRS)
1961 989 : m_poSRS->Release();
1962 1984 : }
1963 :
1964 : /************************************************************************/
1965 : /* GetLayer() */
1966 : /************************************************************************/
1967 :
1968 1722 : const OGRLayer *OGRMVTDataset::GetLayer(int iLayer) const
1969 :
1970 : {
1971 1722 : if (iLayer < 0 || iLayer >= GetLayerCount())
1972 4 : return nullptr;
1973 1718 : return m_apoLayers[iLayer].get();
1974 : }
1975 :
1976 : /************************************************************************/
1977 : /* Identify() */
1978 : /************************************************************************/
1979 :
1980 55091 : static int OGRMVTDriverIdentify(GDALOpenInfo *poOpenInfo)
1981 :
1982 : {
1983 55091 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "MVT:"))
1984 1825 : return TRUE;
1985 :
1986 53266 : if (STARTS_WITH(poOpenInfo->pszFilename, "/vsicurl"))
1987 : {
1988 1 : if (CPLGetValueType(CPLGetFilename(poOpenInfo->pszFilename)) ==
1989 : CPL_VALUE_INTEGER)
1990 : {
1991 0 : return TRUE;
1992 : }
1993 : }
1994 :
1995 53266 : if (poOpenInfo->bIsDirectory)
1996 : {
1997 722 : if (CPLGetValueType(CPLGetFilename(poOpenInfo->pszFilename)) ==
1998 : CPL_VALUE_INTEGER)
1999 : {
2000 : VSIStatBufL sStat;
2001 68 : CPLString osMetadataFile(CPLFormFilenameSafe(
2002 34 : CPLGetPathSafe(poOpenInfo->pszFilename).c_str(),
2003 34 : "metadata.json", nullptr));
2004 68 : const char *pszMetadataFile = CSLFetchNameValue(
2005 34 : poOpenInfo->papszOpenOptions, "METADATA_FILE");
2006 34 : if (pszMetadataFile)
2007 : {
2008 4 : osMetadataFile = pszMetadataFile;
2009 : }
2010 64 : if (!osMetadataFile.empty() &&
2011 30 : (STARTS_WITH(osMetadataFile, "http://") ||
2012 30 : STARTS_WITH(osMetadataFile, "https://") ||
2013 30 : VSIStatL(osMetadataFile, &sStat) == 0))
2014 : {
2015 26 : return TRUE;
2016 : }
2017 8 : if (pszMetadataFile == nullptr)
2018 : {
2019 : // tileserver-gl metadata file:
2020 : // If opening /path/to/foo/0, try looking for /path/to/foo.json
2021 4 : CPLString osParentDir(CPLGetPathSafe(poOpenInfo->pszFilename));
2022 : osMetadataFile =
2023 8 : CPLFormFilenameSafe(CPLGetPathSafe(osParentDir).c_str(),
2024 4 : CPLGetFilename(osParentDir), "json");
2025 4 : if (VSIStatL(osMetadataFile, &sStat) == 0)
2026 : {
2027 2 : return TRUE;
2028 : }
2029 : }
2030 :
2031 : // At least 3 files, to include the dummy . and ..
2032 : const CPLStringList aosDirContent = StripDummyEntries(
2033 6 : CPLStringList(VSIReadDirEx(poOpenInfo->pszFilename, 3)));
2034 12 : if (!aosDirContent.empty() &&
2035 6 : CPLGetValueType(aosDirContent[0]) == CPL_VALUE_INTEGER)
2036 : {
2037 : const std::string osSubDir = CPLFormFilenameSafe(
2038 6 : poOpenInfo->pszFilename, aosDirContent[0], nullptr);
2039 : // At least 3 files, to include the dummy . and ..
2040 : const CPLStringList aosSubDirContent = StripDummyEntries(
2041 6 : CPLStringList(VSIReadDirEx(osSubDir.c_str(), 10)));
2042 : const std::string osTileExtension(CSLFetchNameValueDef(
2043 6 : poOpenInfo->papszOpenOptions, "TILE_EXTENSION", "pbf"));
2044 6 : for (int i = 0; i < aosSubDirContent.Count(); i++)
2045 : {
2046 6 : if (CPLGetValueType(
2047 12 : CPLGetBasenameSafe(aosSubDirContent[i]).c_str()) ==
2048 : CPL_VALUE_INTEGER)
2049 : {
2050 : const std::string osExtension(
2051 6 : CPLGetExtensionSafe(aosSubDirContent[i]));
2052 6 : if (EQUAL(osExtension.c_str(),
2053 6 : osTileExtension.c_str()) ||
2054 0 : EQUAL(osExtension.c_str(), "mvt"))
2055 : {
2056 6 : return TRUE;
2057 : }
2058 : }
2059 : }
2060 : }
2061 : }
2062 688 : return FALSE;
2063 : }
2064 :
2065 52544 : if (poOpenInfo->nHeaderBytes <= 2)
2066 49696 : return FALSE;
2067 :
2068 : // GZip header ?
2069 2848 : if (poOpenInfo->pabyHeader[0] == 0x1F && poOpenInfo->pabyHeader[1] == 0x8B)
2070 : {
2071 : // Prevent recursion
2072 36 : if (STARTS_WITH(poOpenInfo->pszFilename, "/vsigzip/"))
2073 : {
2074 0 : return FALSE;
2075 : }
2076 : CPLConfigOptionSetter oSetter("CPL_VSIL_GZIP_WRITE_PROPERTIES", "NO",
2077 72 : false);
2078 : GDALOpenInfo oOpenInfo(
2079 72 : (CPLString("/vsigzip/") + poOpenInfo->pszFilename).c_str(),
2080 72 : GA_ReadOnly);
2081 36 : return OGRMVTDriverIdentify(&oOpenInfo);
2082 : }
2083 :
2084 : // The GPB macros assume that the buffer is nul terminated,
2085 : // which is the case
2086 2812 : const GByte *pabyData = reinterpret_cast<GByte *>(poOpenInfo->pabyHeader);
2087 2812 : const GByte *const pabyDataStart = pabyData;
2088 : const GByte *pabyLayerStart;
2089 2812 : const GByte *const pabyDataLimit = pabyData + poOpenInfo->nHeaderBytes;
2090 2812 : const GByte *pabyLayerEnd = pabyDataLimit;
2091 2812 : int nKey = 0;
2092 2812 : unsigned int nLayerLength = 0;
2093 2812 : bool bLayerNameFound = false;
2094 2812 : bool bKeyFound = false;
2095 2812 : bool bFeatureFound = false;
2096 2812 : bool bVersionFound = false;
2097 :
2098 : try
2099 : {
2100 2812 : READ_FIELD_KEY(nKey);
2101 2810 : if (nKey != MAKE_KEY(knLAYER, WT_DATA))
2102 2761 : return FALSE;
2103 49 : READ_VARUINT32(pabyData, pabyDataLimit, nLayerLength);
2104 49 : pabyLayerStart = pabyData;
2105 :
2106 : // Sanity check on layer length
2107 49 : if (nLayerLength < static_cast<unsigned>(poOpenInfo->nHeaderBytes -
2108 49 : (pabyData - pabyDataStart)))
2109 : {
2110 7 : if (pabyData[nLayerLength] != MAKE_KEY(knLAYER, WT_DATA))
2111 1 : return FALSE;
2112 6 : pabyLayerEnd = pabyData + nLayerLength;
2113 : }
2114 42 : else if (nLayerLength > 10 * 1024 * 1024)
2115 : {
2116 0 : return FALSE;
2117 : }
2118 :
2119 : // Quick scan on partial layer content to see if it seems to conform to
2120 : // the proto
2121 544 : while (pabyData < pabyLayerEnd)
2122 : {
2123 498 : READ_VARUINT32(pabyData, pabyLayerEnd, nKey);
2124 498 : auto nFieldNumber = GET_FIELDNUMBER(nKey);
2125 498 : auto nWireType = GET_WIRETYPE(nKey);
2126 498 : if (nFieldNumber == knLAYER_NAME)
2127 : {
2128 48 : if (nWireType != WT_DATA)
2129 : {
2130 0 : CPLDebug("MVT", "Invalid wire type for layer_name field");
2131 : }
2132 48 : char *pszLayerName = nullptr;
2133 48 : unsigned int nTextSize = 0;
2134 48 : READ_TEXT_WITH_SIZE(pabyData, pabyLayerEnd, pszLayerName,
2135 : nTextSize);
2136 48 : if (nTextSize == 0 || !CPLIsUTF8(pszLayerName, nTextSize))
2137 : {
2138 0 : CPLFree(pszLayerName);
2139 0 : CPLDebug("MVT", "Protobuf error: line %d", __LINE__);
2140 0 : return FALSE;
2141 : }
2142 48 : CPLFree(pszLayerName);
2143 48 : bLayerNameFound = true;
2144 : }
2145 450 : else if (nFieldNumber == knLAYER_FEATURES)
2146 : {
2147 52 : if (nWireType != WT_DATA)
2148 : {
2149 0 : CPLDebug("MVT",
2150 : "Invalid wire type for layer_features field");
2151 : }
2152 52 : unsigned int nFeatureLength = 0;
2153 52 : unsigned int nGeomType = 0;
2154 52 : READ_VARUINT32(pabyData, pabyLayerEnd, nFeatureLength);
2155 52 : if (nFeatureLength > nLayerLength - (pabyData - pabyLayerStart))
2156 : {
2157 0 : CPLDebug("MVT", "Protobuf error: line %d", __LINE__);
2158 0 : return FALSE;
2159 : }
2160 52 : bFeatureFound = true;
2161 :
2162 52 : const GByte *const pabyDataFeatureStart = pabyData;
2163 : const GByte *const pabyDataFeatureEnd =
2164 : pabyDataStart +
2165 104 : std::min(static_cast<int>(pabyData + nFeatureLength -
2166 : pabyDataStart),
2167 52 : poOpenInfo->nHeaderBytes);
2168 168 : while (pabyData < pabyDataFeatureEnd)
2169 : {
2170 118 : READ_VARUINT32(pabyData, pabyDataFeatureEnd, nKey);
2171 118 : nFieldNumber = GET_FIELDNUMBER(nKey);
2172 118 : nWireType = GET_WIRETYPE(nKey);
2173 118 : if (nFieldNumber == knFEATURE_TYPE)
2174 : {
2175 48 : if (nWireType != WT_VARINT)
2176 : {
2177 0 : CPLDebug(
2178 : "MVT",
2179 : "Invalid wire type for feature_type field");
2180 0 : return FALSE;
2181 : }
2182 48 : READ_VARUINT32(pabyData, pabyDataFeatureEnd, nGeomType);
2183 48 : if (nGeomType > knGEOM_TYPE_POLYGON)
2184 : {
2185 0 : CPLDebug("MVT", "Protobuf error: line %d",
2186 : __LINE__);
2187 0 : return FALSE;
2188 : }
2189 : }
2190 70 : else if (nFieldNumber == knFEATURE_TAGS)
2191 : {
2192 18 : if (nWireType != WT_DATA)
2193 : {
2194 0 : CPLDebug(
2195 : "MVT",
2196 : "Invalid wire type for feature_tags field");
2197 0 : return FALSE;
2198 : }
2199 18 : unsigned int nTagsSize = 0;
2200 18 : READ_VARUINT32(pabyData, pabyDataFeatureEnd, nTagsSize);
2201 18 : if (nTagsSize == 0 ||
2202 18 : nTagsSize > nFeatureLength -
2203 18 : (pabyData - pabyDataFeatureStart))
2204 : {
2205 0 : CPLDebug("MVT", "Protobuf error: line %d",
2206 : __LINE__);
2207 0 : return FALSE;
2208 : }
2209 : const GByte *const pabyDataTagsEnd =
2210 : pabyDataStart +
2211 36 : std::min(static_cast<int>(pabyData + nTagsSize -
2212 : pabyDataStart),
2213 18 : poOpenInfo->nHeaderBytes);
2214 400 : while (pabyData < pabyDataTagsEnd)
2215 : {
2216 382 : unsigned int nKeyIdx = 0;
2217 382 : unsigned int nValIdx = 0;
2218 382 : READ_VARUINT32(pabyData, pabyDataTagsEnd, nKeyIdx);
2219 382 : READ_VARUINT32(pabyData, pabyDataTagsEnd, nValIdx);
2220 382 : if (nKeyIdx > 10 * 1024 * 1024 ||
2221 : nValIdx > 10 * 1024 * 1024)
2222 : {
2223 0 : CPLDebug("MVT", "Protobuf error: line %d",
2224 : __LINE__);
2225 0 : return FALSE;
2226 : }
2227 : }
2228 : }
2229 52 : else if (nFieldNumber == knFEATURE_GEOMETRY &&
2230 : nWireType != WT_DATA)
2231 : {
2232 0 : CPLDebug(
2233 : "MVT",
2234 : "Invalid wire type for feature_geometry field");
2235 0 : return FALSE;
2236 : }
2237 52 : else if (nKey == MAKE_KEY(knFEATURE_GEOMETRY, WT_DATA) &&
2238 48 : nGeomType >= knGEOM_TYPE_POINT &&
2239 : nGeomType <= knGEOM_TYPE_POLYGON)
2240 : {
2241 48 : unsigned int nGeometrySize = 0;
2242 48 : READ_VARUINT32(pabyData, pabyDataFeatureEnd,
2243 : nGeometrySize);
2244 48 : if (nGeometrySize == 0 ||
2245 48 : nGeometrySize >
2246 48 : nFeatureLength -
2247 48 : (pabyData - pabyDataFeatureStart))
2248 : {
2249 0 : CPLDebug("MVT", "Protobuf error: line %d",
2250 : __LINE__);
2251 0 : return FALSE;
2252 : }
2253 : const GByte *const pabyDataGeometryEnd =
2254 : pabyDataStart +
2255 96 : std::min(static_cast<int>(pabyData + nGeometrySize -
2256 : pabyDataStart),
2257 48 : poOpenInfo->nHeaderBytes);
2258 :
2259 48 : if (nGeomType == knGEOM_TYPE_POINT)
2260 : {
2261 14 : unsigned int nCmdCountCombined = 0;
2262 : unsigned int nCount;
2263 14 : READ_VARUINT32(pabyData, pabyDataGeometryEnd,
2264 : nCmdCountCombined);
2265 14 : nCount = GetCmdCount(nCmdCountCombined);
2266 28 : if (GetCmdId(nCmdCountCombined) != knCMD_MOVETO ||
2267 28 : nCount == 0 || nCount > 10 * 1024 * 1024)
2268 : {
2269 0 : CPLDebug("MVT", "Protobuf error: line %d",
2270 : __LINE__);
2271 0 : return FALSE;
2272 : }
2273 46 : for (unsigned i = 0; i < 2 * nCount; i++)
2274 : {
2275 32 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
2276 : }
2277 : }
2278 34 : else if (nGeomType == knGEOM_TYPE_LINESTRING)
2279 : {
2280 56 : while (pabyData < pabyDataGeometryEnd)
2281 : {
2282 32 : unsigned int nCmdCountCombined = 0;
2283 : unsigned int nLineToCount;
2284 : // Should be a moveto
2285 32 : READ_VARUINT32(pabyData, pabyDataGeometryEnd,
2286 : nCmdCountCombined);
2287 32 : if (GetCmdId(nCmdCountCombined) !=
2288 64 : knCMD_MOVETO ||
2289 32 : GetCmdCount(nCmdCountCombined) != 1)
2290 : {
2291 0 : CPLDebug("MVT", "Protobuf error: line %d",
2292 : __LINE__);
2293 0 : return FALSE;
2294 : }
2295 32 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
2296 32 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
2297 32 : READ_VARUINT32(pabyData, pabyDataGeometryEnd,
2298 : nCmdCountCombined);
2299 32 : if (GetCmdId(nCmdCountCombined) != knCMD_LINETO)
2300 : {
2301 0 : CPLDebug("MVT", "Protobuf error: line %d",
2302 : __LINE__);
2303 0 : return FALSE;
2304 : }
2305 32 : nLineToCount = GetCmdCount(nCmdCountCombined);
2306 96 : for (unsigned i = 0; i < 2 * nLineToCount; i++)
2307 : {
2308 64 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
2309 : }
2310 : }
2311 : }
2312 : else /* if( nGeomType == knGEOM_TYPE_POLYGON ) */
2313 : {
2314 382 : while (pabyData < pabyDataGeometryEnd)
2315 : {
2316 374 : unsigned int nCmdCountCombined = 0;
2317 : unsigned int nLineToCount;
2318 : // Should be a moveto
2319 374 : READ_VARUINT32(pabyData, pabyDataGeometryEnd,
2320 : nCmdCountCombined);
2321 374 : if (GetCmdId(nCmdCountCombined) !=
2322 748 : knCMD_MOVETO ||
2323 374 : GetCmdCount(nCmdCountCombined) != 1)
2324 : {
2325 0 : CPLDebug("MVT", "Protobuf error: line %d",
2326 : __LINE__);
2327 0 : return FALSE;
2328 : }
2329 374 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
2330 374 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
2331 374 : READ_VARUINT32(pabyData, pabyDataGeometryEnd,
2332 : nCmdCountCombined);
2333 374 : if (GetCmdId(nCmdCountCombined) != knCMD_LINETO)
2334 : {
2335 0 : CPLDebug("MVT", "Protobuf error: line %d",
2336 : __LINE__);
2337 0 : return FALSE;
2338 : }
2339 374 : nLineToCount = GetCmdCount(nCmdCountCombined);
2340 7020 : for (unsigned i = 0; i < 2 * nLineToCount; i++)
2341 : {
2342 6648 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
2343 : }
2344 : // Should be a closepath
2345 372 : READ_VARUINT32(pabyData, pabyDataGeometryEnd,
2346 : nCmdCountCombined);
2347 372 : if (GetCmdId(nCmdCountCombined) !=
2348 744 : knCMD_CLOSEPATH ||
2349 372 : GetCmdCount(nCmdCountCombined) != 1)
2350 : {
2351 0 : CPLDebug("MVT", "Protobuf error: line %d",
2352 : __LINE__);
2353 0 : return FALSE;
2354 : }
2355 : }
2356 : }
2357 :
2358 46 : pabyData = pabyDataGeometryEnd;
2359 : }
2360 : else
2361 : {
2362 4 : SKIP_UNKNOWN_FIELD(pabyData, pabyDataFeatureEnd, FALSE);
2363 : }
2364 : }
2365 :
2366 50 : pabyData = pabyDataFeatureEnd;
2367 : }
2368 398 : else if (nFieldNumber == knLAYER_KEYS)
2369 : {
2370 152 : if (nWireType != WT_DATA)
2371 : {
2372 0 : CPLDebug("MVT", "Invalid wire type for keys field");
2373 0 : return FALSE;
2374 : }
2375 152 : char *pszKey = nullptr;
2376 152 : unsigned int nTextSize = 0;
2377 152 : READ_TEXT_WITH_SIZE(pabyData, pabyLayerEnd, pszKey, nTextSize);
2378 152 : if (!CPLIsUTF8(pszKey, nTextSize))
2379 : {
2380 0 : CPLDebug("MVT", "Protobuf error: line %d", __LINE__);
2381 0 : CPLFree(pszKey);
2382 0 : return FALSE;
2383 : }
2384 152 : CPLFree(pszKey);
2385 152 : bKeyFound = true;
2386 : }
2387 246 : else if (nFieldNumber == knLAYER_VALUES)
2388 : {
2389 156 : if (nWireType != WT_DATA)
2390 : {
2391 0 : CPLDebug("MVT", "Invalid wire type for values field");
2392 0 : return FALSE;
2393 : }
2394 156 : unsigned int nValueLength = 0;
2395 156 : READ_VARUINT32(pabyData, pabyLayerEnd, nValueLength);
2396 156 : if (nValueLength == 0 ||
2397 156 : nValueLength > nLayerLength - (pabyData - pabyLayerStart))
2398 : {
2399 0 : CPLDebug("MVT", "Protobuf error: line %d", __LINE__);
2400 0 : return FALSE;
2401 : }
2402 156 : pabyData += nValueLength;
2403 : }
2404 90 : else if (GET_FIELDNUMBER(nKey) == knLAYER_EXTENT &&
2405 42 : GET_WIRETYPE(nKey) != WT_VARINT)
2406 : {
2407 0 : CPLDebug("MVT", "Invalid wire type for extent field");
2408 0 : return FALSE;
2409 : }
2410 : #if 0
2411 : // The check on extent is too fragile. Values of 65536 can be found
2412 : else if( nKey == MAKE_KEY(knLAYER_EXTENT, WT_VARINT) )
2413 : {
2414 : unsigned int nExtent = 0;
2415 : READ_VARUINT32(pabyData, pabyLayerEnd, nExtent);
2416 : if( nExtent < 128 || nExtent > 16834 )
2417 : {
2418 : CPLDebug("MVT", "Invalid extent: %u", nExtent);
2419 : return FALSE;
2420 : }
2421 : }
2422 : #endif
2423 90 : else if (nFieldNumber == knLAYER_VERSION)
2424 : {
2425 46 : if (nWireType != WT_VARINT)
2426 : {
2427 0 : CPLDebug("MVT", "Invalid wire type for version field");
2428 0 : return FALSE;
2429 : }
2430 46 : unsigned int nVersion = 0;
2431 46 : READ_VARUINT32(pabyData, pabyLayerEnd, nVersion);
2432 46 : if (nVersion != 1 && nVersion != 2)
2433 : {
2434 0 : CPLDebug("MVT", "Invalid version: %u", nVersion);
2435 0 : return FALSE;
2436 : }
2437 46 : bVersionFound = true;
2438 : }
2439 : else
2440 : {
2441 44 : SKIP_UNKNOWN_FIELD(pabyData, pabyLayerEnd, FALSE);
2442 : }
2443 : }
2444 : }
2445 4 : catch (const GPBException &)
2446 : {
2447 : }
2448 :
2449 50 : return bLayerNameFound && (bKeyFound || bFeatureFound || bVersionFound);
2450 : }
2451 :
2452 : /************************************************************************/
2453 : /* LongLatToSphericalMercator() */
2454 : /************************************************************************/
2455 :
2456 30 : static void LongLatToSphericalMercator(double *x, double *y)
2457 : {
2458 30 : double X = kmSPHERICAL_RADIUS * (*x) / 180 * M_PI;
2459 : double Y =
2460 30 : kmSPHERICAL_RADIUS * log(tan(M_PI / 4 + 0.5 * (*y) / 180 * M_PI));
2461 30 : *x = X;
2462 30 : *y = Y;
2463 30 : }
2464 :
2465 : /************************************************************************/
2466 : /* LoadMetadata() */
2467 : /************************************************************************/
2468 :
2469 930 : static bool LoadMetadata(const CPLString &osMetadataFile,
2470 : const CPLString &osMetadataContent,
2471 : CPLJSONArray &oVectorLayers,
2472 : CPLJSONArray &oTileStatLayers, CPLJSONObject &oBounds,
2473 : OGRSpatialReference *poSRS, double &dfTopX,
2474 : double &dfTopY, double &dfTileDim0,
2475 : int &nTileMatrixWidth0, int &nTileMatrixHeight0,
2476 : const CPLString &osMetadataMemFilename)
2477 :
2478 : {
2479 1860 : CPLJSONDocument oDoc;
2480 :
2481 : bool bLoadOK;
2482 930 : if (!osMetadataContent.empty())
2483 : {
2484 3 : bLoadOK = oDoc.LoadMemory(osMetadataContent);
2485 : }
2486 1854 : else if (STARTS_WITH(osMetadataFile, "http://") ||
2487 927 : STARTS_WITH(osMetadataFile, "https://"))
2488 : {
2489 0 : bLoadOK = oDoc.LoadUrl(osMetadataFile, nullptr);
2490 : }
2491 : else
2492 : {
2493 927 : bLoadOK = oDoc.Load(osMetadataFile);
2494 : }
2495 930 : if (!bLoadOK)
2496 2 : return false;
2497 :
2498 2784 : const CPLJSONObject oCrs(oDoc.GetRoot().GetObj("crs"));
2499 : const CPLJSONObject oTopX(
2500 2784 : oDoc.GetRoot().GetObj("tile_origin_upper_left_x"));
2501 : const CPLJSONObject oTopY(
2502 2784 : oDoc.GetRoot().GetObj("tile_origin_upper_left_y"));
2503 : const CPLJSONObject oTileDim0(
2504 2784 : oDoc.GetRoot().GetObj("tile_dimension_zoom_0"));
2505 928 : nTileMatrixWidth0 = 1;
2506 928 : nTileMatrixHeight0 = 1;
2507 934 : if (oCrs.IsValid() && oTopX.IsValid() && oTopY.IsValid() &&
2508 6 : oTileDim0.IsValid())
2509 : {
2510 6 : poSRS->SetFromUserInput(oCrs.ToString().c_str());
2511 6 : dfTopX = oTopX.ToDouble();
2512 6 : dfTopY = oTopY.ToDouble();
2513 6 : dfTileDim0 = oTileDim0.ToDouble();
2514 : const CPLJSONObject oTMWidth0(
2515 18 : oDoc.GetRoot().GetObj("tile_matrix_width_zoom_0"));
2516 6 : if (oTMWidth0.GetType() == CPLJSONObject::Type::Integer)
2517 6 : nTileMatrixWidth0 = std::max(1, oTMWidth0.ToInteger());
2518 :
2519 : const CPLJSONObject oTMHeight0(
2520 18 : oDoc.GetRoot().GetObj("tile_matrix_height_zoom_0"));
2521 6 : if (oTMHeight0.GetType() == CPLJSONObject::Type::Integer)
2522 6 : nTileMatrixHeight0 = std::max(1, oTMHeight0.ToInteger());
2523 :
2524 : // Assumes WorldCRS84Quad with 2 tiles in width
2525 : // cf https://github.com/OSGeo/gdal/issues/11749
2526 6 : if (!oTMWidth0.IsValid() && dfTopX == -180 && dfTileDim0 == 180)
2527 0 : nTileMatrixWidth0 = 2;
2528 : }
2529 :
2530 928 : oVectorLayers.Deinit();
2531 928 : oTileStatLayers.Deinit();
2532 :
2533 2784 : CPLJSONObject oJson = oDoc.GetRoot().GetObj("json");
2534 928 : if (!(oJson.IsValid() && oJson.GetType() == CPLJSONObject::Type::String))
2535 : {
2536 534 : oVectorLayers = oDoc.GetRoot().GetArray("vector_layers");
2537 :
2538 534 : oTileStatLayers = oDoc.GetRoot().GetArray("tilestats/layers");
2539 : }
2540 : else
2541 : {
2542 394 : CPLJSONDocument oJsonDoc;
2543 394 : if (!oJsonDoc.LoadMemory(oJson.ToString()))
2544 : {
2545 1 : return false;
2546 : }
2547 :
2548 393 : oVectorLayers = oJsonDoc.GetRoot().GetArray("vector_layers");
2549 :
2550 393 : oTileStatLayers = oJsonDoc.GetRoot().GetArray("tilestats/layers");
2551 : }
2552 :
2553 927 : oBounds = oDoc.GetRoot().GetObj("bounds");
2554 :
2555 927 : if (!osMetadataMemFilename.empty())
2556 : {
2557 18 : oDoc.Save(osMetadataMemFilename);
2558 : }
2559 :
2560 927 : return oVectorLayers.IsValid();
2561 : }
2562 :
2563 : /************************************************************************/
2564 : /* ConvertFromWGS84() */
2565 : /************************************************************************/
2566 :
2567 18 : static void ConvertFromWGS84(OGRSpatialReference *poTargetSRS, double &dfX0,
2568 : double &dfY0, double &dfX1, double &dfY1)
2569 : {
2570 36 : OGRSpatialReference oSRS_EPSG3857;
2571 18 : oSRS_EPSG3857.SetFromUserInput(SRS_EPSG_3857);
2572 :
2573 18 : if (poTargetSRS->IsSame(&oSRS_EPSG3857))
2574 : {
2575 15 : LongLatToSphericalMercator(&dfX0, &dfY0);
2576 15 : LongLatToSphericalMercator(&dfX1, &dfY1);
2577 : }
2578 : else
2579 : {
2580 6 : OGRSpatialReference oSRS_EPSG4326;
2581 3 : oSRS_EPSG4326.SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
2582 3 : oSRS_EPSG4326.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2583 : OGRCoordinateTransformation *poCT =
2584 3 : OGRCreateCoordinateTransformation(&oSRS_EPSG4326, poTargetSRS);
2585 3 : if (poCT)
2586 : {
2587 3 : poCT->Transform(1, &dfX0, &dfY0);
2588 3 : poCT->Transform(1, &dfX1, &dfY1);
2589 3 : delete poCT;
2590 : }
2591 : }
2592 18 : }
2593 :
2594 : /************************************************************************/
2595 : /* OpenDirectory() */
2596 : /************************************************************************/
2597 :
2598 25 : GDALDataset *OGRMVTDataset::OpenDirectory(GDALOpenInfo *poOpenInfo)
2599 :
2600 : {
2601 50 : const CPLString osZ(CPLGetFilename(poOpenInfo->pszFilename));
2602 25 : if (CPLGetValueType(osZ) != CPL_VALUE_INTEGER)
2603 1 : return nullptr;
2604 :
2605 24 : const int nZ = atoi(osZ);
2606 24 : if (nZ < 0 || nZ > 30)
2607 1 : return nullptr;
2608 :
2609 : CPLString osMetadataFile(
2610 46 : CPLFormFilenameSafe(CPLGetPathSafe(poOpenInfo->pszFilename).c_str(),
2611 46 : "metadata.json", nullptr));
2612 : const char *pszMetadataFile =
2613 23 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "METADATA_FILE");
2614 23 : if (pszMetadataFile)
2615 : {
2616 2 : osMetadataFile = pszMetadataFile;
2617 : }
2618 :
2619 23 : CPLString osTileExtension(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
2620 46 : "TILE_EXTENSION", "pbf"));
2621 : bool bJsonField =
2622 23 : CPLFetchBool(poOpenInfo->papszOpenOptions, "JSON_FIELD", false);
2623 : VSIStatBufL sStat;
2624 :
2625 23 : bool bMetadataFileExists = false;
2626 46 : CPLString osMetadataContent;
2627 41 : if (STARTS_WITH(osMetadataFile, "http://") ||
2628 18 : STARTS_WITH(osMetadataFile, "https://"))
2629 : {
2630 8 : for (int i = 0; i < 2; i++)
2631 : {
2632 8 : if (pszMetadataFile == nullptr)
2633 8 : CPLPushErrorHandler(CPLQuietErrorHandler);
2634 8 : CPLHTTPResult *psResult = CPLHTTPFetch(osMetadataFile, nullptr);
2635 8 : if (pszMetadataFile == nullptr)
2636 8 : CPLPopErrorHandler();
2637 8 : if (psResult == nullptr)
2638 : {
2639 0 : osMetadataFile.clear();
2640 : }
2641 8 : else if (psResult->pszErrBuf != nullptr ||
2642 3 : psResult->pabyData == nullptr)
2643 : {
2644 5 : CPLHTTPDestroyResult(psResult);
2645 5 : osMetadataFile.clear();
2646 :
2647 5 : if (i == 0 && pszMetadataFile == nullptr)
2648 : {
2649 : // tileserver-gl metadata file:
2650 : // If opening /path/to/foo/0, try looking for
2651 : // /path/to/foo.json
2652 : CPLString osParentDir(
2653 6 : CPLGetPathSafe(poOpenInfo->pszFilename));
2654 6 : osMetadataFile = CPLFormFilenameSafe(
2655 6 : CPLGetPathSafe(osParentDir).c_str(),
2656 3 : CPLGetFilename(osParentDir), "json");
2657 3 : continue;
2658 2 : }
2659 : }
2660 : else
2661 : {
2662 3 : bMetadataFileExists = true;
2663 : osMetadataContent =
2664 3 : reinterpret_cast<const char *>(psResult->pabyData);
2665 3 : CPLHTTPDestroyResult(psResult);
2666 : }
2667 5 : break;
2668 : }
2669 : }
2670 18 : else if (!osMetadataFile.empty())
2671 : {
2672 16 : bMetadataFileExists = (VSIStatL(osMetadataFile, &sStat) == 0);
2673 16 : if (!bMetadataFileExists && pszMetadataFile == nullptr)
2674 : {
2675 : // tileserver-gl metadata file:
2676 : // If opening /path/to/foo/0, try looking for /path/to/foo.json
2677 2 : CPLString osParentDir(CPLGetPathSafe(poOpenInfo->pszFilename));
2678 : osMetadataFile =
2679 4 : CPLFormFilenameSafe(CPLGetPathSafe(osParentDir).c_str(),
2680 2 : CPLGetFilename(osParentDir), "json");
2681 2 : bMetadataFileExists = (VSIStatL(osMetadataFile, &sStat) == 0);
2682 : }
2683 : }
2684 :
2685 23 : if (!bMetadataFileExists)
2686 : {
2687 : // If we don't have a metadata file, iterate through all tiles to
2688 : // establish the layer definitions.
2689 5 : OGRMVTDataset *poDS = nullptr;
2690 5 : bool bTryToListDir =
2691 10 : !STARTS_WITH(poOpenInfo->pszFilename, "/vsicurl/") &&
2692 5 : !STARTS_WITH(poOpenInfo->pszFilename, "/vsicurl_streaming/") &&
2693 5 : !STARTS_WITH(poOpenInfo->pszFilename, "/vsicurl?") &&
2694 13 : !STARTS_WITH(poOpenInfo->pszFilename, "http://") &&
2695 3 : !STARTS_WITH(poOpenInfo->pszFilename, "https://");
2696 5 : CPLStringList aosDirContent;
2697 5 : if (bTryToListDir)
2698 : {
2699 3 : aosDirContent = VSIReadDir(poOpenInfo->pszFilename);
2700 3 : aosDirContent = StripDummyEntries(aosDirContent);
2701 : }
2702 10 : const int nMaxTiles = atoi(CSLFetchNameValueDef(
2703 5 : poOpenInfo->papszOpenOptions,
2704 : "TILE_COUNT_TO_ESTABLISH_FEATURE_DEFN", "1000"));
2705 5 : int nCountTiles = 0;
2706 5 : int nFailedAttempts = 0;
2707 11 : for (int i = 0; i < (bTryToListDir ? aosDirContent.Count() : (1 << nZ));
2708 : i++)
2709 : {
2710 6 : if (bTryToListDir)
2711 : {
2712 4 : if (CPLGetValueType(aosDirContent[i]) != CPL_VALUE_INTEGER)
2713 : {
2714 0 : continue;
2715 : }
2716 : }
2717 6 : CPLString osSubDir = CPLFormFilenameSafe(
2718 6 : poOpenInfo->pszFilename,
2719 6 : bTryToListDir ? aosDirContent[i] : CPLSPrintf("%d", i),
2720 6 : nullptr);
2721 6 : CPLStringList aosSubDirContent;
2722 6 : if (bTryToListDir)
2723 : {
2724 4 : aosSubDirContent = VSIReadDir(osSubDir);
2725 4 : aosSubDirContent = StripDummyEntries(aosSubDirContent);
2726 : }
2727 14 : for (int j = 0;
2728 14 : j < (bTryToListDir ? aosSubDirContent.Count() : (1 << nZ));
2729 : j++)
2730 : {
2731 8 : if (bTryToListDir)
2732 : {
2733 6 : if (CPLGetValueType(
2734 12 : CPLGetBasenameSafe(aosSubDirContent[j]).c_str()) !=
2735 : CPL_VALUE_INTEGER)
2736 : {
2737 0 : continue;
2738 : }
2739 : }
2740 : const std::string osFilename(CPLFormFilenameSafe(
2741 : osSubDir,
2742 : bTryToListDir
2743 6 : ? aosSubDirContent[j]
2744 2 : : CPLSPrintf("%d.%s", j, osTileExtension.c_str()),
2745 16 : nullptr));
2746 8 : GDALOpenInfo oOpenInfo(("MVT:" + osFilename).c_str(),
2747 8 : GA_ReadOnly);
2748 8 : oOpenInfo.papszOpenOptions =
2749 8 : CSLSetNameValue(nullptr, "METADATA_FILE", "");
2750 8 : oOpenInfo.papszOpenOptions =
2751 8 : CSLSetNameValue(oOpenInfo.papszOpenOptions,
2752 : "DO_NOT_ERROR_ON_MISSING_TILE", "YES");
2753 8 : auto poTileDS = OGRMVTDataset::Open(
2754 : &oOpenInfo, /* bRecurseAllowed = */ false);
2755 8 : if (poTileDS)
2756 : {
2757 7 : if (poDS == nullptr)
2758 : {
2759 4 : poDS = new OGRMVTDataset(nullptr);
2760 4 : poDS->m_osTileExtension = osTileExtension;
2761 4 : poDS->SetDescription(poOpenInfo->pszFilename);
2762 8 : poDS->m_bClip =
2763 4 : CPLFetchBool(poOpenInfo->papszOpenOptions, "CLIP",
2764 4 : poDS->m_bClip);
2765 : }
2766 :
2767 16 : for (int k = 0; k < poTileDS->GetLayerCount(); k++)
2768 : {
2769 9 : OGRLayer *poTileLayer = poTileDS->GetLayer(k);
2770 : OGRFeatureDefn *poTileLDefn =
2771 9 : poTileLayer->GetLayerDefn();
2772 : OGRwkbGeometryType eTileGeomType =
2773 9 : poTileLDefn->GetGeomType();
2774 : OGRwkbGeometryType eTileGeomTypeColl =
2775 9 : OGR_GT_GetCollection(eTileGeomType);
2776 9 : if (eTileGeomTypeColl != wkbUnknown &&
2777 : eTileGeomTypeColl != eTileGeomType)
2778 : {
2779 4 : eTileGeomType = eTileGeomTypeColl;
2780 : }
2781 :
2782 : OGRLayer *poLayer =
2783 9 : poDS->GetLayerByName(poTileLayer->GetName());
2784 : OGRFeatureDefn *poLDefn;
2785 9 : if (poLayer == nullptr)
2786 : {
2787 10 : CPLJSONObject oFields;
2788 5 : oFields.Deinit();
2789 5 : poDS->m_apoLayers.push_back(
2790 5 : std::make_unique<OGRMVTDirectoryLayer>(
2791 5 : poDS, poTileLayer->GetName(),
2792 5 : poOpenInfo->pszFilename, oFields,
2793 5 : CPLJSONArray(), bJsonField, wkbUnknown,
2794 5 : nullptr));
2795 5 : poLayer = poDS->m_apoLayers.back().get();
2796 5 : poLDefn = poLayer->GetLayerDefn();
2797 5 : poLDefn->SetGeomType(eTileGeomType);
2798 : }
2799 : else
2800 : {
2801 4 : poLDefn = poLayer->GetLayerDefn();
2802 4 : if (poLayer->GetGeomType() != eTileGeomType)
2803 : {
2804 0 : poLDefn->SetGeomType(wkbUnknown);
2805 : }
2806 : }
2807 :
2808 9 : if (!bJsonField)
2809 : {
2810 12 : for (int l = 1; l < poTileLDefn->GetFieldCount();
2811 : l++)
2812 : {
2813 : OGRFieldDefn *poTileFDefn =
2814 4 : poTileLDefn->GetFieldDefn(l);
2815 4 : int nFieldIdx = poLDefn->GetFieldIndex(
2816 4 : poTileFDefn->GetNameRef());
2817 4 : if (nFieldIdx < 0)
2818 : {
2819 1 : poLDefn->AddFieldDefn(poTileFDefn);
2820 : }
2821 : else
2822 : {
2823 6 : MergeFieldDefn(
2824 3 : poLDefn->GetFieldDefn(nFieldIdx),
2825 : poTileFDefn->GetType(),
2826 : poTileFDefn->GetSubType());
2827 : }
2828 : }
2829 : }
2830 : }
2831 7 : nCountTiles++;
2832 : }
2833 1 : else if (!bTryToListDir)
2834 : {
2835 1 : nFailedAttempts++;
2836 : }
2837 8 : delete poTileDS;
2838 8 : CSLDestroy(oOpenInfo.papszOpenOptions);
2839 :
2840 8 : if (nFailedAttempts == 10)
2841 0 : break;
2842 8 : if (nMaxTiles > 0 && nCountTiles == nMaxTiles)
2843 0 : break;
2844 : }
2845 :
2846 6 : if (nFailedAttempts == 10)
2847 0 : break;
2848 6 : if (nMaxTiles > 0 && nCountTiles == nMaxTiles)
2849 0 : break;
2850 : }
2851 5 : return poDS;
2852 : }
2853 :
2854 36 : CPLJSONArray oVectorLayers;
2855 36 : CPLJSONArray oTileStatLayers;
2856 36 : CPLJSONObject oBounds;
2857 :
2858 18 : OGRMVTDataset *poDS = new OGRMVTDataset(nullptr);
2859 :
2860 : CPLString osMetadataMemFilename =
2861 36 : VSIMemGenerateHiddenFilename("mvt_metadata.json");
2862 18 : if (!LoadMetadata(osMetadataFile, osMetadataContent, oVectorLayers,
2863 : oTileStatLayers, oBounds, poDS->m_poSRS,
2864 18 : poDS->m_dfTopXOrigin, poDS->m_dfTopYOrigin,
2865 18 : poDS->m_dfTileDim0, poDS->m_nTileMatrixWidth0,
2866 18 : poDS->m_nTileMatrixHeight0, osMetadataMemFilename))
2867 : {
2868 0 : delete poDS;
2869 0 : return nullptr;
2870 : }
2871 :
2872 18 : OGREnvelope sExtent;
2873 18 : bool bExtentValid = false;
2874 18 : if (oBounds.IsValid() && oBounds.GetType() == CPLJSONObject::Type::String)
2875 : {
2876 : CPLStringList aosTokens(
2877 51 : CSLTokenizeString2(oBounds.ToString().c_str(), ",", 0));
2878 17 : if (aosTokens.Count() == 4)
2879 : {
2880 17 : double dfX0 = CPLAtof(aosTokens[0]);
2881 17 : double dfY0 = CPLAtof(aosTokens[1]);
2882 17 : double dfX1 = CPLAtof(aosTokens[2]);
2883 17 : double dfY1 = CPLAtof(aosTokens[3]);
2884 17 : ConvertFromWGS84(poDS->m_poSRS, dfX0, dfY0, dfX1, dfY1);
2885 17 : bExtentValid = true;
2886 17 : sExtent.MinX = dfX0;
2887 17 : sExtent.MinY = dfY0;
2888 17 : sExtent.MaxX = dfX1;
2889 17 : sExtent.MaxY = dfY1;
2890 : }
2891 : }
2892 2 : else if (oBounds.IsValid() &&
2893 1 : oBounds.GetType() == CPLJSONObject::Type::Array)
2894 : {
2895 : // Cf https://free.tilehosting.com/data/v3.json?key=THE_KEY
2896 2 : CPLJSONArray oBoundArray = oBounds.ToArray();
2897 1 : if (oBoundArray.Size() == 4)
2898 : {
2899 1 : bExtentValid = true;
2900 1 : sExtent.MinX = oBoundArray[0].ToDouble();
2901 1 : sExtent.MinY = oBoundArray[1].ToDouble();
2902 1 : sExtent.MaxX = oBoundArray[2].ToDouble();
2903 1 : sExtent.MaxY = oBoundArray[3].ToDouble();
2904 1 : ConvertFromWGS84(poDS->m_poSRS, sExtent.MinX, sExtent.MinY,
2905 : sExtent.MaxX, sExtent.MaxY);
2906 : }
2907 : }
2908 :
2909 18 : poDS->SetDescription(poOpenInfo->pszFilename);
2910 36 : poDS->m_bClip =
2911 18 : CPLFetchBool(poOpenInfo->papszOpenOptions, "CLIP", poDS->m_bClip);
2912 18 : poDS->m_osTileExtension = std::move(osTileExtension);
2913 18 : poDS->m_osMetadataMemFilename = std::move(osMetadataMemFilename);
2914 38 : for (int i = 0; i < oVectorLayers.Size(); i++)
2915 : {
2916 60 : CPLJSONObject oId = oVectorLayers[i].GetObj("id");
2917 20 : if (oId.IsValid() && oId.GetType() == CPLJSONObject::Type::String)
2918 : {
2919 20 : OGRwkbGeometryType eGeomType = wkbUnknown;
2920 20 : if (oTileStatLayers.IsValid())
2921 : {
2922 19 : eGeomType = OGRMVTFindGeomTypeFromTileStat(
2923 38 : oTileStatLayers, oId.ToString().c_str());
2924 : }
2925 :
2926 60 : CPLJSONObject oFields = oVectorLayers[i].GetObj("fields");
2927 : CPLJSONArray oAttributesFromTileStats =
2928 : OGRMVTFindAttributesFromTileStat(oTileStatLayers,
2929 40 : oId.ToString().c_str());
2930 :
2931 20 : poDS->m_apoLayers.push_back(std::make_unique<OGRMVTDirectoryLayer>(
2932 40 : poDS, oId.ToString().c_str(), poOpenInfo->pszFilename, oFields,
2933 : oAttributesFromTileStats, bJsonField, eGeomType,
2934 40 : (bExtentValid) ? &sExtent : nullptr));
2935 : }
2936 : }
2937 :
2938 18 : return poDS;
2939 : }
2940 :
2941 : /************************************************************************/
2942 : /* Open() */
2943 : /************************************************************************/
2944 :
2945 906 : GDALDataset *OGRMVTDataset::Open(GDALOpenInfo *poOpenInfo)
2946 : {
2947 906 : return Open(poOpenInfo, true);
2948 : }
2949 :
2950 1001 : GDALDataset *OGRMVTDataset::Open(GDALOpenInfo *poOpenInfo, bool bRecurseAllowed)
2951 :
2952 : {
2953 1001 : if (!OGRMVTDriverIdentify(poOpenInfo) || poOpenInfo->eAccess == GA_Update)
2954 0 : return nullptr;
2955 :
2956 1001 : VSILFILE *fp = poOpenInfo->fpL;
2957 2002 : CPLString osFilename(poOpenInfo->pszFilename);
2958 1001 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "MVT:"))
2959 : {
2960 960 : osFilename = poOpenInfo->pszFilename + strlen("MVT:");
2961 1920 : if (STARTS_WITH(osFilename, "/vsigzip/http://") ||
2962 960 : STARTS_WITH(osFilename, "/vsigzip/https://"))
2963 : {
2964 0 : osFilename = osFilename.substr(strlen("/vsigzip/"));
2965 : }
2966 :
2967 : // If the filename has no extension and is a directory, consider
2968 : // we open a directory
2969 : VSIStatBufL sStat;
2970 865 : if (bRecurseAllowed && !STARTS_WITH(osFilename, "/vsigzip/") &&
2971 864 : strchr((CPLGetFilename(osFilename)), '.') == nullptr &&
2972 1825 : VSIStatL(osFilename, &sStat) == 0 && VSI_ISDIR(sStat.st_mode))
2973 : {
2974 3 : GDALOpenInfo oOpenInfo(osFilename, GA_ReadOnly);
2975 3 : oOpenInfo.papszOpenOptions = poOpenInfo->papszOpenOptions;
2976 3 : GDALDataset *poDS = OpenDirectory(&oOpenInfo);
2977 3 : if (poDS)
2978 1 : poDS->SetDescription(poOpenInfo->pszFilename);
2979 3 : return poDS;
2980 : }
2981 :
2982 : // For a network resource, if the filename is an integer, consider it
2983 : // is a directory and open as such
2984 1819 : if (bRecurseAllowed &&
2985 862 : (STARTS_WITH(osFilename, "/vsicurl") ||
2986 862 : STARTS_WITH(osFilename, "http://") ||
2987 1819 : STARTS_WITH(osFilename, "https://")) &&
2988 6 : CPLGetValueType(CPLGetFilename(osFilename)) == CPL_VALUE_INTEGER)
2989 : {
2990 5 : GDALOpenInfo oOpenInfo(osFilename, GA_ReadOnly);
2991 5 : oOpenInfo.papszOpenOptions = poOpenInfo->papszOpenOptions;
2992 5 : GDALDataset *poDS = OpenDirectory(&oOpenInfo);
2993 5 : if (poDS)
2994 4 : poDS->SetDescription(poOpenInfo->pszFilename);
2995 5 : return poDS;
2996 : }
2997 :
2998 1894 : if (!STARTS_WITH(osFilename, "http://") &&
2999 942 : !STARTS_WITH(osFilename, "https://"))
3000 : {
3001 : CPLConfigOptionSetter oSetter("CPL_VSIL_GZIP_WRITE_PROPERTIES",
3002 1884 : "NO", false);
3003 : CPLConfigOptionSetter oSetter2("CPL_VSIL_GZIP_SAVE_INFO", "NO",
3004 1884 : false);
3005 942 : fp = VSIFOpenL(osFilename, "rb");
3006 : // Is it a gzipped file ?
3007 942 : if (fp && !STARTS_WITH(osFilename, "/vsigzip/"))
3008 : {
3009 940 : GByte abyHeaderBytes[2] = {0, 0};
3010 940 : VSIFReadL(abyHeaderBytes, 2, 1, fp);
3011 940 : if (abyHeaderBytes[0] == 0x1F && abyHeaderBytes[1] == 0x8B)
3012 : {
3013 404 : VSIFCloseL(fp);
3014 404 : fp = VSIFOpenL(("/vsigzip/" + osFilename).c_str(), "rb");
3015 : }
3016 : }
3017 : }
3018 : }
3019 82 : else if (bRecurseAllowed &&
3020 41 : (poOpenInfo->bIsDirectory ||
3021 24 : (STARTS_WITH(poOpenInfo->pszFilename, "/vsicurl") &&
3022 0 : CPLGetValueType(CPLGetFilename(poOpenInfo->pszFilename)) ==
3023 : CPL_VALUE_INTEGER)))
3024 : {
3025 17 : return OpenDirectory(poOpenInfo);
3026 : }
3027 : // Is it a gzipped file ?
3028 24 : else if (poOpenInfo->nHeaderBytes >= 2 &&
3029 24 : poOpenInfo->pabyHeader[0] == 0x1F &&
3030 18 : poOpenInfo->pabyHeader[1] == 0x8B)
3031 : {
3032 : CPLConfigOptionSetter oSetter("CPL_VSIL_GZIP_WRITE_PROPERTIES", "NO",
3033 18 : false);
3034 18 : fp = VSIFOpenL(("/vsigzip/" + osFilename).c_str(), "rb");
3035 : }
3036 : else
3037 : {
3038 6 : poOpenInfo->fpL = nullptr;
3039 : }
3040 977 : if (fp == nullptr && !STARTS_WITH(osFilename, "http://") &&
3041 1 : !STARTS_WITH(osFilename, "https://"))
3042 : {
3043 1 : return nullptr;
3044 : }
3045 :
3046 1950 : CPLString osY = CPLGetBasenameSafe(osFilename);
3047 2925 : CPLString osX = CPLGetBasenameSafe(CPLGetPathSafe(osFilename).c_str());
3048 1950 : CPLString osZ = CPLGetBasenameSafe(
3049 3900 : CPLGetPathSafe(CPLGetPathSafe(osFilename).c_str()).c_str());
3050 975 : size_t nPos = osY.find('.');
3051 975 : if (nPos != std::string::npos)
3052 0 : osY.resize(nPos);
3053 :
3054 1950 : CPLString osMetadataFile;
3055 975 : if (CSLFetchNameValue(poOpenInfo->papszOpenOptions, "METADATA_FILE"))
3056 : {
3057 : osMetadataFile =
3058 953 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "METADATA_FILE");
3059 : }
3060 22 : else if (CPLGetValueType(osX) == CPL_VALUE_INTEGER &&
3061 34 : CPLGetValueType(osY) == CPL_VALUE_INTEGER &&
3062 12 : CPLGetValueType(osZ) == CPL_VALUE_INTEGER)
3063 : {
3064 12 : osMetadataFile = CPLFormFilenameSafe(
3065 24 : CPLGetPathSafe(
3066 24 : CPLGetPathSafe(CPLGetPathSafe(osFilename).c_str()).c_str())
3067 : .c_str(),
3068 12 : "metadata.json", nullptr);
3069 12 : if (osMetadataFile.find("/vsigzip/") == 0)
3070 : {
3071 2 : osMetadataFile = osMetadataFile.substr(strlen("/vsigzip/"));
3072 : }
3073 : VSIStatBufL sStat;
3074 12 : if (osMetadataFile.empty() || VSIStatL(osMetadataFile, &sStat) != 0)
3075 : {
3076 1 : osMetadataFile.clear();
3077 : }
3078 : }
3079 :
3080 975 : if (CSLFetchNameValue(poOpenInfo->papszOpenOptions, "X") &&
3081 1804 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "Y") &&
3082 829 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "Z"))
3083 : {
3084 829 : osX = CSLFetchNameValue(poOpenInfo->papszOpenOptions, "X");
3085 829 : osY = CSLFetchNameValue(poOpenInfo->papszOpenOptions, "Y");
3086 829 : osZ = CSLFetchNameValue(poOpenInfo->papszOpenOptions, "Z");
3087 : }
3088 :
3089 : GByte *pabyDataMod;
3090 : size_t nFileSize;
3091 :
3092 975 : if (fp == nullptr)
3093 : {
3094 : bool bSilenceErrors =
3095 10 : CPLFetchBool(poOpenInfo->papszOpenOptions,
3096 : "DO_NOT_ERROR_ON_MISSING_TILE", false);
3097 10 : if (bSilenceErrors)
3098 9 : CPLPushErrorHandler(CPLQuietErrorHandler);
3099 10 : CPLHTTPResult *psResult = CPLHTTPFetch(osFilename, nullptr);
3100 10 : if (bSilenceErrors)
3101 9 : CPLPopErrorHandler();
3102 10 : if (psResult == nullptr)
3103 0 : return nullptr;
3104 10 : if (psResult->pszErrBuf != nullptr)
3105 : {
3106 5 : CPLHTTPDestroyResult(psResult);
3107 5 : return nullptr;
3108 : }
3109 5 : pabyDataMod = psResult->pabyData;
3110 5 : if (pabyDataMod == nullptr)
3111 : {
3112 0 : CPLHTTPDestroyResult(psResult);
3113 0 : return nullptr;
3114 : }
3115 5 : nFileSize = psResult->nDataLen;
3116 5 : psResult->pabyData = nullptr;
3117 5 : CPLHTTPDestroyResult(psResult);
3118 :
3119 : // zlib decompress if needed
3120 5 : if (nFileSize > 2 && pabyDataMod[0] == 0x1F && pabyDataMod[1] == 0x8B)
3121 : {
3122 5 : size_t nOutBytes = 0;
3123 : void *pUncompressed =
3124 5 : CPLZLibInflate(pabyDataMod, nFileSize, nullptr, 0, &nOutBytes);
3125 5 : CPLFree(pabyDataMod);
3126 5 : if (pUncompressed == nullptr)
3127 : {
3128 0 : return nullptr;
3129 : }
3130 5 : pabyDataMod = static_cast<GByte *>(pUncompressed);
3131 5 : nFileSize = nOutBytes;
3132 : }
3133 : }
3134 : else
3135 : {
3136 : // Check file size and ingest into memory
3137 965 : VSIFSeekL(fp, 0, SEEK_END);
3138 965 : vsi_l_offset nFileSizeL = VSIFTellL(fp);
3139 965 : if (nFileSizeL > 10 * 1024 * 1024)
3140 : {
3141 0 : VSIFCloseL(fp);
3142 0 : return nullptr;
3143 : }
3144 965 : nFileSize = static_cast<size_t>(nFileSizeL);
3145 965 : pabyDataMod = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nFileSize + 1));
3146 965 : if (pabyDataMod == nullptr)
3147 : {
3148 0 : VSIFCloseL(fp);
3149 0 : return nullptr;
3150 : }
3151 965 : VSIFSeekL(fp, 0, SEEK_SET);
3152 965 : VSIFReadL(pabyDataMod, 1, nFileSize, fp);
3153 965 : pabyDataMod[nFileSize] = 0;
3154 965 : VSIFCloseL(fp);
3155 : }
3156 :
3157 970 : const GByte *pabyData = pabyDataMod;
3158 :
3159 : // First scan to browse through layers
3160 970 : const GByte *pabyDataLimit = pabyData + nFileSize;
3161 970 : int nKey = 0;
3162 970 : OGRMVTDataset *poDS = new OGRMVTDataset(pabyDataMod);
3163 970 : poDS->SetDescription(poOpenInfo->pszFilename);
3164 1940 : poDS->m_bClip =
3165 970 : CPLFetchBool(poOpenInfo->papszOpenOptions, "CLIP", poDS->m_bClip);
3166 :
3167 1933 : if (!(CPLGetValueType(osX) == CPL_VALUE_INTEGER &&
3168 963 : CPLGetValueType(osY) == CPL_VALUE_INTEGER &&
3169 938 : CPLGetValueType(osZ) == CPL_VALUE_INTEGER))
3170 : {
3171 : // See
3172 : // https://github.com/mapbox/mvt-fixtures/tree/master/real-world/compressed
3173 32 : int nX = 0;
3174 32 : int nY = 0;
3175 32 : int nZ = 0;
3176 : CPLString osBasename(
3177 96 : CPLGetBasenameSafe(CPLGetBasenameSafe(osFilename).c_str()));
3178 63 : if (sscanf(osBasename, "%d-%d-%d", &nZ, &nX, &nY) == 3 ||
3179 31 : sscanf(osBasename, "%d_%d_%d", &nZ, &nX, &nY) == 3)
3180 : {
3181 1 : osX = CPLSPrintf("%d", nX);
3182 1 : osY = CPLSPrintf("%d", nY);
3183 1 : osZ = CPLSPrintf("%d", nZ);
3184 : }
3185 : }
3186 :
3187 1940 : CPLJSONArray oVectorLayers;
3188 970 : oVectorLayers.Deinit();
3189 :
3190 1940 : CPLJSONArray oTileStatLayers;
3191 970 : oTileStatLayers.Deinit();
3192 :
3193 970 : if (!osMetadataFile.empty())
3194 : {
3195 912 : CPLJSONObject oBounds;
3196 912 : LoadMetadata(osMetadataFile, CPLString(), oVectorLayers,
3197 : oTileStatLayers, oBounds, poDS->m_poSRS,
3198 912 : poDS->m_dfTopXOrigin, poDS->m_dfTopYOrigin,
3199 912 : poDS->m_dfTileDim0, poDS->m_nTileMatrixWidth0,
3200 1824 : poDS->m_nTileMatrixHeight0, CPLString());
3201 : }
3202 :
3203 : const char *pszGeorefTopX =
3204 970 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "GEOREF_TOPX");
3205 : const char *pszGeorefTopY =
3206 970 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "GEOREF_TOPY");
3207 : const char *pszGeorefTileDimX =
3208 970 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "GEOREF_TILEDIMX");
3209 : const char *pszGeorefTileDimY =
3210 970 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "GEOREF_TILEDIMY");
3211 970 : if (pszGeorefTopX && pszGeorefTopY && pszGeorefTileDimX &&
3212 : pszGeorefTileDimY)
3213 : {
3214 3 : poDS->m_bGeoreferenced = true;
3215 3 : poDS->m_dfTileDimX = CPLAtof(pszGeorefTileDimX);
3216 3 : poDS->m_dfTileDimY = CPLAtof(pszGeorefTileDimY);
3217 3 : poDS->m_dfTopX = CPLAtof(pszGeorefTopX);
3218 3 : poDS->m_dfTopY = CPLAtof(pszGeorefTopY);
3219 3 : poDS->m_poSRS->Release();
3220 3 : poDS->m_poSRS = nullptr;
3221 : }
3222 967 : else if (CPLGetValueType(osX) == CPL_VALUE_INTEGER &&
3223 1906 : CPLGetValueType(osY) == CPL_VALUE_INTEGER &&
3224 939 : CPLGetValueType(osZ) == CPL_VALUE_INTEGER)
3225 : {
3226 939 : int nX = atoi(osX);
3227 939 : int nY = atoi(osY);
3228 939 : int nZ = atoi(osZ);
3229 939 : if (nZ >= 0 && nZ < 30 && nX >= 0 &&
3230 939 : nX < (static_cast<int64_t>(1) << nZ) * poDS->m_nTileMatrixWidth0 &&
3231 937 : nY >= 0 &&
3232 937 : nY < (static_cast<int64_t>(1) << nZ) * poDS->m_nTileMatrixHeight0)
3233 : {
3234 937 : poDS->m_bGeoreferenced = true;
3235 937 : poDS->m_dfTileDimX = poDS->m_dfTileDim0 / (1 << nZ);
3236 937 : poDS->m_dfTileDimY = poDS->m_dfTileDimX;
3237 937 : poDS->m_dfTopX = poDS->m_dfTopXOrigin + nX * poDS->m_dfTileDimX;
3238 937 : poDS->m_dfTopY = poDS->m_dfTopYOrigin - nY * poDS->m_dfTileDimY;
3239 : }
3240 : }
3241 :
3242 : try
3243 : {
3244 2023 : while (pabyData < pabyDataLimit)
3245 : {
3246 1054 : READ_FIELD_KEY(nKey);
3247 1054 : if (nKey == MAKE_KEY(knLAYER, WT_DATA))
3248 : {
3249 1053 : unsigned int nLayerSize = 0;
3250 1053 : READ_SIZE(pabyData, pabyDataLimit, nLayerSize);
3251 1053 : const GByte *pabyDataLayer = pabyData;
3252 1053 : const GByte *pabyDataLimitLayer = pabyData + nLayerSize;
3253 1301 : while (pabyData < pabyDataLimitLayer)
3254 : {
3255 1301 : READ_VARINT32(pabyData, pabyDataLimitLayer, nKey);
3256 1301 : if (nKey == MAKE_KEY(knLAYER_NAME, WT_DATA))
3257 : {
3258 1053 : char *pszLayerName = nullptr;
3259 1053 : READ_TEXT(pabyData, pabyDataLimitLayer, pszLayerName);
3260 :
3261 2106 : CPLJSONObject oFields;
3262 1053 : oFields.Deinit();
3263 1053 : if (oVectorLayers.IsValid())
3264 : {
3265 1098 : for (int i = 0; i < oVectorLayers.Size(); i++)
3266 : {
3267 : CPLJSONObject oId =
3268 2196 : oVectorLayers[i].GetObj("id");
3269 2196 : if (oId.IsValid() &&
3270 1098 : oId.GetType() ==
3271 : CPLJSONObject::Type::String)
3272 : {
3273 1098 : if (oId.ToString() == pszLayerName)
3274 : {
3275 : oFields =
3276 969 : oVectorLayers[i].GetObj("fields");
3277 969 : break;
3278 : }
3279 : }
3280 : }
3281 : }
3282 :
3283 1053 : OGRwkbGeometryType eGeomType = wkbUnknown;
3284 1053 : if (oTileStatLayers.IsValid())
3285 : {
3286 566 : eGeomType = OGRMVTFindGeomTypeFromTileStat(
3287 : oTileStatLayers, pszLayerName);
3288 : }
3289 : CPLJSONArray oAttributesFromTileStats =
3290 : OGRMVTFindAttributesFromTileStat(oTileStatLayers,
3291 2106 : pszLayerName);
3292 :
3293 1053 : poDS->m_apoLayers.push_back(
3294 2106 : std::make_unique<OGRMVTLayer>(
3295 : poDS, pszLayerName, pabyDataLayer, nLayerSize,
3296 : oFields, oAttributesFromTileStats, eGeomType));
3297 1053 : CPLFree(pszLayerName);
3298 1053 : break;
3299 : }
3300 : else
3301 : {
3302 248 : SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimitLayer, FALSE);
3303 : }
3304 : }
3305 1053 : pabyData = pabyDataLimitLayer;
3306 : }
3307 : else
3308 : {
3309 1 : if (nKey == 0 && !poDS->m_apoLayers.empty())
3310 : {
3311 : // File attached to https://github.com/OSGeo/gdal/issues/13268
3312 : // has 0-byte padding after the layer definition.
3313 1 : break;
3314 : }
3315 0 : SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, FALSE);
3316 : }
3317 : }
3318 :
3319 970 : return poDS;
3320 : }
3321 0 : catch (const GPBException &e)
3322 : {
3323 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
3324 0 : delete poDS;
3325 0 : return nullptr;
3326 : }
3327 : }
3328 :
3329 : #ifdef HAVE_MVT_WRITE_SUPPORT
3330 :
3331 : /************************************************************************/
3332 : /* OGRMVTWriterDataset */
3333 : /************************************************************************/
3334 :
3335 : class OGRMVTWriterLayer;
3336 :
3337 : struct OGRMVTFeatureContent
3338 : {
3339 : std::vector<std::pair<std::string, MVTTileLayerValue>> oValues;
3340 : GIntBig nFID;
3341 : };
3342 :
3343 : class OGRMVTWriterDataset final : public GDALDataset
3344 : {
3345 : class MVTFieldProperties
3346 : {
3347 : public:
3348 : CPLString m_osName;
3349 : std::set<MVTTileLayerValue> m_oSetValues;
3350 : std::set<MVTTileLayerValue> m_oSetAllValues;
3351 : double m_dfMinVal = 0;
3352 : double m_dfMaxVal = 0;
3353 : bool m_bAllInt = false;
3354 : MVTTileLayerValue::ValueType m_eType =
3355 : MVTTileLayerValue::ValueType::NONE;
3356 : };
3357 :
3358 : class MVTLayerProperties
3359 : {
3360 : public:
3361 : int m_nMinZoom = 0;
3362 : int m_nMaxZoom = 0;
3363 : std::map<MVTTileLayerFeature::GeomType, GIntBig> m_oCountGeomType;
3364 : std::map<CPLString, size_t> m_oMapFieldNameToIdx;
3365 : std::vector<MVTFieldProperties> m_aoFields;
3366 : std::set<CPLString> m_oSetFields;
3367 : };
3368 :
3369 : std::vector<std::unique_ptr<OGRMVTWriterLayer>> m_apoLayers;
3370 : CPLString m_osTempDB;
3371 : mutable std::mutex m_oDBMutex;
3372 : mutable bool m_bWriteFeatureError = false;
3373 : sqlite3_vfs *m_pMyVFS = nullptr;
3374 : sqlite3 *m_hDB = nullptr;
3375 : sqlite3_stmt *m_hInsertStmt = nullptr;
3376 : int m_nMinZoom = 0;
3377 : int m_nMaxZoom = 5;
3378 : double m_dfSimplification = 0.0;
3379 : double m_dfSimplificationMaxZoom = 0.0;
3380 : CPLJSONDocument m_oConf;
3381 : unsigned m_nExtent = knDEFAULT_EXTENT;
3382 : int m_nMetadataVersion = 2;
3383 : int m_nMVTVersion = 2;
3384 : int m_nBuffer = 5 * knDEFAULT_EXTENT / 256;
3385 : bool m_bGZip = true;
3386 : mutable CPLWorkerThreadPool m_oThreadPool;
3387 : bool m_bThreadPoolOK = false;
3388 : mutable GIntBig m_nTempTiles = 0;
3389 : CPLString m_osName;
3390 : CPLString m_osDescription;
3391 : CPLString m_osType{"overlay"};
3392 : sqlite3 *m_hDBMBTILES = nullptr;
3393 : OGREnvelope m_oEnvelope;
3394 : bool m_bMaxTileSizeOptSpecified = false;
3395 : bool m_bMaxFeaturesOptSpecified = false;
3396 : unsigned m_nMaxTileSize = 500000;
3397 : unsigned m_nMaxFeatures = 200000;
3398 : std::map<std::string, std::string> m_oMapLayerNameToDesc;
3399 : std::map<std::string, GIntBig> m_oMapLayerNameToFeatureCount;
3400 : CPLString m_osBounds;
3401 : CPLString m_osCenter;
3402 : CPLString m_osExtension{"pbf"};
3403 : OGRSpatialReference *m_poSRS = nullptr;
3404 : double m_dfTopX = 0.0;
3405 : double m_dfTopY = 0.0;
3406 : double m_dfTileDim0 = 0.0;
3407 : int m_nTileMatrixWidth0 =
3408 : 1; // Number of tiles along X axis at zoom level 0
3409 : int m_nTileMatrixHeight0 =
3410 : 1; // Number of tiles along Y axis at zoom level 0
3411 : bool m_bReuseTempFile = false; // debug only
3412 :
3413 : OGRErr PreGenerateForTile(
3414 : int nZ, int nX, int nY, const CPLString &osTargetName,
3415 : bool bIsMaxZoomForLayer,
3416 : const std::shared_ptr<OGRMVTFeatureContent> &poFeatureContent,
3417 : GIntBig nSerial, const std::shared_ptr<OGRGeometry> &poGeom,
3418 : const OGREnvelope &sEnvelope) const;
3419 :
3420 : static void WriterTaskFunc(void *pParam);
3421 :
3422 : OGRErr PreGenerateForTileReal(int nZ, int nX, int nY,
3423 : const CPLString &osTargetName,
3424 : bool bIsMaxZoomForLayer,
3425 : const OGRMVTFeatureContent *poFeatureContent,
3426 : GIntBig nSerial, const OGRGeometry *poGeom,
3427 : const OGREnvelope &sEnvelope) const;
3428 :
3429 : void ConvertToTileCoords(double dfX, double dfY, int &nX, int &nY,
3430 : double dfTopX, double dfTopY,
3431 : double dfTileDim) const;
3432 :
3433 : enum class ExpectedWindingOrder
3434 : {
3435 : NONE,
3436 : CLOCKWISE,
3437 : COUNTERCLOCKWISE,
3438 : };
3439 : bool EncodeLineString(MVTTileLayerFeature *poGPBFeature,
3440 : const OGRLineString *poLS, OGRLineString *poOutLS,
3441 : bool bWriteLastPoint,
3442 : ExpectedWindingOrder eExpectedWindingOrder,
3443 : GUInt32 nMinLineTo, double dfTopX, double dfTopY,
3444 : double dfTileDim, int &nLastX, int &nLastY) const;
3445 : bool EncodePolygon(MVTTileLayerFeature *poGPBFeature,
3446 : const OGRPolygon *poPoly, OGRPolygon *poOutPoly,
3447 : double dfTopX, double dfTopY, double dfTileDim,
3448 : int &nLastX, int &nLastY, double &dfArea) const;
3449 : #ifdef notdef
3450 : bool EncodeRepairedOuterRing(MVTTileLayerFeature *poGPBFeature,
3451 : OGRPolygon &oOutPoly, int &nLastX,
3452 : int &nLastY) const;
3453 : #endif
3454 :
3455 : static void UpdateLayerProperties(MVTLayerProperties *poLayerProperties,
3456 : const std::string &osKey,
3457 : const MVTTileLayerValue &oValue);
3458 :
3459 : void EncodeFeature(const void *pabyBlob, int nBlobSize,
3460 : std::shared_ptr<MVTTileLayer> &poTargetLayer,
3461 : std::map<CPLString, GUInt32> &oMapKeyToIdx,
3462 : std::map<MVTTileLayerValue, GUInt32> &oMapValueToIdx,
3463 : MVTLayerProperties *poLayerProperties, GUInt32 nExtent,
3464 : unsigned &nFeaturesInTile);
3465 :
3466 : std::string
3467 : EncodeTile(int nZ, int nX, int nY, sqlite3_stmt *hStmtLayer,
3468 : sqlite3_stmt *hStmtRows,
3469 : std::map<CPLString, MVTLayerProperties> &oMapLayerProps,
3470 : std::set<CPLString> &oSetLayers, GIntBig &nTempTilesRead);
3471 :
3472 : std::string RecodeTileLowerResolution(int nZ, int nX, int nY, int nExtent,
3473 : sqlite3_stmt *hStmtLayer,
3474 : sqlite3_stmt *hStmtRows);
3475 :
3476 : bool CreateOutput();
3477 :
3478 : bool GenerateMetadata(size_t nLayers,
3479 : const std::map<CPLString, MVTLayerProperties> &oMap);
3480 :
3481 : public:
3482 : OGRMVTWriterDataset();
3483 : ~OGRMVTWriterDataset() override;
3484 :
3485 : CPLErr Close() override;
3486 :
3487 : OGRLayer *ICreateLayer(const char *pszName,
3488 : const OGRGeomFieldDefn *poGeomFieldDefn,
3489 : CSLConstList papszOptions) override;
3490 :
3491 : int TestCapability(const char *) const override;
3492 :
3493 : OGRErr WriteFeature(OGRMVTWriterLayer *poLayer, OGRFeature *poFeature,
3494 : GIntBig nSerial, OGRGeometry *poGeom);
3495 :
3496 : static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
3497 : int nBandsIn, GDALDataType eDT,
3498 : char **papszOptions);
3499 :
3500 182 : OGRSpatialReference *GetSRS()
3501 : {
3502 182 : return m_poSRS;
3503 : }
3504 : };
3505 :
3506 : /************************************************************************/
3507 : /* OGRMVTWriterLayer */
3508 : /************************************************************************/
3509 :
3510 : class OGRMVTWriterLayer final : public OGRLayer
3511 : {
3512 : friend class OGRMVTWriterDataset;
3513 :
3514 : OGRMVTWriterDataset *m_poDS = nullptr;
3515 : OGRFeatureDefn *m_poFeatureDefn = nullptr;
3516 : OGRCoordinateTransformation *m_poCT = nullptr;
3517 : GIntBig m_nSerial = 0;
3518 : int m_nMinZoom = 0;
3519 : int m_nMaxZoom = 5;
3520 : CPLString m_osTargetName;
3521 :
3522 : public:
3523 : OGRMVTWriterLayer(OGRMVTWriterDataset *poDS, const char *pszLayerName,
3524 : OGRSpatialReference *poSRS);
3525 : ~OGRMVTWriterLayer() override;
3526 :
3527 48 : void ResetReading() override
3528 : {
3529 48 : }
3530 :
3531 48 : OGRFeature *GetNextFeature() override
3532 : {
3533 48 : return nullptr;
3534 : }
3535 :
3536 1441 : const OGRFeatureDefn *GetLayerDefn() const override
3537 : {
3538 1441 : return m_poFeatureDefn;
3539 : }
3540 :
3541 : int TestCapability(const char *) const override;
3542 : OGRErr ICreateFeature(OGRFeature *) override;
3543 : OGRErr CreateField(const OGRFieldDefn *, int) override;
3544 :
3545 49 : GDALDataset *GetDataset() override
3546 : {
3547 49 : return m_poDS;
3548 : }
3549 : };
3550 :
3551 : /************************************************************************/
3552 : /* OGRMVTWriterLayer() */
3553 : /************************************************************************/
3554 :
3555 171 : OGRMVTWriterLayer::OGRMVTWriterLayer(OGRMVTWriterDataset *poDS,
3556 : const char *pszLayerName,
3557 171 : OGRSpatialReference *poSRSIn)
3558 : {
3559 171 : m_poDS = poDS;
3560 171 : m_poFeatureDefn = new OGRFeatureDefn(pszLayerName);
3561 171 : SetDescription(m_poFeatureDefn->GetName());
3562 171 : m_poFeatureDefn->Reference();
3563 :
3564 171 : m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poDS->GetSRS());
3565 :
3566 171 : if (poSRSIn != nullptr && !poDS->GetSRS()->IsSame(poSRSIn))
3567 : {
3568 3 : m_poCT = OGRCreateCoordinateTransformation(poSRSIn, poDS->GetSRS());
3569 3 : if (m_poCT == nullptr)
3570 : {
3571 : // If we can't create a transformation, issue a warning - but
3572 : // continue the transformation.
3573 1 : CPLError(CE_Warning, CPLE_AppDefined,
3574 : "Failed to create coordinate transformation between the "
3575 : "input and target coordinate systems.");
3576 : }
3577 : }
3578 171 : }
3579 :
3580 : /************************************************************************/
3581 : /* ~OGRMVTWriterLayer() */
3582 : /************************************************************************/
3583 :
3584 342 : OGRMVTWriterLayer::~OGRMVTWriterLayer()
3585 : {
3586 171 : m_poFeatureDefn->Release();
3587 171 : delete m_poCT;
3588 342 : }
3589 :
3590 : /************************************************************************/
3591 : /* TestCapability() */
3592 : /************************************************************************/
3593 :
3594 359 : int OGRMVTWriterLayer::TestCapability(const char *pszCap) const
3595 : {
3596 :
3597 359 : if (EQUAL(pszCap, OLCSequentialWrite) || EQUAL(pszCap, OLCCreateField))
3598 96 : return true;
3599 263 : return false;
3600 : }
3601 :
3602 : /************************************************************************/
3603 : /* CreateField() */
3604 : /************************************************************************/
3605 :
3606 313 : OGRErr OGRMVTWriterLayer::CreateField(const OGRFieldDefn *poFieldDefn, int)
3607 : {
3608 313 : m_poFeatureDefn->AddFieldDefn(poFieldDefn);
3609 313 : return OGRERR_NONE;
3610 : }
3611 :
3612 : /************************************************************************/
3613 : /* ICreateFeature() */
3614 : /************************************************************************/
3615 :
3616 295 : OGRErr OGRMVTWriterLayer::ICreateFeature(OGRFeature *poFeature)
3617 : {
3618 295 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
3619 295 : if (poGeom == nullptr || poGeom->IsEmpty())
3620 102 : return OGRERR_NONE;
3621 193 : if (m_poCT)
3622 : {
3623 2 : poGeom->transform(m_poCT);
3624 : }
3625 193 : m_nSerial++;
3626 193 : return m_poDS->WriteFeature(this, poFeature, m_nSerial, poGeom);
3627 : }
3628 :
3629 : /************************************************************************/
3630 : /* OGRMVTWriterDataset() */
3631 : /************************************************************************/
3632 :
3633 133 : OGRMVTWriterDataset::OGRMVTWriterDataset()
3634 : {
3635 : // Default WebMercator tiling scheme
3636 133 : m_poSRS = new OGRSpatialReference();
3637 133 : m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
3638 :
3639 133 : InitWebMercatorTilingScheme(m_poSRS, m_dfTopX, m_dfTopY, m_dfTileDim0);
3640 133 : }
3641 :
3642 : /************************************************************************/
3643 : /* ~OGRMVTWriterDataset() */
3644 : /************************************************************************/
3645 :
3646 266 : OGRMVTWriterDataset::~OGRMVTWriterDataset()
3647 : {
3648 133 : OGRMVTWriterDataset::Close();
3649 :
3650 133 : if (m_pMyVFS)
3651 : {
3652 133 : sqlite3_vfs_unregister(m_pMyVFS);
3653 133 : CPLFree(m_pMyVFS->pAppData);
3654 133 : CPLFree(m_pMyVFS);
3655 : }
3656 :
3657 133 : m_poSRS->Release();
3658 266 : }
3659 :
3660 : /************************************************************************/
3661 : /* Close() */
3662 : /************************************************************************/
3663 :
3664 256 : CPLErr OGRMVTWriterDataset::Close()
3665 : {
3666 256 : CPLErr eErr = CE_None;
3667 256 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
3668 : {
3669 133 : if (GetDescription()[0] != '\0')
3670 : {
3671 123 : if (!CreateOutput())
3672 3 : eErr = CE_Failure;
3673 : }
3674 133 : if (m_hInsertStmt != nullptr)
3675 : {
3676 130 : sqlite3_finalize(m_hInsertStmt);
3677 : }
3678 133 : if (m_hDB)
3679 : {
3680 130 : sqlite3_close(m_hDB);
3681 : }
3682 133 : if (m_hDBMBTILES)
3683 : {
3684 76 : sqlite3_close(m_hDBMBTILES);
3685 : }
3686 262 : if (!m_osTempDB.empty() && !m_bReuseTempFile &&
3687 129 : CPLTestBool(CPLGetConfigOption("OGR_MVT_REMOVE_TEMP_FILE", "YES")))
3688 : {
3689 128 : VSIUnlink(m_osTempDB);
3690 : }
3691 :
3692 133 : if (GDALDataset::Close() != CE_None)
3693 0 : eErr = CE_Failure;
3694 : }
3695 256 : return eErr;
3696 : }
3697 :
3698 : /************************************************************************/
3699 : /* ConvertToTileCoords() */
3700 : /************************************************************************/
3701 :
3702 19997 : void OGRMVTWriterDataset::ConvertToTileCoords(double dfX, double dfY, int &nX,
3703 : int &nY, double dfTopX,
3704 : double dfTopY,
3705 : double dfTileDim) const
3706 : {
3707 19997 : if (dfTileDim == 0)
3708 : {
3709 3022 : nX = static_cast<int>(dfX);
3710 3022 : nY = static_cast<int>(dfY);
3711 : }
3712 : else
3713 : {
3714 16975 : nX = static_cast<int>(
3715 16975 : std::round((dfX - dfTopX) * m_nExtent / dfTileDim));
3716 16975 : nY = static_cast<int>(
3717 16975 : std::round((dfTopY - dfY) * m_nExtent / dfTileDim));
3718 : }
3719 19997 : }
3720 :
3721 : /************************************************************************/
3722 : /* GetCmdCountCombined() */
3723 : /************************************************************************/
3724 :
3725 4065 : static unsigned GetCmdCountCombined(unsigned int nCmdId, unsigned int nCmdCount)
3726 : {
3727 4065 : return (nCmdId | (nCmdCount << 3));
3728 : }
3729 :
3730 : /************************************************************************/
3731 : /* EncodeLineString() */
3732 : /************************************************************************/
3733 :
3734 2995 : bool OGRMVTWriterDataset::EncodeLineString(
3735 : MVTTileLayerFeature *poGPBFeature, const OGRLineString *poLS,
3736 : OGRLineString *poOutLS, bool bWriteLastPoint,
3737 : ExpectedWindingOrder eExpectedWindingOrder, GUInt32 nMinLineTo,
3738 : double dfTopX, double dfTopY, double dfTileDim, int &nLastX,
3739 : int &nLastY) const
3740 : {
3741 2995 : const GUInt32 nInitialSize = poGPBFeature->getGeometryCount();
3742 2991 : const int nLastXOri = nLastX;
3743 2991 : const int nLastYOri = nLastY;
3744 2991 : GUInt32 nLineToCount = 0;
3745 2991 : const int nPoints = poLS->getNumPoints() - (bWriteLastPoint ? 0 : 1);
3746 2994 : bool bReverseOrder = false;
3747 :
3748 4657 : if (eExpectedWindingOrder != ExpectedWindingOrder::NONE &&
3749 1665 : poLS->getNumPoints() >= 4)
3750 : {
3751 : // Do the check on winding order in integer coordinates, since very flat
3752 : // rings in non rounded coordinates can change orientation after going
3753 : // to integer coordinates! In that case, let's remove them if they are
3754 : // inner rings.
3755 1650 : int nLastXTmp = nLastX;
3756 1650 : int nLastYTmp = nLastY;
3757 1650 : OGRLinearRing oRingInteger;
3758 12685 : for (int i = 0; i < nPoints; i++)
3759 : {
3760 : int nX, nY;
3761 11016 : const double dfX = poLS->getX(i);
3762 11021 : const double dfY = poLS->getY(i);
3763 10996 : ConvertToTileCoords(dfX, dfY, nX, nY, dfTopX, dfTopY, dfTileDim);
3764 11014 : const int nDiffX = nX - nLastXTmp;
3765 11014 : const int nDiffY = nY - nLastYTmp;
3766 11014 : if (i == 0 || nDiffX != 0 || nDiffY != 0)
3767 : {
3768 : // The minus sign is because the Y axis is positive-downward
3769 : // in vector tile coordinates!
3770 : // Cf https://docs.mapbox.com/data/tilesets/guides/vector-tiles-standards/#winding-order
3771 5001 : oRingInteger.addPoint(nX, -nY);
3772 5000 : nLastXTmp = nX;
3773 5000 : nLastYTmp = nY;
3774 : }
3775 : }
3776 1669 : oRingInteger.closeRings();
3777 1663 : if (oRingInteger.getNumPoints() < 4)
3778 1202 : return false;
3779 464 : const auto bIsClockWise = oRingInteger.isClockwise();
3780 456 : if (eExpectedWindingOrder == ExpectedWindingOrder::COUNTERCLOCKWISE)
3781 : {
3782 387 : if ((dfTileDim != 0 && bIsClockWise != poLS->isClockwise()) ||
3783 124 : (dfTileDim == 0 && bIsClockWise == poLS->isClockwise()))
3784 : {
3785 2 : return false;
3786 : }
3787 : }
3788 454 : bReverseOrder =
3789 193 : (eExpectedWindingOrder == ExpectedWindingOrder::CLOCKWISE &&
3790 908 : !bIsClockWise) ||
3791 261 : (eExpectedWindingOrder == ExpectedWindingOrder::COUNTERCLOCKWISE &&
3792 : bIsClockWise);
3793 : }
3794 :
3795 1796 : int nFirstX = 0;
3796 1796 : int nFirstY = 0;
3797 1796 : int nLastXValid = nLastX;
3798 1796 : int nLastYValid = nLastY;
3799 1796 : if (poOutLS)
3800 1779 : poOutLS->setNumPoints(nPoints);
3801 :
3802 9399 : for (int i = 0; i < nPoints; i++)
3803 : {
3804 : int nX, nY;
3805 7615 : int nSrcIdx = bReverseOrder ? poLS->getNumPoints() - 1 - i : i;
3806 7615 : double dfX = poLS->getX(nSrcIdx);
3807 7610 : double dfY = poLS->getY(nSrcIdx);
3808 7608 : ConvertToTileCoords(dfX, dfY, nX, nY, dfTopX, dfTopY, dfTileDim);
3809 7616 : int nDiffX = nX - nLastX;
3810 7616 : int nDiffY = nY - nLastY;
3811 7616 : if (i == 0 || nDiffX != 0 || nDiffY != 0)
3812 : {
3813 5127 : if (i > 0)
3814 : {
3815 3352 : nLineToCount++;
3816 3352 : if (nLineToCount == 1)
3817 : {
3818 524 : poGPBFeature->addGeometry(
3819 : GetCmdCountCombined(knCMD_MOVETO, 1));
3820 527 : const int nLastDiffX = nLastX - nLastXOri;
3821 527 : const int nLastDiffY = nLastY - nLastYOri;
3822 527 : poGPBFeature->addGeometry(EncodeSInt(nLastDiffX));
3823 526 : poGPBFeature->addGeometry(EncodeSInt(nLastDiffY));
3824 525 : if (poOutLS)
3825 525 : poOutLS->setPoint(0, nLastX, nLastY);
3826 :
3827 : // To be modified later
3828 521 : poGPBFeature->addGeometry(
3829 : GetCmdCountCombined(knCMD_LINETO, 0));
3830 : }
3831 :
3832 3355 : poGPBFeature->addGeometry(EncodeSInt(nDiffX));
3833 3355 : poGPBFeature->addGeometry(EncodeSInt(nDiffY));
3834 3354 : if (poOutLS)
3835 3354 : poOutLS->setPoint(nLineToCount, nX, nY);
3836 : }
3837 : else
3838 : {
3839 1775 : nFirstX = nX;
3840 1775 : nFirstY = nY;
3841 : }
3842 5133 : nLastXValid = nLastX;
3843 5133 : nLastYValid = nLastY;
3844 5133 : nLastX = nX;
3845 5133 : nLastY = nY;
3846 : }
3847 : }
3848 :
3849 : // If last point of ring is identical to first one, discard it
3850 1784 : if (nMinLineTo == 2 && nLineToCount > 0 && nFirstX == nLastX &&
3851 81 : nFirstY == nLastY)
3852 : {
3853 41 : poGPBFeature->resizeGeometryArray(poGPBFeature->getGeometryCount() - 2);
3854 35 : nLineToCount--;
3855 35 : nLastX = nLastXValid;
3856 35 : nLastY = nLastYValid;
3857 : }
3858 :
3859 1778 : if (nLineToCount >= nMinLineTo)
3860 : {
3861 523 : if (poOutLS)
3862 523 : poOutLS->setNumPoints(1 + nLineToCount);
3863 : // Patch actual number of points in LINETO command
3864 525 : poGPBFeature->setGeometry(
3865 : nInitialSize + 3, GetCmdCountCombined(knCMD_LINETO, nLineToCount));
3866 520 : return true;
3867 : }
3868 : else
3869 : {
3870 1255 : poGPBFeature->resizeGeometryArray(nInitialSize);
3871 1246 : nLastX = nLastXOri;
3872 1246 : nLastY = nLastYOri;
3873 1246 : return false;
3874 : }
3875 : }
3876 :
3877 : #ifdef notdef
3878 : /************************************************************************/
3879 : /* EncodeRepairedOuterRing() */
3880 : /************************************************************************/
3881 :
3882 : bool OGRMVTWriterDataset::EncodeRepairedOuterRing(
3883 : MVTTileLayerFeature *poGPBFeature, OGRPolygon &oInPoly, int &nLastX,
3884 : int &nLastY) const
3885 : {
3886 : std::unique_ptr<OGRGeometry> poFixedGeom(oInPoly.Buffer(0));
3887 : if (!poFixedGeom.get() || poFixedGeom->IsEmpty())
3888 : {
3889 : return false;
3890 : }
3891 :
3892 : OGRPolygon *poPoly = nullptr;
3893 : if (wkbFlatten(poFixedGeom->getGeometryType()) == wkbMultiPolygon)
3894 : {
3895 : OGRMultiPolygon *poMP = poFixedGeom.get()->toMultiPolygon();
3896 : poPoly = poMP->getGeometryRef(0)->toPolygon();
3897 : }
3898 : else if (wkbFlatten(poFixedGeom->getGeometryType()) == wkbPolygon)
3899 : {
3900 : poPoly = poFixedGeom.get()->toPolygon();
3901 : }
3902 : if (!poPoly)
3903 : return false;
3904 :
3905 : OGRLinearRing *poRing = poPoly->getExteriorRing();
3906 : const bool bReverseOrder = !poRing->isClockwise();
3907 :
3908 : const GUInt32 nInitialSize = poGPBFeature->getGeometryCount();
3909 : const int nLastXOri = nLastX;
3910 : const int nLastYOri = nLastY;
3911 : GUInt32 nLineToCount = 0;
3912 : const int nPoints = poRing->getNumPoints() - 1;
3913 : auto poOutLinearRing = std::make_unique<OGRLinearRing>();
3914 : poOutLinearRing->setNumPoints(nPoints);
3915 : for (int i = 0; i < nPoints; i++)
3916 : {
3917 : int nSrcIdx = bReverseOrder ? poRing->getNumPoints() - 1 - i : i;
3918 : double dfX = poRing->getX(nSrcIdx);
3919 : double dfY = poRing->getY(nSrcIdx);
3920 : int nX = static_cast<int>(std::round(dfX));
3921 : int nY = static_cast<int>(std::round(dfY));
3922 : if (nX != dfX || nY != dfY)
3923 : continue;
3924 : int nDiffX = nX - nLastX;
3925 : int nDiffY = nY - nLastY;
3926 : if (i == 0 || nDiffX != 0 || nDiffY != 0)
3927 : {
3928 : if (i > 0)
3929 : {
3930 : nLineToCount++;
3931 : if (nLineToCount == 1)
3932 : {
3933 : poGPBFeature->addGeometry(
3934 : GetCmdCountCombined(knCMD_MOVETO, 1));
3935 : const int nLastDiffX = nLastX - nLastXOri;
3936 : const int nLastDiffY = nLastY - nLastYOri;
3937 : poGPBFeature->addGeometry(EncodeSInt(nLastDiffX));
3938 : poGPBFeature->addGeometry(EncodeSInt(nLastDiffY));
3939 : poOutLinearRing->setPoint(0, nLastX, nLastY);
3940 :
3941 : // To be modified later
3942 : poGPBFeature->addGeometry(
3943 : GetCmdCountCombined(knCMD_LINETO, 0));
3944 : }
3945 :
3946 : poGPBFeature->addGeometry(EncodeSInt(nDiffX));
3947 : poGPBFeature->addGeometry(EncodeSInt(nDiffY));
3948 : poOutLinearRing->setPoint(nLineToCount, nX, nY);
3949 : }
3950 : nLastX = nX;
3951 : nLastY = nY;
3952 : }
3953 : }
3954 : if (nLineToCount >= 2)
3955 : {
3956 : poOutLinearRing->setNumPoints(1 + nLineToCount);
3957 : OGRPolygon oOutPoly;
3958 : oOutPoly.addRingDirectly(poOutLinearRing.release());
3959 : int bIsValid;
3960 : {
3961 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
3962 : bIsValid = oOutPoly.IsValid();
3963 : }
3964 : if (bIsValid)
3965 : {
3966 : // Patch actual number of points in LINETO command
3967 : poGPBFeature->setGeometry(
3968 : nInitialSize + 3,
3969 : GetCmdCountCombined(knCMD_LINETO, nLineToCount));
3970 : poGPBFeature->addGeometry(GetCmdCountCombined(knCMD_CLOSEPATH, 1));
3971 : return true;
3972 : }
3973 : }
3974 :
3975 : poGPBFeature->resizeGeometryArray(nInitialSize);
3976 : nLastX = nLastXOri;
3977 : nLastY = nLastYOri;
3978 : return false;
3979 : }
3980 : #endif
3981 :
3982 : /************************************************************************/
3983 : /* EncodePolygon() */
3984 : /************************************************************************/
3985 :
3986 1410 : bool OGRMVTWriterDataset::EncodePolygon(MVTTileLayerFeature *poGPBFeature,
3987 : const OGRPolygon *poPoly,
3988 : OGRPolygon *poOutPoly, double dfTopX,
3989 : double dfTopY, double dfTileDim,
3990 : int &nLastX, int &nLastY,
3991 : double &dfArea) const
3992 : {
3993 1410 : dfArea = 0;
3994 2811 : auto poOutOuterRing = std::make_unique<OGRLinearRing>();
3995 1863 : for (int i = 0; i < 1 + poPoly->getNumInteriorRings(); i++)
3996 : {
3997 1656 : const OGRLinearRing *poRing = (i == 0) ? poPoly->getExteriorRing()
3998 270 : : poPoly->getInteriorRing(i - 1);
3999 3339 : if (poRing->getNumPoints() < 4 ||
4000 3333 : poRing->getX(0) != poRing->getX(poRing->getNumPoints() - 1) ||
4001 1675 : poRing->getY(0) != poRing->getY(poRing->getNumPoints() - 1))
4002 : {
4003 0 : if (i == 0)
4004 1195 : return false;
4005 193 : continue;
4006 : }
4007 1664 : const bool bWriteLastPoint = false;
4008 1664 : const auto eExpectedWindingOrder =
4009 1664 : ((i == 0) ? ExpectedWindingOrder::CLOCKWISE
4010 : : ExpectedWindingOrder::COUNTERCLOCKWISE);
4011 1664 : const GUInt32 nMinLineTo = 2;
4012 0 : std::unique_ptr<OGRLinearRing> poOutInnerRing;
4013 1664 : if (i > 0)
4014 270 : poOutInnerRing = std::make_unique<OGRLinearRing>();
4015 : OGRLinearRing *poOutRing =
4016 1664 : poOutInnerRing.get() ? poOutInnerRing.get() : poOutOuterRing.get();
4017 :
4018 : bool bSuccess =
4019 1664 : EncodeLineString(poGPBFeature, poRing, poOutRing, bWriteLastPoint,
4020 : eExpectedWindingOrder, nMinLineTo, dfTopX, dfTopY,
4021 : dfTileDim, nLastX, nLastY);
4022 1655 : if (!bSuccess)
4023 : {
4024 1187 : if (i == 0)
4025 1182 : return false;
4026 5 : continue;
4027 : }
4028 :
4029 468 : if (poOutPoly == nullptr)
4030 : {
4031 180 : poGPBFeature->addGeometry(GetCmdCountCombined(knCMD_CLOSEPATH, 1));
4032 180 : continue;
4033 : }
4034 :
4035 288 : poOutRing->closeRings();
4036 :
4037 275 : poOutPoly->addRing(poOutRing);
4038 276 : if (i > 0)
4039 139 : dfArea -= poOutRing->get_Area();
4040 : else
4041 137 : dfArea = poOutRing->get_Area();
4042 :
4043 273 : poGPBFeature->addGeometry(GetCmdCountCombined(knCMD_CLOSEPATH, 1));
4044 : }
4045 :
4046 206 : return true;
4047 : }
4048 :
4049 : /************************************************************************/
4050 : /* PreGenerateForTile() */
4051 : /************************************************************************/
4052 :
4053 4028 : OGRErr OGRMVTWriterDataset::PreGenerateForTileReal(
4054 : int nZ, int nTileX, int nTileY, const CPLString &osTargetName,
4055 : bool bIsMaxZoomForLayer, const OGRMVTFeatureContent *poFeatureContent,
4056 : GIntBig nSerial, const OGRGeometry *poGeom,
4057 : const OGREnvelope &sEnvelope) const
4058 : {
4059 4028 : double dfTileDim = m_dfTileDim0 / (1 << nZ);
4060 4028 : double dfBuffer = dfTileDim * m_nBuffer / m_nExtent;
4061 4028 : double dfTopX = m_dfTopX + nTileX * dfTileDim;
4062 4028 : double dfTopY = m_dfTopY - nTileY * dfTileDim;
4063 4028 : double dfBottomRightX = dfTopX + dfTileDim;
4064 4028 : double dfBottomRightY = dfTopY - dfTileDim;
4065 4028 : double dfIntersectTopX = dfTopX - dfBuffer;
4066 4028 : double dfIntersectTopY = dfTopY + dfBuffer;
4067 4028 : double dfIntersectBottomRightX = dfBottomRightX + dfBuffer;
4068 4028 : double dfIntersectBottomRightY = dfBottomRightY - dfBuffer;
4069 :
4070 : const OGRGeometry *poIntersection;
4071 4013 : std::unique_ptr<OGRGeometry> poIntersectionHolder; // keep in that scope
4072 4028 : if (sEnvelope.MinX >= dfIntersectTopX &&
4073 4002 : sEnvelope.MinY >= dfIntersectBottomRightY &&
4074 3995 : sEnvelope.MaxX <= dfIntersectBottomRightX &&
4075 3977 : sEnvelope.MaxY <= dfIntersectTopY)
4076 : {
4077 3971 : poIntersection = poGeom;
4078 : }
4079 : else
4080 : {
4081 57 : OGRLinearRing *poLR = new OGRLinearRing();
4082 54 : poLR->addPoint(dfIntersectTopX, dfIntersectTopY);
4083 54 : poLR->addPoint(dfIntersectTopX, dfIntersectBottomRightY);
4084 54 : poLR->addPoint(dfIntersectBottomRightX, dfIntersectBottomRightY);
4085 54 : poLR->addPoint(dfIntersectBottomRightX, dfIntersectTopY);
4086 54 : poLR->addPoint(dfIntersectTopX, dfIntersectTopY);
4087 54 : OGRPolygon oPoly;
4088 54 : oPoly.addRingDirectly(poLR);
4089 :
4090 54 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
4091 53 : auto poTmp = poGeom->Intersection(&oPoly);
4092 51 : poIntersection = poTmp;
4093 51 : poIntersectionHolder.reset(poTmp);
4094 52 : if (poIntersection == nullptr || poIntersection->IsEmpty())
4095 : {
4096 3 : return OGRERR_NONE;
4097 : }
4098 : }
4099 :
4100 : // Create a layer with a single feature in it
4101 8018 : auto poLayer = std::make_shared<MVTTileLayer>();
4102 8028 : auto poGPBFeature = std::make_shared<MVTTileLayerFeature>();
4103 4001 : poLayer->addFeature(poGPBFeature);
4104 :
4105 3997 : OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType());
4106 4014 : if (eGeomType == wkbPoint || eGeomType == wkbMultiPoint)
4107 1385 : poGPBFeature->setType(MVTTileLayerFeature::GeomType::POINT);
4108 2629 : else if (eGeomType == wkbLineString || eGeomType == wkbMultiLineString)
4109 1309 : poGPBFeature->setType(MVTTileLayerFeature::GeomType::LINESTRING);
4110 1320 : else if (eGeomType == wkbPolygon || eGeomType == wkbMultiPolygon)
4111 1320 : poGPBFeature->setType(MVTTileLayerFeature::GeomType::POLYGON);
4112 : else
4113 : {
4114 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported geometry type");
4115 0 : return OGRERR_NONE;
4116 : }
4117 :
4118 : OGRwkbGeometryType eGeomToEncodeType =
4119 3990 : wkbFlatten(poIntersection->getGeometryType());
4120 :
4121 : // Simplify contour if requested by user
4122 3992 : const OGRGeometry *poGeomToEncode = poIntersection;
4123 4010 : std::unique_ptr<OGRGeometry> poGeomSimplified;
4124 3992 : const double dfSimplification =
4125 3992 : bIsMaxZoomForLayer ? m_dfSimplificationMaxZoom : m_dfSimplification;
4126 3992 : if (dfSimplification > 0 &&
4127 12 : (eGeomType == wkbLineString || eGeomType == wkbMultiLineString ||
4128 12 : eGeomType == wkbPolygon || eGeomType == wkbMultiPolygon))
4129 : {
4130 7 : const double dfTol = dfTileDim / m_nExtent;
4131 19 : poGeomSimplified = std::unique_ptr<OGRGeometry>(
4132 12 : poIntersection->SimplifyPreserveTopology(dfTol * dfSimplification));
4133 12 : if (poGeomSimplified.get())
4134 : {
4135 12 : poGeomToEncode = poGeomSimplified.get();
4136 12 : eGeomToEncodeType = wkbFlatten(poGeomSimplified->getGeometryType());
4137 : }
4138 : }
4139 :
4140 3997 : bool bGeomOK = false;
4141 3997 : double dfAreaOrLength = 0.0;
4142 :
4143 : const auto EmitValidPolygon =
4144 58 : [this, &bGeomOK, &dfAreaOrLength,
4145 186 : &poGPBFeature](const OGRGeometry *poValidGeom)
4146 : {
4147 58 : bGeomOK = false;
4148 58 : dfAreaOrLength = 0;
4149 58 : int nLastX = 0;
4150 58 : int nLastY = 0;
4151 :
4152 58 : if (wkbFlatten(poValidGeom->getGeometryType()) == wkbPolygon)
4153 : {
4154 12 : const OGRPolygon *poPoly = poValidGeom->toPolygon();
4155 12 : double dfPartArea = 0.0;
4156 12 : bGeomOK = EncodePolygon(poGPBFeature.get(), poPoly, nullptr, 0, 0,
4157 : 0, nLastX, nLastY, dfPartArea);
4158 12 : dfAreaOrLength = dfPartArea;
4159 : }
4160 46 : else if (OGR_GT_IsSubClassOf(poValidGeom->getGeometryType(),
4161 46 : wkbGeometryCollection))
4162 : {
4163 130 : for (auto &&poSubGeom : poValidGeom->toGeometryCollection())
4164 : {
4165 88 : if (wkbFlatten(poSubGeom->getGeometryType()) == wkbPolygon)
4166 : {
4167 36 : const OGRPolygon *poPoly = poSubGeom->toPolygon();
4168 36 : double dfPartArea = 0.0;
4169 36 : bGeomOK |=
4170 36 : EncodePolygon(poGPBFeature.get(), poPoly, nullptr, 0, 0,
4171 36 : 0, nLastX, nLastY, dfPartArea);
4172 36 : dfAreaOrLength += dfPartArea;
4173 : }
4174 52 : else if (wkbFlatten(poSubGeom->getGeometryType()) ==
4175 : wkbMultiPolygon)
4176 : {
4177 : const OGRMultiPolygon *poMPoly =
4178 5 : poSubGeom->toMultiPolygon();
4179 15 : for (const auto *poPoly : poMPoly)
4180 : {
4181 10 : double dfPartArea = 0.0;
4182 10 : bGeomOK |=
4183 10 : EncodePolygon(poGPBFeature.get(), poPoly, nullptr,
4184 10 : 0, 0, 0, nLastX, nLastY, dfPartArea);
4185 10 : dfAreaOrLength += dfPartArea;
4186 : }
4187 : }
4188 : }
4189 : }
4190 58 : };
4191 :
4192 3997 : if (eGeomType == wkbPoint || eGeomType == wkbMultiPoint)
4193 : {
4194 1362 : if (eGeomToEncodeType == wkbPoint)
4195 : {
4196 979 : const OGRPoint *poPoint = poIntersection->toPoint();
4197 : int nX, nY;
4198 981 : double dfX = poPoint->getX();
4199 984 : double dfY = poPoint->getY();
4200 984 : bGeomOK = true;
4201 984 : ConvertToTileCoords(dfX, dfY, nX, nY, dfTopX, dfTopY, dfTileDim);
4202 982 : poGPBFeature->addGeometry(GetCmdCountCombined(knCMD_MOVETO, 1));
4203 984 : poGPBFeature->addGeometry(EncodeSInt(nX));
4204 983 : poGPBFeature->addGeometry(EncodeSInt(nY));
4205 : }
4206 383 : else if (eGeomToEncodeType == wkbMultiPoint ||
4207 : eGeomToEncodeType == wkbGeometryCollection)
4208 : {
4209 : const OGRGeometryCollection *poGC =
4210 382 : poIntersection->toGeometryCollection();
4211 764 : std::set<std::pair<int, int>> oSetUniqueCoords;
4212 383 : poGPBFeature->addGeometry(
4213 : GetCmdCountCombined(knCMD_MOVETO, 0)); // To be modified later
4214 384 : int nLastX = 0;
4215 384 : int nLastY = 0;
4216 774 : for (auto &&poSubGeom : poGC)
4217 : {
4218 389 : if (wkbFlatten(poSubGeom->getGeometryType()) == wkbPoint)
4219 : {
4220 388 : const OGRPoint *poPoint = poSubGeom->toPoint();
4221 : int nX, nY;
4222 389 : double dfX = poPoint->getX();
4223 388 : double dfY = poPoint->getY();
4224 390 : ConvertToTileCoords(dfX, dfY, nX, nY, dfTopX, dfTopY,
4225 : dfTileDim);
4226 387 : if (oSetUniqueCoords.find(std::pair<int, int>(nX, nY)) ==
4227 778 : oSetUniqueCoords.end())
4228 : {
4229 389 : oSetUniqueCoords.insert(std::pair<int, int>(nX, nY));
4230 :
4231 390 : int nDiffX = nX - nLastX;
4232 390 : int nDiffY = nY - nLastY;
4233 390 : poGPBFeature->addGeometry(EncodeSInt(nDiffX));
4234 388 : poGPBFeature->addGeometry(EncodeSInt(nDiffY));
4235 390 : nLastX = nX;
4236 390 : nLastY = nY;
4237 : }
4238 : }
4239 : }
4240 384 : GUInt32 nPoints = static_cast<GUInt32>(oSetUniqueCoords.size());
4241 383 : bGeomOK = nPoints > 0;
4242 383 : poGPBFeature->setGeometry(
4243 : 0, GetCmdCountCombined(knCMD_MOVETO, nPoints));
4244 1366 : }
4245 : }
4246 2635 : else if (eGeomType == wkbLineString || eGeomType == wkbMultiLineString)
4247 : {
4248 1312 : const bool bWriteLastPoint = true;
4249 1312 : const GUInt32 nMinLineTo = 1;
4250 :
4251 1312 : if (eGeomToEncodeType == wkbLineString)
4252 : {
4253 925 : const OGRLineString *poLS = poGeomToEncode->toLineString();
4254 927 : int nLastX = 0;
4255 927 : int nLastY = 0;
4256 927 : OGRLineString oOutLS;
4257 927 : bGeomOK = EncodeLineString(
4258 : poGPBFeature.get(), poLS, &oOutLS, bWriteLastPoint,
4259 : ExpectedWindingOrder::NONE, nMinLineTo, dfTopX, dfTopY,
4260 : dfTileDim, nLastX, nLastY);
4261 930 : dfAreaOrLength = oOutLS.get_Length();
4262 : }
4263 387 : else if (eGeomToEncodeType == wkbMultiLineString ||
4264 : eGeomToEncodeType == wkbGeometryCollection)
4265 : {
4266 : const OGRGeometryCollection *poGC =
4267 390 : poGeomToEncode->toGeometryCollection();
4268 384 : int nLastX = 0;
4269 384 : int nLastY = 0;
4270 778 : for (auto &&poSubGeom : poGC)
4271 : {
4272 394 : if (wkbFlatten(poSubGeom->getGeometryType()) == wkbLineString)
4273 : {
4274 395 : const OGRLineString *poLS = poSubGeom->toLineString();
4275 392 : OGRLineString oOutLS;
4276 393 : bool bSubGeomOK = EncodeLineString(
4277 : poGPBFeature.get(), poLS, &oOutLS, bWriteLastPoint,
4278 : ExpectedWindingOrder::NONE, nMinLineTo, dfTopX, dfTopY,
4279 : dfTileDim, nLastX, nLastY);
4280 395 : if (bSubGeomOK)
4281 18 : dfAreaOrLength += oOutLS.get_Length();
4282 395 : bGeomOK |= bSubGeomOK;
4283 : }
4284 : }
4285 1310 : }
4286 : }
4287 1323 : else if (eGeomType == wkbPolygon || eGeomType == wkbMultiPolygon)
4288 : {
4289 1328 : if (eGeomToEncodeType == wkbPolygon)
4290 : {
4291 951 : const OGRPolygon *poPoly = poGeomToEncode->toPolygon();
4292 951 : int nLastX = 0;
4293 951 : int nLastY = 0;
4294 1891 : OGRPolygon oOutPoly;
4295 946 : const GUInt32 nInitialSize = poGPBFeature->getGeometryCount();
4296 945 : CPL_IGNORE_RET_VAL(nInitialSize);
4297 951 : bGeomOK = EncodePolygon(poGPBFeature.get(), poPoly, &oOutPoly,
4298 : dfTopX, dfTopY, dfTileDim, nLastX, nLastY,
4299 : dfAreaOrLength);
4300 : int bIsValid;
4301 : {
4302 952 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
4303 941 : bIsValid = oOutPoly.IsValid();
4304 : }
4305 940 : if (!bIsValid)
4306 : {
4307 : // Build a valid geometry from the initial MVT geometry and emit
4308 : // it
4309 110 : std::unique_ptr<OGRGeometry> poPolyValid(oOutPoly.MakeValid());
4310 55 : if (poPolyValid)
4311 : {
4312 55 : poGPBFeature->resizeGeometryArray(nInitialSize);
4313 55 : EmitValidPolygon(poPolyValid.get());
4314 : }
4315 : }
4316 : }
4317 377 : else if (eGeomToEncodeType == wkbMultiPolygon ||
4318 : eGeomToEncodeType == wkbGeometryCollection)
4319 : {
4320 : const OGRGeometryCollection *poGC =
4321 354 : poGeomToEncode->toGeometryCollection();
4322 378 : int nLastX = 0;
4323 378 : int nLastY = 0;
4324 750 : OGRMultiPolygon oOutMP;
4325 376 : const GUInt32 nInitialSize = poGPBFeature->getGeometryCount();
4326 376 : CPL_IGNORE_RET_VAL(nInitialSize);
4327 763 : for (auto &&poSubGeom : poGC)
4328 : {
4329 391 : if (wkbFlatten(poSubGeom->getGeometryType()) == wkbPolygon)
4330 : {
4331 396 : const OGRPolygon *poPoly = poSubGeom->toPolygon();
4332 393 : double dfPartArea = 0.0;
4333 781 : auto poOutPoly = std::make_unique<OGRPolygon>();
4334 399 : bGeomOK |= EncodePolygon(
4335 : poGPBFeature.get(), poPoly, poOutPoly.get(), dfTopX,
4336 393 : dfTopY, dfTileDim, nLastX, nLastY, dfPartArea);
4337 393 : dfAreaOrLength += dfPartArea;
4338 393 : oOutMP.addGeometryDirectly(poOutPoly.release());
4339 : }
4340 : }
4341 : int bIsValid;
4342 : {
4343 372 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
4344 374 : bIsValid = oOutMP.IsValid();
4345 : }
4346 372 : if (!bIsValid)
4347 : {
4348 : // Build a valid geometry from the initial MVT geometry and emit
4349 : // it
4350 6 : std::unique_ptr<OGRGeometry> poMPValid(oOutMP.MakeValid());
4351 3 : if (poMPValid)
4352 : {
4353 3 : poGPBFeature->resizeGeometryArray(nInitialSize);
4354 3 : EmitValidPolygon(poMPValid.get());
4355 : }
4356 : }
4357 : }
4358 : }
4359 4002 : if (!bGeomOK)
4360 2468 : return OGRERR_NONE;
4361 :
4362 5871 : for (const auto &pair : poFeatureContent->oValues)
4363 : {
4364 4335 : GUInt32 nKey = poLayer->addKey(pair.first);
4365 4339 : GUInt32 nVal = poLayer->addValue(pair.second);
4366 4329 : poGPBFeature->addTag(nKey);
4367 4331 : poGPBFeature->addTag(nVal);
4368 : }
4369 1531 : if (poFeatureContent->nFID >= 0)
4370 : {
4371 52 : poGPBFeature->setId(poFeatureContent->nFID);
4372 : }
4373 :
4374 : #ifdef notdef
4375 : {
4376 : MVTTile oTile;
4377 : poLayer->setName("x");
4378 : oTile.addLayer(poLayer);
4379 :
4380 : CPLString oBuffer(oTile.write());
4381 :
4382 : VSILFILE *fp = VSIFOpenL(
4383 : CPLSPrintf("/tmp/%d-%d-%d.pbf", nZ, nTileX, nTileY), "wb");
4384 : VSIFWriteL(oBuffer.data(), 1, oBuffer.size(), fp);
4385 : VSIFCloseL(fp);
4386 : }
4387 : #endif
4388 :
4389 : // GPB encode the layer with our single feature
4390 3072 : CPLString oBuffer(poLayer->write());
4391 :
4392 : // Compress buffer
4393 1536 : size_t nCompressedSize = 0;
4394 1536 : void *pCompressed = CPLZLibDeflate(oBuffer.data(), oBuffer.size(), -1,
4395 : nullptr, 0, &nCompressedSize);
4396 1536 : oBuffer.assign(static_cast<char *>(pCompressed), nCompressedSize);
4397 1530 : CPLFree(pCompressed);
4398 :
4399 1542 : const auto InsertIntoDb = [&]()
4400 : {
4401 15420 : m_nTempTiles++;
4402 1542 : sqlite3_bind_int(m_hInsertStmt, 1, nZ);
4403 1542 : sqlite3_bind_int(m_hInsertStmt, 2, nTileX);
4404 1542 : sqlite3_bind_int(m_hInsertStmt, 3, nTileY);
4405 1542 : sqlite3_bind_text(m_hInsertStmt, 4, osTargetName.c_str(), -1,
4406 : SQLITE_STATIC);
4407 1542 : sqlite3_bind_int64(m_hInsertStmt, 5, nSerial);
4408 1542 : sqlite3_bind_blob(m_hInsertStmt, 6, oBuffer.data(),
4409 1542 : static_cast<int>(oBuffer.size()), SQLITE_STATIC);
4410 1542 : sqlite3_bind_int(m_hInsertStmt, 7,
4411 1542 : static_cast<int>(poGPBFeature->getType()));
4412 1542 : sqlite3_bind_double(m_hInsertStmt, 8, dfAreaOrLength);
4413 1542 : int rc = sqlite3_step(m_hInsertStmt);
4414 1542 : sqlite3_reset(m_hInsertStmt);
4415 1542 : return rc;
4416 1531 : };
4417 :
4418 : int rc;
4419 1531 : if (m_bThreadPoolOK)
4420 : {
4421 1511 : std::lock_guard<std::mutex> oLock(m_oDBMutex);
4422 1517 : rc = InsertIntoDb();
4423 : }
4424 : else
4425 : {
4426 20 : rc = InsertIntoDb();
4427 : }
4428 :
4429 1542 : if (!(rc == SQLITE_OK || rc == SQLITE_DONE))
4430 : {
4431 5 : return OGRERR_FAILURE;
4432 : }
4433 :
4434 1537 : return OGRERR_NONE;
4435 : }
4436 :
4437 : /************************************************************************/
4438 : /* MVTWriterTask() */
4439 : /************************************************************************/
4440 :
4441 : class MVTWriterTask
4442 : {
4443 : public:
4444 : const OGRMVTWriterDataset *poDS;
4445 : int nZ;
4446 : int nTileX;
4447 : int nTileY;
4448 : CPLString osTargetName;
4449 : bool bIsMaxZoomForLayer;
4450 : std::shared_ptr<OGRMVTFeatureContent> poFeatureContent;
4451 : GIntBig nSerial;
4452 : std::shared_ptr<OGRGeometry> poGeom;
4453 : OGREnvelope sEnvelope;
4454 : };
4455 :
4456 : /************************************************************************/
4457 : /* WriterTaskFunc() */
4458 : /************************************************************************/
4459 :
4460 4006 : void OGRMVTWriterDataset::WriterTaskFunc(void *pParam)
4461 : {
4462 4006 : MVTWriterTask *poTask = static_cast<MVTWriterTask *>(pParam);
4463 15993 : OGRErr eErr = poTask->poDS->PreGenerateForTileReal(
4464 4000 : poTask->nZ, poTask->nTileX, poTask->nTileY, poTask->osTargetName,
4465 4001 : poTask->bIsMaxZoomForLayer, poTask->poFeatureContent.get(),
4466 4006 : poTask->nSerial, poTask->poGeom.get(), poTask->sEnvelope);
4467 3986 : if (eErr != OGRERR_NONE)
4468 : {
4469 4 : std::lock_guard oLock(poTask->poDS->m_oDBMutex);
4470 4 : poTask->poDS->m_bWriteFeatureError = true;
4471 : }
4472 3986 : delete poTask;
4473 4006 : }
4474 :
4475 : /************************************************************************/
4476 : /* PreGenerateForTile() */
4477 : /************************************************************************/
4478 :
4479 4036 : OGRErr OGRMVTWriterDataset::PreGenerateForTile(
4480 : int nZ, int nTileX, int nTileY, const CPLString &osTargetName,
4481 : bool bIsMaxZoomForLayer,
4482 : const std::shared_ptr<OGRMVTFeatureContent> &poFeatureContent,
4483 : GIntBig nSerial, const std::shared_ptr<OGRGeometry> &poGeom,
4484 : const OGREnvelope &sEnvelope) const
4485 : {
4486 4036 : if (!m_bThreadPoolOK)
4487 : {
4488 25 : return PreGenerateForTileReal(
4489 : nZ, nTileX, nTileY, osTargetName, bIsMaxZoomForLayer,
4490 50 : poFeatureContent.get(), nSerial, poGeom.get(), sEnvelope);
4491 : }
4492 : else
4493 : {
4494 4011 : MVTWriterTask *poTask = new MVTWriterTask;
4495 4011 : poTask->poDS = this;
4496 4011 : poTask->nZ = nZ;
4497 4011 : poTask->nTileX = nTileX;
4498 4011 : poTask->nTileY = nTileY;
4499 4011 : poTask->osTargetName = osTargetName;
4500 4011 : poTask->bIsMaxZoomForLayer = bIsMaxZoomForLayer;
4501 4011 : poTask->poFeatureContent = poFeatureContent;
4502 4011 : poTask->nSerial = nSerial;
4503 4011 : poTask->poGeom = poGeom;
4504 4011 : poTask->sEnvelope = sEnvelope;
4505 4011 : m_oThreadPool.SubmitJob(OGRMVTWriterDataset::WriterTaskFunc, poTask);
4506 : // Do not queue more than 1000 jobs to avoid memory exhaustion
4507 4011 : m_oThreadPool.WaitCompletion(1000);
4508 :
4509 4011 : std::lock_guard oLock(m_oDBMutex);
4510 4011 : return m_bWriteFeatureError ? OGRERR_FAILURE : OGRERR_NONE;
4511 : }
4512 : }
4513 :
4514 : /************************************************************************/
4515 : /* UpdateLayerProperties() */
4516 : /************************************************************************/
4517 :
4518 4353 : void OGRMVTWriterDataset::UpdateLayerProperties(
4519 : MVTLayerProperties *poLayerProperties, const std::string &osKey,
4520 : const MVTTileLayerValue &oValue)
4521 : {
4522 4353 : auto oFieldIter = poLayerProperties->m_oMapFieldNameToIdx.find(osKey);
4523 4353 : MVTFieldProperties *poFieldProps = nullptr;
4524 4353 : if (oFieldIter == poLayerProperties->m_oMapFieldNameToIdx.end())
4525 : {
4526 181 : if (poLayerProperties->m_oSetFields.size() < knMAX_COUNT_FIELDS)
4527 : {
4528 181 : poLayerProperties->m_oSetFields.insert(osKey);
4529 181 : if (poLayerProperties->m_oMapFieldNameToIdx.size() <
4530 : knMAX_REPORT_FIELDS)
4531 : {
4532 362 : MVTFieldProperties oFieldProps;
4533 181 : oFieldProps.m_osName = osKey;
4534 181 : if (oValue.isNumeric())
4535 : {
4536 73 : oFieldProps.m_dfMinVal = oValue.getNumericValue();
4537 73 : oFieldProps.m_dfMaxVal = oValue.getNumericValue();
4538 73 : oFieldProps.m_bAllInt = true; // overridden just below
4539 : }
4540 181 : oFieldProps.m_eType =
4541 289 : oValue.isNumeric() ? MVTTileLayerValue::ValueType::DOUBLE
4542 108 : : oValue.isString() ? MVTTileLayerValue::ValueType::STRING
4543 : : MVTTileLayerValue::ValueType::BOOL;
4544 :
4545 181 : poLayerProperties->m_oMapFieldNameToIdx[osKey] =
4546 181 : poLayerProperties->m_aoFields.size();
4547 181 : poLayerProperties->m_aoFields.push_back(std::move(oFieldProps));
4548 181 : poFieldProps = &(poLayerProperties->m_aoFields.back());
4549 : }
4550 : }
4551 : }
4552 : else
4553 : {
4554 4172 : poFieldProps = &(poLayerProperties->m_aoFields[oFieldIter->second]);
4555 : }
4556 :
4557 4353 : if (poFieldProps)
4558 : {
4559 4353 : if (oValue.getType() == MVTTileLayerValue::ValueType::BOOL)
4560 : {
4561 24 : MVTTileLayerValue oUniqVal;
4562 12 : oUniqVal.setBoolValue(oValue.getBoolValue());
4563 12 : poFieldProps->m_oSetAllValues.insert(oUniqVal);
4564 12 : poFieldProps->m_oSetValues.insert(oUniqVal);
4565 : }
4566 4341 : else if (oValue.isNumeric())
4567 : {
4568 1780 : if (poFieldProps->m_bAllInt)
4569 : {
4570 935 : poFieldProps->m_bAllInt =
4571 1870 : oValue.getType() == MVTTileLayerValue::ValueType::INT ||
4572 2751 : oValue.getType() == MVTTileLayerValue::ValueType::SINT ||
4573 1798 : (oValue.getType() == MVTTileLayerValue::ValueType::UINT &&
4574 881 : oValue.getUIntValue() < GINT64_MAX);
4575 : }
4576 1780 : double dfVal = oValue.getNumericValue();
4577 1780 : poFieldProps->m_dfMinVal =
4578 1780 : std::min(poFieldProps->m_dfMinVal, dfVal);
4579 1780 : poFieldProps->m_dfMaxVal =
4580 1780 : std::max(poFieldProps->m_dfMaxVal, dfVal);
4581 1780 : if (poFieldProps->m_oSetAllValues.size() < knMAX_COUNT_VALUES)
4582 : {
4583 3560 : MVTTileLayerValue oUniqVal;
4584 1780 : oUniqVal.setDoubleValue(dfVal);
4585 1780 : poFieldProps->m_oSetAllValues.insert(oUniqVal);
4586 1780 : if (poFieldProps->m_oSetValues.size() < knMAX_REPORT_VALUES)
4587 : {
4588 1780 : poFieldProps->m_oSetValues.insert(oUniqVal);
4589 : }
4590 : }
4591 : }
4592 5122 : else if (oValue.isString() &&
4593 2561 : poFieldProps->m_oSetAllValues.size() < knMAX_COUNT_VALUES)
4594 : {
4595 5122 : auto osVal = oValue.getStringValue();
4596 5122 : MVTTileLayerValue oUniqVal;
4597 2561 : oUniqVal.setStringValue(osVal);
4598 2561 : poFieldProps->m_oSetAllValues.insert(oUniqVal);
4599 5122 : if (osVal.size() <= knMAX_STRING_VALUE_LENGTH &&
4600 2561 : poFieldProps->m_oSetValues.size() < knMAX_REPORT_VALUES)
4601 : {
4602 2561 : poFieldProps->m_oSetValues.insert(oUniqVal);
4603 : }
4604 : }
4605 : }
4606 4353 : }
4607 :
4608 : /************************************************************************/
4609 : /* GZIPCompress() */
4610 : /************************************************************************/
4611 :
4612 977 : static void GZIPCompress(std::string &oTileBuffer)
4613 : {
4614 977 : if (!oTileBuffer.empty())
4615 : {
4616 : const CPLString osTmpFilename(
4617 1954 : VSIMemGenerateHiddenFilename("mvt_temp.gz"));
4618 1954 : CPLString osTmpGZipFilename("/vsigzip/" + osTmpFilename);
4619 977 : VSILFILE *fpGZip = VSIFOpenL(osTmpGZipFilename, "wb");
4620 977 : if (fpGZip)
4621 : {
4622 977 : VSIFWriteL(oTileBuffer.data(), 1, oTileBuffer.size(), fpGZip);
4623 977 : VSIFCloseL(fpGZip);
4624 :
4625 977 : vsi_l_offset nCompressedSize = 0;
4626 : GByte *pabyCompressed =
4627 977 : VSIGetMemFileBuffer(osTmpFilename, &nCompressedSize, false);
4628 : oTileBuffer.assign(reinterpret_cast<char *>(pabyCompressed),
4629 977 : static_cast<size_t>(nCompressedSize));
4630 : }
4631 977 : VSIUnlink(osTmpFilename);
4632 : }
4633 977 : }
4634 :
4635 : /************************************************************************/
4636 : /* GetReducedPrecisionGeometry() */
4637 : /************************************************************************/
4638 :
4639 : static std::vector<GUInt32>
4640 167 : GetReducedPrecisionGeometry(MVTTileLayerFeature::GeomType eGeomType,
4641 : const std::vector<GUInt32> &anSrcGeometry,
4642 : GUInt32 nSrcExtent, GUInt32 nDstExtent)
4643 : {
4644 167 : std::vector<GUInt32> anDstGeometry;
4645 167 : size_t nLastMoveToIdx = 0;
4646 167 : int nX = 0;
4647 167 : int nY = 0;
4648 167 : int nFirstReducedX = 0;
4649 167 : int nFirstReducedY = 0;
4650 167 : int nLastReducedX = 0;
4651 167 : int nLastReducedY = 0;
4652 167 : int nLastReducedXValid = 0;
4653 167 : int nLastReducedYValid = 0;
4654 167 : std::unique_ptr<OGRLinearRing> poInRing;
4655 167 : std::unique_ptr<OGRLinearRing> poOutRing;
4656 167 : std::unique_ptr<OGRLinearRing> poOutOuterRing;
4657 167 : bool bDiscardInnerRings = false;
4658 167 : const bool bIsPoly = eGeomType == MVTTileLayerFeature::GeomType::POLYGON;
4659 506 : for (size_t iSrc = 0; iSrc < anSrcGeometry.size();)
4660 : {
4661 339 : const unsigned nCount = GetCmdCount(anSrcGeometry[iSrc]);
4662 339 : switch (GetCmdId(anSrcGeometry[iSrc]))
4663 : {
4664 185 : case knCMD_MOVETO:
4665 : {
4666 185 : nLastMoveToIdx = anDstGeometry.size();
4667 :
4668 185 : anDstGeometry.push_back(anSrcGeometry[iSrc]);
4669 185 : iSrc++;
4670 :
4671 185 : unsigned nDstPoints = 0;
4672 185 : for (unsigned j = 0;
4673 370 : iSrc + 1 < anSrcGeometry.size() && j < nCount;
4674 185 : j++, iSrc += 2)
4675 : {
4676 185 : nX += DecodeSInt(anSrcGeometry[iSrc]);
4677 185 : nY += DecodeSInt(anSrcGeometry[iSrc + 1]);
4678 :
4679 185 : int nReducedX = static_cast<int>(static_cast<GIntBig>(nX) *
4680 185 : nDstExtent / nSrcExtent);
4681 185 : int nReducedY = static_cast<int>(static_cast<GIntBig>(nY) *
4682 185 : nDstExtent / nSrcExtent);
4683 185 : int nDiffX = nReducedX - nLastReducedX;
4684 185 : int nDiffY = nReducedY - nLastReducedY;
4685 185 : if (j == 0)
4686 : {
4687 185 : if (bIsPoly)
4688 : {
4689 82 : poInRing = std::unique_ptr<OGRLinearRing>(
4690 82 : new OGRLinearRing());
4691 82 : poOutRing = std::unique_ptr<OGRLinearRing>(
4692 82 : new OGRLinearRing());
4693 : }
4694 185 : nFirstReducedX = nReducedX;
4695 185 : nFirstReducedY = nReducedY;
4696 : }
4697 185 : if (j == 0 || nDiffX != 0 || nDiffY != 0)
4698 : {
4699 185 : if (bIsPoly)
4700 : {
4701 41 : poInRing->addPoint(nX, nY);
4702 41 : poOutRing->addPoint(nReducedX, nReducedY);
4703 : }
4704 185 : nDstPoints++;
4705 185 : anDstGeometry.push_back(EncodeSInt(nDiffX));
4706 185 : anDstGeometry.push_back(EncodeSInt(nDiffY));
4707 185 : nLastReducedX = nReducedX;
4708 185 : nLastReducedY = nReducedY;
4709 : }
4710 : }
4711 : // Patch count of MOVETO
4712 185 : anDstGeometry[nLastMoveToIdx] = GetCmdCountCombined(
4713 185 : GetCmdId(anDstGeometry[nLastMoveToIdx]), nDstPoints);
4714 185 : break;
4715 : }
4716 113 : case knCMD_LINETO:
4717 : {
4718 113 : size_t nIdxToPatch = anDstGeometry.size();
4719 113 : anDstGeometry.push_back(anSrcGeometry[iSrc]);
4720 113 : iSrc++;
4721 113 : unsigned nDstPoints = 0;
4722 113 : int nLastReducedXBefore = nLastReducedX;
4723 113 : int nLastReducedYBefore = nLastReducedY;
4724 113 : for (unsigned j = 0;
4725 267 : iSrc + 1 < anSrcGeometry.size() && j < nCount;
4726 154 : j++, iSrc += 2)
4727 : {
4728 154 : nX += DecodeSInt(anSrcGeometry[iSrc]);
4729 154 : nY += DecodeSInt(anSrcGeometry[iSrc + 1]);
4730 :
4731 154 : int nReducedX = static_cast<int>(static_cast<GIntBig>(nX) *
4732 154 : nDstExtent / nSrcExtent);
4733 154 : int nReducedY = static_cast<int>(static_cast<GIntBig>(nY) *
4734 154 : nDstExtent / nSrcExtent);
4735 154 : int nDiffX = nReducedX - nLastReducedX;
4736 154 : int nDiffY = nReducedY - nLastReducedY;
4737 154 : if (nDiffX != 0 || nDiffY != 0)
4738 : {
4739 114 : if (bIsPoly)
4740 : {
4741 60 : CPLAssert(poInRing);
4742 60 : CPLAssert(poOutRing);
4743 60 : poInRing->addPoint(nX, nY);
4744 60 : poOutRing->addPoint(nReducedX, nReducedY);
4745 : }
4746 114 : nDstPoints++;
4747 114 : anDstGeometry.push_back(EncodeSInt(nDiffX));
4748 114 : anDstGeometry.push_back(EncodeSInt(nDiffY));
4749 114 : nLastReducedXBefore = nLastReducedX;
4750 114 : nLastReducedYBefore = nLastReducedY;
4751 114 : nLastReducedX = nReducedX;
4752 114 : nLastReducedY = nReducedY;
4753 : }
4754 : }
4755 :
4756 : // If last point of ring is identical to first one, discard it
4757 113 : if (nDstPoints > 0 && bIsPoly &&
4758 1 : nLastReducedX == nFirstReducedX &&
4759 : nLastReducedY == nFirstReducedY)
4760 : {
4761 0 : nLastReducedX = nLastReducedXBefore;
4762 0 : nLastReducedY = nLastReducedYBefore;
4763 0 : nDstPoints -= 1;
4764 0 : anDstGeometry.resize(anDstGeometry.size() - 2);
4765 0 : poOutRing->setNumPoints(poOutRing->getNumPoints() - 1);
4766 : }
4767 :
4768 : // Patch count of LINETO
4769 113 : anDstGeometry[nIdxToPatch] = GetCmdCountCombined(
4770 113 : GetCmdId(anDstGeometry[nIdxToPatch]), nDstPoints);
4771 :
4772 : // A valid linestring should have at least one MOVETO +
4773 : // one coord pair + one LINETO + one coord pair
4774 113 : if (eGeomType == MVTTileLayerFeature::GeomType::LINESTRING)
4775 : {
4776 72 : if (anDstGeometry.size() < nLastMoveToIdx + 1 + 2 + 1 + 2)
4777 : {
4778 : // Remove last linestring
4779 18 : nLastReducedX = nLastReducedXValid;
4780 18 : nLastReducedY = nLastReducedYValid;
4781 18 : anDstGeometry.resize(nLastMoveToIdx);
4782 : }
4783 : else
4784 : {
4785 54 : nLastReducedXValid = nLastReducedX;
4786 54 : nLastReducedYValid = nLastReducedY;
4787 : }
4788 : }
4789 :
4790 113 : break;
4791 : }
4792 41 : case knCMD_CLOSEPATH:
4793 : {
4794 41 : CPLAssert(bIsPoly);
4795 41 : CPLAssert(poInRing);
4796 41 : CPLAssert(poOutRing);
4797 41 : int bIsValid = true;
4798 :
4799 : // A valid ring should have at least one MOVETO + one
4800 : // coord pair + one LINETO + two coord pairs
4801 41 : if (anDstGeometry.size() < nLastMoveToIdx + 1 + 2 + 1 + 2 * 2)
4802 : {
4803 : // Remove ring. Normally if we remove an outer ring,
4804 : // its inner rings should also be removed, given they are
4805 : // smaller than the outer ring.
4806 14 : bIsValid = false;
4807 : }
4808 : else
4809 : {
4810 27 : poInRing->closeRings();
4811 27 : poOutRing->closeRings();
4812 27 : bool bIsOuterRing = !poInRing->isClockwise();
4813 : // Normally the first ring of a polygon geometry should
4814 : // be a outer ring, except when it is degenerate enough
4815 : // in which case poOutOuterRing might be null.
4816 27 : if (bIsOuterRing)
4817 : {
4818 : // if the outer ring turned out to be a inner ring
4819 : // once reduced
4820 18 : if (poOutRing->isClockwise())
4821 : {
4822 0 : bIsValid = false;
4823 0 : bDiscardInnerRings = true;
4824 : }
4825 : else
4826 : {
4827 18 : OGRPolygon oPoly;
4828 18 : oPoly.addRing(poOutRing.get());
4829 36 : poOutOuterRing = std::unique_ptr<OGRLinearRing>(
4830 18 : poOutRing.release());
4831 : {
4832 : CPLErrorStateBackuper oErrorStateBackuper(
4833 18 : CPLQuietErrorHandler);
4834 18 : bIsValid = oPoly.IsValid();
4835 : }
4836 18 : bDiscardInnerRings = !bIsValid;
4837 : }
4838 : }
4839 9 : else if (bDiscardInnerRings ||
4840 18 : poOutOuterRing.get() == nullptr ||
4841 : // if the inner ring turned out to be a outer ring
4842 : // once reduced
4843 9 : !poOutRing->isClockwise())
4844 : {
4845 0 : bIsValid = false;
4846 : }
4847 : else
4848 : {
4849 18 : OGRPolygon oPoly;
4850 9 : oPoly.addRing(poOutOuterRing.get());
4851 9 : oPoly.addRingDirectly(poOutRing.release());
4852 : {
4853 : CPLErrorStateBackuper oErrorStateBackuper(
4854 9 : CPLQuietErrorHandler);
4855 9 : bIsValid = oPoly.IsValid();
4856 : }
4857 : }
4858 : }
4859 :
4860 41 : if (bIsValid)
4861 : {
4862 24 : nLastReducedXValid = nLastReducedX;
4863 24 : nLastReducedYValid = nLastReducedY;
4864 24 : anDstGeometry.push_back(anSrcGeometry[iSrc]);
4865 : }
4866 : else
4867 : {
4868 : // Remove this ring
4869 17 : nLastReducedX = nLastReducedXValid;
4870 17 : nLastReducedY = nLastReducedYValid;
4871 17 : anDstGeometry.resize(nLastMoveToIdx);
4872 : }
4873 :
4874 41 : iSrc++;
4875 41 : break;
4876 : }
4877 0 : default:
4878 : {
4879 0 : CPLAssert(false);
4880 : break;
4881 : }
4882 : }
4883 : }
4884 :
4885 334 : return anDstGeometry;
4886 : }
4887 :
4888 : /************************************************************************/
4889 : /* EncodeFeature() */
4890 : /************************************************************************/
4891 :
4892 1705 : void OGRMVTWriterDataset::EncodeFeature(
4893 : const void *pabyBlob, int nBlobSize,
4894 : std::shared_ptr<MVTTileLayer> &poTargetLayer,
4895 : std::map<CPLString, GUInt32> &oMapKeyToIdx,
4896 : std::map<MVTTileLayerValue, GUInt32> &oMapValueToIdx,
4897 : MVTLayerProperties *poLayerProperties, GUInt32 nExtent,
4898 : unsigned &nFeaturesInTile)
4899 : {
4900 1705 : size_t nUncompressedSize = 0;
4901 : void *pCompressed =
4902 1705 : CPLZLibInflate(pabyBlob, nBlobSize, nullptr, 0, &nUncompressedSize);
4903 1705 : GByte *pabyUncompressed = static_cast<GByte *>(pCompressed);
4904 :
4905 3410 : MVTTileLayer oSrcTileLayer;
4906 1705 : if (nUncompressedSize &&
4907 1705 : oSrcTileLayer.read(pabyUncompressed,
4908 1705 : pabyUncompressed + nUncompressedSize))
4909 : {
4910 1705 : const auto &srcFeatures = oSrcTileLayer.getFeatures();
4911 1705 : if (srcFeatures.size() == 1) // should always be true !
4912 : {
4913 1705 : const auto &poSrcFeature = srcFeatures[0];
4914 : std::shared_ptr<MVTTileLayerFeature> poFeature(
4915 3410 : new MVTTileLayerFeature());
4916 :
4917 1705 : if (poSrcFeature->hasId())
4918 53 : poFeature->setId(poSrcFeature->getId());
4919 1705 : poFeature->setType(poSrcFeature->getType());
4920 1705 : if (poLayerProperties)
4921 : {
4922 1526 : poLayerProperties->m_oCountGeomType[poSrcFeature->getType()]++;
4923 : }
4924 1705 : bool bOK = true;
4925 1705 : if (nExtent < m_nExtent)
4926 : {
4927 : #ifdef for_debugging
4928 : const auto &srcKeys = oSrcTileLayer.getKeys();
4929 : const auto &srcValues = oSrcTileLayer.getValues();
4930 : const auto &anSrcTags = poSrcFeature->getTags();
4931 : for (size_t i = 0; i + 1 < anSrcTags.size(); i += 2)
4932 : {
4933 : GUInt32 nSrcIdxKey = anSrcTags[i];
4934 : GUInt32 nSrcIdxValue = anSrcTags[i + 1];
4935 : if (nSrcIdxKey < srcKeys.size() &&
4936 : nSrcIdxValue < srcValues.size())
4937 : {
4938 : auto &osKey = srcKeys[nSrcIdxKey];
4939 : auto &oValue = srcValues[nSrcIdxValue];
4940 : if (osKey == "tunnus" &&
4941 : oValue.getUIntValue() == 28799760)
4942 : {
4943 : printf("foo\n"); /* ok */
4944 : break;
4945 : }
4946 : }
4947 : }
4948 : #endif
4949 :
4950 167 : poFeature->setGeometry(GetReducedPrecisionGeometry(
4951 : poSrcFeature->getType(), poSrcFeature->getGeometry(),
4952 : m_nExtent, nExtent));
4953 167 : if (poFeature->getGeometry().empty())
4954 : {
4955 23 : bOK = false;
4956 : }
4957 : }
4958 : else
4959 : {
4960 1538 : poFeature->setGeometry(poSrcFeature->getGeometry());
4961 : }
4962 1705 : if (bOK)
4963 : {
4964 1682 : const auto &srcKeys = oSrcTileLayer.getKeys();
4965 6125 : for (const auto &osKey : srcKeys)
4966 : {
4967 4443 : auto oIter = oMapKeyToIdx.find(osKey);
4968 4443 : if (oIter == oMapKeyToIdx.end())
4969 : {
4970 3645 : oMapKeyToIdx[osKey] = poTargetLayer->addKey(osKey);
4971 : }
4972 : }
4973 :
4974 1682 : const auto &srcValues = oSrcTileLayer.getValues();
4975 6125 : for (const auto &oValue : srcValues)
4976 : {
4977 4443 : auto oIter = oMapValueToIdx.find(oValue);
4978 4443 : if (oIter == oMapValueToIdx.end())
4979 : {
4980 3771 : oMapValueToIdx[oValue] =
4981 3771 : poTargetLayer->addValue(oValue);
4982 : }
4983 : }
4984 :
4985 1682 : const auto &anSrcTags = poSrcFeature->getTags();
4986 6125 : for (size_t i = 0; i + 1 < anSrcTags.size(); i += 2)
4987 : {
4988 4443 : GUInt32 nSrcIdxKey = anSrcTags[i];
4989 4443 : GUInt32 nSrcIdxValue = anSrcTags[i + 1];
4990 8886 : if (nSrcIdxKey < srcKeys.size() &&
4991 4443 : nSrcIdxValue < srcValues.size())
4992 : {
4993 4443 : const auto &osKey = srcKeys[nSrcIdxKey];
4994 4443 : const auto &oValue = srcValues[nSrcIdxValue];
4995 :
4996 4443 : if (poLayerProperties)
4997 : {
4998 4353 : UpdateLayerProperties(poLayerProperties, osKey,
4999 : oValue);
5000 : }
5001 :
5002 4443 : poFeature->addTag(oMapKeyToIdx[osKey]);
5003 4443 : poFeature->addTag(oMapValueToIdx[oValue]);
5004 : }
5005 : }
5006 :
5007 1682 : nFeaturesInTile++;
5008 1682 : poTargetLayer->addFeature(std::move(poFeature));
5009 : }
5010 : }
5011 : }
5012 : else
5013 : {
5014 : // Shouldn't fail
5015 0 : CPLError(CE_Failure, CPLE_AppDefined, "Deserialization failure");
5016 : }
5017 :
5018 1705 : CPLFree(pabyUncompressed);
5019 1705 : }
5020 :
5021 : /************************************************************************/
5022 : /* EncodeTile() */
5023 : /************************************************************************/
5024 :
5025 870 : std::string OGRMVTWriterDataset::EncodeTile(
5026 : int nZ, int nX, int nY, sqlite3_stmt *hStmtLayer, sqlite3_stmt *hStmtRows,
5027 : std::map<CPLString, MVTLayerProperties> &oMapLayerProps,
5028 : std::set<CPLString> &oSetLayers, GIntBig &nTempTilesRead)
5029 : {
5030 1740 : MVTTile oTargetTile;
5031 :
5032 870 : sqlite3_bind_int(hStmtLayer, 1, nZ);
5033 870 : sqlite3_bind_int(hStmtLayer, 2, nX);
5034 870 : sqlite3_bind_int(hStmtLayer, 3, nY);
5035 :
5036 870 : unsigned nFeaturesInTile = 0;
5037 : const GIntBig nProgressStep =
5038 870 : std::max(static_cast<GIntBig>(1), m_nTempTiles / 10);
5039 :
5040 4350 : while (nFeaturesInTile < m_nMaxFeatures &&
5041 2169 : sqlite3_step(hStmtLayer) == SQLITE_ROW)
5042 : {
5043 : const char *pszLayerName =
5044 1311 : reinterpret_cast<const char *>(sqlite3_column_text(hStmtLayer, 0));
5045 1311 : sqlite3_bind_int(hStmtRows, 1, nZ);
5046 1311 : sqlite3_bind_int(hStmtRows, 2, nX);
5047 1311 : sqlite3_bind_int(hStmtRows, 3, nY);
5048 1311 : sqlite3_bind_text(hStmtRows, 4, pszLayerName, -1, SQLITE_STATIC);
5049 :
5050 1311 : auto oIterMapLayerProps = oMapLayerProps.find(pszLayerName);
5051 1311 : MVTLayerProperties *poLayerProperties = nullptr;
5052 1311 : if (oIterMapLayerProps == oMapLayerProps.end())
5053 : {
5054 76 : if (oSetLayers.size() < knMAX_COUNT_LAYERS)
5055 : {
5056 76 : oSetLayers.insert(pszLayerName);
5057 76 : if (oMapLayerProps.size() < knMAX_REPORT_LAYERS)
5058 : {
5059 76 : MVTLayerProperties props;
5060 76 : props.m_nMinZoom = nZ;
5061 76 : props.m_nMaxZoom = nZ;
5062 76 : oMapLayerProps[pszLayerName] = std::move(props);
5063 76 : poLayerProperties = &(oMapLayerProps[pszLayerName]);
5064 : }
5065 : }
5066 : }
5067 : else
5068 : {
5069 1235 : poLayerProperties = &(oIterMapLayerProps->second);
5070 : }
5071 1311 : if (poLayerProperties)
5072 : {
5073 1311 : poLayerProperties->m_nMinZoom =
5074 1311 : std::min(nZ, poLayerProperties->m_nMinZoom);
5075 1311 : poLayerProperties->m_nMaxZoom =
5076 1311 : std::max(nZ, poLayerProperties->m_nMaxZoom);
5077 : }
5078 :
5079 2622 : auto poTargetLayer = std::make_shared<MVTTileLayer>();
5080 1311 : oTargetTile.addLayer(poTargetLayer);
5081 1311 : poTargetLayer->setName(pszLayerName);
5082 1311 : poTargetLayer->setVersion(m_nMVTVersion);
5083 1311 : poTargetLayer->setExtent(m_nExtent);
5084 :
5085 2622 : std::map<CPLString, GUInt32> oMapKeyToIdx;
5086 2622 : std::map<MVTTileLayerValue, GUInt32> oMapValueToIdx;
5087 :
5088 5662 : while (nFeaturesInTile < m_nMaxFeatures &&
5089 2825 : sqlite3_step(hStmtRows) == SQLITE_ROW)
5090 : {
5091 1526 : int nBlobSize = sqlite3_column_bytes(hStmtRows, 0);
5092 1526 : const void *pabyBlob = sqlite3_column_blob(hStmtRows, 0);
5093 :
5094 1526 : EncodeFeature(pabyBlob, nBlobSize, poTargetLayer, oMapKeyToIdx,
5095 : oMapValueToIdx, poLayerProperties, m_nExtent,
5096 : nFeaturesInTile);
5097 :
5098 1526 : nTempTilesRead++;
5099 1526 : if (nTempTilesRead == m_nTempTiles ||
5100 1474 : (nTempTilesRead % nProgressStep) == 0)
5101 : {
5102 534 : const int nPct =
5103 534 : static_cast<int>((100 * nTempTilesRead) / m_nTempTiles);
5104 534 : CPLDebug("MVT", "%d%%...", nPct);
5105 : }
5106 : }
5107 1311 : sqlite3_reset(hStmtRows);
5108 : }
5109 :
5110 870 : sqlite3_reset(hStmtLayer);
5111 :
5112 1740 : std::string oTileBuffer(oTargetTile.write());
5113 870 : size_t nSizeBefore = oTileBuffer.size();
5114 870 : if (m_bGZip)
5115 870 : GZIPCompress(oTileBuffer);
5116 870 : const size_t nSizeAfter = oTileBuffer.size();
5117 870 : const double dfCompressionRatio =
5118 870 : static_cast<double>(nSizeAfter) / nSizeBefore;
5119 :
5120 870 : const bool bTooManyFeatures = nFeaturesInTile >= m_nMaxFeatures;
5121 870 : if (bTooManyFeatures && !m_bMaxFeaturesOptSpecified)
5122 : {
5123 1 : m_bMaxFeaturesOptSpecified = true;
5124 1 : CPLError(CE_Warning, CPLE_AppDefined,
5125 : "At least one tile exceeded the default maximum number of "
5126 : "features per tile (%u) and was truncated to satisfy it.",
5127 : m_nMaxFeatures);
5128 : }
5129 :
5130 : // If the tile size is above the allowed values or there are too many
5131 : // features, then sort by descending area / length until we get to the
5132 : // limit.
5133 870 : bool bTooBigTile = oTileBuffer.size() > m_nMaxTileSize;
5134 870 : if (bTooBigTile && !m_bMaxTileSizeOptSpecified)
5135 : {
5136 1 : m_bMaxTileSizeOptSpecified = true;
5137 1 : CPLError(CE_Warning, CPLE_AppDefined,
5138 : "At least one tile exceeded the default maximum tile size of "
5139 : "%u bytes and was encoded at lower resolution",
5140 : m_nMaxTileSize);
5141 : }
5142 :
5143 870 : GUInt32 nExtent = m_nExtent;
5144 952 : while (bTooBigTile && !bTooManyFeatures && nExtent >= 256)
5145 : {
5146 82 : nExtent /= 2;
5147 82 : nSizeBefore = oTileBuffer.size();
5148 164 : oTileBuffer = RecodeTileLowerResolution(nZ, nX, nY, nExtent, hStmtLayer,
5149 82 : hStmtRows);
5150 82 : bTooBigTile = oTileBuffer.size() > m_nMaxTileSize;
5151 82 : CPLDebug("MVT",
5152 : "Recoding tile %d/%d/%d with extent = %u. "
5153 : "From %u to %u bytes",
5154 : nZ, nX, nY, nExtent, static_cast<unsigned>(nSizeBefore),
5155 82 : static_cast<unsigned>(oTileBuffer.size()));
5156 : }
5157 :
5158 870 : if (bTooBigTile || bTooManyFeatures)
5159 : {
5160 25 : if (bTooBigTile)
5161 : {
5162 13 : CPLDebug("MVT", "For tile %d/%d/%d, tile size is %u > %u", nZ, nX,
5163 13 : nY, static_cast<unsigned>(oTileBuffer.size()),
5164 : m_nMaxTileSize);
5165 : }
5166 25 : if (bTooManyFeatures)
5167 : {
5168 12 : CPLDebug("MVT",
5169 : "For tile %d/%d/%d, feature count limit of %u is reached",
5170 : nZ, nX, nY, m_nMaxFeatures);
5171 : }
5172 :
5173 25 : oTargetTile.clear();
5174 :
5175 : const unsigned nTotalFeaturesInTile =
5176 25 : std::min(m_nMaxFeatures, nFeaturesInTile);
5177 : char *pszSQL =
5178 25 : sqlite3_mprintf("SELECT layer, feature FROM temp "
5179 : "WHERE z = %d AND x = %d AND y = %d ORDER BY "
5180 : "area_or_length DESC LIMIT %d",
5181 : nZ, nX, nY, nTotalFeaturesInTile);
5182 25 : sqlite3_stmt *hTmpStmt = nullptr;
5183 25 : CPL_IGNORE_RET_VAL(
5184 25 : sqlite3_prepare_v2(m_hDB, pszSQL, -1, &hTmpStmt, nullptr));
5185 25 : sqlite3_free(pszSQL);
5186 25 : if (!hTmpStmt)
5187 0 : return std::string();
5188 :
5189 : class TargetTileLayerProps
5190 : {
5191 : public:
5192 : std::shared_ptr<MVTTileLayer> m_poLayer;
5193 : std::map<CPLString, GUInt32> m_oMapKeyToIdx;
5194 : std::map<MVTTileLayerValue, GUInt32> m_oMapValueToIdx;
5195 : };
5196 :
5197 50 : std::map<std::string, TargetTileLayerProps> oMapLayerNameToTargetLayer;
5198 :
5199 25 : nFeaturesInTile = 0;
5200 25 : const unsigned nCheckStep = std::max(1U, nTotalFeaturesInTile / 100);
5201 49 : while (sqlite3_step(hTmpStmt) == SQLITE_ROW)
5202 : {
5203 : const char *pszLayerName = reinterpret_cast<const char *>(
5204 37 : sqlite3_column_text(hTmpStmt, 0));
5205 37 : int nBlobSize = sqlite3_column_bytes(hTmpStmt, 1);
5206 37 : const void *pabyBlob = sqlite3_column_blob(hTmpStmt, 1);
5207 :
5208 0 : std::shared_ptr<MVTTileLayer> poTargetLayer;
5209 : std::map<CPLString, GUInt32> *poMapKeyToIdx;
5210 : std::map<MVTTileLayerValue, GUInt32> *poMapValueToIdx;
5211 37 : auto oIter = oMapLayerNameToTargetLayer.find(pszLayerName);
5212 37 : if (oIter == oMapLayerNameToTargetLayer.end())
5213 : {
5214 25 : poTargetLayer = std::make_shared<MVTTileLayer>();
5215 25 : TargetTileLayerProps props;
5216 25 : props.m_poLayer = poTargetLayer;
5217 25 : oTargetTile.addLayer(poTargetLayer);
5218 25 : poTargetLayer->setName(pszLayerName);
5219 25 : poTargetLayer->setVersion(m_nMVTVersion);
5220 25 : poTargetLayer->setExtent(nExtent);
5221 25 : oMapLayerNameToTargetLayer[pszLayerName] = std::move(props);
5222 25 : poMapKeyToIdx =
5223 25 : &oMapLayerNameToTargetLayer[pszLayerName].m_oMapKeyToIdx;
5224 25 : poMapValueToIdx =
5225 25 : &oMapLayerNameToTargetLayer[pszLayerName].m_oMapValueToIdx;
5226 : }
5227 : else
5228 : {
5229 12 : poTargetLayer = oIter->second.m_poLayer;
5230 12 : poMapKeyToIdx = &oIter->second.m_oMapKeyToIdx;
5231 12 : poMapValueToIdx = &oIter->second.m_oMapValueToIdx;
5232 : }
5233 :
5234 37 : EncodeFeature(pabyBlob, nBlobSize, poTargetLayer, *poMapKeyToIdx,
5235 : *poMapValueToIdx, nullptr, nExtent, nFeaturesInTile);
5236 :
5237 37 : if (nFeaturesInTile == nTotalFeaturesInTile ||
5238 18 : (bTooBigTile && (nFeaturesInTile % nCheckStep == 0)))
5239 : {
5240 37 : if (oTargetTile.getSize() * dfCompressionRatio > m_nMaxTileSize)
5241 : {
5242 13 : break;
5243 : }
5244 : }
5245 : }
5246 :
5247 25 : oTileBuffer = oTargetTile.write();
5248 25 : if (m_bGZip)
5249 25 : GZIPCompress(oTileBuffer);
5250 :
5251 25 : if (bTooBigTile)
5252 : {
5253 13 : CPLDebug("MVT", "For tile %d/%d/%d, final tile size is %u", nZ, nX,
5254 13 : nY, static_cast<unsigned>(oTileBuffer.size()));
5255 : }
5256 :
5257 25 : sqlite3_finalize(hTmpStmt);
5258 : }
5259 :
5260 870 : return oTileBuffer;
5261 : }
5262 :
5263 : /************************************************************************/
5264 : /* RecodeTileLowerResolution() */
5265 : /************************************************************************/
5266 :
5267 82 : std::string OGRMVTWriterDataset::RecodeTileLowerResolution(
5268 : int nZ, int nX, int nY, int nExtent, sqlite3_stmt *hStmtLayer,
5269 : sqlite3_stmt *hStmtRows)
5270 : {
5271 164 : MVTTile oTargetTile;
5272 :
5273 82 : sqlite3_bind_int(hStmtLayer, 1, nZ);
5274 82 : sqlite3_bind_int(hStmtLayer, 2, nX);
5275 82 : sqlite3_bind_int(hStmtLayer, 3, nY);
5276 :
5277 82 : unsigned nFeaturesInTile = 0;
5278 328 : while (nFeaturesInTile < m_nMaxFeatures &&
5279 164 : sqlite3_step(hStmtLayer) == SQLITE_ROW)
5280 : {
5281 : const char *pszLayerName =
5282 82 : reinterpret_cast<const char *>(sqlite3_column_text(hStmtLayer, 0));
5283 82 : sqlite3_bind_int(hStmtRows, 1, nZ);
5284 82 : sqlite3_bind_int(hStmtRows, 2, nX);
5285 82 : sqlite3_bind_int(hStmtRows, 3, nY);
5286 82 : sqlite3_bind_text(hStmtRows, 4, pszLayerName, -1, SQLITE_STATIC);
5287 :
5288 164 : auto poTargetLayer = std::make_shared<MVTTileLayer>();
5289 82 : oTargetTile.addLayer(poTargetLayer);
5290 82 : poTargetLayer->setName(pszLayerName);
5291 82 : poTargetLayer->setVersion(m_nMVTVersion);
5292 82 : poTargetLayer->setExtent(nExtent);
5293 :
5294 164 : std::map<CPLString, GUInt32> oMapKeyToIdx;
5295 164 : std::map<MVTTileLayerValue, GUInt32> oMapValueToIdx;
5296 :
5297 448 : while (nFeaturesInTile < m_nMaxFeatures &&
5298 224 : sqlite3_step(hStmtRows) == SQLITE_ROW)
5299 : {
5300 142 : int nBlobSize = sqlite3_column_bytes(hStmtRows, 0);
5301 142 : const void *pabyBlob = sqlite3_column_blob(hStmtRows, 0);
5302 :
5303 142 : EncodeFeature(pabyBlob, nBlobSize, poTargetLayer, oMapKeyToIdx,
5304 : oMapValueToIdx, nullptr, nExtent, nFeaturesInTile);
5305 : }
5306 82 : sqlite3_reset(hStmtRows);
5307 : }
5308 :
5309 82 : sqlite3_reset(hStmtLayer);
5310 :
5311 82 : std::string oTileBuffer(oTargetTile.write());
5312 82 : if (m_bGZip)
5313 82 : GZIPCompress(oTileBuffer);
5314 :
5315 164 : return oTileBuffer;
5316 : }
5317 :
5318 : /************************************************************************/
5319 : /* CreateOutput() */
5320 : /************************************************************************/
5321 :
5322 123 : bool OGRMVTWriterDataset::CreateOutput()
5323 : {
5324 123 : if (m_bThreadPoolOK)
5325 120 : m_oThreadPool.WaitCompletion();
5326 :
5327 246 : std::map<CPLString, MVTLayerProperties> oMapLayerProps;
5328 246 : std::set<CPLString> oSetLayers;
5329 :
5330 123 : if (!m_oEnvelope.IsInit())
5331 : {
5332 50 : return GenerateMetadata(0, oMapLayerProps);
5333 : }
5334 :
5335 73 : CPLDebug("MVT", "Building output file from temporary database...");
5336 :
5337 73 : sqlite3_stmt *hStmtZXY = nullptr;
5338 73 : CPL_IGNORE_RET_VAL(sqlite3_prepare_v2(
5339 : m_hDB, "SELECT DISTINCT z, x, y FROM temp ORDER BY z, x, y", -1,
5340 : &hStmtZXY, nullptr));
5341 73 : if (hStmtZXY == nullptr)
5342 : {
5343 2 : CPLError(CE_Failure, CPLE_AppDefined, "Prepared statement failed");
5344 2 : return false;
5345 : }
5346 :
5347 71 : sqlite3_stmt *hStmtLayer = nullptr;
5348 71 : CPL_IGNORE_RET_VAL(
5349 71 : sqlite3_prepare_v2(m_hDB,
5350 : "SELECT DISTINCT layer FROM temp "
5351 : "WHERE z = ? AND x = ? AND y = ? ORDER BY layer",
5352 : -1, &hStmtLayer, nullptr));
5353 71 : if (hStmtLayer == nullptr)
5354 : {
5355 0 : CPLError(CE_Failure, CPLE_AppDefined, "Prepared statement failed");
5356 0 : sqlite3_finalize(hStmtZXY);
5357 0 : return false;
5358 : }
5359 71 : sqlite3_stmt *hStmtRows = nullptr;
5360 71 : CPL_IGNORE_RET_VAL(sqlite3_prepare_v2(
5361 : m_hDB,
5362 : "SELECT feature FROM temp "
5363 : "WHERE z = ? AND x = ? AND y = ? AND layer = ? ORDER BY idx",
5364 : -1, &hStmtRows, nullptr));
5365 71 : if (hStmtRows == nullptr)
5366 : {
5367 0 : CPLError(CE_Failure, CPLE_AppDefined, "Prepared statement failed");
5368 0 : sqlite3_finalize(hStmtZXY);
5369 0 : sqlite3_finalize(hStmtLayer);
5370 0 : return false;
5371 : }
5372 :
5373 71 : sqlite3_stmt *hInsertStmt = nullptr;
5374 71 : if (m_hDBMBTILES)
5375 : {
5376 43 : CPL_IGNORE_RET_VAL(sqlite3_prepare_v2(
5377 : m_hDBMBTILES,
5378 : "INSERT INTO tiles(zoom_level, tile_column, tile_row, "
5379 : "tile_data) VALUES (?,?,?,?)",
5380 : -1, &hInsertStmt, nullptr));
5381 43 : if (hInsertStmt == nullptr)
5382 : {
5383 0 : CPLError(CE_Failure, CPLE_AppDefined, "Prepared statement failed");
5384 0 : sqlite3_finalize(hStmtZXY);
5385 0 : sqlite3_finalize(hStmtLayer);
5386 0 : sqlite3_finalize(hStmtRows);
5387 0 : return false;
5388 : }
5389 : }
5390 :
5391 71 : int nLastZ = -1;
5392 71 : int nLastX = -1;
5393 71 : bool bRet = true;
5394 71 : GIntBig nTempTilesRead = 0;
5395 :
5396 940 : while (sqlite3_step(hStmtZXY) == SQLITE_ROW)
5397 : {
5398 870 : int nZ = sqlite3_column_int(hStmtZXY, 0);
5399 870 : int nX = sqlite3_column_int(hStmtZXY, 1);
5400 870 : int nY = sqlite3_column_int(hStmtZXY, 2);
5401 :
5402 : std::string oTileBuffer(EncodeTile(nZ, nX, nY, hStmtLayer, hStmtRows,
5403 : oMapLayerProps, oSetLayers,
5404 870 : nTempTilesRead));
5405 :
5406 870 : if (oTileBuffer.empty())
5407 : {
5408 0 : bRet = false;
5409 : }
5410 870 : else if (hInsertStmt)
5411 : {
5412 531 : sqlite3_bind_int(hInsertStmt, 1, nZ);
5413 531 : sqlite3_bind_int(hInsertStmt, 2, nX);
5414 531 : sqlite3_bind_int(hInsertStmt, 3, (1 << nZ) - 1 - nY);
5415 531 : sqlite3_bind_blob(hInsertStmt, 4, oTileBuffer.data(),
5416 531 : static_cast<int>(oTileBuffer.size()),
5417 : SQLITE_STATIC);
5418 531 : const int rc = sqlite3_step(hInsertStmt);
5419 531 : bRet = (rc == SQLITE_OK || rc == SQLITE_DONE);
5420 531 : sqlite3_reset(hInsertStmt);
5421 : }
5422 : else
5423 : {
5424 : const std::string osZDirname(CPLFormFilenameSafe(
5425 678 : GetDescription(), CPLSPrintf("%d", nZ), nullptr));
5426 : const std::string osXDirname(CPLFormFilenameSafe(
5427 678 : osZDirname.c_str(), CPLSPrintf("%d", nX), nullptr));
5428 339 : if (nZ != nLastZ)
5429 : {
5430 114 : VSIMkdir(osZDirname.c_str(), 0755);
5431 114 : nLastZ = nZ;
5432 114 : nLastX = -1;
5433 : }
5434 339 : if (nX != nLastX)
5435 : {
5436 194 : VSIMkdir(osXDirname.c_str(), 0755);
5437 194 : nLastX = nX;
5438 : }
5439 : const std::string osTileFilename(
5440 : CPLFormFilenameSafe(osXDirname.c_str(), CPLSPrintf("%d", nY),
5441 678 : m_osExtension.c_str()));
5442 339 : VSILFILE *fpOut = VSIFOpenL(osTileFilename.c_str(), "wb");
5443 339 : if (fpOut)
5444 : {
5445 338 : const size_t nRet = VSIFWriteL(oTileBuffer.data(), 1,
5446 : oTileBuffer.size(), fpOut);
5447 338 : bRet = (nRet == oTileBuffer.size());
5448 338 : VSIFCloseL(fpOut);
5449 : }
5450 : else
5451 : {
5452 1 : bRet = false;
5453 : }
5454 : }
5455 :
5456 870 : if (!bRet)
5457 : {
5458 1 : CPLError(CE_Failure, CPLE_AppDefined,
5459 : "Error while writing tile %d/%d/%d", nZ, nX, nY);
5460 1 : break;
5461 : }
5462 : }
5463 71 : sqlite3_finalize(hStmtZXY);
5464 71 : sqlite3_finalize(hStmtLayer);
5465 71 : sqlite3_finalize(hStmtRows);
5466 71 : if (hInsertStmt)
5467 43 : sqlite3_finalize(hInsertStmt);
5468 :
5469 71 : bRet &= GenerateMetadata(oSetLayers.size(), oMapLayerProps);
5470 :
5471 71 : return bRet;
5472 : }
5473 :
5474 : /************************************************************************/
5475 : /* SphericalMercatorToLongLat() */
5476 : /************************************************************************/
5477 :
5478 234 : static void SphericalMercatorToLongLat(double *x, double *y)
5479 : {
5480 234 : double lng = *x / kmSPHERICAL_RADIUS / M_PI * 180;
5481 : double lat =
5482 234 : 2 * (atan(exp(*y / kmSPHERICAL_RADIUS)) - M_PI / 4) / M_PI * 180;
5483 234 : *x = lng;
5484 234 : *y = lat;
5485 234 : }
5486 :
5487 : /************************************************************************/
5488 : /* WriteMetadataItem() */
5489 : /************************************************************************/
5490 :
5491 : template <class T>
5492 1309 : static bool WriteMetadataItemT(const char *pszKey, T value,
5493 : const char *pszValueFormat, sqlite3 *hDBMBTILES,
5494 : CPLJSONObject &oRoot)
5495 : {
5496 1309 : if (hDBMBTILES)
5497 : {
5498 : char *pszSQL;
5499 :
5500 825 : pszSQL = sqlite3_mprintf(
5501 : CPLSPrintf("INSERT INTO metadata(name, value) VALUES('%%q', '%s')",
5502 : pszValueFormat),
5503 : pszKey, value);
5504 825 : OGRErr eErr = SQLCommand(hDBMBTILES, pszSQL);
5505 825 : sqlite3_free(pszSQL);
5506 825 : return eErr == OGRERR_NONE;
5507 : }
5508 : else
5509 : {
5510 484 : oRoot.Add(pszKey, value);
5511 484 : return true;
5512 : }
5513 : }
5514 :
5515 : /************************************************************************/
5516 : /* WriteMetadataItem() */
5517 : /************************************************************************/
5518 :
5519 926 : static bool WriteMetadataItem(const char *pszKey, const char *pszValue,
5520 : sqlite3 *hDBMBTILES, CPLJSONObject &oRoot)
5521 : {
5522 926 : return WriteMetadataItemT(pszKey, pszValue, "%q", hDBMBTILES, oRoot);
5523 : }
5524 :
5525 : /************************************************************************/
5526 : /* WriteMetadataItem() */
5527 : /************************************************************************/
5528 :
5529 371 : static bool WriteMetadataItem(const char *pszKey, int nValue,
5530 : sqlite3 *hDBMBTILES, CPLJSONObject &oRoot)
5531 : {
5532 371 : return WriteMetadataItemT(pszKey, nValue, "%d", hDBMBTILES, oRoot);
5533 : }
5534 :
5535 : /************************************************************************/
5536 : /* WriteMetadataItem() */
5537 : /************************************************************************/
5538 :
5539 12 : static bool WriteMetadataItem(const char *pszKey, double dfValue,
5540 : sqlite3 *hDBMBTILES, CPLJSONObject &oRoot)
5541 : {
5542 12 : return WriteMetadataItemT(pszKey, dfValue, "%.17g", hDBMBTILES, oRoot);
5543 : }
5544 :
5545 : /************************************************************************/
5546 : /* GenerateMetadata() */
5547 : /************************************************************************/
5548 :
5549 121 : bool OGRMVTWriterDataset::GenerateMetadata(
5550 : size_t nLayers, const std::map<CPLString, MVTLayerProperties> &oMap)
5551 : {
5552 242 : CPLJSONDocument oDoc;
5553 242 : CPLJSONObject oRoot = oDoc.GetRoot();
5554 :
5555 242 : OGRSpatialReference oSRS_EPSG3857;
5556 : double dfTopXWebMercator;
5557 : double dfTopYWebMercator;
5558 : double dfTileDim0WebMercator;
5559 121 : InitWebMercatorTilingScheme(&oSRS_EPSG3857, dfTopXWebMercator,
5560 : dfTopYWebMercator, dfTileDim0WebMercator);
5561 : const bool bIsStandardTilingScheme =
5562 238 : m_poSRS->IsSame(&oSRS_EPSG3857) && m_dfTopX == dfTopXWebMercator &&
5563 238 : m_dfTopY == dfTopYWebMercator && m_dfTileDim0 == dfTileDim0WebMercator;
5564 121 : if (bIsStandardTilingScheme)
5565 : {
5566 117 : SphericalMercatorToLongLat(&(m_oEnvelope.MinX), &(m_oEnvelope.MinY));
5567 117 : SphericalMercatorToLongLat(&(m_oEnvelope.MaxX), &(m_oEnvelope.MaxY));
5568 117 : m_oEnvelope.MinY = std::max(-85.0, m_oEnvelope.MinY);
5569 117 : m_oEnvelope.MaxY = std::min(85.0, m_oEnvelope.MaxY);
5570 : }
5571 : else
5572 : {
5573 8 : OGRSpatialReference oSRS_EPSG4326;
5574 4 : oSRS_EPSG4326.SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
5575 4 : oSRS_EPSG4326.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
5576 : OGRCoordinateTransformation *poCT =
5577 4 : OGRCreateCoordinateTransformation(m_poSRS, &oSRS_EPSG4326);
5578 4 : if (poCT)
5579 : {
5580 8 : OGRPoint oPoint1(m_oEnvelope.MinX, m_oEnvelope.MinY);
5581 4 : oPoint1.transform(poCT);
5582 8 : OGRPoint oPoint2(m_oEnvelope.MinX, m_oEnvelope.MaxY);
5583 4 : oPoint2.transform(poCT);
5584 8 : OGRPoint oPoint3(m_oEnvelope.MaxX, m_oEnvelope.MaxY);
5585 4 : oPoint3.transform(poCT);
5586 8 : OGRPoint oPoint4(m_oEnvelope.MaxX, m_oEnvelope.MinY);
5587 4 : oPoint4.transform(poCT);
5588 4 : m_oEnvelope.MinX =
5589 4 : std::min(std::min(oPoint1.getX(), oPoint2.getX()),
5590 8 : std::min(oPoint3.getX(), oPoint4.getX()));
5591 4 : m_oEnvelope.MinY =
5592 4 : std::min(std::min(oPoint1.getY(), oPoint2.getY()),
5593 8 : std::min(oPoint3.getY(), oPoint4.getY()));
5594 4 : m_oEnvelope.MaxX =
5595 4 : std::max(std::max(oPoint1.getX(), oPoint2.getX()),
5596 8 : std::max(oPoint3.getX(), oPoint4.getX()));
5597 4 : m_oEnvelope.MaxY =
5598 4 : std::max(std::max(oPoint1.getY(), oPoint2.getY()),
5599 8 : std::max(oPoint3.getY(), oPoint4.getY()));
5600 4 : delete poCT;
5601 : }
5602 : }
5603 121 : const double dfCenterX = (m_oEnvelope.MinX + m_oEnvelope.MaxX) / 2;
5604 121 : const double dfCenterY = (m_oEnvelope.MinY + m_oEnvelope.MaxY) / 2;
5605 : CPLString osCenter(
5606 242 : CPLSPrintf("%.7f,%.7f,%d", dfCenterX, dfCenterY, m_nMinZoom));
5607 : CPLString osBounds(CPLSPrintf("%.7f,%.7f,%.7f,%.7f", m_oEnvelope.MinX,
5608 : m_oEnvelope.MinY, m_oEnvelope.MaxX,
5609 242 : m_oEnvelope.MaxY));
5610 :
5611 121 : WriteMetadataItem("name", m_osName, m_hDBMBTILES, oRoot);
5612 121 : WriteMetadataItem("description", m_osDescription, m_hDBMBTILES, oRoot);
5613 121 : WriteMetadataItem("version", m_nMetadataVersion, m_hDBMBTILES, oRoot);
5614 121 : WriteMetadataItem("minzoom", m_nMinZoom, m_hDBMBTILES, oRoot);
5615 121 : WriteMetadataItem("maxzoom", m_nMaxZoom, m_hDBMBTILES, oRoot);
5616 121 : WriteMetadataItem("center", !m_osCenter.empty() ? m_osCenter : osCenter,
5617 : m_hDBMBTILES, oRoot);
5618 121 : WriteMetadataItem("bounds", !m_osBounds.empty() ? m_osBounds : osBounds,
5619 : m_hDBMBTILES, oRoot);
5620 121 : WriteMetadataItem("type", m_osType, m_hDBMBTILES, oRoot);
5621 121 : WriteMetadataItem("format", "pbf", m_hDBMBTILES, oRoot);
5622 121 : if (m_hDBMBTILES)
5623 : {
5624 75 : WriteMetadataItem("scheme", "tms", m_hDBMBTILES, oRoot);
5625 : }
5626 :
5627 : // GDAL extension for custom tiling schemes
5628 121 : if (!bIsStandardTilingScheme)
5629 : {
5630 4 : const char *pszAuthName = m_poSRS->GetAuthorityName(nullptr);
5631 4 : const char *pszAuthCode = m_poSRS->GetAuthorityCode(nullptr);
5632 4 : if (pszAuthName && pszAuthCode)
5633 : {
5634 4 : WriteMetadataItem("crs",
5635 : CPLSPrintf("%s:%s", pszAuthName, pszAuthCode),
5636 : m_hDBMBTILES, oRoot);
5637 : }
5638 : else
5639 : {
5640 0 : char *pszWKT = nullptr;
5641 0 : m_poSRS->exportToWkt(&pszWKT);
5642 0 : WriteMetadataItem("crs", pszWKT, m_hDBMBTILES, oRoot);
5643 0 : CPLFree(pszWKT);
5644 : }
5645 4 : WriteMetadataItem("tile_origin_upper_left_x", m_dfTopX, m_hDBMBTILES,
5646 : oRoot);
5647 4 : WriteMetadataItem("tile_origin_upper_left_y", m_dfTopY, m_hDBMBTILES,
5648 : oRoot);
5649 4 : WriteMetadataItem("tile_dimension_zoom_0", m_dfTileDim0, m_hDBMBTILES,
5650 : oRoot);
5651 4 : WriteMetadataItem("tile_matrix_width_zoom_0", m_nTileMatrixWidth0,
5652 : m_hDBMBTILES, oRoot);
5653 4 : WriteMetadataItem("tile_matrix_height_zoom_0", m_nTileMatrixHeight0,
5654 : m_hDBMBTILES, oRoot);
5655 : }
5656 :
5657 242 : CPLJSONDocument oJsonDoc;
5658 242 : CPLJSONObject oJsonRoot = oJsonDoc.GetRoot();
5659 :
5660 242 : CPLJSONArray oVectorLayers;
5661 121 : oJsonRoot.Add("vector_layers", oVectorLayers);
5662 242 : std::set<std::string> oAlreadyVisited;
5663 289 : for (const auto &poLayer : m_apoLayers)
5664 : {
5665 168 : auto oIter = oMap.find(poLayer->m_osTargetName);
5666 244 : if (oIter != oMap.end() &&
5667 76 : oAlreadyVisited.find(poLayer->m_osTargetName) ==
5668 244 : oAlreadyVisited.end())
5669 : {
5670 76 : oAlreadyVisited.insert(poLayer->m_osTargetName);
5671 :
5672 152 : CPLJSONObject oLayerObj;
5673 76 : oLayerObj.Add("id", poLayer->m_osTargetName);
5674 76 : oLayerObj.Add("description",
5675 76 : m_oMapLayerNameToDesc[poLayer->m_osTargetName]);
5676 76 : oLayerObj.Add("minzoom", oIter->second.m_nMinZoom);
5677 76 : oLayerObj.Add("maxzoom", oIter->second.m_nMaxZoom);
5678 :
5679 152 : CPLJSONObject oFields;
5680 76 : oLayerObj.Add("fields", oFields);
5681 76 : auto poFDefn = poLayer->GetLayerDefn();
5682 280 : for (int i = 0; i < poFDefn->GetFieldCount(); i++)
5683 : {
5684 204 : auto poFieldDefn = poFDefn->GetFieldDefn(i);
5685 204 : auto eType = poFieldDefn->GetType();
5686 239 : if (eType == OFTInteger &&
5687 35 : poFieldDefn->GetSubType() == OFSTBoolean)
5688 : {
5689 1 : oFields.Add(poFieldDefn->GetNameRef(), "Boolean");
5690 : }
5691 203 : else if (eType == OFTInteger || eType == OFTInteger64 ||
5692 : eType == OFTReal)
5693 : {
5694 73 : oFields.Add(poFieldDefn->GetNameRef(), "Number");
5695 : }
5696 : else
5697 : {
5698 130 : oFields.Add(poFieldDefn->GetNameRef(), "String");
5699 : }
5700 : }
5701 :
5702 76 : oVectorLayers.Add(oLayerObj);
5703 : }
5704 : }
5705 :
5706 242 : CPLJSONObject oTileStats;
5707 121 : oJsonRoot.Add("tilestats", oTileStats);
5708 121 : oTileStats.Add("layerCount", static_cast<int>(nLayers));
5709 242 : CPLJSONArray oTileStatsLayers;
5710 121 : oTileStats.Add("layers", oTileStatsLayers);
5711 121 : oAlreadyVisited.clear();
5712 289 : for (const auto &poLayer : m_apoLayers)
5713 : {
5714 168 : auto oIter = oMap.find(poLayer->m_osTargetName);
5715 244 : if (oIter != oMap.end() &&
5716 76 : oAlreadyVisited.find(poLayer->m_osTargetName) ==
5717 244 : oAlreadyVisited.end())
5718 : {
5719 76 : oAlreadyVisited.insert(poLayer->m_osTargetName);
5720 76 : auto &oLayerProps = oIter->second;
5721 152 : CPLJSONObject oLayerObj;
5722 :
5723 152 : std::string osName(poLayer->m_osTargetName);
5724 76 : osName.resize(std::min(knMAX_LAYER_NAME_LENGTH, osName.size()));
5725 76 : oLayerObj.Add("layer", osName);
5726 76 : oLayerObj.Add(
5727 : "count",
5728 76 : m_oMapLayerNameToFeatureCount[poLayer->m_osTargetName]);
5729 :
5730 : // Find majority geometry type
5731 76 : MVTTileLayerFeature::GeomType eMaxGeomType =
5732 : MVTTileLayerFeature::GeomType::UNKNOWN;
5733 76 : GIntBig nMaxCountGeom = 0;
5734 304 : for (int i = static_cast<int>(MVTTileLayerFeature::GeomType::POINT);
5735 304 : i <= static_cast<int>(MVTTileLayerFeature::GeomType::POLYGON);
5736 : i++)
5737 : {
5738 228 : MVTTileLayerFeature::GeomType eGeomType =
5739 228 : static_cast<MVTTileLayerFeature::GeomType>(i);
5740 : auto oIterCountGeom =
5741 228 : oLayerProps.m_oCountGeomType.find(eGeomType);
5742 228 : if (oIterCountGeom != oLayerProps.m_oCountGeomType.end())
5743 : {
5744 80 : if (oIterCountGeom->second >= nMaxCountGeom)
5745 : {
5746 79 : eMaxGeomType = eGeomType;
5747 79 : nMaxCountGeom = oIterCountGeom->second;
5748 : }
5749 : }
5750 : }
5751 76 : if (eMaxGeomType == MVTTileLayerFeature::GeomType::POINT)
5752 63 : oLayerObj.Add("geometry", "Point");
5753 13 : else if (eMaxGeomType == MVTTileLayerFeature::GeomType::LINESTRING)
5754 6 : oLayerObj.Add("geometry", "LineString");
5755 7 : else if (eMaxGeomType == MVTTileLayerFeature::GeomType::POLYGON)
5756 7 : oLayerObj.Add("geometry", "Polygon");
5757 :
5758 76 : oLayerObj.Add("attributeCount",
5759 76 : static_cast<int>(oLayerProps.m_oSetFields.size()));
5760 152 : CPLJSONArray oAttributes;
5761 76 : oLayerObj.Add("attributes", oAttributes);
5762 257 : for (const auto &oFieldProps : oLayerProps.m_aoFields)
5763 : {
5764 362 : CPLJSONObject oFieldObj;
5765 181 : oAttributes.Add(oFieldObj);
5766 362 : std::string osFieldNameTruncated(oFieldProps.m_osName);
5767 181 : osFieldNameTruncated.resize(std::min(
5768 181 : knMAX_FIELD_NAME_LENGTH, osFieldNameTruncated.size()));
5769 181 : oFieldObj.Add("attribute", osFieldNameTruncated);
5770 181 : oFieldObj.Add("count", static_cast<int>(
5771 181 : oFieldProps.m_oSetAllValues.size()));
5772 181 : oFieldObj.Add("type",
5773 181 : oFieldProps.m_eType ==
5774 : MVTTileLayerValue::ValueType::DOUBLE
5775 : ? "number"
5776 108 : : oFieldProps.m_eType ==
5777 : MVTTileLayerValue::ValueType::STRING
5778 108 : ? "string"
5779 : : "boolean");
5780 :
5781 362 : CPLJSONArray oValues;
5782 181 : oFieldObj.Add("values", oValues);
5783 408 : for (const auto &oIterValue : oFieldProps.m_oSetValues)
5784 : {
5785 227 : if (oIterValue.getType() ==
5786 : MVTTileLayerValue::ValueType::BOOL)
5787 : {
5788 1 : oValues.Add(oIterValue.getBoolValue());
5789 : }
5790 226 : else if (oIterValue.isNumeric())
5791 : {
5792 104 : if (oFieldProps.m_bAllInt)
5793 : {
5794 53 : oValues.Add(static_cast<GInt64>(
5795 53 : oIterValue.getNumericValue()));
5796 : }
5797 : else
5798 : {
5799 51 : oValues.Add(oIterValue.getNumericValue());
5800 : }
5801 : }
5802 122 : else if (oIterValue.isString())
5803 : {
5804 122 : oValues.Add(oIterValue.getStringValue());
5805 : }
5806 : }
5807 :
5808 181 : if (oFieldProps.m_eType == MVTTileLayerValue::ValueType::DOUBLE)
5809 : {
5810 73 : if (oFieldProps.m_bAllInt)
5811 : {
5812 37 : oFieldObj.Add(
5813 37 : "min", static_cast<GInt64>(oFieldProps.m_dfMinVal));
5814 37 : oFieldObj.Add(
5815 37 : "max", static_cast<GInt64>(oFieldProps.m_dfMaxVal));
5816 : }
5817 : else
5818 : {
5819 36 : oFieldObj.Add("min", oFieldProps.m_dfMinVal);
5820 36 : oFieldObj.Add("max", oFieldProps.m_dfMaxVal);
5821 : }
5822 : }
5823 : }
5824 :
5825 76 : oTileStatsLayers.Add(oLayerObj);
5826 : }
5827 : }
5828 :
5829 121 : WriteMetadataItem("json", oJsonDoc.SaveAsString().c_str(), m_hDBMBTILES,
5830 : oRoot);
5831 :
5832 121 : if (m_hDBMBTILES)
5833 : {
5834 75 : return true;
5835 : }
5836 :
5837 46 : return oDoc.Save(
5838 92 : CPLFormFilenameSafe(GetDescription(), "metadata.json", nullptr));
5839 : }
5840 :
5841 : /************************************************************************/
5842 : /* WriteFeature() */
5843 : /************************************************************************/
5844 :
5845 251 : OGRErr OGRMVTWriterDataset::WriteFeature(OGRMVTWriterLayer *poLayer,
5846 : OGRFeature *poFeature, GIntBig nSerial,
5847 : OGRGeometry *poGeom)
5848 : {
5849 251 : if (poFeature->GetGeometryRef() == poGeom)
5850 : {
5851 193 : m_oMapLayerNameToFeatureCount[poLayer->m_osTargetName]++;
5852 : }
5853 :
5854 251 : OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType());
5855 251 : if (eGeomType == wkbGeometryCollection)
5856 : {
5857 21 : OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
5858 77 : for (int i = 0; i < poGC->getNumGeometries(); i++)
5859 : {
5860 58 : if (WriteFeature(poLayer, poFeature, nSerial,
5861 58 : poGC->getGeometryRef(i)) != OGRERR_NONE)
5862 : {
5863 2 : return OGRERR_FAILURE;
5864 : }
5865 : }
5866 19 : return OGRERR_NONE;
5867 : }
5868 :
5869 230 : OGREnvelope sExtent;
5870 230 : poGeom->getEnvelope(&sExtent);
5871 :
5872 230 : if (!m_oEnvelope.IsInit())
5873 : {
5874 73 : CPLDebug("MVT", "Creating temporary database...");
5875 : }
5876 :
5877 230 : m_oEnvelope.Merge(sExtent);
5878 :
5879 230 : if (!m_bReuseTempFile)
5880 : {
5881 229 : auto poFeatureContent = std::make_shared<OGRMVTFeatureContent>();
5882 229 : auto poSharedGeom = std::shared_ptr<OGRGeometry>(poGeom->clone());
5883 :
5884 229 : poFeatureContent->nFID = poFeature->GetFID();
5885 :
5886 229 : const OGRFeatureDefn *poFDefn = poFeature->GetDefnRef();
5887 996 : for (int i = 0; i < poFeature->GetFieldCount(); i++)
5888 : {
5889 767 : if (poFeature->IsFieldSetAndNotNull(i))
5890 : {
5891 666 : MVTTileLayerValue oValue;
5892 666 : const OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn(i);
5893 666 : OGRFieldType eFieldType = poFieldDefn->GetType();
5894 666 : if (eFieldType == OFTInteger || eFieldType == OFTInteger64)
5895 : {
5896 145 : if (poFieldDefn->GetSubType() == OFSTBoolean)
5897 : {
5898 2 : oValue.setBoolValue(poFeature->GetFieldAsInteger(i) !=
5899 : 0);
5900 : }
5901 : else
5902 : {
5903 143 : oValue.setValue(poFeature->GetFieldAsInteger64(i));
5904 : }
5905 : }
5906 521 : else if (eFieldType == OFTReal)
5907 : {
5908 140 : oValue.setValue(poFeature->GetFieldAsDouble(i));
5909 : }
5910 381 : else if (eFieldType == OFTDate || eFieldType == OFTDateTime)
5911 : {
5912 : int nYear, nMonth, nDay, nHour, nMin, nTZ;
5913 : float fSec;
5914 238 : poFeature->GetFieldAsDateTime(i, &nYear, &nMonth, &nDay,
5915 : &nHour, &nMin, &fSec, &nTZ);
5916 476 : CPLString osFormatted;
5917 238 : if (eFieldType == OFTDate)
5918 : {
5919 119 : osFormatted.Printf("%04d-%02d-%02d", nYear, nMonth,
5920 119 : nDay);
5921 : }
5922 : else
5923 : {
5924 : char *pszFormatted =
5925 119 : OGRGetXMLDateTime(poFeature->GetRawFieldRef(i));
5926 119 : osFormatted = pszFormatted;
5927 119 : CPLFree(pszFormatted);
5928 : }
5929 476 : oValue.setStringValue(osFormatted);
5930 : }
5931 : else
5932 : {
5933 143 : oValue.setStringValue(
5934 286 : std::string(poFeature->GetFieldAsString(i)));
5935 : }
5936 :
5937 666 : poFeatureContent->oValues.emplace_back(
5938 666 : std::pair<std::string, MVTTileLayerValue>(
5939 1332 : poFieldDefn->GetNameRef(), oValue));
5940 : }
5941 : }
5942 :
5943 1559 : for (int nZ = poLayer->m_nMinZoom; nZ <= poLayer->m_nMaxZoom; nZ++)
5944 : {
5945 1332 : double dfTileDim = m_dfTileDim0 / (1 << nZ);
5946 1332 : double dfBuffer = dfTileDim * m_nBuffer / m_nExtent;
5947 : const int nTileMinX = std::max(
5948 2664 : 0, static_cast<int>((sExtent.MinX - m_dfTopX - dfBuffer) /
5949 1332 : dfTileDim));
5950 : const int nTileMinY = std::max(
5951 2664 : 0, static_cast<int>((m_dfTopY - sExtent.MaxY - dfBuffer) /
5952 1332 : dfTileDim));
5953 : const int nTileMaxX =
5954 2664 : std::min(static_cast<int>((sExtent.MaxX - m_dfTopX + dfBuffer) /
5955 : dfTileDim),
5956 2664 : static_cast<int>(std::min<int64_t>(
5957 2664 : INT_MAX, (static_cast<int64_t>(1) << nZ) *
5958 2664 : m_nTileMatrixWidth0 -
5959 1332 : 1)));
5960 : const int nTileMaxY =
5961 2664 : std::min(static_cast<int>((m_dfTopY - sExtent.MinY + dfBuffer) /
5962 : dfTileDim),
5963 2664 : static_cast<int>(std::min<int64_t>(
5964 2664 : INT_MAX, (static_cast<int64_t>(1) << nZ) *
5965 2664 : m_nTileMatrixHeight0 -
5966 1332 : 1)));
5967 3575 : for (int iX = nTileMinX; iX <= nTileMaxX; iX++)
5968 : {
5969 6279 : for (int iY = nTileMinY; iY <= nTileMaxY; iY++)
5970 : {
5971 8072 : if (PreGenerateForTile(
5972 4036 : nZ, iX, iY, poLayer->m_osTargetName,
5973 4036 : (nZ == poLayer->m_nMaxZoom), poFeatureContent,
5974 4036 : nSerial, poSharedGeom, sExtent) != OGRERR_NONE)
5975 : {
5976 2 : return OGRERR_FAILURE;
5977 : }
5978 : }
5979 : }
5980 : }
5981 : }
5982 :
5983 228 : return OGRERR_NONE;
5984 : }
5985 :
5986 : /************************************************************************/
5987 : /* TestCapability() */
5988 : /************************************************************************/
5989 :
5990 207 : int OGRMVTWriterDataset::TestCapability(const char *pszCap) const
5991 : {
5992 207 : if (EQUAL(pszCap, ODsCCreateLayer))
5993 117 : return true;
5994 90 : return false;
5995 : }
5996 :
5997 : /************************************************************************/
5998 : /* ValidateMinMaxZoom() */
5999 : /************************************************************************/
6000 :
6001 301 : static bool ValidateMinMaxZoom(int nMinZoom, int nMaxZoom)
6002 : {
6003 301 : if (nMinZoom < 0 || nMinZoom > 22)
6004 : {
6005 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid MINZOOM");
6006 2 : return false;
6007 : }
6008 299 : if (nMaxZoom < 0 || nMaxZoom > 22)
6009 : {
6010 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid MAXZOOM");
6011 1 : return false;
6012 : }
6013 298 : if (nMaxZoom < nMinZoom)
6014 : {
6015 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid MAXZOOM < MINZOOM");
6016 1 : return false;
6017 : }
6018 297 : return true;
6019 : }
6020 :
6021 : /************************************************************************/
6022 : /* ICreateLayer() */
6023 : /************************************************************************/
6024 :
6025 : OGRLayer *
6026 171 : OGRMVTWriterDataset::ICreateLayer(const char *pszLayerName,
6027 : const OGRGeomFieldDefn *poGeomFieldDefn,
6028 : CSLConstList papszOptions)
6029 : {
6030 171 : OGRSpatialReference *poSRSClone = nullptr;
6031 : const auto poSRS =
6032 171 : poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
6033 171 : if (poSRS)
6034 : {
6035 8 : poSRSClone = poSRS->Clone();
6036 8 : poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
6037 : }
6038 : OGRMVTWriterLayer *poLayer =
6039 171 : new OGRMVTWriterLayer(this, pszLayerName, poSRSClone);
6040 171 : if (poSRSClone)
6041 8 : poSRSClone->Release();
6042 171 : poLayer->m_nMinZoom = m_nMinZoom;
6043 171 : poLayer->m_nMaxZoom = m_nMaxZoom;
6044 171 : poLayer->m_osTargetName = pszLayerName;
6045 :
6046 : /*
6047 :
6048 : {
6049 : "src_layer":
6050 : { "target_name": "",
6051 : "description": "",
6052 : "minzoom": 0,
6053 : "maxzoom": 0
6054 : }
6055 : }
6056 : */
6057 :
6058 513 : CPLJSONObject oObj = m_oConf.GetRoot().GetObj(pszLayerName);
6059 342 : CPLString osDescription;
6060 171 : if (oObj.IsValid())
6061 : {
6062 4 : std::string osTargetName = oObj.GetString("target_name");
6063 2 : if (!osTargetName.empty())
6064 2 : poLayer->m_osTargetName = std::move(osTargetName);
6065 2 : int nMinZoom = oObj.GetInteger("minzoom", -1);
6066 2 : if (nMinZoom >= 0)
6067 2 : poLayer->m_nMinZoom = nMinZoom;
6068 2 : int nMaxZoom = oObj.GetInteger("maxzoom", -1);
6069 2 : if (nMaxZoom >= 0)
6070 2 : poLayer->m_nMaxZoom = nMaxZoom;
6071 2 : osDescription = oObj.GetString("description");
6072 : }
6073 :
6074 171 : poLayer->m_nMinZoom = atoi(CSLFetchNameValueDef(
6075 : papszOptions, "MINZOOM", CPLSPrintf("%d", poLayer->m_nMinZoom)));
6076 171 : poLayer->m_nMaxZoom = atoi(CSLFetchNameValueDef(
6077 : papszOptions, "MAXZOOM", CPLSPrintf("%d", poLayer->m_nMaxZoom)));
6078 171 : if (!ValidateMinMaxZoom(poLayer->m_nMinZoom, poLayer->m_nMaxZoom))
6079 : {
6080 1 : delete poLayer;
6081 1 : return nullptr;
6082 : }
6083 : poLayer->m_osTargetName = CSLFetchNameValueDef(
6084 170 : papszOptions, "NAME", poLayer->m_osTargetName.c_str());
6085 : osDescription =
6086 170 : CSLFetchNameValueDef(papszOptions, "DESCRIPTION", osDescription);
6087 170 : if (!osDescription.empty())
6088 4 : m_oMapLayerNameToDesc[poLayer->m_osTargetName] =
6089 2 : std::move(osDescription);
6090 :
6091 170 : m_apoLayers.push_back(std::unique_ptr<OGRMVTWriterLayer>(poLayer));
6092 170 : return m_apoLayers.back().get();
6093 : }
6094 :
6095 : /************************************************************************/
6096 : /* Create() */
6097 : /************************************************************************/
6098 :
6099 140 : GDALDataset *OGRMVTWriterDataset::Create(const char *pszFilename, int nXSize,
6100 : int nYSize, int nBandsIn,
6101 : GDALDataType eDT, char **papszOptions)
6102 : {
6103 140 : if (nXSize != 0 || nYSize != 0 || nBandsIn != 0 || eDT != GDT_Unknown)
6104 : {
6105 1 : CPLError(CE_Failure, CPLE_NotSupported,
6106 : "Only vector creation supported");
6107 1 : return nullptr;
6108 : }
6109 :
6110 139 : const char *pszFormat = CSLFetchNameValue(papszOptions, "FORMAT");
6111 : const bool bMBTILESExt =
6112 139 : EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "mbtiles");
6113 139 : if (pszFormat == nullptr && bMBTILESExt)
6114 : {
6115 4 : pszFormat = "MBTILES";
6116 : }
6117 139 : const bool bMBTILES = pszFormat != nullptr && EQUAL(pszFormat, "MBTILES");
6118 :
6119 : // For debug only
6120 : bool bReuseTempFile =
6121 139 : CPLTestBool(CPLGetConfigOption("OGR_MVT_REUSE_TEMP_FILE", "NO"));
6122 :
6123 139 : if (bMBTILES)
6124 : {
6125 80 : if (!bMBTILESExt)
6126 : {
6127 1 : CPLError(CE_Failure, CPLE_FileIO,
6128 : "%s should have mbtiles extension", pszFilename);
6129 1 : return nullptr;
6130 : }
6131 :
6132 79 : VSIUnlink(pszFilename);
6133 : }
6134 : else
6135 : {
6136 : VSIStatBufL sStat;
6137 59 : if (VSIStatL(pszFilename, &sStat) == 0)
6138 : {
6139 3 : CPLError(CE_Failure, CPLE_FileIO, "%s already exists", pszFilename);
6140 5 : return nullptr;
6141 : }
6142 :
6143 56 : if (VSIMkdir(pszFilename, 0755) != 0)
6144 : {
6145 2 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create directory %s",
6146 : pszFilename);
6147 2 : return nullptr;
6148 : }
6149 : }
6150 :
6151 133 : OGRMVTWriterDataset *poDS = new OGRMVTWriterDataset();
6152 133 : poDS->m_pMyVFS = OGRSQLiteCreateVFS(nullptr, poDS);
6153 133 : sqlite3_vfs_register(poDS->m_pMyVFS, 0);
6154 :
6155 399 : CPLString osTempDBDefault = CPLString(pszFilename) + ".temp.db";
6156 133 : if (STARTS_WITH(osTempDBDefault, "/vsizip/"))
6157 : {
6158 : osTempDBDefault =
6159 0 : CPLString(pszFilename + strlen("/vsizip/")) + ".temp.db";
6160 : }
6161 : CPLString osTempDB = CSLFetchNameValueDef(papszOptions, "TEMPORARY_DB",
6162 266 : osTempDBDefault.c_str());
6163 133 : if (!bReuseTempFile)
6164 132 : VSIUnlink(osTempDB);
6165 :
6166 133 : sqlite3 *hDB = nullptr;
6167 133 : if (sqlite3_open_v2(osTempDB, &hDB,
6168 : SQLITE_OPEN_READWRITE |
6169 : (bReuseTempFile ? 0 : SQLITE_OPEN_CREATE) |
6170 : SQLITE_OPEN_NOMUTEX,
6171 396 : poDS->m_pMyVFS->zName) != SQLITE_OK ||
6172 130 : hDB == nullptr)
6173 : {
6174 3 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", osTempDB.c_str());
6175 3 : delete poDS;
6176 3 : sqlite3_close(hDB);
6177 3 : return nullptr;
6178 : }
6179 130 : poDS->m_osTempDB = osTempDB;
6180 130 : poDS->m_hDB = hDB;
6181 130 : poDS->m_bReuseTempFile = bReuseTempFile;
6182 :
6183 : // For Unix
6184 259 : if (!poDS->m_bReuseTempFile &&
6185 129 : CPLTestBool(CPLGetConfigOption("OGR_MVT_REMOVE_TEMP_FILE", "YES")))
6186 : {
6187 126 : VSIUnlink(osTempDB);
6188 : }
6189 :
6190 130 : if (poDS->m_bReuseTempFile)
6191 : {
6192 1 : poDS->m_nTempTiles =
6193 1 : SQLGetInteger64(hDB, "SELECT COUNT(*) FROM temp", nullptr);
6194 : }
6195 : else
6196 : {
6197 129 : CPL_IGNORE_RET_VAL(SQLCommand(
6198 : hDB,
6199 : "PRAGMA page_size = 4096;" // 4096: default since sqlite 3.12
6200 : "PRAGMA synchronous = OFF;"
6201 : "PRAGMA journal_mode = OFF;"
6202 : "PRAGMA temp_store = MEMORY;"
6203 : "CREATE TABLE temp(z INTEGER, x INTEGER, y INTEGER, layer TEXT, "
6204 : "idx INTEGER, feature BLOB, geomtype INTEGER, area_or_length "
6205 : "DOUBLE);"
6206 : "CREATE INDEX temp_index ON temp (z, x, y, layer, idx);"));
6207 : }
6208 :
6209 130 : sqlite3_stmt *hInsertStmt = nullptr;
6210 130 : CPL_IGNORE_RET_VAL(sqlite3_prepare_v2(
6211 : hDB,
6212 : "INSERT INTO temp (z,x,y,layer,idx,feature,geomtype,area_or_length) "
6213 : "VALUES (?,?,?,?,?,?,?,?)",
6214 : -1, &hInsertStmt, nullptr));
6215 130 : if (hInsertStmt == nullptr)
6216 : {
6217 0 : delete poDS;
6218 0 : return nullptr;
6219 : }
6220 130 : poDS->m_hInsertStmt = hInsertStmt;
6221 :
6222 130 : poDS->m_nMinZoom = atoi(CSLFetchNameValueDef(
6223 : papszOptions, "MINZOOM", CPLSPrintf("%d", poDS->m_nMinZoom)));
6224 130 : poDS->m_nMaxZoom = atoi(CSLFetchNameValueDef(
6225 : papszOptions, "MAXZOOM", CPLSPrintf("%d", poDS->m_nMaxZoom)));
6226 130 : if (!ValidateMinMaxZoom(poDS->m_nMinZoom, poDS->m_nMaxZoom))
6227 : {
6228 3 : delete poDS;
6229 3 : return nullptr;
6230 : }
6231 :
6232 127 : const char *pszConf = CSLFetchNameValue(papszOptions, "CONF");
6233 127 : if (pszConf)
6234 : {
6235 : VSIStatBufL sStat;
6236 : bool bSuccess;
6237 3 : if (VSIStatL(pszConf, &sStat) == 0)
6238 : {
6239 2 : bSuccess = poDS->m_oConf.Load(pszConf);
6240 : }
6241 : else
6242 : {
6243 1 : bSuccess = poDS->m_oConf.LoadMemory(pszConf);
6244 : }
6245 3 : if (!bSuccess)
6246 : {
6247 1 : delete poDS;
6248 1 : return nullptr;
6249 : }
6250 : }
6251 :
6252 126 : poDS->m_dfSimplification =
6253 126 : CPLAtof(CSLFetchNameValueDef(papszOptions, "SIMPLIFICATION", "0"));
6254 126 : poDS->m_dfSimplificationMaxZoom = CPLAtof(
6255 : CSLFetchNameValueDef(papszOptions, "SIMPLIFICATION_MAX_ZOOM",
6256 : CPLSPrintf("%g", poDS->m_dfSimplification)));
6257 126 : poDS->m_nExtent = static_cast<unsigned>(atoi(CSLFetchNameValueDef(
6258 : papszOptions, "EXTENT", CPLSPrintf("%u", poDS->m_nExtent))));
6259 126 : poDS->m_nBuffer = static_cast<unsigned>(atoi(CSLFetchNameValueDef(
6260 126 : papszOptions, "BUFFER", CPLSPrintf("%u", 5 * poDS->m_nExtent / 256))));
6261 :
6262 : {
6263 126 : const char *pszMaxSize = CSLFetchNameValue(papszOptions, "MAX_SIZE");
6264 126 : poDS->m_bMaxTileSizeOptSpecified = pszMaxSize != nullptr;
6265 : // This is used by unit tests
6266 126 : pszMaxSize = CSLFetchNameValueDef(papszOptions, "@MAX_SIZE_FOR_TEST",
6267 : pszMaxSize);
6268 126 : if (pszMaxSize)
6269 : {
6270 3 : poDS->m_nMaxTileSize =
6271 3 : std::max(100U, static_cast<unsigned>(atoi(pszMaxSize)));
6272 : }
6273 : }
6274 :
6275 : {
6276 : const char *pszMaxFeatures =
6277 126 : CSLFetchNameValue(papszOptions, "MAX_FEATURES");
6278 126 : poDS->m_bMaxFeaturesOptSpecified = pszMaxFeatures != nullptr;
6279 126 : pszMaxFeatures = CSLFetchNameValueDef(
6280 : // This is used by unit tests
6281 : papszOptions, "@MAX_FEATURES_FOR_TEST", pszMaxFeatures);
6282 126 : if (pszMaxFeatures)
6283 : {
6284 2 : poDS->m_nMaxFeatures =
6285 2 : std::max(1U, static_cast<unsigned>(atoi(pszMaxFeatures)));
6286 : }
6287 : }
6288 :
6289 : poDS->m_osName = CSLFetchNameValueDef(
6290 126 : papszOptions, "NAME", CPLGetBasenameSafe(pszFilename).c_str());
6291 : poDS->m_osDescription = CSLFetchNameValueDef(papszOptions, "DESCRIPTION",
6292 126 : poDS->m_osDescription.c_str());
6293 : poDS->m_osType =
6294 126 : CSLFetchNameValueDef(papszOptions, "TYPE", poDS->m_osType.c_str());
6295 126 : poDS->m_bGZip = CPLFetchBool(papszOptions, "COMPRESS", poDS->m_bGZip);
6296 126 : poDS->m_osBounds = CSLFetchNameValueDef(papszOptions, "BOUNDS", "");
6297 126 : poDS->m_osCenter = CSLFetchNameValueDef(papszOptions, "CENTER", "");
6298 : poDS->m_osExtension = CSLFetchNameValueDef(papszOptions, "TILE_EXTENSION",
6299 126 : poDS->m_osExtension);
6300 :
6301 : const char *pszTilingScheme =
6302 126 : CSLFetchNameValue(papszOptions, "TILING_SCHEME");
6303 126 : if (pszTilingScheme)
6304 : {
6305 6 : if (bMBTILES)
6306 : {
6307 1 : CPLError(CE_Failure, CPLE_NotSupported,
6308 : "Custom TILING_SCHEME not supported with MBTILES output");
6309 1 : delete poDS;
6310 2 : return nullptr;
6311 : }
6312 :
6313 5 : const CPLStringList aoList(CSLTokenizeString2(pszTilingScheme, ",", 0));
6314 5 : if (aoList.Count() >= 4)
6315 : {
6316 4 : poDS->m_poSRS->SetFromUserInput(aoList[0]);
6317 4 : poDS->m_dfTopX = CPLAtof(aoList[1]);
6318 4 : poDS->m_dfTopY = CPLAtof(aoList[2]);
6319 4 : poDS->m_dfTileDim0 = CPLAtof(aoList[3]);
6320 4 : if (aoList.Count() == 6)
6321 : {
6322 2 : poDS->m_nTileMatrixWidth0 = std::max(1, atoi(aoList[4]));
6323 2 : poDS->m_nTileMatrixHeight0 = std::max(1, atoi(aoList[5]));
6324 : }
6325 2 : else if (poDS->m_dfTopX == -180 && poDS->m_dfTileDim0 == 180)
6326 : {
6327 : // Assumes WorldCRS84Quad with 2 tiles in width
6328 : // cf https://github.com/OSGeo/gdal/issues/11749
6329 1 : poDS->m_nTileMatrixWidth0 = 2;
6330 : }
6331 : }
6332 : else
6333 : {
6334 1 : CPLError(CE_Failure, CPLE_AppDefined,
6335 : "Wrong format for TILING_SCHEME. "
6336 : "Expecting EPSG:XXXX,tile_origin_upper_left_x,"
6337 : "tile_origin_upper_left_y,tile_dimension_zoom_0[,tile_"
6338 : "matrix_width_zoom_0,tile_matrix_height_zoom_0]");
6339 1 : delete poDS;
6340 1 : return nullptr;
6341 : }
6342 : }
6343 :
6344 124 : if (bMBTILES)
6345 : {
6346 228 : if (sqlite3_open_v2(pszFilename, &poDS->m_hDBMBTILES,
6347 : SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
6348 : SQLITE_OPEN_NOMUTEX,
6349 151 : poDS->m_pMyVFS->zName) != SQLITE_OK ||
6350 75 : poDS->m_hDBMBTILES == nullptr)
6351 : {
6352 1 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", pszFilename);
6353 1 : delete poDS;
6354 1 : return nullptr;
6355 : }
6356 :
6357 75 : if (SQLCommand(
6358 : poDS->m_hDBMBTILES,
6359 : "PRAGMA page_size = 4096;" // 4096: default since sqlite 3.12
6360 : "PRAGMA synchronous = OFF;"
6361 : "PRAGMA journal_mode = OFF;"
6362 : "PRAGMA temp_store = MEMORY;"
6363 : "CREATE TABLE metadata (name text, value text);"
6364 : "CREATE TABLE tiles (zoom_level integer, tile_column integer, "
6365 : "tile_row integer, tile_data blob, "
6366 75 : "UNIQUE (zoom_level, tile_column, tile_row))") != OGRERR_NONE)
6367 : {
6368 0 : delete poDS;
6369 0 : return nullptr;
6370 : }
6371 : }
6372 :
6373 123 : int nThreads = CPLGetNumCPUs();
6374 123 : const char *pszNumThreads = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
6375 123 : if (pszNumThreads && CPLGetValueType(pszNumThreads) == CPL_VALUE_INTEGER)
6376 : {
6377 3 : nThreads = atoi(pszNumThreads);
6378 : }
6379 123 : if (nThreads > 1)
6380 : {
6381 120 : poDS->m_bThreadPoolOK =
6382 120 : poDS->m_oThreadPool.Setup(nThreads, nullptr, nullptr);
6383 : }
6384 :
6385 123 : poDS->SetDescription(pszFilename);
6386 123 : poDS->poDriver = GDALDriver::FromHandle(GDALGetDriverByName("MVT"));
6387 :
6388 123 : return poDS;
6389 : }
6390 :
6391 75 : GDALDataset *OGRMVTWriterDatasetCreate(const char *pszFilename, int nXSize,
6392 : int nYSize, int nBandsIn,
6393 : GDALDataType eDT, char **papszOptions)
6394 : {
6395 75 : return OGRMVTWriterDataset::Create(pszFilename, nXSize, nYSize, nBandsIn,
6396 75 : eDT, papszOptions);
6397 : }
6398 :
6399 : #endif // HAVE_MVT_WRITE_SUPPORT
6400 :
6401 : /************************************************************************/
6402 : /* RegisterOGRMVT() */
6403 : /************************************************************************/
6404 :
6405 2054 : void RegisterOGRMVT()
6406 :
6407 : {
6408 2054 : if (GDALGetDriverByName("MVT") != nullptr)
6409 283 : return;
6410 :
6411 1771 : GDALDriver *poDriver = new GDALDriver();
6412 :
6413 1771 : poDriver->SetDescription("MVT");
6414 1771 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
6415 1771 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Mapbox Vector Tiles");
6416 1771 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/mvt.html");
6417 1771 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
6418 1771 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "mvt mvt.gz pbf");
6419 1771 : poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "SQLITE OGRSQL");
6420 :
6421 1771 : poDriver->SetMetadataItem(
6422 : GDAL_DMD_OPENOPTIONLIST,
6423 : "<OpenOptionList>"
6424 : " <Option name='X' type='int' description='X coordinate of tile'/>"
6425 : " <Option name='Y' type='int' description='Y coordinate of tile'/>"
6426 : " <Option name='Z' type='int' description='Z coordinate of tile'/>"
6427 : //" <Option name='@GEOREF_TOPX' type='float' description='X coordinate
6428 : // of top-left corner of tile'/>" " <Option name='@GEOREF_TOPY'
6429 : // type='float' description='Y coordinate of top-left corner of tile'/>"
6430 : //" <Option name='@GEOREF_TILEDIMX' type='float' description='Tile
6431 : // width in georeferenced units'/>" " <Option name='@GEOREF_TILEDIMY'
6432 : // type='float' description='Tile height in georeferenced units'/>"
6433 : " <Option name='METADATA_FILE' type='string' "
6434 : "description='Path to metadata.json'/>"
6435 : " <Option name='CLIP' type='boolean' "
6436 : "description='Whether to clip geometries to tile extent' "
6437 : "default='YES'/>"
6438 : " <Option name='TILE_EXTENSION' type='string' default='pbf' "
6439 : "description="
6440 : "'For tilesets, extension of tiles'/>"
6441 : " <Option name='TILE_COUNT_TO_ESTABLISH_FEATURE_DEFN' type='int' "
6442 : "description="
6443 : "'For tilesets without metadata file, maximum number of tiles to use "
6444 : "to "
6445 : "establish the layer schemas' default='1000'/>"
6446 : " <Option name='JSON_FIELD' type='boolean' description='For tilesets, "
6447 : "whether to put all attributes as a serialized JSon dictionary'/>"
6448 1771 : "</OpenOptionList>");
6449 :
6450 1771 : poDriver->pfnIdentify = OGRMVTDriverIdentify;
6451 1771 : poDriver->pfnOpen = OGRMVTDataset::Open;
6452 : #ifdef HAVE_MVT_WRITE_SUPPORT
6453 1771 : poDriver->pfnCreate = OGRMVTWriterDataset::Create;
6454 1771 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
6455 1771 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
6456 1771 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
6457 1771 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES,
6458 1771 : "Integer Integer64 Real String");
6459 1771 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES,
6460 1771 : "Boolean Float32");
6461 1771 : poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "SQLITE OGRSQL");
6462 :
6463 1771 : poDriver->SetMetadataItem(GDAL_DS_LAYER_CREATIONOPTIONLIST, MVT_LCO);
6464 :
6465 1771 : poDriver->SetMetadataItem(
6466 : GDAL_DMD_CREATIONOPTIONLIST,
6467 : "<CreationOptionList>"
6468 : " <Option name='NAME' type='string' description='Tileset name'/>"
6469 : " <Option name='DESCRIPTION' type='string' "
6470 : "description='A description of the tileset'/>"
6471 : " <Option name='TYPE' type='string-select' description='Layer type' "
6472 : "default='overlay'>"
6473 : " <Value>overlay</Value>"
6474 : " <Value>baselayer</Value>"
6475 : " </Option>"
6476 : " <Option name='FORMAT' type='string-select' description='Format'>"
6477 : " <Value>DIRECTORY</Value>"
6478 : " <Value>MBTILES</Value>"
6479 : " </Option>"
6480 : " <Option name='TILE_EXTENSION' type='string' default='pbf' "
6481 : "description="
6482 : "'For tilesets as directories of files, extension of "
6483 : "tiles'/>" MVT_MBTILES_COMMON_DSCO
6484 : " <Option name='BOUNDS' type='string' "
6485 : "description='Override default value for bounds metadata item'/>"
6486 : " <Option name='CENTER' type='string' "
6487 : "description='Override default value for center metadata item'/>"
6488 : " <Option name='TILING_SCHEME' type='string' "
6489 : "description='Custom tiling scheme with following format "
6490 : "\"EPSG:XXXX,tile_origin_upper_left_x,tile_origin_upper_left_y,"
6491 : "tile_dimension_zoom_0[,tile_matrix_width_zoom_0,tile_matrix_height_"
6492 : "zoom_0]\"'/>"
6493 1771 : "</CreationOptionList>");
6494 : #endif // HAVE_MVT_WRITE_SUPPORT
6495 :
6496 1771 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
6497 :
6498 1771 : GetGDALDriverManager()->RegisterDriver(poDriver);
6499 : }
|