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