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