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