Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: MVT Translator
4 : * Purpose: Mapbox Vector Tile decoder
5 : * Author: Even Rouault, Even Rouault <even dot rouault at spatialys dot com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2018, Even Rouault <even dot rouault at spatialys dot com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #if defined(HAVE_SQLITE) && defined(HAVE_GEOS)
14 : // Needed by mvtutils.h
15 : #define HAVE_MVT_WRITE_SUPPORT
16 : #endif
17 :
18 : #include "ogrsf_frmts.h"
19 : #include "cpl_conv.h"
20 : #include "cpl_json.h"
21 : #include "cpl_http.h"
22 : #include "ogr_p.h"
23 :
24 : #include "mvt_tile.h"
25 : #include "mvtutils.h"
26 :
27 : #include "ogr_geos.h"
28 :
29 : #include "gpb.h"
30 :
31 : #include <algorithm>
32 : #include <memory>
33 : #include <vector>
34 : #include <set>
35 :
36 : const char *SRS_EPSG_3857 =
37 : "PROJCS[\"WGS 84 / Pseudo-Mercator\",GEOGCS[\"WGS "
38 : "84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS "
39 : "84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY["
40 : "\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
41 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
42 : "AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"Mercator_1SP\"],PARAMETER["
43 : "\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_"
44 : "easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY["
45 : "\"EPSG\",\"9001\"]],AXIS[\"X\",EAST],AXIS[\"Y\",NORTH],EXTENSION["
46 : "\"PROJ4\",\"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 "
47 : "+x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext "
48 : "+no_defs\"],AUTHORITY[\"EPSG\",\"3857\"]]";
49 :
50 : // WebMercator related constants
51 : constexpr double kmSPHERICAL_RADIUS = 6378137.0;
52 :
53 : constexpr int knMAX_FILES_PER_DIR = 10000;
54 :
55 : #ifdef HAVE_MVT_WRITE_SUPPORT
56 :
57 : #include <sqlite3.h>
58 : #include "../sqlite/ogrsqliteutility.h"
59 :
60 : #include "../sqlite/ogrsqlitevfs.h"
61 :
62 : #include "cpl_worker_thread_pool.h"
63 :
64 : #include <mutex>
65 :
66 : // Limitations from https://github.com/mapbox/mapbox-geostats
67 : constexpr size_t knMAX_COUNT_LAYERS = 1000;
68 : constexpr size_t knMAX_REPORT_LAYERS = 100;
69 : constexpr size_t knMAX_COUNT_FIELDS = 1000;
70 : constexpr size_t knMAX_REPORT_FIELDS = 100;
71 : constexpr size_t knMAX_COUNT_VALUES = 1000;
72 : constexpr size_t knMAX_REPORT_VALUES = 100;
73 : constexpr size_t knMAX_STRING_VALUE_LENGTH = 256;
74 : constexpr size_t knMAX_LAYER_NAME_LENGTH = 256;
75 : constexpr size_t knMAX_FIELD_NAME_LENGTH = 256;
76 :
77 : #undef SQLITE_STATIC
78 : #define SQLITE_STATIC ((sqlite3_destructor_type) nullptr)
79 :
80 : #endif
81 :
82 : /************************************************************************/
83 : /* InitWebMercatorTilingScheme() */
84 : /************************************************************************/
85 :
86 1255 : static void InitWebMercatorTilingScheme(OGRSpatialReference *poSRS,
87 : double &dfTopX, double &dfTopY,
88 : double &dfTileDim0)
89 : {
90 1255 : constexpr double kmMAX_GM =
91 : kmSPHERICAL_RADIUS * M_PI; // 20037508.342789244
92 1255 : poSRS->SetFromUserInput(SRS_EPSG_3857);
93 1255 : dfTopX = -kmMAX_GM;
94 1255 : dfTopY = kmMAX_GM;
95 1255 : dfTileDim0 = 2 * kmMAX_GM;
96 1255 : }
97 :
98 : /************************************************************************/
99 : /* GetCmdId() */
100 : /************************************************************************/
101 :
102 : /* For a drawing instruction combining a command id and a command count,
103 : * return the command id */
104 2036 : static unsigned GetCmdId(unsigned int nCmdCountCombined)
105 : {
106 2036 : return nCmdCountCombined & 0x7;
107 : }
108 :
109 : /************************************************************************/
110 : /* GetCmdCount() */
111 : /************************************************************************/
112 :
113 : /* For a drawing instruction combining a command id and a command count,
114 : * return the command count */
115 6097 : static unsigned GetCmdCount(unsigned int nCmdCountCombined)
116 : {
117 6097 : return nCmdCountCombined >> 3;
118 : }
119 :
120 : /************************************************************************/
121 : /* OGRMVTLayerBase */
122 : /************************************************************************/
123 :
124 : class OGRMVTLayerBase CPL_NON_FINAL
125 : : public OGRLayer,
126 : public OGRGetNextFeatureThroughRaw<OGRMVTLayerBase>
127 : {
128 : virtual OGRFeature *GetNextRawFeature() = 0;
129 :
130 : protected:
131 : OGRFeatureDefn *m_poFeatureDefn = nullptr;
132 :
133 : void InitFields(const CPLJSONObject &oFields,
134 : const CPLJSONArray &oAttributesFromTileStats);
135 :
136 : public:
137 : ~OGRMVTLayerBase() override;
138 :
139 5821 : OGRFeatureDefn *GetLayerDefn() const override
140 : {
141 5821 : return m_poFeatureDefn;
142 : }
143 :
144 2971 : DEFINE_GET_NEXT_FEATURE_THROUGH_RAW(OGRMVTLayerBase)
145 :
146 : int TestCapability(const char *) const override;
147 : };
148 :
149 : /************************************************************************/
150 : /* OGRMVTLayer */
151 : /************************************************************************/
152 :
153 : class OGRMVTDataset;
154 :
155 : class OGRMVTLayer final : public OGRMVTLayerBase
156 : {
157 : OGRMVTDataset *m_poDS;
158 : const GByte *m_pabyDataStart;
159 : const GByte *m_pabyDataEnd;
160 : const GByte *m_pabyDataCur = nullptr;
161 : const GByte *m_pabyDataFeatureStart = nullptr;
162 : bool m_bError = false;
163 : unsigned int m_nExtent = knDEFAULT_EXTENT;
164 : std::vector<CPLString> m_aosKeys;
165 :
166 : typedef struct
167 : {
168 : OGRFieldType eType;
169 : OGRFieldSubType eSubType;
170 : OGRField sValue;
171 : } Value;
172 :
173 : std::vector<Value> m_asValues;
174 : GIntBig m_nFID = 0;
175 : GIntBig m_nFeatureCount = -1;
176 : OGRPolygon m_oClipPoly;
177 : double m_dfTileMinX = 0;
178 : double m_dfTileMinY = 0;
179 : double m_dfTileMaxX = 0;
180 : double m_dfTileMaxY = 0;
181 : bool m_bEnforceExternalIsClockwise = false;
182 :
183 : void Init(const CPLJSONObject &oFields,
184 : const CPLJSONArray &oAttributesFromTileStats);
185 : bool QuickScanFeature(const GByte *pabyData,
186 : const GByte *pabyDataFeatureEnd, bool bScanFields,
187 : bool bScanGeometries, bool &bGeomTypeSet);
188 : void GetXY(int nX, int nY, double &dfX, double &dfY);
189 : std::unique_ptr<OGRGeometry>
190 : ParseGeometry(unsigned int nGeomType, const GByte *pabyDataGeometryEnd);
191 : void SanitizeClippedGeometry(std::unique_ptr<OGRGeometry> &poGeom);
192 :
193 : OGRFeature *GetNextRawFeature() override;
194 :
195 : public:
196 : OGRMVTLayer(OGRMVTDataset *poDS, const char *pszLayerName,
197 : const GByte *pabyData, int nLayerSize,
198 : const CPLJSONObject &oFields,
199 : const CPLJSONArray &oAttributesFromTileStats,
200 : OGRwkbGeometryType eGeomType);
201 : ~OGRMVTLayer() override;
202 :
203 : void ResetReading() override;
204 :
205 : GIntBig GetFeatureCount(int bForce) override;
206 :
207 : GDALDataset *GetDataset() override;
208 : };
209 :
210 : /************************************************************************/
211 : /* OGRMVTDirectoryLayer */
212 : /************************************************************************/
213 :
214 : class OGRMVTDirectoryLayer final : public OGRMVTLayerBase
215 : {
216 : OGRMVTDataset *m_poDS;
217 : int m_nZ = 0;
218 : bool m_bUseReadDir = true;
219 : CPLString m_osDirName;
220 : CPLStringList m_aosDirContent;
221 : CPLString m_aosSubDirName;
222 : CPLStringList m_aosSubDirContent;
223 : bool m_bEOF = false;
224 : int m_nXIndex = 0;
225 : int m_nYIndex = 0;
226 : int m_nTileX = -1;
227 : int m_nTileY = -1;
228 : GDALDataset *m_poCurrentTile = nullptr;
229 : bool m_bJsonField = false;
230 : bool m_bAddTileFields = false;
231 : GIntBig m_nFIDBase = 0;
232 : OGREnvelope m_sExtent;
233 : int m_nFilterMinX = 0;
234 : int m_nFilterMinY = 0;
235 : int m_nFilterMaxX = 0;
236 : int m_nFilterMaxY = 0;
237 :
238 : OGRFeature *GetNextRawFeature() override;
239 : OGRFeature *CreateFeatureFrom(OGRFeature *poSrcFeature);
240 : void ReadNewSubDir();
241 : void OpenTile();
242 : void OpenTileIfNeeded();
243 :
244 : public:
245 : OGRMVTDirectoryLayer(OGRMVTDataset *poDS, const char *pszLayerName,
246 : const char *pszDirectoryName,
247 : const CPLJSONObject &oFields,
248 : const CPLJSONArray &oAttributesFromTileStats,
249 : bool bJsonField, bool bAddTileFields,
250 : OGRwkbGeometryType eGeomType,
251 : const OGREnvelope *psExtent);
252 : ~OGRMVTDirectoryLayer() override;
253 :
254 : void ResetReading() override;
255 :
256 : GIntBig GetFeatureCount(int bForce) override;
257 : OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
258 : bool bForce) override;
259 :
260 : OGRErr ISetSpatialFilter(int iGeomField,
261 : const OGRGeometry *poGeom) override;
262 :
263 : OGRFeature *GetFeature(GIntBig nFID) override;
264 :
265 : int TestCapability(const char *) const override;
266 :
267 : GDALDataset *GetDataset() override;
268 : };
269 :
270 : /************************************************************************/
271 : /* OGRMVTDataset */
272 : /************************************************************************/
273 :
274 : class OGRMVTDataset final : public GDALDataset
275 : {
276 : friend class OGRMVTLayer;
277 : friend class OGRMVTDirectoryLayer;
278 :
279 : GByte *m_pabyData;
280 : std::vector<std::unique_ptr<OGRLayer>> m_apoLayers;
281 : bool m_bGeoreferenced = false;
282 : double m_dfTileDimX = 0.0;
283 : double m_dfTileDimY = 0.0;
284 : double m_dfTopX = 0.0;
285 : double m_dfTopY = 0.0;
286 : CPLString m_osMetadataMemFilename;
287 : bool m_bClip = true;
288 : CPLString m_osTileExtension{"pbf"};
289 : OGRSpatialReference *m_poSRS = nullptr;
290 : double m_dfTileDim0 =
291 : 0.0; // Extent (in CRS units) of a tile at zoom level 0
292 : double m_dfTopXOrigin = 0.0; // top-left X of tile matrix scheme
293 : double m_dfTopYOrigin = 0.0; // top-left Y of tile matrix scheme
294 : int m_nTileMatrixWidth0 =
295 : 1; // Number of tiles along X axis at zoom level 0
296 : int m_nTileMatrixHeight0 =
297 : 1; // Number of tiles along Y axis at zoom level 0
298 :
299 : static GDALDataset *OpenDirectory(GDALOpenInfo *);
300 :
301 : public:
302 : explicit OGRMVTDataset(GByte *pabyData);
303 : ~OGRMVTDataset() override;
304 :
305 3543 : int GetLayerCount() const override
306 : {
307 3543 : return static_cast<int>(m_apoLayers.size());
308 : }
309 :
310 : const OGRLayer *GetLayer(int) const override;
311 :
312 12 : int TestCapability(const char *) const override
313 : {
314 12 : return FALSE;
315 : }
316 :
317 : static GDALDataset *Open(GDALOpenInfo *);
318 : static GDALDataset *Open(GDALOpenInfo *, bool bRecurseAllowed);
319 :
320 1048 : OGRSpatialReference *GetSRS()
321 : {
322 1048 : return m_poSRS;
323 : }
324 :
325 195 : inline double GetTileDim0() const
326 : {
327 195 : return m_dfTileDim0;
328 : }
329 :
330 78 : inline double GetTopXOrigin() const
331 : {
332 78 : return m_dfTopXOrigin;
333 : }
334 :
335 78 : inline double GetTopYOrigin() const
336 : {
337 78 : return m_dfTopYOrigin;
338 : }
339 :
340 90 : inline int GetTileMatrixWidth0() const
341 : {
342 90 : return m_nTileMatrixWidth0;
343 : }
344 :
345 90 : inline int GetTileMatrixHeight0() const
346 : {
347 90 : return m_nTileMatrixHeight0;
348 : }
349 : };
350 :
351 : /************************************************************************/
352 : /* ~OGRMVTLayerBase() */
353 : /************************************************************************/
354 :
355 1092 : OGRMVTLayerBase::~OGRMVTLayerBase()
356 : {
357 1092 : m_poFeatureDefn->Release();
358 1092 : }
359 :
360 : /************************************************************************/
361 : /* InitFields() */
362 : /************************************************************************/
363 :
364 1091 : void OGRMVTLayerBase::InitFields(const CPLJSONObject &oFields,
365 : const CPLJSONArray &oAttributesFromTileStats)
366 : {
367 1091 : OGRMVTInitFields(m_poFeatureDefn, oFields, oAttributesFromTileStats);
368 1091 : }
369 :
370 : /************************************************************************/
371 : /* TestCapability() */
372 : /************************************************************************/
373 :
374 71 : int OGRMVTLayerBase::TestCapability(const char *pszCap) const
375 : {
376 71 : if (EQUAL(pszCap, OLCStringsAsUTF8) || EQUAL(pszCap, OLCFastSpatialFilter))
377 : {
378 25 : return TRUE;
379 : }
380 46 : return FALSE;
381 : }
382 :
383 : /************************************************************************/
384 : /* OGRMVTLayer() */
385 : /************************************************************************/
386 :
387 1065 : OGRMVTLayer::OGRMVTLayer(OGRMVTDataset *poDS, const char *pszLayerName,
388 : const GByte *pabyData, int nLayerSize,
389 : const CPLJSONObject &oFields,
390 : const CPLJSONArray &oAttributesFromTileStats,
391 1065 : OGRwkbGeometryType eGeomType)
392 : : m_poDS(poDS), m_pabyDataStart(pabyData),
393 1065 : m_pabyDataEnd(pabyData + nLayerSize)
394 : {
395 1065 : m_poFeatureDefn = new OGRFeatureDefn(pszLayerName);
396 1065 : SetDescription(m_poFeatureDefn->GetName());
397 1065 : m_poFeatureDefn->SetGeomType(eGeomType);
398 1065 : m_poFeatureDefn->Reference();
399 :
400 1065 : if (m_poDS->m_bGeoreferenced)
401 : {
402 1021 : m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_poDS->GetSRS());
403 : }
404 :
405 1065 : Init(oFields, oAttributesFromTileStats);
406 :
407 1065 : GetXY(0, 0, m_dfTileMinX, m_dfTileMaxY);
408 1065 : GetXY(m_nExtent, m_nExtent, m_dfTileMaxX, m_dfTileMinY);
409 1065 : OGRLinearRing *poLR = new OGRLinearRing();
410 1065 : poLR->addPoint(m_dfTileMinX, m_dfTileMinY);
411 1065 : poLR->addPoint(m_dfTileMinX, m_dfTileMaxY);
412 1065 : poLR->addPoint(m_dfTileMaxX, m_dfTileMaxY);
413 1065 : poLR->addPoint(m_dfTileMaxX, m_dfTileMinY);
414 1065 : poLR->addPoint(m_dfTileMinX, m_dfTileMinY);
415 1065 : m_oClipPoly.addRingDirectly(poLR);
416 :
417 : // Config option only for tests for now. When set, it ensures that
418 : // the first ring (exterior ring) of a polygon is clockwise oriented,
419 : // as per the MVT spec.
420 : // By default, we are more tolerant and only use reversal of winding order
421 : // to detect inner rings.
422 1065 : m_bEnforceExternalIsClockwise = CPLTestBool(
423 : CPLGetConfigOption("OGR_MVT_ENFORE_EXTERNAL_RING_IS_CLOCKWISE", "NO"));
424 1065 : }
425 :
426 : /************************************************************************/
427 : /* ~OGRMVTLayer() */
428 : /************************************************************************/
429 :
430 2130 : OGRMVTLayer::~OGRMVTLayer()
431 : {
432 22608 : for (auto &sValue : m_asValues)
433 : {
434 21543 : if (sValue.eType == OFTString)
435 : {
436 12216 : CPLFree(sValue.sValue.String);
437 : }
438 : }
439 2130 : }
440 :
441 : /************************************************************************/
442 : /* Init() */
443 : /************************************************************************/
444 :
445 1065 : void OGRMVTLayer::Init(const CPLJSONObject &oFields,
446 : const CPLJSONArray &oAttributesFromTileStats)
447 : {
448 : // First pass to collect keys and values
449 1065 : const GByte *pabyData = m_pabyDataStart;
450 1065 : const GByte *pabyDataLimit = m_pabyDataEnd;
451 1065 : unsigned int nKey = 0;
452 1065 : bool bGeomTypeSet = false;
453 1065 : const bool bScanFields = !oFields.IsValid();
454 1065 : const bool bScanGeometries = m_poFeatureDefn->GetGeomType() == wkbUnknown;
455 1065 : const bool bQuickScanFeature = bScanFields || bScanGeometries;
456 :
457 : try
458 : {
459 45387 : while (pabyData < pabyDataLimit)
460 : {
461 44322 : READ_FIELD_KEY(nKey);
462 44322 : if (nKey == MAKE_KEY(knLAYER_KEYS, WT_DATA))
463 : {
464 16928 : char *pszKey = nullptr;
465 16928 : READ_TEXT(pabyData, pabyDataLimit, pszKey);
466 16928 : m_aosKeys.push_back(pszKey);
467 16928 : CPLFree(pszKey);
468 : }
469 27394 : else if (nKey == MAKE_KEY(knLAYER_VALUES, WT_DATA))
470 : {
471 21544 : unsigned int nValueLength = 0;
472 21544 : READ_SIZE(pabyData, pabyDataLimit, nValueLength);
473 21544 : const GByte *pabyDataValueEnd = pabyData + nValueLength;
474 21544 : READ_VARUINT32(pabyData, pabyDataLimit, nKey);
475 21544 : if (nKey == MAKE_KEY(knVALUE_STRING, WT_DATA))
476 : {
477 12216 : char *pszValue = nullptr;
478 12216 : READ_TEXT(pabyData, pabyDataLimit, pszValue);
479 : Value sValue;
480 12216 : sValue.eType = OFTString;
481 12216 : sValue.eSubType = OFSTNone;
482 12216 : sValue.sValue.String = pszValue;
483 12216 : m_asValues.push_back(sValue);
484 : }
485 9328 : else if (nKey == MAKE_KEY(knVALUE_FLOAT, WT_32BIT))
486 : {
487 : Value sValue;
488 604 : sValue.eType = OFTReal;
489 604 : sValue.eSubType = OFSTFloat32;
490 604 : sValue.sValue.Real = ReadFloat32(&pabyData, pabyDataLimit);
491 604 : m_asValues.push_back(sValue);
492 : }
493 8724 : else if (nKey == MAKE_KEY(knVALUE_DOUBLE, WT_64BIT))
494 : {
495 : Value sValue;
496 693 : sValue.eType = OFTReal;
497 693 : sValue.eSubType = OFSTNone;
498 693 : sValue.sValue.Real = ReadFloat64(&pabyData, pabyDataLimit);
499 693 : m_asValues.push_back(sValue);
500 : }
501 8031 : else if (nKey == MAKE_KEY(knVALUE_INT, WT_VARINT))
502 : {
503 261 : GIntBig nVal = 0;
504 261 : READ_VARINT64(pabyData, pabyDataLimit, nVal);
505 : Value sValue;
506 203 : sValue.eType = (nVal >= INT_MIN && nVal <= INT_MAX)
507 464 : ? OFTInteger
508 : : OFTInteger64;
509 261 : sValue.eSubType = OFSTNone;
510 261 : if (sValue.eType == OFTInteger)
511 140 : sValue.sValue.Integer = static_cast<int>(nVal);
512 : else
513 121 : sValue.sValue.Integer64 = nVal;
514 261 : m_asValues.push_back(sValue);
515 : }
516 7770 : else if (nKey == MAKE_KEY(knVALUE_UINT, WT_VARINT))
517 : {
518 7150 : GUIntBig nVal = 0;
519 7150 : READ_VARUINT64(pabyData, pabyDataLimit, nVal);
520 : Value sValue;
521 7150 : sValue.eType =
522 7150 : (nVal <= INT_MAX) ? OFTInteger : OFTInteger64;
523 7150 : sValue.eSubType = OFSTNone;
524 7150 : if (sValue.eType == OFTInteger)
525 7090 : sValue.sValue.Integer = static_cast<int>(nVal);
526 : else
527 60 : sValue.sValue.Integer64 = static_cast<GIntBig>(nVal);
528 7150 : m_asValues.push_back(sValue);
529 : }
530 620 : else if (nKey == MAKE_KEY(knVALUE_SINT, WT_VARINT))
531 : {
532 490 : GIntBig nVal = 0;
533 490 : READ_VARSINT64(pabyData, pabyDataLimit, nVal);
534 : Value sValue;
535 430 : sValue.eType = (nVal >= INT_MIN && nVal <= INT_MAX)
536 920 : ? OFTInteger
537 : : OFTInteger64;
538 490 : sValue.eSubType = OFSTNone;
539 490 : if (sValue.eType == OFTInteger)
540 372 : sValue.sValue.Integer = static_cast<int>(nVal);
541 : else
542 118 : sValue.sValue.Integer64 = nVal;
543 490 : m_asValues.push_back(sValue);
544 : }
545 130 : else if (nKey == MAKE_KEY(knVALUE_BOOL, WT_VARINT))
546 : {
547 129 : unsigned nVal = 0;
548 129 : READ_VARUINT32(pabyData, pabyDataLimit, nVal);
549 : Value sValue;
550 129 : sValue.eType = OFTInteger;
551 129 : sValue.eSubType = OFSTBoolean;
552 129 : sValue.sValue.Integer = static_cast<int>(nVal);
553 129 : m_asValues.push_back(sValue);
554 : }
555 :
556 21544 : pabyData = pabyDataValueEnd;
557 : }
558 5850 : else if (nKey == MAKE_KEY(knLAYER_EXTENT, WT_VARINT))
559 : {
560 1052 : GUInt32 nExtent = 0;
561 1052 : READ_VARUINT32(pabyData, pabyDataLimit, nExtent);
562 1052 : m_nExtent = std::max(1U, nExtent); // to avoid divide by zero
563 : }
564 : else
565 : {
566 4798 : SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, FALSE);
567 : }
568 : }
569 :
570 1065 : InitFields(oFields, oAttributesFromTileStats);
571 :
572 1065 : m_nFeatureCount = 0;
573 1065 : pabyData = m_pabyDataStart;
574 : // Second pass to iterate over features to figure out the geometry type
575 : // and attribute schema
576 45376 : while (pabyData < pabyDataLimit)
577 : {
578 44314 : const GByte *pabyDataBefore = pabyData;
579 44314 : READ_FIELD_KEY(nKey);
580 44314 : if (nKey == MAKE_KEY(knLAYER_FEATURES, WT_DATA))
581 : {
582 2667 : if (m_pabyDataFeatureStart == nullptr)
583 : {
584 1063 : m_pabyDataFeatureStart = pabyDataBefore;
585 1063 : m_pabyDataCur = pabyDataBefore;
586 : }
587 :
588 2667 : unsigned int nFeatureLength = 0;
589 2667 : READ_SIZE(pabyData, pabyDataLimit, nFeatureLength);
590 2667 : const GByte *pabyDataFeatureEnd = pabyData + nFeatureLength;
591 2667 : if (bQuickScanFeature)
592 : {
593 526 : if (!QuickScanFeature(pabyData, pabyDataFeatureEnd,
594 : bScanFields, bScanGeometries,
595 : bGeomTypeSet))
596 : {
597 3 : return;
598 : }
599 : }
600 2664 : pabyData = pabyDataFeatureEnd;
601 :
602 2664 : m_nFeatureCount++;
603 : }
604 : else
605 : {
606 41647 : SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, FALSE);
607 : }
608 : }
609 : }
610 0 : catch (const GPBException &e)
611 : {
612 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
613 : }
614 : }
615 :
616 : /************************************************************************/
617 : /* MergeFieldDefn() */
618 : /************************************************************************/
619 :
620 14 : static void MergeFieldDefn(OGRFieldDefn *poFieldDefn, OGRFieldType eSrcType,
621 : OGRFieldSubType eSrcSubType)
622 : {
623 14 : if (eSrcType == OFTString)
624 : {
625 7 : poFieldDefn->SetSubType(OFSTNone);
626 7 : poFieldDefn->SetType(OFTString);
627 : }
628 7 : else if (poFieldDefn->GetType() == OFTInteger && eSrcType == OFTInteger64)
629 : {
630 1 : poFieldDefn->SetSubType(OFSTNone);
631 1 : poFieldDefn->SetType(OFTInteger64);
632 : }
633 10 : else if ((poFieldDefn->GetType() == OFTInteger ||
634 10 : poFieldDefn->GetType() == OFTInteger64) &&
635 : eSrcType == OFTReal)
636 : {
637 2 : poFieldDefn->SetSubType(OFSTNone);
638 2 : poFieldDefn->SetType(OFTReal);
639 2 : poFieldDefn->SetSubType(eSrcSubType);
640 : }
641 4 : else if (poFieldDefn->GetType() == OFTReal && eSrcType == OFTReal &&
642 : eSrcSubType == OFSTNone)
643 : {
644 1 : poFieldDefn->SetSubType(OFSTNone);
645 : }
646 3 : else if (poFieldDefn->GetType() == OFTInteger && eSrcType == OFTInteger &&
647 : eSrcSubType == OFSTNone)
648 : {
649 1 : poFieldDefn->SetSubType(OFSTNone);
650 : }
651 14 : }
652 :
653 : /************************************************************************/
654 : /* QuickScanFeature() */
655 : /************************************************************************/
656 :
657 526 : bool OGRMVTLayer::QuickScanFeature(const GByte *pabyData,
658 : const GByte *pabyDataFeatureEnd,
659 : bool bScanFields, bool bScanGeometries,
660 : bool &bGeomTypeSet)
661 : {
662 526 : unsigned int nKey = 0;
663 526 : unsigned int nGeomType = 0;
664 : try
665 : {
666 2016 : while (pabyData < pabyDataFeatureEnd)
667 : {
668 1493 : READ_VARUINT32(pabyData, pabyDataFeatureEnd, nKey);
669 1493 : if (nKey == MAKE_KEY(knFEATURE_TYPE, WT_VARINT))
670 : {
671 507 : READ_VARUINT32(pabyData, pabyDataFeatureEnd, nGeomType);
672 : }
673 986 : else if (nKey == MAKE_KEY(knFEATURE_TAGS, WT_DATA) && bScanFields)
674 : {
675 61 : unsigned int nTagsSize = 0;
676 61 : READ_SIZE(pabyData, pabyDataFeatureEnd, nTagsSize);
677 61 : const GByte *pabyDataTagsEnd = pabyData + nTagsSize;
678 240 : while (pabyData < pabyDataTagsEnd)
679 : {
680 182 : unsigned int nKeyIdx = 0;
681 182 : unsigned int nValIdx = 0;
682 182 : READ_VARUINT32(pabyData, pabyDataTagsEnd, nKeyIdx);
683 182 : READ_VARUINT32(pabyData, pabyDataTagsEnd, nValIdx);
684 181 : if (nKeyIdx >= m_aosKeys.size())
685 : {
686 1 : CPLError(CE_Failure, CPLE_AppDefined,
687 : "Invalid tag key index: %u", nKeyIdx);
688 1 : m_bError = true;
689 1 : return false;
690 : }
691 180 : if (nValIdx >= m_asValues.size())
692 : {
693 1 : CPLError(CE_Failure, CPLE_AppDefined,
694 : "Invalid tag value index: %u", nValIdx);
695 1 : m_bError = true;
696 1 : return false;
697 : }
698 : const int nFieldIdx =
699 179 : m_poFeatureDefn->GetFieldIndex(m_aosKeys[nKeyIdx]);
700 179 : if (nFieldIdx < 0)
701 : {
702 151 : OGRFieldDefn oFieldDefn(m_aosKeys[nKeyIdx],
703 302 : m_asValues[nValIdx].eType);
704 151 : oFieldDefn.SetSubType(m_asValues[nValIdx].eSubType);
705 151 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
706 : }
707 56 : else if (m_poFeatureDefn->GetFieldDefn(nFieldIdx)
708 52 : ->GetType() != m_asValues[nValIdx].eType ||
709 24 : m_poFeatureDefn->GetFieldDefn(nFieldIdx)
710 24 : ->GetSubType() !=
711 24 : m_asValues[nValIdx].eSubType)
712 : {
713 : OGRFieldDefn *poFieldDefn =
714 8 : m_poFeatureDefn->GetFieldDefn(nFieldIdx);
715 8 : OGRFieldType eSrcType(m_asValues[nValIdx].eType);
716 : OGRFieldSubType eSrcSubType(
717 8 : m_asValues[nValIdx].eSubType);
718 8 : MergeFieldDefn(poFieldDefn, eSrcType, eSrcSubType);
719 : }
720 58 : }
721 : }
722 925 : else if (nKey == MAKE_KEY(knFEATURE_GEOMETRY, WT_DATA) &&
723 506 : bScanGeometries && nGeomType >= knGEOM_TYPE_POINT &&
724 : nGeomType <= knGEOM_TYPE_POLYGON)
725 : {
726 505 : unsigned int nGeometrySize = 0;
727 505 : READ_SIZE(pabyData, pabyDataFeatureEnd, nGeometrySize);
728 505 : const GByte *pabyDataGeometryEnd = pabyData + nGeometrySize;
729 505 : OGRwkbGeometryType eType = wkbUnknown;
730 :
731 505 : if (nGeomType == knGEOM_TYPE_POINT)
732 : {
733 83 : eType = wkbPoint;
734 83 : unsigned int nCmdCountCombined = 0;
735 83 : READ_VARUINT32(pabyData, pabyDataGeometryEnd,
736 : nCmdCountCombined);
737 166 : if (GetCmdId(nCmdCountCombined) == knCMD_MOVETO &&
738 83 : GetCmdCount(nCmdCountCombined) > 1)
739 : {
740 0 : eType = wkbMultiPoint;
741 : }
742 : }
743 422 : else if (nGeomType == knGEOM_TYPE_LINESTRING)
744 : {
745 16 : eType = wkbLineString;
746 32 : for (int iIter = 0; pabyData < pabyDataGeometryEnd; iIter++)
747 : {
748 17 : if (iIter == 1)
749 : {
750 1 : eType = wkbMultiLineString;
751 1 : break;
752 : }
753 16 : unsigned int nCmdCountCombined = 0;
754 : unsigned int nLineToCount;
755 : // Should be a moveto
756 16 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
757 16 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
758 16 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
759 16 : READ_VARUINT32(pabyData, pabyDataGeometryEnd,
760 : nCmdCountCombined);
761 16 : nLineToCount = GetCmdCount(nCmdCountCombined);
762 50 : for (unsigned i = 0; i < 2 * nLineToCount; i++)
763 : {
764 34 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
765 : }
766 : }
767 : }
768 : else /* if( nGeomType == knGEOM_TYPE_POLYGON ) */
769 : {
770 406 : eType = wkbPolygon;
771 812 : for (int iIter = 0; pabyData < pabyDataGeometryEnd; iIter++)
772 : {
773 452 : if (iIter == 1)
774 : {
775 46 : eType = wkbMultiPolygon;
776 46 : break;
777 : }
778 406 : unsigned int nCmdCountCombined = 0;
779 : unsigned int nLineToCount;
780 : // Should be a moveto
781 406 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
782 406 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
783 406 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
784 406 : READ_VARUINT32(pabyData, pabyDataGeometryEnd,
785 : nCmdCountCombined);
786 406 : nLineToCount = GetCmdCount(nCmdCountCombined);
787 3592 : for (unsigned i = 0; i < 2 * nLineToCount; i++)
788 : {
789 3186 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
790 : }
791 : // Should be a closepath
792 406 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
793 : }
794 : }
795 :
796 522 : if (bGeomTypeSet && m_poFeatureDefn->GetGeomType() ==
797 17 : OGR_GT_GetCollection(eType))
798 : {
799 : // do nothing
800 : }
801 512 : else if (bGeomTypeSet &&
802 12 : eType == OGR_GT_GetCollection(
803 12 : m_poFeatureDefn->GetGeomType()))
804 : {
805 0 : m_poFeatureDefn->SetGeomType(eType);
806 : }
807 512 : else if (bGeomTypeSet &&
808 12 : m_poFeatureDefn->GetGeomType() != eType)
809 : {
810 0 : m_poFeatureDefn->SetGeomType(wkbUnknown);
811 : }
812 : else
813 : {
814 500 : m_poFeatureDefn->SetGeomType(eType);
815 : }
816 505 : bGeomTypeSet = true;
817 :
818 505 : pabyData = pabyDataGeometryEnd;
819 : }
820 : else
821 : {
822 420 : SKIP_UNKNOWN_FIELD(pabyData, pabyDataFeatureEnd, FALSE);
823 : }
824 : }
825 523 : return true;
826 : }
827 1 : catch (const GPBException &e)
828 : {
829 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
830 1 : return false;
831 : }
832 : }
833 :
834 : /************************************************************************/
835 : /* GetFeatureCount() */
836 : /************************************************************************/
837 :
838 51 : GIntBig OGRMVTLayer::GetFeatureCount(int bForce)
839 : {
840 51 : if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr &&
841 45 : m_nFeatureCount >= 0)
842 : {
843 45 : return m_nFeatureCount;
844 : }
845 6 : return OGRLayer::GetFeatureCount(bForce);
846 : }
847 :
848 : /************************************************************************/
849 : /* ResetReading() */
850 : /************************************************************************/
851 :
852 133 : void OGRMVTLayer::ResetReading()
853 : {
854 133 : m_nFID = 0;
855 133 : m_pabyDataCur = m_pabyDataFeatureStart;
856 133 : }
857 :
858 : /************************************************************************/
859 : /* GetXY() */
860 : /************************************************************************/
861 :
862 370904 : void OGRMVTLayer::GetXY(int nX, int nY, double &dfX, double &dfY)
863 : {
864 370904 : if (m_poDS->m_bGeoreferenced)
865 : {
866 369341 : dfX = m_poDS->m_dfTopX + nX * m_poDS->m_dfTileDimX / m_nExtent;
867 369341 : dfY = m_poDS->m_dfTopY - nY * m_poDS->m_dfTileDimY / m_nExtent;
868 : }
869 : else
870 : {
871 1563 : dfX = nX;
872 1563 : dfY = static_cast<double>(m_nExtent) - nY;
873 : }
874 370904 : }
875 :
876 : /************************************************************************/
877 : /* AddWithOverflowAccepted() */
878 : /************************************************************************/
879 :
880 : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
881 737322 : static int AddWithOverflowAccepted(int a, int b)
882 : {
883 : // In fact in normal situations a+b should not overflow. That can only
884 : // happen with corrupted datasets. But we don't really want to add code
885 : // to detect that situation, so basically this is just a trick to perform
886 : // the addition without the various sanitizers to yell about the overflow.
887 : //
888 : // Assumes complement-to-two signed integer representation and that
889 : // the compiler will safely cast a big unsigned to negative integer.
890 737322 : return static_cast<int>(static_cast<unsigned>(a) +
891 737322 : static_cast<unsigned>(b));
892 : }
893 :
894 : /************************************************************************/
895 : /* ParseGeometry() */
896 : /************************************************************************/
897 :
898 : std::unique_ptr<OGRGeometry>
899 2112 : OGRMVTLayer::ParseGeometry(unsigned int nGeomType,
900 : const GByte *pabyDataGeometryEnd)
901 : {
902 : try
903 : {
904 2112 : if (nGeomType == knGEOM_TYPE_POINT)
905 : {
906 1 : std::unique_ptr<OGRMultiPoint> poMultiPoint;
907 116 : unsigned int nCmdCountCombined = 0;
908 : unsigned int nCount;
909 116 : READ_VARUINT32(m_pabyDataCur, pabyDataGeometryEnd,
910 : nCmdCountCombined);
911 116 : nCount = GetCmdCount(nCmdCountCombined);
912 116 : if (GetCmdId(nCmdCountCombined) == knCMD_MOVETO && nCount == 1)
913 : {
914 114 : int nX = 0;
915 114 : int nY = 0;
916 114 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nX);
917 113 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nY);
918 : double dfX;
919 : double dfY;
920 113 : GetXY(nX, nY, dfX, dfY);
921 226 : auto poPoint = std::make_unique<OGRPoint>(dfX, dfY);
922 113 : if (m_poFeatureDefn->GetGeomType() == wkbMultiPoint)
923 : {
924 8 : poMultiPoint = std::make_unique<OGRMultiPoint>();
925 8 : poMultiPoint->addGeometry(std::move(poPoint));
926 8 : return poMultiPoint;
927 : }
928 : else
929 : {
930 105 : return poPoint;
931 : }
932 : }
933 2 : else if (GetCmdId(nCmdCountCombined) == knCMD_MOVETO && nCount > 1)
934 : {
935 2 : int nX = 0;
936 2 : int nY = 0;
937 2 : poMultiPoint = std::make_unique<OGRMultiPoint>();
938 6 : for (unsigned i = 0; i < nCount; i++)
939 : {
940 4 : int nDX = 0;
941 4 : int nDY = 0;
942 4 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDX);
943 4 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDY);
944 : // if( nDX != 0 || nDY != 0 )
945 : {
946 4 : nX = AddWithOverflowAccepted(nX, nDX);
947 4 : nY = AddWithOverflowAccepted(nY, nDY);
948 : double dfX;
949 : double dfY;
950 4 : GetXY(nX, nY, dfX, dfY);
951 4 : auto poPoint = std::make_unique<OGRPoint>(dfX, dfY);
952 4 : if (i == 0 && nCount == 2 &&
953 2 : m_pabyDataCur == pabyDataGeometryEnd)
954 : {
955 : // Current versions of Mapserver at time of writing
956 : // wrongly encode a point with nCount = 2
957 : static bool bWarned = false;
958 0 : if (!bWarned)
959 : {
960 0 : CPLDebug(
961 : "MVT",
962 : "Reading likely a broken point as "
963 : "produced by some versions of Mapserver");
964 0 : bWarned = true;
965 : }
966 0 : return poPoint;
967 : }
968 4 : poMultiPoint->addGeometry(std::move(poPoint));
969 : }
970 : }
971 2 : return poMultiPoint;
972 : }
973 : }
974 1996 : else if (nGeomType == knGEOM_TYPE_LINESTRING)
975 : {
976 23 : std::unique_ptr<OGRMultiLineString> poMultiLS;
977 23 : std::unique_ptr<OGRLineString> poLine;
978 23 : int nX = 0;
979 23 : int nY = 0;
980 23 : bool bFirstLine = true;
981 51 : while (m_pabyDataCur < pabyDataGeometryEnd)
982 : {
983 28 : unsigned int nCmdCountCombined = 0;
984 : unsigned int nLineToCount;
985 : // Should be a moveto
986 28 : SKIP_VARINT(m_pabyDataCur, pabyDataGeometryEnd);
987 28 : int nDX = 0;
988 28 : int nDY = 0;
989 28 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDX);
990 28 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDY);
991 28 : nX = AddWithOverflowAccepted(nX, nDX);
992 28 : nY = AddWithOverflowAccepted(nY, nDY);
993 : double dfX;
994 : double dfY;
995 28 : GetXY(nX, nY, dfX, dfY);
996 51 : if (!bFirstLine ||
997 23 : m_poFeatureDefn->GetGeomType() == wkbMultiLineString)
998 : {
999 22 : if (!poMultiLS)
1000 17 : poMultiLS = std::make_unique<OGRMultiLineString>();
1001 22 : if (poLine)
1002 0 : poMultiLS->addGeometry(std::move(poLine));
1003 : }
1004 28 : poLine = std::make_unique<OGRLineString>();
1005 28 : poLine->addPoint(dfX, dfY);
1006 28 : READ_VARUINT32(m_pabyDataCur, pabyDataGeometryEnd,
1007 : nCmdCountCombined);
1008 28 : nLineToCount = GetCmdCount(nCmdCountCombined);
1009 59 : for (unsigned i = 0; i < nLineToCount; i++)
1010 : {
1011 31 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDX);
1012 31 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDY);
1013 : // if( nDX != 0 || nDY != 0 )
1014 : {
1015 31 : nX = AddWithOverflowAccepted(nX, nDX);
1016 31 : nY = AddWithOverflowAccepted(nY, nDY);
1017 31 : GetXY(nX, nY, dfX, dfY);
1018 31 : poLine->addPoint(dfX, dfY);
1019 : }
1020 : }
1021 28 : if (poMultiLS)
1022 22 : poMultiLS->addGeometry(std::move(poLine));
1023 28 : bFirstLine = false;
1024 : }
1025 23 : if (poMultiLS)
1026 : {
1027 17 : return poMultiLS;
1028 : }
1029 : else
1030 : {
1031 6 : return poLine;
1032 : }
1033 : }
1034 1973 : else if (nGeomType == knGEOM_TYPE_POLYGON)
1035 : {
1036 1973 : std::unique_ptr<OGRMultiPolygon> poMultiPoly;
1037 1973 : std::unique_ptr<OGRPolygon> poPoly;
1038 1973 : int externalIsClockwise = 0;
1039 1973 : int nX = 0;
1040 1973 : int nY = 0;
1041 1973 : OGREnvelope sExteriorRingEnvelope;
1042 5884 : while (m_pabyDataCur < pabyDataGeometryEnd)
1043 : {
1044 3911 : unsigned int nCmdCountCombined = 0;
1045 : unsigned int nLineToCount;
1046 : // Should be a moveto
1047 3911 : SKIP_VARINT(m_pabyDataCur, pabyDataGeometryEnd);
1048 3911 : int nDX = 0;
1049 3911 : int nDY = 0;
1050 3911 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDX);
1051 3911 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDY);
1052 3911 : nX = AddWithOverflowAccepted(nX, nDX);
1053 3911 : nY = AddWithOverflowAccepted(nY, nDY);
1054 : double dfX;
1055 : double dfY;
1056 3911 : GetXY(nX, nY, dfX, dfY);
1057 3911 : auto poRing = std::make_unique<OGRLinearRing>();
1058 3911 : poRing->addPoint(dfX, dfY);
1059 3911 : READ_VARUINT32(m_pabyDataCur, pabyDataGeometryEnd,
1060 : nCmdCountCombined);
1061 3911 : nLineToCount = GetCmdCount(nCmdCountCombined);
1062 368598 : for (unsigned i = 0; i < nLineToCount; i++)
1063 : {
1064 364687 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDX);
1065 364687 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDY);
1066 : // if( nDX != 0 || nDY != 0 )
1067 : {
1068 364687 : nX = AddWithOverflowAccepted(nX, nDX);
1069 364687 : nY = AddWithOverflowAccepted(nY, nDY);
1070 364687 : GetXY(nX, nY, dfX, dfY);
1071 364687 : poRing->addPoint(dfX, dfY);
1072 : }
1073 : }
1074 : // Should be a closepath
1075 3911 : SKIP_VARINT(m_pabyDataCur, pabyDataGeometryEnd);
1076 3911 : poRing->closeRings();
1077 3911 : if (!poPoly)
1078 : {
1079 1973 : poPoly = std::make_unique<OGRPolygon>();
1080 1973 : externalIsClockwise = poRing->isClockwise();
1081 1973 : if (!externalIsClockwise)
1082 : {
1083 743 : if (m_bEnforceExternalIsClockwise)
1084 : {
1085 0 : CPLError(CE_Failure, CPLE_AppDefined,
1086 : "Bad ring orientation detected");
1087 0 : return nullptr;
1088 : }
1089 : else
1090 : {
1091 743 : CPLDebugOnce(
1092 : "MVT",
1093 : "Bad ring orientation detected. Auto-fixing");
1094 : }
1095 : }
1096 1973 : poPoly->addRing(std::move(poRing));
1097 : }
1098 : else
1099 : {
1100 : // Detect change of winding order to figure out if this is
1101 : // an interior or exterior ring
1102 1938 : if (externalIsClockwise != poRing->isClockwise())
1103 : {
1104 312 : poPoly->addRing(std::move(poRing));
1105 : }
1106 : else
1107 : {
1108 : #ifdef HAVE_GEOS
1109 : {
1110 : // This block is just to deal with potential bad
1111 : // oriented rings
1112 : // Such as those produced by GDAL < 3.12 in some
1113 : // situations like
1114 : // https://github.com/OSGeo/gdal/issues/13305
1115 1626 : if (!sExteriorRingEnvelope.IsInit())
1116 1625 : poPoly->getEnvelope(&sExteriorRingEnvelope);
1117 1626 : OGREnvelope sCurRingEnvelope;
1118 1626 : poRing->getEnvelope(&sCurRingEnvelope);
1119 : // Cheap heuristics to detect potentially inner
1120 : // rings
1121 1626 : if (sExteriorRingEnvelope.Contains(
1122 1626 : sCurRingEnvelope))
1123 : {
1124 : // Now do the real check
1125 253 : OGRLineString oLS(*poRing);
1126 253 : if (poPoly->Contains(&oLS))
1127 : {
1128 2 : CPLDebugOnce("MVT",
1129 : "Bad ring orientation "
1130 : "detected. Auto-fixing");
1131 2 : poPoly->addRing(std::move(poRing));
1132 2 : continue;
1133 : }
1134 : }
1135 : }
1136 : #endif
1137 :
1138 1624 : if (!poMultiPoly)
1139 : {
1140 750 : poMultiPoly = std::make_unique<OGRMultiPolygon>();
1141 : }
1142 1624 : poMultiPoly->addGeometry(std::move(poPoly));
1143 :
1144 1624 : poPoly = std::make_unique<OGRPolygon>();
1145 1624 : poPoly->addRing(std::move(poRing));
1146 1624 : sExteriorRingEnvelope = OGREnvelope();
1147 : }
1148 : }
1149 : }
1150 1973 : if (poMultiPoly)
1151 : {
1152 750 : CPLAssert(poPoly);
1153 750 : poMultiPoly->addGeometry(std::move(poPoly));
1154 : }
1155 2446 : else if (poPoly &&
1156 2446 : m_poFeatureDefn->GetGeomType() == wkbMultiPolygon)
1157 : {
1158 869 : poMultiPoly = std::make_unique<OGRMultiPolygon>();
1159 869 : poMultiPoly->addGeometry(std::move(poPoly));
1160 : }
1161 1973 : if (poMultiPoly)
1162 : {
1163 1619 : return poMultiPoly;
1164 : }
1165 : else
1166 : {
1167 354 : return poPoly;
1168 : }
1169 : }
1170 : }
1171 2 : catch (const GPBException &e)
1172 : {
1173 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1174 : }
1175 1 : return nullptr;
1176 : }
1177 :
1178 : /************************************************************************/
1179 : /* SanitizeClippedGeometry() */
1180 : /************************************************************************/
1181 :
1182 667 : void OGRMVTLayer::SanitizeClippedGeometry(std::unique_ptr<OGRGeometry> &poGeom)
1183 : {
1184 667 : OGRwkbGeometryType eInGeomType = wkbFlatten(poGeom->getGeometryType());
1185 667 : const OGRwkbGeometryType eLayerGeomType = GetGeomType();
1186 667 : if (eLayerGeomType == wkbUnknown)
1187 : {
1188 0 : return;
1189 : }
1190 :
1191 : // GEOS intersection may return a mix of polygon and linestrings when
1192 : // intersection a multipolygon and a polygon
1193 667 : if (eInGeomType == wkbGeometryCollection)
1194 : {
1195 122 : std::unique_ptr<OGRGeometryCollection> poTargetGC;
1196 122 : const OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
1197 0 : std::unique_ptr<OGRGeometry> poTargetSingleGeom;
1198 : OGRwkbGeometryType ePartGeom;
1199 122 : if (eLayerGeomType == wkbPoint || eLayerGeomType == wkbMultiPoint)
1200 : {
1201 0 : ePartGeom = wkbPoint;
1202 : }
1203 122 : else if (eLayerGeomType == wkbLineString ||
1204 : eLayerGeomType == wkbMultiLineString)
1205 : {
1206 0 : ePartGeom = wkbLineString;
1207 : }
1208 : else
1209 : {
1210 122 : ePartGeom = wkbPolygon;
1211 : }
1212 419 : for (const auto *poSubGeom : poGC)
1213 : {
1214 297 : if (wkbFlatten(poSubGeom->getGeometryType()) == ePartGeom)
1215 : {
1216 175 : if (poTargetSingleGeom)
1217 : {
1218 53 : if (!poTargetGC)
1219 : {
1220 53 : poTargetGC.reset(OGRGeometryFactory::createGeometry(
1221 : OGR_GT_GetCollection(ePartGeom))
1222 : ->toGeometryCollection());
1223 : // cppcheck-suppress nullPointerRedundantCheck
1224 53 : poTargetGC->addGeometry(std::move(poTargetSingleGeom));
1225 : }
1226 :
1227 53 : poTargetGC->addGeometry(poSubGeom);
1228 : }
1229 : else
1230 : {
1231 122 : poTargetSingleGeom.reset(poSubGeom->clone());
1232 : }
1233 : }
1234 : }
1235 122 : if (poTargetGC)
1236 53 : poGeom = std::move(poTargetGC);
1237 69 : else if (poTargetSingleGeom)
1238 69 : poGeom = std::move(poTargetSingleGeom);
1239 122 : eInGeomType = wkbFlatten(poGeom->getGeometryType());
1240 : }
1241 :
1242 : // Wrap single into multi if requested by the layer geometry type
1243 667 : if (OGR_GT_GetCollection(eInGeomType) == eLayerGeomType)
1244 : {
1245 : auto poGC = std::unique_ptr<OGRGeometryCollection>(
1246 : OGRGeometryFactory::createGeometry(eLayerGeomType)
1247 804 : ->toGeometryCollection());
1248 402 : poGC->addGeometry(std::move(poGeom));
1249 402 : poGeom = std::move(poGC);
1250 : }
1251 : }
1252 :
1253 : /************************************************************************/
1254 : /* GetNextRawFeature() */
1255 : /************************************************************************/
1256 :
1257 2897 : OGRFeature *OGRMVTLayer::GetNextRawFeature()
1258 : {
1259 2897 : if (m_pabyDataCur == nullptr || m_pabyDataCur >= m_pabyDataEnd || m_bError)
1260 : {
1261 123 : return nullptr;
1262 : }
1263 :
1264 2774 : unsigned int nKey = 0;
1265 2774 : const GByte *pabyDataLimit = m_pabyDataEnd;
1266 2774 : std::unique_ptr<OGRFeature> poFeature;
1267 2774 : unsigned int nFeatureLength = 0;
1268 2774 : unsigned int nGeomType = 0;
1269 :
1270 : try
1271 : {
1272 : while (true)
1273 : {
1274 2779 : bool bOK = true;
1275 :
1276 34652 : while (m_pabyDataCur < pabyDataLimit)
1277 : {
1278 33993 : READ_VARUINT32(m_pabyDataCur, pabyDataLimit, nKey);
1279 33993 : if (nKey == MAKE_KEY(knLAYER_FEATURES, WT_DATA))
1280 : {
1281 2120 : poFeature = std::make_unique<OGRFeature>(m_poFeatureDefn);
1282 2120 : break;
1283 : }
1284 : else
1285 : {
1286 31873 : SKIP_UNKNOWN_FIELD(m_pabyDataCur, pabyDataLimit, FALSE);
1287 : }
1288 : }
1289 :
1290 2779 : if (poFeature == nullptr)
1291 659 : return nullptr;
1292 :
1293 2120 : READ_SIZE(m_pabyDataCur, pabyDataLimit, nFeatureLength);
1294 2120 : const GByte *pabyDataFeatureEnd = m_pabyDataCur + nFeatureLength;
1295 8417 : while (m_pabyDataCur < pabyDataFeatureEnd)
1296 : {
1297 6297 : READ_VARUINT32(m_pabyDataCur, pabyDataFeatureEnd, nKey);
1298 6297 : if (nKey == MAKE_KEY(knFEATURE_ID, WT_VARINT))
1299 : {
1300 14 : GUIntBig nID = 0;
1301 14 : READ_VARUINT64(m_pabyDataCur, pabyDataFeatureEnd, nID);
1302 14 : poFeature->SetField("mvt_id", static_cast<GIntBig>(nID));
1303 : }
1304 6283 : else if (nKey == MAKE_KEY(knFEATURE_TYPE, WT_VARINT))
1305 : {
1306 2114 : READ_VARUINT32(m_pabyDataCur, pabyDataFeatureEnd,
1307 : nGeomType);
1308 : }
1309 4169 : else if (nKey == MAKE_KEY(knFEATURE_TAGS, WT_DATA))
1310 : {
1311 2055 : unsigned int nTagsSize = 0;
1312 2055 : READ_SIZE(m_pabyDataCur, pabyDataFeatureEnd, nTagsSize);
1313 2055 : const GByte *pabyDataTagsEnd = m_pabyDataCur + nTagsSize;
1314 62992 : while (m_pabyDataCur < pabyDataTagsEnd)
1315 : {
1316 60937 : unsigned int nKeyIdx = 0;
1317 60937 : unsigned int nValIdx = 0;
1318 60937 : READ_VARUINT32(m_pabyDataCur, pabyDataTagsEnd, nKeyIdx);
1319 60937 : READ_VARUINT32(m_pabyDataCur, pabyDataTagsEnd, nValIdx);
1320 121874 : if (nKeyIdx < m_aosKeys.size() &&
1321 60937 : nValIdx < m_asValues.size())
1322 : {
1323 : const int nFieldIdx =
1324 60937 : m_poFeatureDefn->GetFieldIndex(
1325 60937 : m_aosKeys[nKeyIdx]);
1326 60937 : if (nFieldIdx >= 0)
1327 : {
1328 60937 : if (m_asValues[nValIdx].eType == OFTString)
1329 : {
1330 33105 : poFeature->SetField(
1331 : nFieldIdx,
1332 33105 : m_asValues[nValIdx].sValue.String);
1333 : }
1334 27832 : else if (m_asValues[nValIdx].eType ==
1335 : OFTInteger)
1336 : {
1337 52608 : poFeature->SetField(
1338 : nFieldIdx,
1339 26304 : m_asValues[nValIdx].sValue.Integer);
1340 : }
1341 1528 : else if (m_asValues[nValIdx].eType ==
1342 : OFTInteger64)
1343 : {
1344 912 : poFeature->SetField(
1345 : nFieldIdx,
1346 456 : m_asValues[nValIdx].sValue.Integer64);
1347 : }
1348 1072 : else if (m_asValues[nValIdx].eType == OFTReal)
1349 : {
1350 2144 : poFeature->SetField(
1351 : nFieldIdx,
1352 1072 : m_asValues[nValIdx].sValue.Real);
1353 : }
1354 : }
1355 : }
1356 : }
1357 : }
1358 2114 : else if (nKey == MAKE_KEY(knFEATURE_GEOMETRY, WT_DATA) &&
1359 2112 : nGeomType >= 1 && nGeomType <= 3)
1360 : {
1361 2112 : unsigned int nGeometrySize = 0;
1362 2112 : READ_SIZE(m_pabyDataCur, pabyDataFeatureEnd, nGeometrySize);
1363 2112 : const GByte *pabyDataGeometryEnd =
1364 2112 : m_pabyDataCur + nGeometrySize;
1365 2112 : auto poGeom = ParseGeometry(nGeomType, pabyDataGeometryEnd);
1366 2112 : if (poGeom)
1367 : {
1368 : // Clip geometry to tile extent if requested
1369 2111 : if (m_poDS->m_bClip && OGRGeometryFactory::haveGEOS())
1370 : {
1371 2106 : OGREnvelope sEnvelope;
1372 2106 : poGeom->getEnvelope(&sEnvelope);
1373 2106 : if (sEnvelope.MinX >= m_dfTileMinX &&
1374 1863 : sEnvelope.MinY >= m_dfTileMinY &&
1375 1701 : sEnvelope.MaxX <= m_dfTileMaxX &&
1376 1480 : sEnvelope.MaxY <= m_dfTileMaxY)
1377 : {
1378 : // do nothing
1379 : }
1380 672 : else if (sEnvelope.MinX < m_dfTileMaxX &&
1381 668 : sEnvelope.MinY < m_dfTileMaxY &&
1382 668 : sEnvelope.MaxX > m_dfTileMinX &&
1383 668 : sEnvelope.MaxY > m_dfTileMinY)
1384 : {
1385 : auto poClipped = std::unique_ptr<OGRGeometry>(
1386 1334 : poGeom->Intersection(&m_oClipPoly));
1387 667 : if (poClipped)
1388 : {
1389 667 : SanitizeClippedGeometry(poClipped);
1390 667 : if (poClipped->IsEmpty())
1391 : {
1392 0 : bOK = false;
1393 : }
1394 : else
1395 : {
1396 1334 : poClipped->assignSpatialReference(
1397 667 : GetSpatialRef());
1398 1334 : poFeature->SetGeometry(
1399 667 : std::move(poClipped));
1400 667 : poGeom.reset();
1401 : }
1402 667 : }
1403 : }
1404 : else
1405 : {
1406 5 : bOK = false;
1407 : }
1408 : }
1409 :
1410 2111 : if (poGeom)
1411 : {
1412 1444 : poGeom->assignSpatialReference(GetSpatialRef());
1413 1444 : poFeature->SetGeometry(std::move(poGeom));
1414 : }
1415 : }
1416 :
1417 2112 : m_pabyDataCur = pabyDataGeometryEnd;
1418 : }
1419 : else
1420 : {
1421 2 : SKIP_UNKNOWN_FIELD(m_pabyDataCur, pabyDataFeatureEnd,
1422 : FALSE);
1423 : }
1424 : }
1425 2120 : m_pabyDataCur = pabyDataFeatureEnd;
1426 :
1427 2120 : if (bOK)
1428 : {
1429 2115 : poFeature->SetFID(m_nFID);
1430 2115 : m_nFID++;
1431 2115 : return poFeature.release();
1432 : }
1433 : else
1434 : {
1435 5 : poFeature.reset();
1436 : }
1437 5 : }
1438 : }
1439 0 : catch (const GPBException &e)
1440 : {
1441 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1442 0 : return nullptr;
1443 : }
1444 : }
1445 :
1446 : /************************************************************************/
1447 : /* GetDataset() */
1448 : /************************************************************************/
1449 :
1450 1 : GDALDataset *OGRMVTLayer::GetDataset()
1451 : {
1452 1 : return m_poDS;
1453 : }
1454 :
1455 : /************************************************************************/
1456 : /* StripDummyEntries() */
1457 : /************************************************************************/
1458 :
1459 122 : static CPLStringList StripDummyEntries(const CPLStringList &aosInput)
1460 : {
1461 244 : CPLStringList aosOutput;
1462 471 : for (int i = 0; i < aosInput.Count(); i++)
1463 : {
1464 853 : if (aosInput[i] != CPLString(".") && aosInput[i] != CPLString("..") &&
1465 504 : CPLString(aosInput[i]).find(".properties") == std::string::npos)
1466 : {
1467 155 : aosOutput.AddString(aosInput[i]);
1468 : }
1469 : }
1470 244 : return aosOutput.Sort();
1471 : }
1472 :
1473 : /************************************************************************/
1474 : /* OGRMVTDirectoryLayer() */
1475 : /************************************************************************/
1476 :
1477 27 : OGRMVTDirectoryLayer::OGRMVTDirectoryLayer(
1478 : OGRMVTDataset *poDS, const char *pszLayerName, const char *pszDirectoryName,
1479 : const CPLJSONObject &oFields, const CPLJSONArray &oAttributesFromTileStats,
1480 : bool bJsonField, bool bAddTileFields, OGRwkbGeometryType eGeomType,
1481 27 : const OGREnvelope *psExtent)
1482 : : m_poDS(poDS), m_osDirName(pszDirectoryName), m_bJsonField(bJsonField),
1483 27 : m_bAddTileFields(bAddTileFields)
1484 : {
1485 27 : m_poFeatureDefn = new OGRFeatureDefn(pszLayerName);
1486 27 : SetDescription(m_poFeatureDefn->GetName());
1487 27 : m_poFeatureDefn->SetGeomType(eGeomType);
1488 27 : m_poFeatureDefn->Reference();
1489 :
1490 27 : m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poDS->GetSRS());
1491 :
1492 27 : if (m_bAddTileFields)
1493 : {
1494 4 : OGRFieldDefn oFieldTileZ("tile_z", OFTInteger);
1495 4 : OGRFieldDefn oFieldTileX("tile_x", OFTInteger);
1496 4 : OGRFieldDefn oFieldTileY("tile_y", OFTInteger);
1497 :
1498 2 : m_poFeatureDefn->AddFieldDefn(&oFieldTileZ);
1499 2 : m_poFeatureDefn->AddFieldDefn(&oFieldTileX);
1500 2 : m_poFeatureDefn->AddFieldDefn(&oFieldTileY);
1501 : }
1502 :
1503 27 : if (m_bJsonField)
1504 : {
1505 2 : OGRFieldDefn oFieldDefnId("mvt_id", OFTInteger64);
1506 1 : m_poFeatureDefn->AddFieldDefn(&oFieldDefnId);
1507 : }
1508 : else
1509 : {
1510 26 : InitFields(oFields, oAttributesFromTileStats);
1511 : }
1512 :
1513 27 : m_nZ = atoi(CPLGetFilename(m_osDirName));
1514 27 : SetMetadataItem("ZOOM_LEVEL", CPLSPrintf("%d", m_nZ));
1515 27 : m_bUseReadDir = CPLTestBool(CPLGetConfigOption(
1516 27 : "MVT_USE_READDIR", (!STARTS_WITH(m_osDirName, "/vsicurl") &&
1517 27 : !STARTS_WITH(m_osDirName, "http://") &&
1518 23 : !STARTS_WITH(m_osDirName, "https://"))
1519 : ? "YES"
1520 : : "NO"));
1521 27 : if (m_bUseReadDir)
1522 : {
1523 22 : m_aosDirContent = VSIReadDirEx(m_osDirName, knMAX_FILES_PER_DIR);
1524 22 : if (m_aosDirContent.Count() >= knMAX_FILES_PER_DIR)
1525 : {
1526 0 : CPLDebug("MVT", "Disabling readdir");
1527 0 : m_aosDirContent.Clear();
1528 0 : m_bUseReadDir = false;
1529 : }
1530 22 : m_aosDirContent = StripDummyEntries(m_aosDirContent);
1531 : }
1532 27 : OGRMVTDirectoryLayer::ResetReading();
1533 :
1534 27 : if (psExtent)
1535 : {
1536 20 : m_sExtent = *psExtent;
1537 : }
1538 :
1539 27 : OGRMVTDirectoryLayer::SetSpatialFilter(nullptr);
1540 :
1541 : // If the metadata contains an empty fields object, this may be a sign
1542 : // that it doesn't know the schema. In that case check if a tile has
1543 : // attributes, and in that case create a json field.
1544 27 : if (!m_bJsonField && oFields.IsValid() && oFields.GetChildren().empty())
1545 : {
1546 14 : m_bJsonField = true;
1547 14 : OpenTileIfNeeded();
1548 14 : m_bJsonField = false;
1549 :
1550 14 : if (m_poCurrentTile)
1551 : {
1552 : OGRLayer *poUnderlyingLayer =
1553 12 : m_poCurrentTile->GetLayerByName(GetName());
1554 : // There is at least the mvt_id field
1555 12 : if (poUnderlyingLayer->GetLayerDefn()->GetFieldCount() > 1)
1556 : {
1557 0 : m_bJsonField = true;
1558 : }
1559 : }
1560 14 : OGRMVTDirectoryLayer::ResetReading();
1561 : }
1562 :
1563 27 : if (m_bJsonField)
1564 : {
1565 2 : OGRFieldDefn oFieldDefn("json", OFTString);
1566 1 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
1567 : }
1568 27 : }
1569 :
1570 : /************************************************************************/
1571 : /* ~OGRMVTDirectoryLayer() */
1572 : /************************************************************************/
1573 :
1574 54 : OGRMVTDirectoryLayer::~OGRMVTDirectoryLayer()
1575 : {
1576 27 : delete m_poCurrentTile;
1577 54 : }
1578 :
1579 : /************************************************************************/
1580 : /* ResetReading() */
1581 : /************************************************************************/
1582 :
1583 146 : void OGRMVTDirectoryLayer::ResetReading()
1584 : {
1585 146 : m_bEOF = false;
1586 146 : m_nXIndex = -1;
1587 146 : m_nYIndex = -1;
1588 146 : delete m_poCurrentTile;
1589 146 : m_poCurrentTile = nullptr;
1590 146 : }
1591 :
1592 : /************************************************************************/
1593 : /* IsBetween() */
1594 : /************************************************************************/
1595 :
1596 153 : static bool IsBetween(int nVal, int nMin, int nMax)
1597 : {
1598 153 : return nVal >= nMin && nVal <= nMax;
1599 : }
1600 :
1601 : /************************************************************************/
1602 : /* ReadNewSubDir() */
1603 : /************************************************************************/
1604 :
1605 123 : void OGRMVTDirectoryLayer::ReadNewSubDir()
1606 : {
1607 123 : delete m_poCurrentTile;
1608 123 : m_poCurrentTile = nullptr;
1609 123 : if (m_bUseReadDir || !m_aosDirContent.empty())
1610 : {
1611 2 : while (
1612 188 : m_nXIndex < m_aosDirContent.Count() &&
1613 76 : (CPLGetValueType(m_aosDirContent[m_nXIndex]) != CPL_VALUE_INTEGER ||
1614 76 : !IsBetween(atoi(m_aosDirContent[m_nXIndex]), m_nFilterMinX,
1615 : m_nFilterMaxX)))
1616 : {
1617 2 : m_nXIndex++;
1618 : }
1619 : }
1620 : else
1621 : {
1622 13 : if (m_nXIndex < m_nFilterMinX)
1623 0 : m_nXIndex = m_nFilterMinX;
1624 13 : else if (m_nXIndex > m_nFilterMaxX)
1625 4 : m_nXIndex = (1 << m_nZ);
1626 : }
1627 136 : if (m_nXIndex < ((m_bUseReadDir || !m_aosDirContent.empty())
1628 123 : ? m_aosDirContent.Count()
1629 13 : : (1 << m_nZ)))
1630 : {
1631 : m_aosSubDirName =
1632 83 : CPLFormFilenameSafe(m_osDirName,
1633 83 : (m_bUseReadDir || !m_aosDirContent.empty())
1634 74 : ? m_aosDirContent[m_nXIndex]
1635 9 : : CPLSPrintf("%d", m_nXIndex),
1636 83 : nullptr);
1637 83 : if (m_bUseReadDir)
1638 : {
1639 : m_aosSubDirContent =
1640 74 : VSIReadDirEx(m_aosSubDirName, knMAX_FILES_PER_DIR);
1641 74 : if (m_aosSubDirContent.Count() >= knMAX_FILES_PER_DIR)
1642 : {
1643 0 : CPLDebug("MVT", "Disabling readdir");
1644 0 : m_aosSubDirContent.Clear();
1645 0 : m_bUseReadDir = false;
1646 : }
1647 74 : m_aosSubDirContent = StripDummyEntries(m_aosSubDirContent);
1648 : }
1649 83 : m_nYIndex = -1;
1650 83 : OpenTileIfNeeded();
1651 : }
1652 : else
1653 : {
1654 40 : m_bEOF = true;
1655 : }
1656 123 : }
1657 :
1658 : /************************************************************************/
1659 : /* OpenTile() */
1660 : /************************************************************************/
1661 :
1662 86 : void OGRMVTDirectoryLayer::OpenTile()
1663 : {
1664 86 : delete m_poCurrentTile;
1665 86 : m_poCurrentTile = nullptr;
1666 86 : if (m_nYIndex < (m_bUseReadDir ? m_aosSubDirContent.Count() : (1 << m_nZ)))
1667 : {
1668 86 : CPLString osFilename = CPLFormFilenameSafe(
1669 : m_aosSubDirName,
1670 86 : m_bUseReadDir ? m_aosSubDirContent[m_nYIndex]
1671 9 : : CPLSPrintf("%d.%s", m_nYIndex,
1672 9 : m_poDS->m_osTileExtension.c_str()),
1673 172 : nullptr);
1674 86 : GDALOpenInfo oOpenInfo(("MVT:" + osFilename).c_str(), GA_ReadOnly);
1675 86 : oOpenInfo.papszOpenOptions = CSLSetNameValue(
1676 : nullptr, "METADATA_FILE",
1677 86 : m_bJsonField ? "" : m_poDS->m_osMetadataMemFilename.c_str());
1678 86 : oOpenInfo.papszOpenOptions = CSLSetNameValue(
1679 : oOpenInfo.papszOpenOptions, "DO_NOT_ERROR_ON_MISSING_TILE", "YES");
1680 86 : m_poCurrentTile =
1681 86 : OGRMVTDataset::Open(&oOpenInfo, /* bRecurseAllowed = */ false);
1682 86 : CSLDestroy(oOpenInfo.papszOpenOptions);
1683 :
1684 9 : m_nTileX = (m_bUseReadDir || !m_aosDirContent.empty())
1685 95 : ? atoi(m_aosDirContent[m_nXIndex])
1686 : : m_nXIndex;
1687 86 : m_nTileY =
1688 86 : m_bUseReadDir ? atoi(m_aosSubDirContent[m_nYIndex]) : m_nYIndex;
1689 86 : m_nFIDBase = (static_cast<GIntBig>(m_nTileX) << m_nZ) | m_nTileY;
1690 : }
1691 86 : }
1692 :
1693 : /************************************************************************/
1694 : /* OpenTileIfNeeded() */
1695 : /************************************************************************/
1696 :
1697 244 : void OGRMVTDirectoryLayer::OpenTileIfNeeded()
1698 : {
1699 244 : if (m_nXIndex < 0)
1700 : {
1701 81 : m_nXIndex = 0;
1702 81 : ReadNewSubDir();
1703 : }
1704 616 : while ((m_poCurrentTile == nullptr && !m_bEOF) ||
1705 244 : (m_poCurrentTile != nullptr &&
1706 197 : m_poCurrentTile->GetLayerByName(GetName()) == nullptr))
1707 : {
1708 128 : m_nYIndex++;
1709 128 : if (m_bUseReadDir)
1710 : {
1711 192 : while (m_nYIndex < m_aosSubDirContent.Count() &&
1712 77 : (CPLGetValueType(
1713 192 : CPLGetBasenameSafe(m_aosSubDirContent[m_nYIndex])
1714 77 : .c_str()) != CPL_VALUE_INTEGER ||
1715 77 : !IsBetween(atoi(m_aosSubDirContent[m_nYIndex]),
1716 : m_nFilterMinY, m_nFilterMaxY)))
1717 : {
1718 0 : m_nYIndex++;
1719 : }
1720 : }
1721 : else
1722 : {
1723 13 : if (m_nYIndex < m_nFilterMinY)
1724 0 : m_nYIndex = m_nFilterMinY;
1725 13 : else if (m_nYIndex > m_nFilterMaxY)
1726 4 : m_nYIndex = (1 << m_nZ);
1727 : }
1728 128 : if (m_nYIndex ==
1729 128 : (m_bUseReadDir ? m_aosSubDirContent.Count() : (1 << m_nZ)))
1730 : {
1731 42 : m_nXIndex++;
1732 42 : ReadNewSubDir();
1733 : }
1734 : else
1735 : {
1736 86 : OpenTile();
1737 : }
1738 : }
1739 244 : }
1740 :
1741 : /************************************************************************/
1742 : /* GetFeatureCount() */
1743 : /************************************************************************/
1744 :
1745 16 : GIntBig OGRMVTDirectoryLayer::GetFeatureCount(int bForce)
1746 : {
1747 16 : if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
1748 : {
1749 10 : GIntBig nFeatureCount = 0;
1750 10 : ResetReading();
1751 : while (true)
1752 : {
1753 21 : OpenTileIfNeeded();
1754 21 : if (m_poCurrentTile == nullptr)
1755 10 : break;
1756 : OGRLayer *poUnderlyingLayer =
1757 11 : m_poCurrentTile->GetLayerByName(GetName());
1758 11 : nFeatureCount += poUnderlyingLayer->GetFeatureCount(bForce);
1759 11 : delete m_poCurrentTile;
1760 11 : m_poCurrentTile = nullptr;
1761 11 : }
1762 10 : ResetReading();
1763 10 : return nFeatureCount;
1764 : }
1765 6 : return OGRLayer::GetFeatureCount(bForce);
1766 : }
1767 :
1768 : /************************************************************************/
1769 : /* ISetSpatialFilter() */
1770 : /************************************************************************/
1771 :
1772 51 : OGRErr OGRMVTDirectoryLayer::ISetSpatialFilter(int iGeomField,
1773 : const OGRGeometry *poGeomIn)
1774 : {
1775 51 : OGRLayer::ISetSpatialFilter(iGeomField, poGeomIn);
1776 :
1777 51 : OGREnvelope sEnvelope;
1778 51 : if (m_poFilterGeom != nullptr)
1779 7 : sEnvelope = m_sFilterEnvelope;
1780 51 : if (m_sExtent.IsInit())
1781 : {
1782 44 : if (sEnvelope.IsInit())
1783 7 : sEnvelope.Intersect(m_sExtent);
1784 : else
1785 37 : sEnvelope = m_sExtent;
1786 : }
1787 :
1788 90 : if (sEnvelope.IsInit() && sEnvelope.MinX >= -10 * m_poDS->GetTileDim0() &&
1789 39 : sEnvelope.MinY >= -10 * m_poDS->GetTileDim0() &&
1790 39 : sEnvelope.MaxX <=
1791 129 : 10 * m_poDS->GetTileDim0() * m_poDS->GetTileMatrixWidth0() &&
1792 39 : sEnvelope.MaxY <=
1793 39 : 10 * m_poDS->GetTileDim0() * m_poDS->GetTileMatrixHeight0())
1794 : {
1795 39 : const double dfTileDim = m_poDS->GetTileDim0() / (1 << m_nZ);
1796 39 : m_nFilterMinX = std::max(
1797 78 : 0, static_cast<int>(floor(
1798 39 : (sEnvelope.MinX - m_poDS->GetTopXOrigin()) / dfTileDim)));
1799 39 : m_nFilterMinY = std::max(
1800 78 : 0, static_cast<int>(floor(
1801 39 : (m_poDS->GetTopYOrigin() - sEnvelope.MaxY) / dfTileDim)));
1802 39 : m_nFilterMaxX = std::min(
1803 78 : static_cast<int>(
1804 78 : ceil((sEnvelope.MaxX - m_poDS->GetTopXOrigin()) / dfTileDim)),
1805 39 : static_cast<int>(std::min<int64_t>(
1806 78 : INT_MAX, (static_cast<int64_t>(1) << m_nZ) *
1807 39 : m_poDS->GetTileMatrixWidth0() -
1808 78 : 1)));
1809 39 : m_nFilterMaxY = std::min(
1810 78 : static_cast<int>(
1811 78 : ceil((m_poDS->GetTopYOrigin() - sEnvelope.MinY) / dfTileDim)),
1812 39 : static_cast<int>(std::min<int64_t>(
1813 78 : INT_MAX, (static_cast<int64_t>(1) << m_nZ) *
1814 78 : m_poDS->GetTileMatrixHeight0() -
1815 78 : 1)));
1816 : }
1817 : else
1818 : {
1819 12 : m_nFilterMinX = 0;
1820 12 : m_nFilterMinY = 0;
1821 12 : m_nFilterMaxX = static_cast<int>(
1822 24 : std::min<int64_t>(INT_MAX, (static_cast<int64_t>(1) << m_nZ) *
1823 12 : m_poDS->GetTileMatrixWidth0() -
1824 12 : 1));
1825 12 : m_nFilterMaxY = static_cast<int>(
1826 24 : std::min<int64_t>(INT_MAX, (static_cast<int64_t>(1) << m_nZ) *
1827 24 : m_poDS->GetTileMatrixHeight0() -
1828 12 : 1));
1829 : }
1830 :
1831 51 : return OGRERR_NONE;
1832 : }
1833 :
1834 : /************************************************************************/
1835 : /* TestCapability() */
1836 : /************************************************************************/
1837 :
1838 36 : int OGRMVTDirectoryLayer::TestCapability(const char *pszCap) const
1839 : {
1840 36 : if (EQUAL(pszCap, OLCFastGetExtent))
1841 : {
1842 2 : return TRUE;
1843 : }
1844 34 : return OGRMVTLayerBase::TestCapability(pszCap);
1845 : }
1846 :
1847 : /************************************************************************/
1848 : /* IGetExtent() */
1849 : /************************************************************************/
1850 :
1851 4 : OGRErr OGRMVTDirectoryLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
1852 : bool bForce)
1853 : {
1854 4 : if (m_sExtent.IsInit())
1855 : {
1856 4 : *psExtent = m_sExtent;
1857 4 : return OGRERR_NONE;
1858 : }
1859 0 : return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
1860 : }
1861 :
1862 : /************************************************************************/
1863 : /* CreateFeatureFrom() */
1864 : /************************************************************************/
1865 :
1866 67 : OGRFeature *OGRMVTDirectoryLayer::CreateFeatureFrom(OGRFeature *poSrcFeature)
1867 : {
1868 :
1869 67 : return OGRMVTCreateFeatureFrom(poSrcFeature, m_poFeatureDefn, m_bJsonField,
1870 134 : GetSpatialRef());
1871 : }
1872 :
1873 : /************************************************************************/
1874 : /* GetNextRawFeature() */
1875 : /************************************************************************/
1876 :
1877 126 : OGRFeature *OGRMVTDirectoryLayer::GetNextRawFeature()
1878 : {
1879 : while (true)
1880 : {
1881 126 : OpenTileIfNeeded();
1882 126 : if (m_poCurrentTile == nullptr)
1883 31 : return nullptr;
1884 : OGRLayer *poUnderlyingLayer =
1885 95 : m_poCurrentTile->GetLayerByName(GetName());
1886 95 : OGRFeature *poUnderlyingFeature = poUnderlyingLayer->GetNextFeature();
1887 95 : if (poUnderlyingFeature != nullptr)
1888 : {
1889 65 : OGRFeature *poFeature = CreateFeatureFrom(poUnderlyingFeature);
1890 130 : poFeature->SetFID(m_nFIDBase +
1891 65 : (poUnderlyingFeature->GetFID() << (2 * m_nZ)));
1892 65 : if (m_bAddTileFields)
1893 : {
1894 4 : poFeature->SetField("tile_z", m_nZ);
1895 4 : poFeature->SetField("tile_x", m_nTileX);
1896 4 : poFeature->SetField("tile_y", m_nTileY);
1897 : }
1898 65 : delete poUnderlyingFeature;
1899 65 : return poFeature;
1900 : }
1901 : else
1902 : {
1903 30 : delete m_poCurrentTile;
1904 30 : m_poCurrentTile = nullptr;
1905 : }
1906 30 : }
1907 : }
1908 :
1909 : /************************************************************************/
1910 : /* GetFeature() */
1911 : /************************************************************************/
1912 :
1913 5 : OGRFeature *OGRMVTDirectoryLayer::GetFeature(GIntBig nFID)
1914 : {
1915 5 : const int nX = static_cast<int>(nFID & ((1 << m_nZ) - 1));
1916 5 : const int nY = static_cast<int>((nFID >> m_nZ) & ((1 << m_nZ) - 1));
1917 5 : const GIntBig nTileFID = nFID >> (2 * m_nZ);
1918 15 : const CPLString osFilename = CPLFormFilenameSafe(
1919 5 : CPLFormFilenameSafe(m_osDirName, CPLSPrintf("%d", nX), nullptr).c_str(),
1920 15 : CPLSPrintf("%d.%s", nY, m_poDS->m_osTileExtension.c_str()), nullptr);
1921 5 : GDALOpenInfo oOpenInfo(("MVT:" + osFilename).c_str(), GA_ReadOnly);
1922 5 : oOpenInfo.papszOpenOptions = CSLSetNameValue(
1923 : nullptr, "METADATA_FILE",
1924 5 : m_bJsonField ? "" : m_poDS->m_osMetadataMemFilename.c_str());
1925 5 : oOpenInfo.papszOpenOptions = CSLSetNameValue(
1926 : oOpenInfo.papszOpenOptions, "DO_NOT_ERROR_ON_MISSING_TILE", "YES");
1927 : GDALDataset *poTile =
1928 5 : OGRMVTDataset::Open(&oOpenInfo, /* bRecurseAllowed = */ false);
1929 5 : CSLDestroy(oOpenInfo.papszOpenOptions);
1930 5 : OGRFeature *poFeature = nullptr;
1931 5 : if (poTile)
1932 : {
1933 5 : OGRLayer *poLayer = poTile->GetLayerByName(GetName());
1934 5 : if (poLayer)
1935 : {
1936 5 : OGRFeature *poUnderlyingFeature = poLayer->GetFeature(nTileFID);
1937 5 : if (poUnderlyingFeature)
1938 : {
1939 2 : poFeature = CreateFeatureFrom(poUnderlyingFeature);
1940 2 : poFeature->SetFID(nFID);
1941 : }
1942 5 : delete poUnderlyingFeature;
1943 : }
1944 : }
1945 5 : delete poTile;
1946 10 : return poFeature;
1947 : }
1948 :
1949 : /************************************************************************/
1950 : /* GetDataset() */
1951 : /************************************************************************/
1952 :
1953 1 : GDALDataset *OGRMVTDirectoryLayer::GetDataset()
1954 : {
1955 1 : return m_poDS;
1956 : }
1957 :
1958 : /************************************************************************/
1959 : /* OGRMVTDataset() */
1960 : /************************************************************************/
1961 :
1962 1001 : OGRMVTDataset::OGRMVTDataset(GByte *pabyData)
1963 1001 : : m_pabyData(pabyData), m_poSRS(new OGRSpatialReference())
1964 : {
1965 1001 : m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1966 :
1967 1001 : m_bClip = CPLTestBool(CPLGetConfigOption("OGR_MVT_CLIP", "YES"));
1968 :
1969 : // Default WebMercator tiling scheme
1970 1001 : InitWebMercatorTilingScheme(m_poSRS, m_dfTopXOrigin, m_dfTopYOrigin,
1971 1001 : m_dfTileDim0);
1972 1001 : }
1973 :
1974 : /************************************************************************/
1975 : /* ~OGRMVTDataset() */
1976 : /************************************************************************/
1977 :
1978 2002 : OGRMVTDataset::~OGRMVTDataset()
1979 : {
1980 1001 : VSIFree(m_pabyData);
1981 1001 : if (!m_osMetadataMemFilename.empty())
1982 18 : VSIUnlink(m_osMetadataMemFilename);
1983 1001 : if (m_poSRS)
1984 998 : m_poSRS->Release();
1985 2002 : }
1986 :
1987 : /************************************************************************/
1988 : /* GetLayer() */
1989 : /************************************************************************/
1990 :
1991 1764 : const OGRLayer *OGRMVTDataset::GetLayer(int iLayer) const
1992 :
1993 : {
1994 1764 : if (iLayer < 0 || iLayer >= GetLayerCount())
1995 4 : return nullptr;
1996 1760 : return m_apoLayers[iLayer].get();
1997 : }
1998 :
1999 : /************************************************************************/
2000 : /* Identify() */
2001 : /************************************************************************/
2002 :
2003 55735 : static int OGRMVTDriverIdentify(GDALOpenInfo *poOpenInfo)
2004 :
2005 : {
2006 55735 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "MVT:"))
2007 1833 : return TRUE;
2008 :
2009 53902 : if (STARTS_WITH(poOpenInfo->pszFilename, "/vsicurl"))
2010 : {
2011 1 : if (CPLGetValueType(CPLGetFilename(poOpenInfo->pszFilename)) ==
2012 : CPL_VALUE_INTEGER)
2013 : {
2014 0 : return TRUE;
2015 : }
2016 : }
2017 :
2018 53902 : if (poOpenInfo->bIsDirectory)
2019 : {
2020 724 : if (CPLGetValueType(CPLGetFilename(poOpenInfo->pszFilename)) ==
2021 : CPL_VALUE_INTEGER)
2022 : {
2023 : VSIStatBufL sStat;
2024 72 : CPLString osMetadataFile(CPLFormFilenameSafe(
2025 36 : CPLGetPathSafe(poOpenInfo->pszFilename).c_str(),
2026 36 : "metadata.json", nullptr));
2027 72 : const char *pszMetadataFile = CSLFetchNameValue(
2028 36 : poOpenInfo->papszOpenOptions, "METADATA_FILE");
2029 36 : if (pszMetadataFile)
2030 : {
2031 6 : osMetadataFile = pszMetadataFile;
2032 : }
2033 66 : if (!osMetadataFile.empty() &&
2034 30 : (STARTS_WITH(osMetadataFile, "http://") ||
2035 30 : STARTS_WITH(osMetadataFile, "https://") ||
2036 30 : VSIStatL(osMetadataFile, &sStat) == 0))
2037 : {
2038 26 : return TRUE;
2039 : }
2040 10 : if (pszMetadataFile == nullptr)
2041 : {
2042 : // tileserver-gl metadata file:
2043 : // If opening /path/to/foo/0, try looking for /path/to/foo.json
2044 4 : CPLString osParentDir(CPLGetPathSafe(poOpenInfo->pszFilename));
2045 : osMetadataFile =
2046 8 : CPLFormFilenameSafe(CPLGetPathSafe(osParentDir).c_str(),
2047 4 : CPLGetFilename(osParentDir), "json");
2048 4 : if (VSIStatL(osMetadataFile, &sStat) == 0)
2049 : {
2050 2 : return TRUE;
2051 : }
2052 : }
2053 :
2054 : // At least 3 files, to include the dummy . and ..
2055 : const CPLStringList aosDirContent = StripDummyEntries(
2056 8 : CPLStringList(VSIReadDirEx(poOpenInfo->pszFilename, 3)));
2057 16 : if (!aosDirContent.empty() &&
2058 8 : CPLGetValueType(aosDirContent[0]) == CPL_VALUE_INTEGER)
2059 : {
2060 : const std::string osSubDir = CPLFormFilenameSafe(
2061 8 : poOpenInfo->pszFilename, aosDirContent[0], nullptr);
2062 : // At least 3 files, to include the dummy . and ..
2063 : const CPLStringList aosSubDirContent = StripDummyEntries(
2064 8 : CPLStringList(VSIReadDirEx(osSubDir.c_str(), 10)));
2065 : const std::string osTileExtension(CSLFetchNameValueDef(
2066 8 : poOpenInfo->papszOpenOptions, "TILE_EXTENSION", "pbf"));
2067 8 : for (int i = 0; i < aosSubDirContent.Count(); i++)
2068 : {
2069 8 : if (CPLGetValueType(
2070 16 : CPLGetBasenameSafe(aosSubDirContent[i]).c_str()) ==
2071 : CPL_VALUE_INTEGER)
2072 : {
2073 : const std::string osExtension(
2074 8 : CPLGetExtensionSafe(aosSubDirContent[i]));
2075 8 : if (EQUAL(osExtension.c_str(),
2076 8 : osTileExtension.c_str()) ||
2077 0 : EQUAL(osExtension.c_str(), "mvt"))
2078 : {
2079 8 : return TRUE;
2080 : }
2081 : }
2082 : }
2083 : }
2084 : }
2085 688 : return FALSE;
2086 : }
2087 :
2088 53178 : if (poOpenInfo->nHeaderBytes <= 2)
2089 50324 : return FALSE;
2090 :
2091 : // GZip header ?
2092 2854 : if (poOpenInfo->pabyHeader[0] == 0x1F && poOpenInfo->pabyHeader[1] == 0x8B)
2093 : {
2094 : // Prevent recursion
2095 36 : if (STARTS_WITH(poOpenInfo->pszFilename, "/vsigzip/"))
2096 : {
2097 0 : return FALSE;
2098 : }
2099 : CPLConfigOptionSetter oSetter("CPL_VSIL_GZIP_WRITE_PROPERTIES", "NO",
2100 72 : false);
2101 : GDALOpenInfo oOpenInfo(
2102 72 : (CPLString("/vsigzip/") + poOpenInfo->pszFilename).c_str(),
2103 72 : GA_ReadOnly);
2104 36 : return OGRMVTDriverIdentify(&oOpenInfo);
2105 : }
2106 :
2107 : // The GPB macros assume that the buffer is nul terminated,
2108 : // which is the case
2109 2818 : const GByte *pabyData = reinterpret_cast<GByte *>(poOpenInfo->pabyHeader);
2110 2818 : const GByte *const pabyDataStart = pabyData;
2111 : const GByte *pabyLayerStart;
2112 2818 : const GByte *const pabyDataLimit = pabyData + poOpenInfo->nHeaderBytes;
2113 2818 : const GByte *pabyLayerEnd = pabyDataLimit;
2114 2818 : int nKey = 0;
2115 2818 : unsigned int nLayerLength = 0;
2116 2818 : bool bLayerNameFound = false;
2117 2818 : bool bKeyFound = false;
2118 2818 : bool bFeatureFound = false;
2119 2818 : bool bVersionFound = false;
2120 :
2121 : try
2122 : {
2123 2818 : READ_FIELD_KEY(nKey);
2124 2816 : if (nKey != MAKE_KEY(knLAYER, WT_DATA))
2125 2767 : return FALSE;
2126 49 : READ_VARUINT32(pabyData, pabyDataLimit, nLayerLength);
2127 49 : pabyLayerStart = pabyData;
2128 :
2129 : // Sanity check on layer length
2130 49 : if (nLayerLength < static_cast<unsigned>(poOpenInfo->nHeaderBytes -
2131 49 : (pabyData - pabyDataStart)))
2132 : {
2133 7 : if (pabyData[nLayerLength] != MAKE_KEY(knLAYER, WT_DATA))
2134 1 : return FALSE;
2135 6 : pabyLayerEnd = pabyData + nLayerLength;
2136 : }
2137 42 : else if (nLayerLength > 10 * 1024 * 1024)
2138 : {
2139 0 : return FALSE;
2140 : }
2141 :
2142 : // Quick scan on partial layer content to see if it seems to conform to
2143 : // the proto
2144 544 : while (pabyData < pabyLayerEnd)
2145 : {
2146 498 : READ_VARUINT32(pabyData, pabyLayerEnd, nKey);
2147 498 : auto nFieldNumber = GET_FIELDNUMBER(nKey);
2148 498 : auto nWireType = GET_WIRETYPE(nKey);
2149 498 : if (nFieldNumber == knLAYER_NAME)
2150 : {
2151 48 : if (nWireType != WT_DATA)
2152 : {
2153 0 : CPLDebug("MVT", "Invalid wire type for layer_name field");
2154 : }
2155 48 : char *pszLayerName = nullptr;
2156 48 : unsigned int nTextSize = 0;
2157 48 : READ_TEXT_WITH_SIZE(pabyData, pabyLayerEnd, pszLayerName,
2158 : nTextSize);
2159 48 : if (nTextSize == 0 || !CPLIsUTF8(pszLayerName, nTextSize))
2160 : {
2161 0 : CPLFree(pszLayerName);
2162 0 : CPLDebug("MVT", "Protobuf error: line %d", __LINE__);
2163 0 : return FALSE;
2164 : }
2165 48 : CPLFree(pszLayerName);
2166 48 : bLayerNameFound = true;
2167 : }
2168 450 : else if (nFieldNumber == knLAYER_FEATURES)
2169 : {
2170 52 : if (nWireType != WT_DATA)
2171 : {
2172 0 : CPLDebug("MVT",
2173 : "Invalid wire type for layer_features field");
2174 : }
2175 52 : unsigned int nFeatureLength = 0;
2176 52 : unsigned int nGeomType = 0;
2177 52 : READ_VARUINT32(pabyData, pabyLayerEnd, nFeatureLength);
2178 52 : if (nFeatureLength > nLayerLength - (pabyData - pabyLayerStart))
2179 : {
2180 0 : CPLDebug("MVT", "Protobuf error: line %d", __LINE__);
2181 0 : return FALSE;
2182 : }
2183 52 : bFeatureFound = true;
2184 :
2185 52 : const GByte *const pabyDataFeatureStart = pabyData;
2186 : const GByte *const pabyDataFeatureEnd =
2187 : pabyDataStart +
2188 104 : std::min(static_cast<int>(pabyData + nFeatureLength -
2189 : pabyDataStart),
2190 52 : poOpenInfo->nHeaderBytes);
2191 168 : while (pabyData < pabyDataFeatureEnd)
2192 : {
2193 118 : READ_VARUINT32(pabyData, pabyDataFeatureEnd, nKey);
2194 118 : nFieldNumber = GET_FIELDNUMBER(nKey);
2195 118 : nWireType = GET_WIRETYPE(nKey);
2196 118 : if (nFieldNumber == knFEATURE_TYPE)
2197 : {
2198 48 : if (nWireType != WT_VARINT)
2199 : {
2200 0 : CPLDebug(
2201 : "MVT",
2202 : "Invalid wire type for feature_type field");
2203 0 : return FALSE;
2204 : }
2205 48 : READ_VARUINT32(pabyData, pabyDataFeatureEnd, nGeomType);
2206 48 : if (nGeomType > knGEOM_TYPE_POLYGON)
2207 : {
2208 0 : CPLDebug("MVT", "Protobuf error: line %d",
2209 : __LINE__);
2210 0 : return FALSE;
2211 : }
2212 : }
2213 70 : else if (nFieldNumber == knFEATURE_TAGS)
2214 : {
2215 18 : if (nWireType != WT_DATA)
2216 : {
2217 0 : CPLDebug(
2218 : "MVT",
2219 : "Invalid wire type for feature_tags field");
2220 0 : return FALSE;
2221 : }
2222 18 : unsigned int nTagsSize = 0;
2223 18 : READ_VARUINT32(pabyData, pabyDataFeatureEnd, nTagsSize);
2224 18 : if (nTagsSize == 0 ||
2225 18 : nTagsSize > nFeatureLength -
2226 18 : (pabyData - pabyDataFeatureStart))
2227 : {
2228 0 : CPLDebug("MVT", "Protobuf error: line %d",
2229 : __LINE__);
2230 0 : return FALSE;
2231 : }
2232 : const GByte *const pabyDataTagsEnd =
2233 : pabyDataStart +
2234 36 : std::min(static_cast<int>(pabyData + nTagsSize -
2235 : pabyDataStart),
2236 18 : poOpenInfo->nHeaderBytes);
2237 400 : while (pabyData < pabyDataTagsEnd)
2238 : {
2239 382 : unsigned int nKeyIdx = 0;
2240 382 : unsigned int nValIdx = 0;
2241 382 : READ_VARUINT32(pabyData, pabyDataTagsEnd, nKeyIdx);
2242 382 : READ_VARUINT32(pabyData, pabyDataTagsEnd, nValIdx);
2243 382 : if (nKeyIdx > 10 * 1024 * 1024 ||
2244 : nValIdx > 10 * 1024 * 1024)
2245 : {
2246 0 : CPLDebug("MVT", "Protobuf error: line %d",
2247 : __LINE__);
2248 0 : return FALSE;
2249 : }
2250 : }
2251 : }
2252 52 : else if (nFieldNumber == knFEATURE_GEOMETRY &&
2253 : nWireType != WT_DATA)
2254 : {
2255 0 : CPLDebug(
2256 : "MVT",
2257 : "Invalid wire type for feature_geometry field");
2258 0 : return FALSE;
2259 : }
2260 52 : else if (nKey == MAKE_KEY(knFEATURE_GEOMETRY, WT_DATA) &&
2261 48 : nGeomType >= knGEOM_TYPE_POINT &&
2262 : nGeomType <= knGEOM_TYPE_POLYGON)
2263 : {
2264 48 : unsigned int nGeometrySize = 0;
2265 48 : READ_VARUINT32(pabyData, pabyDataFeatureEnd,
2266 : nGeometrySize);
2267 48 : if (nGeometrySize == 0 ||
2268 48 : nGeometrySize >
2269 48 : nFeatureLength -
2270 48 : (pabyData - pabyDataFeatureStart))
2271 : {
2272 0 : CPLDebug("MVT", "Protobuf error: line %d",
2273 : __LINE__);
2274 0 : return FALSE;
2275 : }
2276 : const GByte *const pabyDataGeometryEnd =
2277 : pabyDataStart +
2278 96 : std::min(static_cast<int>(pabyData + nGeometrySize -
2279 : pabyDataStart),
2280 48 : poOpenInfo->nHeaderBytes);
2281 :
2282 48 : if (nGeomType == knGEOM_TYPE_POINT)
2283 : {
2284 14 : unsigned int nCmdCountCombined = 0;
2285 : unsigned int nCount;
2286 14 : READ_VARUINT32(pabyData, pabyDataGeometryEnd,
2287 : nCmdCountCombined);
2288 14 : nCount = GetCmdCount(nCmdCountCombined);
2289 28 : if (GetCmdId(nCmdCountCombined) != knCMD_MOVETO ||
2290 28 : nCount == 0 || nCount > 10 * 1024 * 1024)
2291 : {
2292 0 : CPLDebug("MVT", "Protobuf error: line %d",
2293 : __LINE__);
2294 0 : return FALSE;
2295 : }
2296 46 : for (unsigned i = 0; i < 2 * nCount; i++)
2297 : {
2298 32 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
2299 : }
2300 : }
2301 34 : else if (nGeomType == knGEOM_TYPE_LINESTRING)
2302 : {
2303 56 : while (pabyData < pabyDataGeometryEnd)
2304 : {
2305 32 : unsigned int nCmdCountCombined = 0;
2306 : unsigned int nLineToCount;
2307 : // Should be a moveto
2308 32 : READ_VARUINT32(pabyData, pabyDataGeometryEnd,
2309 : nCmdCountCombined);
2310 32 : if (GetCmdId(nCmdCountCombined) !=
2311 64 : knCMD_MOVETO ||
2312 32 : GetCmdCount(nCmdCountCombined) != 1)
2313 : {
2314 0 : CPLDebug("MVT", "Protobuf error: line %d",
2315 : __LINE__);
2316 0 : return FALSE;
2317 : }
2318 32 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
2319 32 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
2320 32 : READ_VARUINT32(pabyData, pabyDataGeometryEnd,
2321 : nCmdCountCombined);
2322 32 : if (GetCmdId(nCmdCountCombined) != knCMD_LINETO)
2323 : {
2324 0 : CPLDebug("MVT", "Protobuf error: line %d",
2325 : __LINE__);
2326 0 : return FALSE;
2327 : }
2328 32 : nLineToCount = GetCmdCount(nCmdCountCombined);
2329 96 : for (unsigned i = 0; i < 2 * nLineToCount; i++)
2330 : {
2331 64 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
2332 : }
2333 : }
2334 : }
2335 : else /* if( nGeomType == knGEOM_TYPE_POLYGON ) */
2336 : {
2337 382 : while (pabyData < pabyDataGeometryEnd)
2338 : {
2339 374 : unsigned int nCmdCountCombined = 0;
2340 : unsigned int nLineToCount;
2341 : // Should be a moveto
2342 374 : READ_VARUINT32(pabyData, pabyDataGeometryEnd,
2343 : nCmdCountCombined);
2344 374 : if (GetCmdId(nCmdCountCombined) !=
2345 748 : knCMD_MOVETO ||
2346 374 : GetCmdCount(nCmdCountCombined) != 1)
2347 : {
2348 0 : CPLDebug("MVT", "Protobuf error: line %d",
2349 : __LINE__);
2350 0 : return FALSE;
2351 : }
2352 374 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
2353 374 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
2354 374 : READ_VARUINT32(pabyData, pabyDataGeometryEnd,
2355 : nCmdCountCombined);
2356 374 : if (GetCmdId(nCmdCountCombined) != knCMD_LINETO)
2357 : {
2358 0 : CPLDebug("MVT", "Protobuf error: line %d",
2359 : __LINE__);
2360 0 : return FALSE;
2361 : }
2362 374 : nLineToCount = GetCmdCount(nCmdCountCombined);
2363 7020 : for (unsigned i = 0; i < 2 * nLineToCount; i++)
2364 : {
2365 6648 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
2366 : }
2367 : // Should be a closepath
2368 372 : READ_VARUINT32(pabyData, pabyDataGeometryEnd,
2369 : nCmdCountCombined);
2370 372 : if (GetCmdId(nCmdCountCombined) !=
2371 744 : knCMD_CLOSEPATH ||
2372 372 : GetCmdCount(nCmdCountCombined) != 1)
2373 : {
2374 0 : CPLDebug("MVT", "Protobuf error: line %d",
2375 : __LINE__);
2376 0 : return FALSE;
2377 : }
2378 : }
2379 : }
2380 :
2381 46 : pabyData = pabyDataGeometryEnd;
2382 : }
2383 : else
2384 : {
2385 4 : SKIP_UNKNOWN_FIELD(pabyData, pabyDataFeatureEnd, FALSE);
2386 : }
2387 : }
2388 :
2389 50 : pabyData = pabyDataFeatureEnd;
2390 : }
2391 398 : else if (nFieldNumber == knLAYER_KEYS)
2392 : {
2393 152 : if (nWireType != WT_DATA)
2394 : {
2395 0 : CPLDebug("MVT", "Invalid wire type for keys field");
2396 0 : return FALSE;
2397 : }
2398 152 : char *pszKey = nullptr;
2399 152 : unsigned int nTextSize = 0;
2400 152 : READ_TEXT_WITH_SIZE(pabyData, pabyLayerEnd, pszKey, nTextSize);
2401 152 : if (!CPLIsUTF8(pszKey, nTextSize))
2402 : {
2403 0 : CPLDebug("MVT", "Protobuf error: line %d", __LINE__);
2404 0 : CPLFree(pszKey);
2405 0 : return FALSE;
2406 : }
2407 152 : CPLFree(pszKey);
2408 152 : bKeyFound = true;
2409 : }
2410 246 : else if (nFieldNumber == knLAYER_VALUES)
2411 : {
2412 156 : if (nWireType != WT_DATA)
2413 : {
2414 0 : CPLDebug("MVT", "Invalid wire type for values field");
2415 0 : return FALSE;
2416 : }
2417 156 : unsigned int nValueLength = 0;
2418 156 : READ_VARUINT32(pabyData, pabyLayerEnd, nValueLength);
2419 156 : if (nValueLength == 0 ||
2420 156 : nValueLength > nLayerLength - (pabyData - pabyLayerStart))
2421 : {
2422 0 : CPLDebug("MVT", "Protobuf error: line %d", __LINE__);
2423 0 : return FALSE;
2424 : }
2425 156 : pabyData += nValueLength;
2426 : }
2427 90 : else if (GET_FIELDNUMBER(nKey) == knLAYER_EXTENT &&
2428 42 : GET_WIRETYPE(nKey) != WT_VARINT)
2429 : {
2430 0 : CPLDebug("MVT", "Invalid wire type for extent field");
2431 0 : return FALSE;
2432 : }
2433 : #if 0
2434 : // The check on extent is too fragile. Values of 65536 can be found
2435 : else if( nKey == MAKE_KEY(knLAYER_EXTENT, WT_VARINT) )
2436 : {
2437 : unsigned int nExtent = 0;
2438 : READ_VARUINT32(pabyData, pabyLayerEnd, nExtent);
2439 : if( nExtent < 128 || nExtent > 16834 )
2440 : {
2441 : CPLDebug("MVT", "Invalid extent: %u", nExtent);
2442 : return FALSE;
2443 : }
2444 : }
2445 : #endif
2446 90 : else if (nFieldNumber == knLAYER_VERSION)
2447 : {
2448 46 : if (nWireType != WT_VARINT)
2449 : {
2450 0 : CPLDebug("MVT", "Invalid wire type for version field");
2451 0 : return FALSE;
2452 : }
2453 46 : unsigned int nVersion = 0;
2454 46 : READ_VARUINT32(pabyData, pabyLayerEnd, nVersion);
2455 46 : if (nVersion != 1 && nVersion != 2)
2456 : {
2457 0 : CPLDebug("MVT", "Invalid version: %u", nVersion);
2458 0 : return FALSE;
2459 : }
2460 46 : bVersionFound = true;
2461 : }
2462 : else
2463 : {
2464 44 : SKIP_UNKNOWN_FIELD(pabyData, pabyLayerEnd, FALSE);
2465 : }
2466 : }
2467 : }
2468 4 : catch (const GPBException &)
2469 : {
2470 : }
2471 :
2472 50 : return bLayerNameFound && (bKeyFound || bFeatureFound || bVersionFound);
2473 : }
2474 :
2475 : /************************************************************************/
2476 : /* LongLatToSphericalMercator() */
2477 : /************************************************************************/
2478 :
2479 30 : static void LongLatToSphericalMercator(double *x, double *y)
2480 : {
2481 30 : double X = kmSPHERICAL_RADIUS * (*x) / 180 * M_PI;
2482 : double Y =
2483 30 : kmSPHERICAL_RADIUS * log(tan(M_PI / 4 + 0.5 * (*y) / 180 * M_PI));
2484 30 : *x = X;
2485 30 : *y = Y;
2486 30 : }
2487 :
2488 : /************************************************************************/
2489 : /* LoadMetadata() */
2490 : /************************************************************************/
2491 :
2492 930 : static bool LoadMetadata(const CPLString &osMetadataFile,
2493 : const CPLString &osMetadataContent,
2494 : CPLJSONArray &oVectorLayers,
2495 : CPLJSONArray &oTileStatLayers, CPLJSONObject &oBounds,
2496 : OGRSpatialReference *poSRS, double &dfTopX,
2497 : double &dfTopY, double &dfTileDim0,
2498 : int &nTileMatrixWidth0, int &nTileMatrixHeight0,
2499 : const CPLString &osMetadataMemFilename)
2500 :
2501 : {
2502 1860 : CPLJSONDocument oDoc;
2503 :
2504 : bool bLoadOK;
2505 930 : if (!osMetadataContent.empty())
2506 : {
2507 3 : bLoadOK = oDoc.LoadMemory(osMetadataContent);
2508 : }
2509 1854 : else if (STARTS_WITH(osMetadataFile, "http://") ||
2510 927 : STARTS_WITH(osMetadataFile, "https://"))
2511 : {
2512 0 : bLoadOK = oDoc.LoadUrl(osMetadataFile, nullptr);
2513 : }
2514 : else
2515 : {
2516 927 : bLoadOK = oDoc.Load(osMetadataFile);
2517 : }
2518 930 : if (!bLoadOK)
2519 2 : return false;
2520 :
2521 2784 : const CPLJSONObject oCrs(oDoc.GetRoot().GetObj("crs"));
2522 : const CPLJSONObject oTopX(
2523 2784 : oDoc.GetRoot().GetObj("tile_origin_upper_left_x"));
2524 : const CPLJSONObject oTopY(
2525 2784 : oDoc.GetRoot().GetObj("tile_origin_upper_left_y"));
2526 : const CPLJSONObject oTileDim0(
2527 2784 : oDoc.GetRoot().GetObj("tile_dimension_zoom_0"));
2528 928 : nTileMatrixWidth0 = 1;
2529 928 : nTileMatrixHeight0 = 1;
2530 934 : if (oCrs.IsValid() && oTopX.IsValid() && oTopY.IsValid() &&
2531 6 : oTileDim0.IsValid())
2532 : {
2533 6 : poSRS->SetFromUserInput(oCrs.ToString().c_str());
2534 6 : dfTopX = oTopX.ToDouble();
2535 6 : dfTopY = oTopY.ToDouble();
2536 6 : dfTileDim0 = oTileDim0.ToDouble();
2537 : const CPLJSONObject oTMWidth0(
2538 18 : oDoc.GetRoot().GetObj("tile_matrix_width_zoom_0"));
2539 6 : if (oTMWidth0.GetType() == CPLJSONObject::Type::Integer)
2540 6 : nTileMatrixWidth0 = std::max(1, oTMWidth0.ToInteger());
2541 :
2542 : const CPLJSONObject oTMHeight0(
2543 18 : oDoc.GetRoot().GetObj("tile_matrix_height_zoom_0"));
2544 6 : if (oTMHeight0.GetType() == CPLJSONObject::Type::Integer)
2545 6 : nTileMatrixHeight0 = std::max(1, oTMHeight0.ToInteger());
2546 :
2547 : // Assumes WorldCRS84Quad with 2 tiles in width
2548 : // cf https://github.com/OSGeo/gdal/issues/11749
2549 6 : if (!oTMWidth0.IsValid() && dfTopX == -180 && dfTileDim0 == 180)
2550 0 : nTileMatrixWidth0 = 2;
2551 : }
2552 :
2553 928 : oVectorLayers.Deinit();
2554 928 : oTileStatLayers.Deinit();
2555 :
2556 2784 : CPLJSONObject oJson = oDoc.GetRoot().GetObj("json");
2557 928 : if (!(oJson.IsValid() && oJson.GetType() == CPLJSONObject::Type::String))
2558 : {
2559 534 : oVectorLayers = oDoc.GetRoot().GetArray("vector_layers");
2560 :
2561 534 : oTileStatLayers = oDoc.GetRoot().GetArray("tilestats/layers");
2562 : }
2563 : else
2564 : {
2565 394 : CPLJSONDocument oJsonDoc;
2566 394 : if (!oJsonDoc.LoadMemory(oJson.ToString()))
2567 : {
2568 1 : return false;
2569 : }
2570 :
2571 393 : oVectorLayers = oJsonDoc.GetRoot().GetArray("vector_layers");
2572 :
2573 393 : oTileStatLayers = oJsonDoc.GetRoot().GetArray("tilestats/layers");
2574 : }
2575 :
2576 927 : oBounds = oDoc.GetRoot().GetObj("bounds");
2577 :
2578 927 : if (!osMetadataMemFilename.empty())
2579 : {
2580 18 : oDoc.Save(osMetadataMemFilename);
2581 : }
2582 :
2583 927 : return oVectorLayers.IsValid();
2584 : }
2585 :
2586 : /************************************************************************/
2587 : /* ConvertFromWGS84() */
2588 : /************************************************************************/
2589 :
2590 18 : static void ConvertFromWGS84(OGRSpatialReference *poTargetSRS, double &dfX0,
2591 : double &dfY0, double &dfX1, double &dfY1)
2592 : {
2593 36 : OGRSpatialReference oSRS_EPSG3857;
2594 18 : oSRS_EPSG3857.SetFromUserInput(SRS_EPSG_3857);
2595 :
2596 18 : if (poTargetSRS->IsSame(&oSRS_EPSG3857))
2597 : {
2598 15 : LongLatToSphericalMercator(&dfX0, &dfY0);
2599 15 : LongLatToSphericalMercator(&dfX1, &dfY1);
2600 : }
2601 : else
2602 : {
2603 6 : OGRSpatialReference oSRS_EPSG4326;
2604 3 : oSRS_EPSG4326.SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
2605 3 : oSRS_EPSG4326.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2606 : OGRCoordinateTransformation *poCT =
2607 3 : OGRCreateCoordinateTransformation(&oSRS_EPSG4326, poTargetSRS);
2608 3 : if (poCT)
2609 : {
2610 3 : poCT->Transform(1, &dfX0, &dfY0);
2611 3 : poCT->Transform(1, &dfX1, &dfY1);
2612 3 : delete poCT;
2613 : }
2614 : }
2615 18 : }
2616 :
2617 : /************************************************************************/
2618 : /* OpenDirectory() */
2619 : /************************************************************************/
2620 :
2621 26 : GDALDataset *OGRMVTDataset::OpenDirectory(GDALOpenInfo *poOpenInfo)
2622 :
2623 : {
2624 52 : const CPLString osZ(CPLGetFilename(poOpenInfo->pszFilename));
2625 26 : if (CPLGetValueType(osZ) != CPL_VALUE_INTEGER)
2626 1 : return nullptr;
2627 :
2628 25 : const int nZ = atoi(osZ);
2629 25 : if (nZ < 0 || nZ > 30)
2630 1 : return nullptr;
2631 :
2632 : CPLString osMetadataFile(
2633 48 : CPLFormFilenameSafe(CPLGetPathSafe(poOpenInfo->pszFilename).c_str(),
2634 48 : "metadata.json", nullptr));
2635 : const char *pszMetadataFile =
2636 24 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "METADATA_FILE");
2637 24 : if (pszMetadataFile)
2638 : {
2639 3 : osMetadataFile = pszMetadataFile;
2640 : }
2641 :
2642 24 : CPLString osTileExtension(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
2643 48 : "TILE_EXTENSION", "pbf"));
2644 : bool bJsonField =
2645 24 : CPLFetchBool(poOpenInfo->papszOpenOptions, "JSON_FIELD", false);
2646 :
2647 : bool bAddTileFields =
2648 24 : CPLFetchBool(poOpenInfo->papszOpenOptions, "ADD_TILE_FIELDS", false);
2649 :
2650 : VSIStatBufL sStat;
2651 :
2652 24 : bool bMetadataFileExists = false;
2653 48 : CPLString osMetadataContent;
2654 43 : if (STARTS_WITH(osMetadataFile, "http://") ||
2655 19 : STARTS_WITH(osMetadataFile, "https://"))
2656 : {
2657 8 : for (int i = 0; i < 2; i++)
2658 : {
2659 8 : if (pszMetadataFile == nullptr)
2660 8 : CPLPushErrorHandler(CPLQuietErrorHandler);
2661 8 : CPLHTTPResult *psResult = CPLHTTPFetch(osMetadataFile, nullptr);
2662 8 : if (pszMetadataFile == nullptr)
2663 8 : CPLPopErrorHandler();
2664 8 : if (psResult == nullptr)
2665 : {
2666 0 : osMetadataFile.clear();
2667 : }
2668 8 : else if (psResult->pszErrBuf != nullptr ||
2669 3 : psResult->pabyData == nullptr)
2670 : {
2671 5 : CPLHTTPDestroyResult(psResult);
2672 5 : osMetadataFile.clear();
2673 :
2674 5 : if (i == 0 && pszMetadataFile == nullptr)
2675 : {
2676 : // tileserver-gl metadata file:
2677 : // If opening /path/to/foo/0, try looking for
2678 : // /path/to/foo.json
2679 : CPLString osParentDir(
2680 6 : CPLGetPathSafe(poOpenInfo->pszFilename));
2681 6 : osMetadataFile = CPLFormFilenameSafe(
2682 6 : CPLGetPathSafe(osParentDir).c_str(),
2683 3 : CPLGetFilename(osParentDir), "json");
2684 3 : continue;
2685 2 : }
2686 : }
2687 : else
2688 : {
2689 3 : bMetadataFileExists = true;
2690 : osMetadataContent =
2691 3 : reinterpret_cast<const char *>(psResult->pabyData);
2692 3 : CPLHTTPDestroyResult(psResult);
2693 : }
2694 5 : break;
2695 : }
2696 : }
2697 19 : else if (!osMetadataFile.empty())
2698 : {
2699 16 : bMetadataFileExists = (VSIStatL(osMetadataFile, &sStat) == 0);
2700 16 : if (!bMetadataFileExists && pszMetadataFile == nullptr)
2701 : {
2702 : // tileserver-gl metadata file:
2703 : // If opening /path/to/foo/0, try looking for /path/to/foo.json
2704 2 : CPLString osParentDir(CPLGetPathSafe(poOpenInfo->pszFilename));
2705 : osMetadataFile =
2706 4 : CPLFormFilenameSafe(CPLGetPathSafe(osParentDir).c_str(),
2707 2 : CPLGetFilename(osParentDir), "json");
2708 2 : bMetadataFileExists = (VSIStatL(osMetadataFile, &sStat) == 0);
2709 : }
2710 : }
2711 :
2712 24 : if (!bMetadataFileExists)
2713 : {
2714 : // If we don't have a metadata file, iterate through all tiles to
2715 : // establish the layer definitions.
2716 6 : OGRMVTDataset *poDS = nullptr;
2717 6 : bool bTryToListDir =
2718 12 : !STARTS_WITH(poOpenInfo->pszFilename, "/vsicurl/") &&
2719 6 : !STARTS_WITH(poOpenInfo->pszFilename, "/vsicurl_streaming/") &&
2720 6 : !STARTS_WITH(poOpenInfo->pszFilename, "/vsicurl?") &&
2721 16 : !STARTS_WITH(poOpenInfo->pszFilename, "http://") &&
2722 4 : !STARTS_WITH(poOpenInfo->pszFilename, "https://");
2723 6 : CPLStringList aosDirContent;
2724 6 : if (bTryToListDir)
2725 : {
2726 4 : aosDirContent = VSIReadDir(poOpenInfo->pszFilename);
2727 4 : aosDirContent = StripDummyEntries(aosDirContent);
2728 : }
2729 12 : const int nMaxTiles = atoi(CSLFetchNameValueDef(
2730 6 : poOpenInfo->papszOpenOptions,
2731 : "TILE_COUNT_TO_ESTABLISH_FEATURE_DEFN", "1000"));
2732 6 : int nCountTiles = 0;
2733 6 : int nFailedAttempts = 0;
2734 14 : for (int i = 0; i < (bTryToListDir ? aosDirContent.Count() : (1 << nZ));
2735 : i++)
2736 : {
2737 8 : if (bTryToListDir)
2738 : {
2739 6 : if (CPLGetValueType(aosDirContent[i]) != CPL_VALUE_INTEGER)
2740 : {
2741 0 : continue;
2742 : }
2743 : }
2744 8 : CPLString osSubDir = CPLFormFilenameSafe(
2745 8 : poOpenInfo->pszFilename,
2746 8 : bTryToListDir ? aosDirContent[i] : CPLSPrintf("%d", i),
2747 8 : nullptr);
2748 8 : CPLStringList aosSubDirContent;
2749 8 : if (bTryToListDir)
2750 : {
2751 6 : aosSubDirContent = VSIReadDir(osSubDir);
2752 6 : aosSubDirContent = StripDummyEntries(aosSubDirContent);
2753 : }
2754 20 : for (int j = 0;
2755 20 : j < (bTryToListDir ? aosSubDirContent.Count() : (1 << nZ));
2756 : j++)
2757 : {
2758 12 : if (bTryToListDir)
2759 : {
2760 10 : if (CPLGetValueType(
2761 20 : CPLGetBasenameSafe(aosSubDirContent[j]).c_str()) !=
2762 : CPL_VALUE_INTEGER)
2763 : {
2764 0 : continue;
2765 : }
2766 : }
2767 : const std::string osFilename(CPLFormFilenameSafe(
2768 : osSubDir,
2769 : bTryToListDir
2770 10 : ? aosSubDirContent[j]
2771 2 : : CPLSPrintf("%d.%s", j, osTileExtension.c_str()),
2772 24 : nullptr));
2773 12 : GDALOpenInfo oOpenInfo(("MVT:" + osFilename).c_str(),
2774 12 : GA_ReadOnly);
2775 12 : oOpenInfo.papszOpenOptions =
2776 12 : CSLSetNameValue(nullptr, "METADATA_FILE", "");
2777 12 : oOpenInfo.papszOpenOptions =
2778 12 : CSLSetNameValue(oOpenInfo.papszOpenOptions,
2779 : "DO_NOT_ERROR_ON_MISSING_TILE", "YES");
2780 12 : auto poTileDS = OGRMVTDataset::Open(
2781 : &oOpenInfo, /* bRecurseAllowed = */ false);
2782 12 : if (poTileDS)
2783 : {
2784 11 : if (poDS == nullptr)
2785 : {
2786 5 : poDS = new OGRMVTDataset(nullptr);
2787 5 : poDS->m_osTileExtension = osTileExtension;
2788 5 : poDS->SetDescription(poOpenInfo->pszFilename);
2789 10 : poDS->m_bClip =
2790 5 : CPLFetchBool(poOpenInfo->papszOpenOptions, "CLIP",
2791 5 : poDS->m_bClip);
2792 : }
2793 :
2794 26 : for (int k = 0; k < poTileDS->GetLayerCount(); k++)
2795 : {
2796 15 : OGRLayer *poTileLayer = poTileDS->GetLayer(k);
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 : char **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 359 : int OGRMVTWriterLayer::TestCapability(const char *pszCap) const
3627 : {
3628 :
3629 359 : if (EQUAL(pszCap, OLCSequentialWrite) || EQUAL(pszCap, OLCCreateField))
3630 96 : return true;
3631 263 : 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 20107 : void OGRMVTWriterDataset::ConvertToTileCoords(double dfX, double dfY, int &nX,
3735 : int &nY, double dfTopX,
3736 : double dfTopY,
3737 : double dfTileDim) const
3738 : {
3739 20107 : if (dfTileDim == 0)
3740 : {
3741 3022 : nX = static_cast<int>(dfX);
3742 3022 : nY = static_cast<int>(dfY);
3743 : }
3744 : else
3745 : {
3746 17085 : nX = static_cast<int>(
3747 17085 : std::round((dfX - dfTopX) * m_nExtent / dfTileDim));
3748 17085 : nY = static_cast<int>(
3749 17085 : std::round((dfTopY - dfY) * m_nExtent / dfTileDim));
3750 : }
3751 20107 : }
3752 :
3753 : /************************************************************************/
3754 : /* GetCmdCountCombined() */
3755 : /************************************************************************/
3756 :
3757 4091 : static unsigned GetCmdCountCombined(unsigned int nCmdId, unsigned int nCmdCount)
3758 : {
3759 4091 : 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 4034 : 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 4034 : double dfTileDim = m_dfTileDim0 / (1 << nZ);
4092 4034 : double dfBuffer = dfTileDim * m_nBuffer / m_nExtent;
4093 4034 : double dfTopX = m_dfTopX + nTileX * dfTileDim;
4094 4034 : double dfTopY = m_dfTopY - nTileY * dfTileDim;
4095 4034 : double dfBottomRightX = dfTopX + dfTileDim;
4096 4034 : double dfBottomRightY = dfTopY - dfTileDim;
4097 4034 : double dfIntersectTopX = dfTopX - dfBuffer;
4098 4034 : double dfIntersectTopY = dfTopY + dfBuffer;
4099 4034 : double dfIntersectBottomRightX = dfBottomRightX + dfBuffer;
4100 4034 : double dfIntersectBottomRightY = dfBottomRightY - dfBuffer;
4101 :
4102 : const OGRGeometry *poIntersection;
4103 4034 : std::unique_ptr<OGRGeometry> poIntersectionHolder; // keep in that scope
4104 4034 : if (sEnvelope.MinX >= dfIntersectTopX &&
4105 4008 : sEnvelope.MinY >= dfIntersectBottomRightY &&
4106 4002 : sEnvelope.MaxX <= dfIntersectBottomRightX &&
4107 3984 : sEnvelope.MaxY <= dfIntersectTopY)
4108 : {
4109 3980 : 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 8062 : auto poLayer = std::make_shared<MVTTileLayer>();
4134 8062 : auto poGPBFeature = std::make_shared<MVTTileLayerFeature>();
4135 4031 : poLayer->addFeature(poGPBFeature);
4136 :
4137 4031 : OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType());
4138 4031 : if (eGeomType == wkbPoint || eGeomType == wkbMultiPoint)
4139 1369 : 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 4031 : wkbFlatten(poIntersection->getGeometryType());
4152 :
4153 : // Simplify contour if requested by user
4154 4031 : const OGRGeometry *poGeomToEncode = poIntersection;
4155 4031 : std::unique_ptr<OGRGeometry> poGeomSimplified;
4156 4031 : const double dfSimplification =
4157 4031 : bIsMaxZoomForLayer ? m_dfSimplificationMaxZoom : m_dfSimplification;
4158 4031 : 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 4031 : bool bGeomOK = false;
4173 4031 : 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 4031 : if (eGeomType == wkbPoint || eGeomType == wkbMultiPoint)
4225 : {
4226 1369 : if (eGeomToEncodeType == wkbPoint)
4227 : {
4228 985 : const OGRPoint *poPoint = poIntersection->toPoint();
4229 : int nX, nY;
4230 985 : double dfX = poPoint->getX();
4231 985 : double dfY = poPoint->getY();
4232 985 : bGeomOK = true;
4233 985 : ConvertToTileCoords(dfX, dfY, nX, nY, dfTopX, dfTopY, dfTileDim);
4234 985 : poGPBFeature->addGeometry(GetCmdCountCombined(knCMD_MOVETO, 1));
4235 985 : poGPBFeature->addGeometry(EncodeSInt(nX));
4236 985 : 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 1369 : }
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 4031 : if (!bGeomOK)
4392 2491 : return OGRERR_NONE;
4393 :
4394 5893 : 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 1540 : 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 3080 : CPLString oBuffer(poLayer->write());
4423 :
4424 : // Compress buffer
4425 1540 : size_t nCompressedSize = 0;
4426 1540 : void *pCompressed = CPLZLibDeflate(oBuffer.data(), oBuffer.size(), -1,
4427 : nullptr, 0, &nCompressedSize);
4428 1540 : oBuffer.assign(static_cast<char *>(pCompressed), nCompressedSize);
4429 1540 : CPLFree(pCompressed);
4430 :
4431 1540 : const auto InsertIntoDb = [&]()
4432 : {
4433 15400 : m_nTempTiles++;
4434 1540 : sqlite3_bind_int(m_hInsertStmt, 1, nZ);
4435 1540 : sqlite3_bind_int(m_hInsertStmt, 2, nTileX);
4436 1540 : sqlite3_bind_int(m_hInsertStmt, 3, nTileY);
4437 1540 : sqlite3_bind_text(m_hInsertStmt, 4, osTargetName.c_str(), -1,
4438 : SQLITE_STATIC);
4439 1540 : sqlite3_bind_int64(m_hInsertStmt, 5, nSerial);
4440 1540 : sqlite3_bind_blob(m_hInsertStmt, 6, oBuffer.data(),
4441 1540 : static_cast<int>(oBuffer.size()), SQLITE_STATIC);
4442 1540 : sqlite3_bind_int(m_hInsertStmt, 7,
4443 1540 : static_cast<int>(poGPBFeature->getType()));
4444 1540 : sqlite3_bind_double(m_hInsertStmt, 8, dfAreaOrLength);
4445 1540 : int rc = sqlite3_step(m_hInsertStmt);
4446 1540 : sqlite3_reset(m_hInsertStmt);
4447 1540 : return rc;
4448 1540 : };
4449 :
4450 : int rc;
4451 1540 : if (m_bThreadPoolOK)
4452 : {
4453 1515 : std::lock_guard<std::mutex> oLock(m_oDBMutex);
4454 1515 : rc = InsertIntoDb();
4455 : }
4456 : else
4457 : {
4458 25 : rc = InsertIntoDb();
4459 : }
4460 :
4461 1540 : if (!(rc == SQLITE_OK || rc == SQLITE_DONE))
4462 : {
4463 3 : 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 4009 : void OGRMVTWriterDataset::WriterTaskFunc(void *pParam)
4493 : {
4494 4009 : MVTWriterTask *poTask = static_cast<MVTWriterTask *>(pParam);
4495 16036 : OGRErr eErr = poTask->poDS->PreGenerateForTileReal(
4496 4009 : poTask->nZ, poTask->nTileX, poTask->nTileY, poTask->osTargetName,
4497 4009 : poTask->bIsMaxZoomForLayer, poTask->poFeatureContent.get(),
4498 4009 : poTask->nSerial, poTask->poGeom.get(), poTask->sEnvelope);
4499 4009 : if (eErr != OGRERR_NONE)
4500 : {
4501 2 : std::lock_guard oLock(poTask->poDS->m_oDBMutex);
4502 2 : poTask->poDS->m_bWriteFeatureError = true;
4503 : }
4504 4009 : delete poTask;
4505 4009 : }
4506 :
4507 : /************************************************************************/
4508 : /* PreGenerateForTile() */
4509 : /************************************************************************/
4510 :
4511 4034 : 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 4034 : 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 4009 : MVTWriterTask *poTask = new MVTWriterTask;
4527 4009 : poTask->poDS = this;
4528 4009 : poTask->nZ = nZ;
4529 4009 : poTask->nTileX = nTileX;
4530 4009 : poTask->nTileY = nTileY;
4531 4009 : poTask->osTargetName = osTargetName;
4532 4009 : poTask->bIsMaxZoomForLayer = bIsMaxZoomForLayer;
4533 4009 : poTask->poFeatureContent = poFeatureContent;
4534 4009 : poTask->nSerial = nSerial;
4535 4009 : poTask->poGeom = poGeom;
4536 4009 : poTask->sEnvelope = sEnvelope;
4537 4009 : m_oThreadPool.SubmitJob(OGRMVTWriterDataset::WriterTaskFunc, poTask);
4538 : // Do not queue more than 1000 jobs to avoid memory exhaustion
4539 4009 : m_oThreadPool.WaitCompletion(1000);
4540 :
4541 4009 : std::lock_guard oLock(m_oDBMutex);
4542 4009 : 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 1559 : for (int nZ = poLayer->m_nMinZoom; nZ <= poLayer->m_nMaxZoom; nZ++)
5976 : {
5977 1332 : double dfTileDim = m_dfTileDim0 / (1 << nZ);
5978 1332 : double dfBuffer = dfTileDim * m_nBuffer / m_nExtent;
5979 : const int nTileMinX = std::max(
5980 2664 : 0, static_cast<int>((sExtent.MinX - m_dfTopX - dfBuffer) /
5981 1332 : dfTileDim));
5982 : const int nTileMinY = std::max(
5983 2664 : 0, static_cast<int>((m_dfTopY - sExtent.MaxY - dfBuffer) /
5984 1332 : dfTileDim));
5985 : const int nTileMaxX =
5986 2664 : std::min(static_cast<int>((sExtent.MaxX - m_dfTopX + dfBuffer) /
5987 : dfTileDim),
5988 2664 : static_cast<int>(std::min<int64_t>(
5989 2664 : INT_MAX, (static_cast<int64_t>(1) << nZ) *
5990 2664 : m_nTileMatrixWidth0 -
5991 1332 : 1)));
5992 : const int nTileMaxY =
5993 2664 : std::min(static_cast<int>((m_dfTopY - sExtent.MinY + dfBuffer) /
5994 : dfTileDim),
5995 2664 : static_cast<int>(std::min<int64_t>(
5996 2664 : INT_MAX, (static_cast<int64_t>(1) << nZ) *
5997 2664 : m_nTileMatrixHeight0 -
5998 1332 : 1)));
5999 3574 : for (int iX = nTileMinX; iX <= nTileMaxX; iX++)
6000 : {
6001 6276 : for (int iY = nTileMinY; iY <= nTileMaxY; iY++)
6002 : {
6003 8068 : if (PreGenerateForTile(
6004 4034 : nZ, iX, iY, poLayer->m_osTargetName,
6005 4034 : (nZ == poLayer->m_nMaxZoom), poFeatureContent,
6006 4034 : 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, char **papszOptions)
6134 : {
6135 140 : if (nXSize != 0 || nYSize != 0 || nBandsIn != 0 || eDT != GDT_Unknown)
6136 : {
6137 1 : CPLError(CE_Failure, CPLE_NotSupported,
6138 : "Only vector creation supported");
6139 1 : return nullptr;
6140 : }
6141 :
6142 139 : const char *pszFormat = CSLFetchNameValue(papszOptions, "FORMAT");
6143 : const bool bMBTILESExt =
6144 139 : EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "mbtiles");
6145 139 : if (pszFormat == nullptr && bMBTILESExt)
6146 : {
6147 4 : pszFormat = "MBTILES";
6148 : }
6149 139 : const bool bMBTILES = pszFormat != nullptr && EQUAL(pszFormat, "MBTILES");
6150 :
6151 : // For debug only
6152 : bool bReuseTempFile =
6153 139 : CPLTestBool(CPLGetConfigOption("OGR_MVT_REUSE_TEMP_FILE", "NO"));
6154 :
6155 139 : if (bMBTILES)
6156 : {
6157 80 : if (!bMBTILESExt)
6158 : {
6159 1 : CPLError(CE_Failure, CPLE_FileIO,
6160 : "%s should have mbtiles extension", pszFilename);
6161 1 : return nullptr;
6162 : }
6163 :
6164 79 : VSIUnlink(pszFilename);
6165 : }
6166 : else
6167 : {
6168 : VSIStatBufL sStat;
6169 59 : if (VSIStatL(pszFilename, &sStat) == 0)
6170 : {
6171 3 : CPLError(CE_Failure, CPLE_FileIO, "%s already exists", pszFilename);
6172 5 : return nullptr;
6173 : }
6174 :
6175 56 : if (VSIMkdir(pszFilename, 0755) != 0)
6176 : {
6177 2 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create directory %s",
6178 : pszFilename);
6179 2 : return nullptr;
6180 : }
6181 : }
6182 :
6183 133 : OGRMVTWriterDataset *poDS = new OGRMVTWriterDataset();
6184 133 : poDS->m_pMyVFS = OGRSQLiteCreateVFS(nullptr, poDS);
6185 133 : sqlite3_vfs_register(poDS->m_pMyVFS, 0);
6186 :
6187 399 : CPLString osTempDBDefault = CPLString(pszFilename) + ".temp.db";
6188 133 : if (STARTS_WITH(osTempDBDefault, "/vsizip/"))
6189 : {
6190 : osTempDBDefault =
6191 0 : CPLString(pszFilename + strlen("/vsizip/")) + ".temp.db";
6192 : }
6193 : CPLString osTempDB = CSLFetchNameValueDef(papszOptions, "TEMPORARY_DB",
6194 266 : osTempDBDefault.c_str());
6195 133 : if (!bReuseTempFile)
6196 132 : VSIUnlink(osTempDB);
6197 :
6198 133 : sqlite3 *hDB = nullptr;
6199 133 : if (sqlite3_open_v2(osTempDB, &hDB,
6200 : SQLITE_OPEN_READWRITE |
6201 : (bReuseTempFile ? 0 : SQLITE_OPEN_CREATE) |
6202 : SQLITE_OPEN_NOMUTEX,
6203 396 : poDS->m_pMyVFS->zName) != SQLITE_OK ||
6204 130 : hDB == nullptr)
6205 : {
6206 3 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", osTempDB.c_str());
6207 3 : delete poDS;
6208 3 : sqlite3_close(hDB);
6209 3 : return nullptr;
6210 : }
6211 130 : poDS->m_osTempDB = osTempDB;
6212 130 : poDS->m_hDB = hDB;
6213 130 : poDS->m_bReuseTempFile = bReuseTempFile;
6214 :
6215 : // For Unix
6216 259 : if (!poDS->m_bReuseTempFile &&
6217 129 : CPLTestBool(CPLGetConfigOption("OGR_MVT_REMOVE_TEMP_FILE", "YES")))
6218 : {
6219 126 : VSIUnlink(osTempDB);
6220 : }
6221 :
6222 130 : if (poDS->m_bReuseTempFile)
6223 : {
6224 1 : poDS->m_nTempTiles =
6225 1 : SQLGetInteger64(hDB, "SELECT COUNT(*) FROM temp", nullptr);
6226 : }
6227 : else
6228 : {
6229 129 : CPL_IGNORE_RET_VAL(SQLCommand(
6230 : hDB,
6231 : "PRAGMA page_size = 4096;" // 4096: default since sqlite 3.12
6232 : "PRAGMA synchronous = OFF;"
6233 : "PRAGMA journal_mode = OFF;"
6234 : "PRAGMA temp_store = MEMORY;"
6235 : "CREATE TABLE temp(z INTEGER, x INTEGER, y INTEGER, layer TEXT, "
6236 : "idx INTEGER, feature BLOB, geomtype INTEGER, area_or_length "
6237 : "DOUBLE);"
6238 : "CREATE INDEX temp_index ON temp (z, x, y, layer, idx);"));
6239 : }
6240 :
6241 130 : sqlite3_stmt *hInsertStmt = nullptr;
6242 130 : CPL_IGNORE_RET_VAL(sqlite3_prepare_v2(
6243 : hDB,
6244 : "INSERT INTO temp (z,x,y,layer,idx,feature,geomtype,area_or_length) "
6245 : "VALUES (?,?,?,?,?,?,?,?)",
6246 : -1, &hInsertStmt, nullptr));
6247 130 : if (hInsertStmt == nullptr)
6248 : {
6249 0 : delete poDS;
6250 0 : return nullptr;
6251 : }
6252 130 : poDS->m_hInsertStmt = hInsertStmt;
6253 :
6254 130 : poDS->m_nMinZoom = atoi(CSLFetchNameValueDef(
6255 : papszOptions, "MINZOOM", CPLSPrintf("%d", poDS->m_nMinZoom)));
6256 130 : poDS->m_nMaxZoom = atoi(CSLFetchNameValueDef(
6257 : papszOptions, "MAXZOOM", CPLSPrintf("%d", poDS->m_nMaxZoom)));
6258 130 : if (!ValidateMinMaxZoom(poDS->m_nMinZoom, poDS->m_nMaxZoom))
6259 : {
6260 3 : delete poDS;
6261 3 : return nullptr;
6262 : }
6263 :
6264 127 : const char *pszConf = CSLFetchNameValue(papszOptions, "CONF");
6265 127 : if (pszConf)
6266 : {
6267 : VSIStatBufL sStat;
6268 : bool bSuccess;
6269 3 : if (VSIStatL(pszConf, &sStat) == 0)
6270 : {
6271 2 : bSuccess = poDS->m_oConf.Load(pszConf);
6272 : }
6273 : else
6274 : {
6275 1 : bSuccess = poDS->m_oConf.LoadMemory(pszConf);
6276 : }
6277 3 : if (!bSuccess)
6278 : {
6279 1 : delete poDS;
6280 1 : return nullptr;
6281 : }
6282 : }
6283 :
6284 126 : poDS->m_dfSimplification =
6285 126 : CPLAtof(CSLFetchNameValueDef(papszOptions, "SIMPLIFICATION", "0"));
6286 126 : poDS->m_dfSimplificationMaxZoom = CPLAtof(
6287 : CSLFetchNameValueDef(papszOptions, "SIMPLIFICATION_MAX_ZOOM",
6288 : CPLSPrintf("%g", poDS->m_dfSimplification)));
6289 126 : poDS->m_nExtent = static_cast<unsigned>(atoi(CSLFetchNameValueDef(
6290 : papszOptions, "EXTENT", CPLSPrintf("%u", poDS->m_nExtent))));
6291 126 : poDS->m_nBuffer = static_cast<unsigned>(atoi(CSLFetchNameValueDef(
6292 126 : papszOptions, "BUFFER", CPLSPrintf("%u", 5 * poDS->m_nExtent / 256))));
6293 :
6294 : {
6295 126 : const char *pszMaxSize = CSLFetchNameValue(papszOptions, "MAX_SIZE");
6296 126 : poDS->m_bMaxTileSizeOptSpecified = pszMaxSize != nullptr;
6297 : // This is used by unit tests
6298 126 : pszMaxSize = CSLFetchNameValueDef(papszOptions, "@MAX_SIZE_FOR_TEST",
6299 : pszMaxSize);
6300 126 : if (pszMaxSize)
6301 : {
6302 3 : poDS->m_nMaxTileSize =
6303 3 : std::max(100U, static_cast<unsigned>(atoi(pszMaxSize)));
6304 : }
6305 : }
6306 :
6307 : {
6308 : const char *pszMaxFeatures =
6309 126 : CSLFetchNameValue(papszOptions, "MAX_FEATURES");
6310 126 : poDS->m_bMaxFeaturesOptSpecified = pszMaxFeatures != nullptr;
6311 126 : pszMaxFeatures = CSLFetchNameValueDef(
6312 : // This is used by unit tests
6313 : papszOptions, "@MAX_FEATURES_FOR_TEST", pszMaxFeatures);
6314 126 : if (pszMaxFeatures)
6315 : {
6316 2 : poDS->m_nMaxFeatures =
6317 2 : std::max(1U, static_cast<unsigned>(atoi(pszMaxFeatures)));
6318 : }
6319 : }
6320 :
6321 : poDS->m_osName = CSLFetchNameValueDef(
6322 126 : papszOptions, "NAME", CPLGetBasenameSafe(pszFilename).c_str());
6323 : poDS->m_osDescription = CSLFetchNameValueDef(papszOptions, "DESCRIPTION",
6324 126 : poDS->m_osDescription.c_str());
6325 : poDS->m_osType =
6326 126 : CSLFetchNameValueDef(papszOptions, "TYPE", poDS->m_osType.c_str());
6327 126 : poDS->m_bGZip = CPLFetchBool(papszOptions, "COMPRESS", poDS->m_bGZip);
6328 126 : poDS->m_osBounds = CSLFetchNameValueDef(papszOptions, "BOUNDS", "");
6329 126 : poDS->m_osCenter = CSLFetchNameValueDef(papszOptions, "CENTER", "");
6330 : poDS->m_osExtension = CSLFetchNameValueDef(papszOptions, "TILE_EXTENSION",
6331 126 : poDS->m_osExtension);
6332 :
6333 : const char *pszTilingScheme =
6334 126 : CSLFetchNameValue(papszOptions, "TILING_SCHEME");
6335 126 : if (pszTilingScheme)
6336 : {
6337 6 : if (bMBTILES)
6338 : {
6339 1 : CPLError(CE_Failure, CPLE_NotSupported,
6340 : "Custom TILING_SCHEME not supported with MBTILES output");
6341 1 : delete poDS;
6342 2 : return nullptr;
6343 : }
6344 :
6345 5 : const CPLStringList aoList(CSLTokenizeString2(pszTilingScheme, ",", 0));
6346 5 : if (aoList.Count() >= 4)
6347 : {
6348 4 : poDS->m_poSRS->SetFromUserInput(aoList[0]);
6349 4 : poDS->m_dfTopX = CPLAtof(aoList[1]);
6350 4 : poDS->m_dfTopY = CPLAtof(aoList[2]);
6351 4 : poDS->m_dfTileDim0 = CPLAtof(aoList[3]);
6352 4 : if (aoList.Count() == 6)
6353 : {
6354 2 : poDS->m_nTileMatrixWidth0 = std::max(1, atoi(aoList[4]));
6355 2 : poDS->m_nTileMatrixHeight0 = std::max(1, atoi(aoList[5]));
6356 : }
6357 2 : else if (poDS->m_dfTopX == -180 && poDS->m_dfTileDim0 == 180)
6358 : {
6359 : // Assumes WorldCRS84Quad with 2 tiles in width
6360 : // cf https://github.com/OSGeo/gdal/issues/11749
6361 1 : poDS->m_nTileMatrixWidth0 = 2;
6362 : }
6363 : }
6364 : else
6365 : {
6366 1 : CPLError(CE_Failure, CPLE_AppDefined,
6367 : "Wrong format for TILING_SCHEME. "
6368 : "Expecting EPSG:XXXX,tile_origin_upper_left_x,"
6369 : "tile_origin_upper_left_y,tile_dimension_zoom_0[,tile_"
6370 : "matrix_width_zoom_0,tile_matrix_height_zoom_0]");
6371 1 : delete poDS;
6372 1 : return nullptr;
6373 : }
6374 : }
6375 :
6376 124 : if (bMBTILES)
6377 : {
6378 228 : if (sqlite3_open_v2(pszFilename, &poDS->m_hDBMBTILES,
6379 : SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
6380 : SQLITE_OPEN_NOMUTEX,
6381 151 : poDS->m_pMyVFS->zName) != SQLITE_OK ||
6382 75 : poDS->m_hDBMBTILES == nullptr)
6383 : {
6384 1 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", pszFilename);
6385 1 : delete poDS;
6386 1 : return nullptr;
6387 : }
6388 :
6389 75 : if (SQLCommand(
6390 : poDS->m_hDBMBTILES,
6391 : "PRAGMA page_size = 4096;" // 4096: default since sqlite 3.12
6392 : "PRAGMA synchronous = OFF;"
6393 : "PRAGMA journal_mode = OFF;"
6394 : "PRAGMA temp_store = MEMORY;"
6395 : "CREATE TABLE metadata (name text, value text);"
6396 : "CREATE TABLE tiles (zoom_level integer, tile_column integer, "
6397 : "tile_row integer, tile_data blob, "
6398 75 : "UNIQUE (zoom_level, tile_column, tile_row))") != OGRERR_NONE)
6399 : {
6400 0 : delete poDS;
6401 0 : return nullptr;
6402 : }
6403 : }
6404 :
6405 123 : int nThreads = CPLGetNumCPUs();
6406 123 : const char *pszNumThreads = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
6407 123 : if (pszNumThreads && CPLGetValueType(pszNumThreads) == CPL_VALUE_INTEGER)
6408 : {
6409 3 : nThreads = atoi(pszNumThreads);
6410 : }
6411 123 : if (nThreads > 1)
6412 : {
6413 120 : poDS->m_bThreadPoolOK =
6414 120 : poDS->m_oThreadPool.Setup(nThreads, nullptr, nullptr);
6415 : }
6416 :
6417 123 : poDS->SetDescription(pszFilename);
6418 123 : poDS->poDriver = GDALDriver::FromHandle(GDALGetDriverByName("MVT"));
6419 :
6420 123 : return poDS;
6421 : }
6422 :
6423 75 : GDALDataset *OGRMVTWriterDatasetCreate(const char *pszFilename, int nXSize,
6424 : int nYSize, int nBandsIn,
6425 : GDALDataType eDT, char **papszOptions)
6426 : {
6427 75 : return OGRMVTWriterDataset::Create(pszFilename, nXSize, nYSize, nBandsIn,
6428 75 : eDT, papszOptions);
6429 : }
6430 :
6431 : #endif // HAVE_MVT_WRITE_SUPPORT
6432 :
6433 : /************************************************************************/
6434 : /* RegisterOGRMVT() */
6435 : /************************************************************************/
6436 :
6437 2058 : void RegisterOGRMVT()
6438 :
6439 : {
6440 2058 : if (GDALGetDriverByName("MVT") != nullptr)
6441 283 : return;
6442 :
6443 1775 : GDALDriver *poDriver = new GDALDriver();
6444 :
6445 1775 : poDriver->SetDescription("MVT");
6446 1775 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
6447 1775 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Mapbox Vector Tiles");
6448 1775 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/mvt.html");
6449 1775 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
6450 1775 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "mvt mvt.gz pbf");
6451 1775 : poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "SQLITE OGRSQL");
6452 :
6453 1775 : poDriver->SetMetadataItem(
6454 : GDAL_DMD_OPENOPTIONLIST,
6455 : "<OpenOptionList>"
6456 : " <Option name='X' type='int' description='X coordinate of tile'/>"
6457 : " <Option name='Y' type='int' description='Y coordinate of tile'/>"
6458 : " <Option name='Z' type='int' description='Z coordinate of tile'/>"
6459 : //" <Option name='@GEOREF_TOPX' type='float' description='X coordinate
6460 : // of top-left corner of tile'/>" " <Option name='@GEOREF_TOPY'
6461 : // type='float' description='Y coordinate of top-left corner of tile'/>"
6462 : //" <Option name='@GEOREF_TILEDIMX' type='float' description='Tile
6463 : // width in georeferenced units'/>" " <Option name='@GEOREF_TILEDIMY'
6464 : // type='float' description='Tile height in georeferenced units'/>"
6465 : " <Option name='METADATA_FILE' type='string' "
6466 : "description='Path to metadata.json'/>"
6467 : " <Option name='CLIP' type='boolean' "
6468 : "description='Whether to clip geometries to tile extent' "
6469 : "default='YES'/>"
6470 : " <Option name='TILE_EXTENSION' type='string' default='pbf' "
6471 : "description="
6472 : "'For tilesets, extension of tiles'/>"
6473 : " <Option name='TILE_COUNT_TO_ESTABLISH_FEATURE_DEFN' type='int' "
6474 : "description="
6475 : "'For tilesets without metadata file, maximum number of tiles to use "
6476 : "to "
6477 : "establish the layer schemas' default='1000'/>"
6478 : " <Option name='JSON_FIELD' type='boolean' description='For tilesets, "
6479 : "whether to put all attributes as a serialized JSon dictionary'/>"
6480 : " <Option name='ADD_TILE_FIELDS' type='boolean' description='For "
6481 : "tilesets, "
6482 : "whether to add fields \"tile_z\", \"tile_x\", \"tile_y\" to each "
6483 : "layer, "
6484 : "containing the Z/X/Y coordinates of the tile from which the feature "
6485 : "originates.' "
6486 : "default='NO'/>"
6487 1775 : "</OpenOptionList>");
6488 :
6489 1775 : poDriver->pfnIdentify = OGRMVTDriverIdentify;
6490 1775 : poDriver->pfnOpen = OGRMVTDataset::Open;
6491 : #ifdef HAVE_MVT_WRITE_SUPPORT
6492 1775 : poDriver->pfnCreate = OGRMVTWriterDataset::Create;
6493 1775 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
6494 1775 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
6495 1775 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
6496 1775 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES,
6497 1775 : "Integer Integer64 Real String");
6498 1775 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES,
6499 1775 : "Boolean Float32");
6500 1775 : poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "SQLITE OGRSQL");
6501 :
6502 1775 : poDriver->SetMetadataItem(GDAL_DS_LAYER_CREATIONOPTIONLIST, MVT_LCO);
6503 :
6504 1775 : poDriver->SetMetadataItem(
6505 : GDAL_DMD_CREATIONOPTIONLIST,
6506 : "<CreationOptionList>"
6507 : " <Option name='NAME' type='string' description='Tileset name'/>"
6508 : " <Option name='DESCRIPTION' type='string' "
6509 : "description='A description of the tileset'/>"
6510 : " <Option name='TYPE' type='string-select' description='Layer type' "
6511 : "default='overlay'>"
6512 : " <Value>overlay</Value>"
6513 : " <Value>baselayer</Value>"
6514 : " </Option>"
6515 : " <Option name='FORMAT' type='string-select' description='Format'>"
6516 : " <Value>DIRECTORY</Value>"
6517 : " <Value>MBTILES</Value>"
6518 : " </Option>"
6519 : " <Option name='TILE_EXTENSION' type='string' default='pbf' "
6520 : "description="
6521 : "'For tilesets as directories of files, extension of "
6522 : "tiles'/>" MVT_MBTILES_COMMON_DSCO
6523 : " <Option name='BOUNDS' type='string' "
6524 : "description='Override default value for bounds metadata item'/>"
6525 : " <Option name='CENTER' type='string' "
6526 : "description='Override default value for center metadata item'/>"
6527 : " <Option name='TILING_SCHEME' type='string' "
6528 : "description='Custom tiling scheme with following format "
6529 : "\"EPSG:XXXX,tile_origin_upper_left_x,tile_origin_upper_left_y,"
6530 : "tile_dimension_zoom_0[,tile_matrix_width_zoom_0,tile_matrix_height_"
6531 : "zoom_0]\"'/>"
6532 1775 : "</CreationOptionList>");
6533 : #endif // HAVE_MVT_WRITE_SUPPORT
6534 :
6535 1775 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
6536 :
6537 1775 : GetGDALDriverManager()->RegisterDriver(poDriver);
6538 : }
|