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