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 1237 : static void InitWebMercatorTilingScheme(OGRSpatialReference *poSRS,
87 : double &dfTopX, double &dfTopY,
88 : double &dfTileDim0)
89 : {
90 1237 : constexpr double kmMAX_GM =
91 : kmSPHERICAL_RADIUS * M_PI; // 20037508.342789244
92 1237 : poSRS->SetFromUserInput(SRS_EPSG_3857);
93 1237 : dfTopX = -kmMAX_GM;
94 1237 : dfTopY = kmMAX_GM;
95 1237 : dfTileDim0 = 2 * kmMAX_GM;
96 1237 : }
97 :
98 : /************************************************************************/
99 : /* GetCmdId() */
100 : /************************************************************************/
101 :
102 : /* For a drawing instruction combining a command id and a command count,
103 : * return the command id */
104 2032 : static unsigned GetCmdId(unsigned int nCmdCountCombined)
105 : {
106 2032 : return nCmdCountCombined & 0x7;
107 : }
108 :
109 : /************************************************************************/
110 : /* GetCmdCount() */
111 : /************************************************************************/
112 :
113 : /* For a drawing instruction combining a command id and a command count,
114 : * return the command count */
115 5819 : static unsigned GetCmdCount(unsigned int nCmdCountCombined)
116 : {
117 5819 : return nCmdCountCombined >> 3;
118 : }
119 :
120 : /************************************************************************/
121 : /* OGRMVTLayerBase */
122 : /************************************************************************/
123 :
124 : class OGRMVTLayerBase CPL_NON_FINAL
125 : : public OGRLayer,
126 : public OGRGetNextFeatureThroughRaw<OGRMVTLayerBase>
127 : {
128 : virtual OGRFeature *GetNextRawFeature() = 0;
129 :
130 : protected:
131 : OGRFeatureDefn *m_poFeatureDefn = nullptr;
132 :
133 : void InitFields(const CPLJSONObject &oFields,
134 : const CPLJSONArray &oAttributesFromTileStats);
135 :
136 : public:
137 : ~OGRMVTLayerBase() override;
138 :
139 5701 : OGRFeatureDefn *GetLayerDefn() const override
140 : {
141 5701 : return m_poFeatureDefn;
142 : }
143 :
144 2955 : DEFINE_GET_NEXT_FEATURE_THROUGH_RAW(OGRMVTLayerBase)
145 :
146 : int TestCapability(const char *) const override;
147 : };
148 :
149 : /************************************************************************/
150 : /* OGRMVTLayer */
151 : /************************************************************************/
152 :
153 : class OGRMVTDataset;
154 :
155 : class OGRMVTLayer final : public OGRMVTLayerBase
156 : {
157 : OGRMVTDataset *m_poDS;
158 : const GByte *m_pabyDataStart;
159 : const GByte *m_pabyDataEnd;
160 : const GByte *m_pabyDataCur = nullptr;
161 : const GByte *m_pabyDataFeatureStart = nullptr;
162 : bool m_bError = false;
163 : unsigned int m_nExtent = knDEFAULT_EXTENT;
164 : std::vector<CPLString> m_aosKeys;
165 :
166 : typedef struct
167 : {
168 : OGRFieldType eType;
169 : OGRFieldSubType eSubType;
170 : OGRField sValue;
171 : } Value;
172 :
173 : std::vector<Value> m_asValues;
174 : GIntBig m_nFID = 0;
175 : GIntBig m_nFeatureCount = -1;
176 : OGRPolygon m_oClipPoly;
177 : double m_dfTileMinX = 0;
178 : double m_dfTileMinY = 0;
179 : double m_dfTileMaxX = 0;
180 : double m_dfTileMaxY = 0;
181 : bool m_bEnforceExternalIsClockwise = false;
182 :
183 : void Init(const CPLJSONObject &oFields,
184 : const CPLJSONArray &oAttributesFromTileStats);
185 : bool QuickScanFeature(const GByte *pabyData,
186 : const GByte *pabyDataFeatureEnd, bool bScanFields,
187 : bool bScanGeometries, bool &bGeomTypeSet);
188 : void GetXY(int nX, int nY, double &dfX, double &dfY);
189 : OGRGeometry *ParseGeometry(unsigned int nGeomType,
190 : const GByte *pabyDataGeometryEnd);
191 : void SanitizeClippedGeometry(OGRGeometry *&poGeom);
192 :
193 : OGRFeature *GetNextRawFeature() override;
194 :
195 : public:
196 : OGRMVTLayer(OGRMVTDataset *poDS, const char *pszLayerName,
197 : const GByte *pabyData, int nLayerSize,
198 : const CPLJSONObject &oFields,
199 : const CPLJSONArray &oAttributesFromTileStats,
200 : OGRwkbGeometryType eGeomType);
201 : ~OGRMVTLayer() override;
202 :
203 : void ResetReading() override;
204 :
205 : GIntBig GetFeatureCount(int bForce) override;
206 :
207 : GDALDataset *GetDataset() override;
208 : };
209 :
210 : /************************************************************************/
211 : /* OGRMVTDirectoryLayer */
212 : /************************************************************************/
213 :
214 : class OGRMVTDirectoryLayer final : public OGRMVTLayerBase
215 : {
216 : OGRMVTDataset *m_poDS;
217 : int m_nZ = 0;
218 : bool m_bUseReadDir = true;
219 : CPLString m_osDirName;
220 : CPLStringList m_aosDirContent;
221 : CPLString m_aosSubDirName;
222 : CPLStringList m_aosSubDirContent;
223 : bool m_bEOF = false;
224 : int m_nXIndex = 0;
225 : int m_nYIndex = 0;
226 : GDALDataset *m_poCurrentTile = nullptr;
227 : bool m_bJsonField = false;
228 : GIntBig m_nFIDBase = 0;
229 : OGREnvelope m_sExtent;
230 : int m_nFilterMinX = 0;
231 : int m_nFilterMinY = 0;
232 : int m_nFilterMaxX = 0;
233 : int m_nFilterMaxY = 0;
234 :
235 : OGRFeature *GetNextRawFeature() override;
236 : OGRFeature *CreateFeatureFrom(OGRFeature *poSrcFeature);
237 : void ReadNewSubDir();
238 : void OpenTile();
239 : void OpenTileIfNeeded();
240 :
241 : public:
242 : OGRMVTDirectoryLayer(OGRMVTDataset *poDS, const char *pszLayerName,
243 : const char *pszDirectoryName,
244 : const CPLJSONObject &oFields,
245 : const CPLJSONArray &oAttributesFromTileStats,
246 : bool bJsonField, OGRwkbGeometryType eGeomType,
247 : const OGREnvelope *psExtent);
248 : ~OGRMVTDirectoryLayer() override;
249 :
250 : void ResetReading() override;
251 :
252 : GIntBig GetFeatureCount(int bForce) override;
253 : OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
254 : bool bForce) override;
255 :
256 : OGRErr ISetSpatialFilter(int iGeomField,
257 : const OGRGeometry *poGeom) override;
258 :
259 : OGRFeature *GetFeature(GIntBig nFID) override;
260 :
261 : int TestCapability(const char *) const override;
262 :
263 : GDALDataset *GetDataset() override;
264 : };
265 :
266 : /************************************************************************/
267 : /* OGRMVTDataset */
268 : /************************************************************************/
269 :
270 : class OGRMVTDataset final : public GDALDataset
271 : {
272 : friend class OGRMVTLayer;
273 : friend class OGRMVTDirectoryLayer;
274 :
275 : GByte *m_pabyData;
276 : std::vector<std::unique_ptr<OGRLayer>> m_apoLayers;
277 : bool m_bGeoreferenced = false;
278 : double m_dfTileDimX = 0.0;
279 : double m_dfTileDimY = 0.0;
280 : double m_dfTopX = 0.0;
281 : double m_dfTopY = 0.0;
282 : CPLString m_osMetadataMemFilename;
283 : bool m_bClip = true;
284 : CPLString m_osTileExtension{"pbf"};
285 : OGRSpatialReference *m_poSRS = nullptr;
286 : double m_dfTileDim0 =
287 : 0.0; // Extent (in CRS units) of a tile at zoom level 0
288 : double m_dfTopXOrigin = 0.0; // top-left X of tile matrix scheme
289 : double m_dfTopYOrigin = 0.0; // top-left Y of tile matrix scheme
290 : int m_nTileMatrixWidth0 =
291 : 1; // Number of tiles along X axis at zoom level 0
292 : int m_nTileMatrixHeight0 =
293 : 1; // Number of tiles along Y axis at zoom level 0
294 :
295 : static GDALDataset *OpenDirectory(GDALOpenInfo *);
296 :
297 : public:
298 : explicit OGRMVTDataset(GByte *pabyData);
299 : ~OGRMVTDataset() override;
300 :
301 3423 : int GetLayerCount() const override
302 : {
303 3423 : return static_cast<int>(m_apoLayers.size());
304 : }
305 :
306 : const OGRLayer *GetLayer(int) const override;
307 :
308 12 : int TestCapability(const char *) const override
309 : {
310 12 : return FALSE;
311 : }
312 :
313 : static GDALDataset *Open(GDALOpenInfo *);
314 : static GDALDataset *Open(GDALOpenInfo *, bool bRecurseAllowed);
315 :
316 1027 : OGRSpatialReference *GetSRS()
317 : {
318 1027 : 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 1071 : OGRMVTLayerBase::~OGRMVTLayerBase()
352 : {
353 1071 : m_poFeatureDefn->Release();
354 1071 : }
355 :
356 : /************************************************************************/
357 : /* InitFields() */
358 : /************************************************************************/
359 :
360 1070 : void OGRMVTLayerBase::InitFields(const CPLJSONObject &oFields,
361 : const CPLJSONArray &oAttributesFromTileStats)
362 : {
363 1070 : OGRMVTInitFields(m_poFeatureDefn, oFields, oAttributesFromTileStats);
364 1070 : }
365 :
366 : /************************************************************************/
367 : /* TestCapability() */
368 : /************************************************************************/
369 :
370 71 : int OGRMVTLayerBase::TestCapability(const char *pszCap) const
371 : {
372 71 : if (EQUAL(pszCap, OLCStringsAsUTF8) || EQUAL(pszCap, OLCFastSpatialFilter))
373 : {
374 25 : return TRUE;
375 : }
376 46 : return FALSE;
377 : }
378 :
379 : /************************************************************************/
380 : /* OGRMVTLayer() */
381 : /************************************************************************/
382 :
383 1048 : OGRMVTLayer::OGRMVTLayer(OGRMVTDataset *poDS, const char *pszLayerName,
384 : const GByte *pabyData, int nLayerSize,
385 : const CPLJSONObject &oFields,
386 : const CPLJSONArray &oAttributesFromTileStats,
387 1048 : OGRwkbGeometryType eGeomType)
388 : : m_poDS(poDS), m_pabyDataStart(pabyData),
389 1048 : m_pabyDataEnd(pabyData + nLayerSize)
390 : {
391 1048 : m_poFeatureDefn = new OGRFeatureDefn(pszLayerName);
392 1048 : SetDescription(m_poFeatureDefn->GetName());
393 1048 : m_poFeatureDefn->SetGeomType(eGeomType);
394 1048 : m_poFeatureDefn->Reference();
395 :
396 1048 : if (m_poDS->m_bGeoreferenced)
397 : {
398 1004 : m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_poDS->GetSRS());
399 : }
400 :
401 1048 : Init(oFields, oAttributesFromTileStats);
402 :
403 1048 : GetXY(0, 0, m_dfTileMinX, m_dfTileMaxY);
404 1048 : GetXY(m_nExtent, m_nExtent, m_dfTileMaxX, m_dfTileMinY);
405 1048 : OGRLinearRing *poLR = new OGRLinearRing();
406 1048 : poLR->addPoint(m_dfTileMinX, m_dfTileMinY);
407 1048 : poLR->addPoint(m_dfTileMinX, m_dfTileMaxY);
408 1048 : poLR->addPoint(m_dfTileMaxX, m_dfTileMaxY);
409 1048 : poLR->addPoint(m_dfTileMaxX, m_dfTileMinY);
410 1048 : poLR->addPoint(m_dfTileMinX, m_dfTileMinY);
411 1048 : 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 1048 : m_bEnforceExternalIsClockwise = CPLTestBool(
419 : CPLGetConfigOption("OGR_MVT_ENFORE_EXTERNAL_RING_IS_CLOCKWISE", "NO"));
420 1048 : }
421 :
422 : /************************************************************************/
423 : /* ~OGRMVTLayer() */
424 : /************************************************************************/
425 :
426 2096 : OGRMVTLayer::~OGRMVTLayer()
427 : {
428 22583 : for (auto &sValue : m_asValues)
429 : {
430 21535 : if (sValue.eType == OFTString)
431 : {
432 12208 : CPLFree(sValue.sValue.String);
433 : }
434 : }
435 2096 : }
436 :
437 : /************************************************************************/
438 : /* Init() */
439 : /************************************************************************/
440 :
441 1048 : void OGRMVTLayer::Init(const CPLJSONObject &oFields,
442 : const CPLJSONArray &oAttributesFromTileStats)
443 : {
444 : // First pass to collect keys and values
445 1048 : const GByte *pabyData = m_pabyDataStart;
446 1048 : const GByte *pabyDataLimit = m_pabyDataEnd;
447 1048 : unsigned int nKey = 0;
448 1048 : bool bGeomTypeSet = false;
449 1048 : const bool bScanFields = !oFields.IsValid();
450 1048 : const bool bScanGeometries = m_poFeatureDefn->GetGeomType() == wkbUnknown;
451 1048 : const bool bQuickScanFeature = bScanFields || bScanGeometries;
452 :
453 : try
454 : {
455 45286 : while (pabyData < pabyDataLimit)
456 : {
457 44238 : READ_FIELD_KEY(nKey);
458 44238 : if (nKey == MAKE_KEY(knLAYER_KEYS, WT_DATA))
459 : {
460 16920 : char *pszKey = nullptr;
461 16920 : READ_TEXT(pabyData, pabyDataLimit, pszKey);
462 16920 : m_aosKeys.push_back(pszKey);
463 16920 : CPLFree(pszKey);
464 : }
465 27318 : else if (nKey == MAKE_KEY(knLAYER_VALUES, WT_DATA))
466 : {
467 21536 : unsigned int nValueLength = 0;
468 21536 : READ_SIZE(pabyData, pabyDataLimit, nValueLength);
469 21536 : const GByte *pabyDataValueEnd = pabyData + nValueLength;
470 21536 : READ_VARUINT32(pabyData, pabyDataLimit, nKey);
471 21536 : if (nKey == MAKE_KEY(knVALUE_STRING, WT_DATA))
472 : {
473 12208 : char *pszValue = nullptr;
474 12208 : READ_TEXT(pabyData, pabyDataLimit, pszValue);
475 : Value sValue;
476 12208 : sValue.eType = OFTString;
477 12208 : sValue.eSubType = OFSTNone;
478 12208 : sValue.sValue.String = pszValue;
479 12208 : m_asValues.push_back(sValue);
480 : }
481 9328 : else if (nKey == MAKE_KEY(knVALUE_FLOAT, WT_32BIT))
482 : {
483 : Value sValue;
484 604 : sValue.eType = OFTReal;
485 604 : sValue.eSubType = OFSTFloat32;
486 604 : sValue.sValue.Real = ReadFloat32(&pabyData, pabyDataLimit);
487 604 : m_asValues.push_back(sValue);
488 : }
489 8724 : else if (nKey == MAKE_KEY(knVALUE_DOUBLE, WT_64BIT))
490 : {
491 : Value sValue;
492 693 : sValue.eType = OFTReal;
493 693 : sValue.eSubType = OFSTNone;
494 693 : sValue.sValue.Real = ReadFloat64(&pabyData, pabyDataLimit);
495 693 : m_asValues.push_back(sValue);
496 : }
497 8031 : else if (nKey == MAKE_KEY(knVALUE_INT, WT_VARINT))
498 : {
499 261 : GIntBig nVal = 0;
500 261 : READ_VARINT64(pabyData, pabyDataLimit, nVal);
501 : Value sValue;
502 203 : sValue.eType = (nVal >= INT_MIN && nVal <= INT_MAX)
503 464 : ? OFTInteger
504 : : OFTInteger64;
505 261 : sValue.eSubType = OFSTNone;
506 261 : if (sValue.eType == OFTInteger)
507 140 : sValue.sValue.Integer = static_cast<int>(nVal);
508 : else
509 121 : sValue.sValue.Integer64 = nVal;
510 261 : m_asValues.push_back(sValue);
511 : }
512 7770 : else if (nKey == MAKE_KEY(knVALUE_UINT, WT_VARINT))
513 : {
514 7150 : GUIntBig nVal = 0;
515 7150 : READ_VARUINT64(pabyData, pabyDataLimit, nVal);
516 : Value sValue;
517 7150 : sValue.eType =
518 7150 : (nVal <= INT_MAX) ? OFTInteger : OFTInteger64;
519 7150 : sValue.eSubType = OFSTNone;
520 7150 : if (sValue.eType == OFTInteger)
521 7090 : sValue.sValue.Integer = static_cast<int>(nVal);
522 : else
523 60 : sValue.sValue.Integer64 = static_cast<GIntBig>(nVal);
524 7150 : m_asValues.push_back(sValue);
525 : }
526 620 : else if (nKey == MAKE_KEY(knVALUE_SINT, WT_VARINT))
527 : {
528 490 : GIntBig nVal = 0;
529 490 : READ_VARSINT64(pabyData, pabyDataLimit, nVal);
530 : Value sValue;
531 430 : sValue.eType = (nVal >= INT_MIN && nVal <= INT_MAX)
532 920 : ? OFTInteger
533 : : OFTInteger64;
534 490 : sValue.eSubType = OFSTNone;
535 490 : if (sValue.eType == OFTInteger)
536 372 : sValue.sValue.Integer = static_cast<int>(nVal);
537 : else
538 118 : sValue.sValue.Integer64 = nVal;
539 490 : m_asValues.push_back(sValue);
540 : }
541 130 : else if (nKey == MAKE_KEY(knVALUE_BOOL, WT_VARINT))
542 : {
543 129 : unsigned nVal = 0;
544 129 : READ_VARUINT32(pabyData, pabyDataLimit, nVal);
545 : Value sValue;
546 129 : sValue.eType = OFTInteger;
547 129 : sValue.eSubType = OFSTBoolean;
548 129 : sValue.sValue.Integer = static_cast<int>(nVal);
549 129 : m_asValues.push_back(sValue);
550 : }
551 :
552 21536 : pabyData = pabyDataValueEnd;
553 : }
554 5782 : else if (nKey == MAKE_KEY(knLAYER_EXTENT, WT_VARINT))
555 : {
556 1035 : GUInt32 nExtent = 0;
557 1035 : READ_VARUINT32(pabyData, pabyDataLimit, nExtent);
558 1035 : m_nExtent = std::max(1U, nExtent); // to avoid divide by zero
559 : }
560 : else
561 : {
562 4747 : SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, FALSE);
563 : }
564 : }
565 :
566 1048 : InitFields(oFields, oAttributesFromTileStats);
567 :
568 1048 : m_nFeatureCount = 0;
569 1048 : pabyData = m_pabyDataStart;
570 : // Second pass to iterate over features to figure out the geometry type
571 : // and attribute schema
572 45275 : while (pabyData < pabyDataLimit)
573 : {
574 44230 : const GByte *pabyDataBefore = pabyData;
575 44230 : READ_FIELD_KEY(nKey);
576 44230 : if (nKey == MAKE_KEY(knLAYER_FEATURES, WT_DATA))
577 : {
578 2650 : if (m_pabyDataFeatureStart == nullptr)
579 : {
580 1046 : m_pabyDataFeatureStart = pabyDataBefore;
581 1046 : m_pabyDataCur = pabyDataBefore;
582 : }
583 :
584 2650 : unsigned int nFeatureLength = 0;
585 2650 : READ_SIZE(pabyData, pabyDataLimit, nFeatureLength);
586 2650 : const GByte *pabyDataFeatureEnd = pabyData + nFeatureLength;
587 2650 : if (bQuickScanFeature)
588 : {
589 511 : if (!QuickScanFeature(pabyData, pabyDataFeatureEnd,
590 : bScanFields, bScanGeometries,
591 : bGeomTypeSet))
592 : {
593 3 : return;
594 : }
595 : }
596 2647 : pabyData = pabyDataFeatureEnd;
597 :
598 2647 : m_nFeatureCount++;
599 : }
600 : else
601 : {
602 41580 : 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 511 : bool OGRMVTLayer::QuickScanFeature(const GByte *pabyData,
654 : const GByte *pabyDataFeatureEnd,
655 : bool bScanFields, bool bScanGeometries,
656 : bool &bGeomTypeSet)
657 : {
658 511 : unsigned int nKey = 0;
659 511 : unsigned int nGeomType = 0;
660 : try
661 : {
662 1963 : while (pabyData < pabyDataFeatureEnd)
663 : {
664 1455 : READ_VARUINT32(pabyData, pabyDataFeatureEnd, nKey);
665 1455 : if (nKey == MAKE_KEY(knFEATURE_TYPE, WT_VARINT))
666 : {
667 492 : READ_VARUINT32(pabyData, pabyDataFeatureEnd, nGeomType);
668 : }
669 963 : else if (nKey == MAKE_KEY(knFEATURE_TAGS, WT_DATA) && bScanFields)
670 : {
671 53 : unsigned int nTagsSize = 0;
672 53 : READ_SIZE(pabyData, pabyDataFeatureEnd, nTagsSize);
673 53 : const GByte *pabyDataTagsEnd = pabyData + nTagsSize;
674 224 : while (pabyData < pabyDataTagsEnd)
675 : {
676 174 : unsigned int nKeyIdx = 0;
677 174 : unsigned int nValIdx = 0;
678 174 : READ_VARUINT32(pabyData, pabyDataTagsEnd, nKeyIdx);
679 174 : READ_VARUINT32(pabyData, pabyDataTagsEnd, nValIdx);
680 173 : if (nKeyIdx >= m_aosKeys.size())
681 : {
682 1 : CPLError(CE_Failure, CPLE_AppDefined,
683 : "Invalid tag key index: %u", nKeyIdx);
684 1 : m_bError = true;
685 1 : return false;
686 : }
687 172 : if (nValIdx >= m_asValues.size())
688 : {
689 1 : CPLError(CE_Failure, CPLE_AppDefined,
690 : "Invalid tag value index: %u", nValIdx);
691 1 : m_bError = true;
692 1 : return false;
693 : }
694 : const int nFieldIdx =
695 171 : m_poFeatureDefn->GetFieldIndex(m_aosKeys[nKeyIdx]);
696 171 : if (nFieldIdx < 0)
697 : {
698 143 : OGRFieldDefn oFieldDefn(m_aosKeys[nKeyIdx],
699 286 : m_asValues[nValIdx].eType);
700 143 : oFieldDefn.SetSubType(m_asValues[nValIdx].eSubType);
701 143 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
702 : }
703 56 : else if (m_poFeatureDefn->GetFieldDefn(nFieldIdx)
704 52 : ->GetType() != m_asValues[nValIdx].eType ||
705 24 : m_poFeatureDefn->GetFieldDefn(nFieldIdx)
706 24 : ->GetSubType() !=
707 24 : m_asValues[nValIdx].eSubType)
708 : {
709 : OGRFieldDefn *poFieldDefn =
710 8 : m_poFeatureDefn->GetFieldDefn(nFieldIdx);
711 8 : OGRFieldType eSrcType(m_asValues[nValIdx].eType);
712 : OGRFieldSubType eSrcSubType(
713 8 : m_asValues[nValIdx].eSubType);
714 8 : MergeFieldDefn(poFieldDefn, eSrcType, eSrcSubType);
715 : }
716 50 : }
717 : }
718 910 : else if (nKey == MAKE_KEY(knFEATURE_GEOMETRY, WT_DATA) &&
719 491 : bScanGeometries && nGeomType >= knGEOM_TYPE_POINT &&
720 : nGeomType <= knGEOM_TYPE_POLYGON)
721 : {
722 490 : unsigned int nGeometrySize = 0;
723 490 : READ_SIZE(pabyData, pabyDataFeatureEnd, nGeometrySize);
724 490 : const GByte *pabyDataGeometryEnd = pabyData + nGeometrySize;
725 490 : OGRwkbGeometryType eType = wkbUnknown;
726 :
727 490 : if (nGeomType == knGEOM_TYPE_POINT)
728 : {
729 79 : eType = wkbPoint;
730 79 : unsigned int nCmdCountCombined = 0;
731 79 : READ_VARUINT32(pabyData, pabyDataGeometryEnd,
732 : nCmdCountCombined);
733 158 : if (GetCmdId(nCmdCountCombined) == knCMD_MOVETO &&
734 79 : GetCmdCount(nCmdCountCombined) > 1)
735 : {
736 0 : eType = wkbMultiPoint;
737 : }
738 : }
739 411 : 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 395 : eType = wkbPolygon;
767 790 : for (int iIter = 0; pabyData < pabyDataGeometryEnd; iIter++)
768 : {
769 430 : if (iIter == 1)
770 : {
771 35 : eType = wkbMultiPolygon;
772 35 : break;
773 : }
774 395 : unsigned int nCmdCountCombined = 0;
775 : unsigned int nLineToCount;
776 : // Should be a moveto
777 395 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
778 395 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
779 395 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
780 395 : READ_VARUINT32(pabyData, pabyDataGeometryEnd,
781 : nCmdCountCombined);
782 395 : nLineToCount = GetCmdCount(nCmdCountCombined);
783 2543 : for (unsigned i = 0; i < 2 * nLineToCount; i++)
784 : {
785 2148 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
786 : }
787 : // Should be a closepath
788 395 : SKIP_VARINT(pabyData, pabyDataGeometryEnd);
789 : }
790 : }
791 :
792 507 : if (bGeomTypeSet && m_poFeatureDefn->GetGeomType() ==
793 17 : OGR_GT_GetCollection(eType))
794 : {
795 : // do nothing
796 : }
797 497 : else if (bGeomTypeSet &&
798 12 : eType == OGR_GT_GetCollection(
799 12 : m_poFeatureDefn->GetGeomType()))
800 : {
801 0 : m_poFeatureDefn->SetGeomType(eType);
802 : }
803 497 : else if (bGeomTypeSet &&
804 12 : m_poFeatureDefn->GetGeomType() != eType)
805 : {
806 0 : m_poFeatureDefn->SetGeomType(wkbUnknown);
807 : }
808 : else
809 : {
810 485 : m_poFeatureDefn->SetGeomType(eType);
811 : }
812 490 : bGeomTypeSet = true;
813 :
814 490 : pabyData = pabyDataGeometryEnd;
815 : }
816 : else
817 : {
818 420 : SKIP_UNKNOWN_FIELD(pabyData, pabyDataFeatureEnd, FALSE);
819 : }
820 : }
821 508 : return true;
822 : }
823 1 : catch (const GPBException &e)
824 : {
825 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
826 1 : return false;
827 : }
828 : }
829 :
830 : /************************************************************************/
831 : /* GetFeatureCount() */
832 : /************************************************************************/
833 :
834 51 : GIntBig OGRMVTLayer::GetFeatureCount(int bForce)
835 : {
836 51 : if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr &&
837 45 : m_nFeatureCount >= 0)
838 : {
839 45 : return m_nFeatureCount;
840 : }
841 6 : return OGRLayer::GetFeatureCount(bForce);
842 : }
843 :
844 : /************************************************************************/
845 : /* ResetReading() */
846 : /************************************************************************/
847 :
848 133 : void OGRMVTLayer::ResetReading()
849 : {
850 133 : m_nFID = 0;
851 133 : m_pabyDataCur = m_pabyDataFeatureStart;
852 133 : }
853 :
854 : /************************************************************************/
855 : /* GetXY() */
856 : /************************************************************************/
857 :
858 368402 : void OGRMVTLayer::GetXY(int nX, int nY, double &dfX, double &dfY)
859 : {
860 368402 : if (m_poDS->m_bGeoreferenced)
861 : {
862 366839 : dfX = m_poDS->m_dfTopX + nX * m_poDS->m_dfTileDimX / m_nExtent;
863 366839 : dfY = m_poDS->m_dfTopY - nY * m_poDS->m_dfTileDimY / m_nExtent;
864 : }
865 : else
866 : {
867 1563 : dfX = nX;
868 1563 : dfY = static_cast<double>(m_nExtent) - nY;
869 : }
870 368402 : }
871 :
872 : /************************************************************************/
873 : /* AddWithOverflowAccepted() */
874 : /************************************************************************/
875 :
876 : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
877 732386 : 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 732386 : return static_cast<int>(static_cast<unsigned>(a) +
887 732386 : static_cast<unsigned>(b));
888 : }
889 :
890 : /************************************************************************/
891 : /* ParseGeometry() */
892 : /************************************************************************/
893 :
894 2105 : OGRGeometry *OGRMVTLayer::ParseGeometry(unsigned int nGeomType,
895 : const GByte *pabyDataGeometryEnd)
896 : {
897 2105 : OGRMultiPoint *poMultiPoint = nullptr;
898 2105 : OGRMultiLineString *poMultiLS = nullptr;
899 2105 : OGRLineString *poLine = nullptr;
900 2105 : OGRMultiPolygon *poMultiPoly = nullptr;
901 2105 : OGRPolygon *poPoly = nullptr;
902 2105 : OGRLinearRing *poRing = nullptr;
903 :
904 : try
905 : {
906 2105 : if (nGeomType == knGEOM_TYPE_POINT)
907 : {
908 116 : unsigned int nCmdCountCombined = 0;
909 : unsigned int nCount;
910 116 : READ_VARUINT32(m_pabyDataCur, pabyDataGeometryEnd,
911 : nCmdCountCombined);
912 116 : nCount = GetCmdCount(nCmdCountCombined);
913 116 : if (GetCmdId(nCmdCountCombined) == knCMD_MOVETO && nCount == 1)
914 : {
915 114 : int nX = 0;
916 114 : int nY = 0;
917 114 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nX);
918 113 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nY);
919 : double dfX;
920 : double dfY;
921 113 : GetXY(nX, nY, dfX, dfY);
922 113 : OGRPoint *poPoint = new OGRPoint(dfX, dfY);
923 113 : 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 105 : return poPoint;
932 : }
933 : }
934 2 : else if (GetCmdId(nCmdCountCombined) == knCMD_MOVETO && nCount > 1)
935 : {
936 2 : int nX = 0;
937 2 : int nY = 0;
938 2 : poMultiPoint = 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 1989 : 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 1966 : else if (nGeomType == knGEOM_TYPE_POLYGON)
1042 : {
1043 1966 : int externalIsClockwise = 0;
1044 1966 : int nX = 0;
1045 1966 : int nY = 0;
1046 5614 : while (m_pabyDataCur < pabyDataGeometryEnd)
1047 : {
1048 3648 : unsigned int nCmdCountCombined = 0;
1049 : unsigned int nLineToCount;
1050 : // Should be a moveto
1051 3648 : SKIP_VARINT(m_pabyDataCur, pabyDataGeometryEnd);
1052 3648 : int nDX = 0;
1053 3648 : int nDY = 0;
1054 3648 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDX);
1055 3648 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDY);
1056 3648 : nX = AddWithOverflowAccepted(nX, nDX);
1057 3648 : nY = AddWithOverflowAccepted(nY, nDY);
1058 : double dfX;
1059 : double dfY;
1060 3648 : GetXY(nX, nY, dfX, dfY);
1061 3648 : poRing = new OGRLinearRing();
1062 3648 : poRing->addPoint(dfX, dfY);
1063 3648 : READ_VARUINT32(m_pabyDataCur, pabyDataGeometryEnd,
1064 : nCmdCountCombined);
1065 3648 : nLineToCount = GetCmdCount(nCmdCountCombined);
1066 366130 : for (unsigned i = 0; i < nLineToCount; i++)
1067 : {
1068 362482 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDX);
1069 362482 : READ_VARSINT32(m_pabyDataCur, pabyDataGeometryEnd, nDY);
1070 : // if( nDX != 0 || nDY != 0 )
1071 : {
1072 362482 : nX = AddWithOverflowAccepted(nX, nDX);
1073 362482 : nY = AddWithOverflowAccepted(nY, nDY);
1074 362482 : GetXY(nX, nY, dfX, dfY);
1075 362482 : poRing->addPoint(dfX, dfY);
1076 : }
1077 : }
1078 : // Should be a closepath
1079 3648 : SKIP_VARINT(m_pabyDataCur, pabyDataGeometryEnd);
1080 3648 : poRing->closeRings();
1081 3648 : if (poPoly == nullptr)
1082 : {
1083 1966 : poPoly = new OGRPolygon();
1084 1966 : poPoly->addRingDirectly(poRing);
1085 1966 : externalIsClockwise = poRing->isClockwise();
1086 1966 : 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 1682 : if (externalIsClockwise != poRing->isClockwise())
1100 : {
1101 69 : poPoly->addRingDirectly(poRing);
1102 : }
1103 : else
1104 : {
1105 1613 : if (poMultiPoly == nullptr)
1106 : {
1107 745 : poMultiPoly = new OGRMultiPolygon();
1108 745 : poMultiPoly->addGeometryDirectly(poPoly);
1109 : }
1110 :
1111 1613 : poPoly = new OGRPolygon();
1112 1613 : poMultiPoly->addGeometryDirectly(poPoly);
1113 1613 : poPoly->addRingDirectly(poRing);
1114 : }
1115 : }
1116 3648 : poRing = nullptr;
1117 : }
1118 3187 : if (poMultiPoly == nullptr && poPoly != nullptr &&
1119 1221 : m_poFeatureDefn->GetGeomType() == wkbMultiPolygon)
1120 : {
1121 867 : poMultiPoly = new OGRMultiPolygon();
1122 867 : poMultiPoly->addGeometryDirectly(poPoly);
1123 : }
1124 1966 : if (poMultiPoly)
1125 : {
1126 1612 : 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 663 : void OGRMVTLayer::SanitizeClippedGeometry(OGRGeometry *&poGeom)
1156 : {
1157 663 : OGRwkbGeometryType eInGeomType = wkbFlatten(poGeom->getGeometryType());
1158 663 : const OGRwkbGeometryType eLayerGeomType = GetGeomType();
1159 663 : 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 663 : if (eInGeomType == wkbGeometryCollection)
1167 : {
1168 118 : OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
1169 118 : OGRGeometry *poTargetSingleGeom = nullptr;
1170 118 : OGRGeometryCollection *poTargetGC = nullptr;
1171 : OGRwkbGeometryType ePartGeom;
1172 118 : if (eLayerGeomType == wkbPoint || eLayerGeomType == wkbMultiPoint)
1173 : {
1174 0 : ePartGeom = wkbPoint;
1175 : }
1176 118 : else if (eLayerGeomType == wkbLineString ||
1177 : eLayerGeomType == wkbMultiLineString)
1178 : {
1179 0 : ePartGeom = wkbLineString;
1180 : }
1181 : else
1182 : {
1183 118 : ePartGeom = wkbPolygon;
1184 : }
1185 405 : for (auto &&poSubGeom : poGC)
1186 : {
1187 287 : if (wkbFlatten(poSubGeom->getGeometryType()) == ePartGeom)
1188 : {
1189 169 : if (poTargetSingleGeom != nullptr)
1190 : {
1191 51 : if (poTargetGC == nullptr)
1192 : {
1193 : poTargetGC = OGRGeometryFactory::createGeometry(
1194 : OGR_GT_GetCollection(ePartGeom))
1195 51 : ->toGeometryCollection();
1196 51 : poGeom = poTargetGC;
1197 51 : poTargetGC->addGeometryDirectly(poTargetSingleGeom);
1198 : }
1199 :
1200 51 : poTargetGC->addGeometry(poSubGeom);
1201 : }
1202 : else
1203 : {
1204 118 : poTargetSingleGeom = poSubGeom->clone();
1205 118 : poGeom = poTargetSingleGeom;
1206 : }
1207 : }
1208 : }
1209 118 : if (poGeom != poGC)
1210 : {
1211 118 : delete poGC;
1212 : }
1213 118 : eInGeomType = wkbFlatten(poGeom->getGeometryType());
1214 : }
1215 :
1216 : // Wrap single into multi if requested by the layer geometry type
1217 663 : if (OGR_GT_GetCollection(eInGeomType) == eLayerGeomType)
1218 : {
1219 : OGRGeometryCollection *poGC =
1220 : OGRGeometryFactory::createGeometry(eLayerGeomType)
1221 400 : ->toGeometryCollection();
1222 400 : poGC->addGeometryDirectly(poGeom);
1223 400 : poGeom = poGC;
1224 400 : return;
1225 : }
1226 : }
1227 :
1228 : /************************************************************************/
1229 : /* GetNextRawFeature() */
1230 : /************************************************************************/
1231 :
1232 2887 : OGRFeature *OGRMVTLayer::GetNextRawFeature()
1233 : {
1234 2887 : if (m_pabyDataCur == nullptr || m_pabyDataCur >= m_pabyDataEnd || m_bError)
1235 : {
1236 120 : return nullptr;
1237 : }
1238 :
1239 2767 : unsigned int nKey = 0;
1240 2767 : const GByte *pabyDataLimit = m_pabyDataEnd;
1241 2767 : OGRFeature *poFeature = nullptr;
1242 2767 : unsigned int nFeatureLength = 0;
1243 2767 : unsigned int nGeomType = 0;
1244 :
1245 : try
1246 : {
1247 : while (true)
1248 : {
1249 2771 : bool bOK = true;
1250 :
1251 34642 : while (m_pabyDataCur < pabyDataLimit)
1252 : {
1253 33984 : READ_VARUINT32(m_pabyDataCur, pabyDataLimit, nKey);
1254 33984 : if (nKey == MAKE_KEY(knLAYER_FEATURES, WT_DATA))
1255 : {
1256 2113 : poFeature = new OGRFeature(m_poFeatureDefn);
1257 2113 : break;
1258 : }
1259 : else
1260 : {
1261 31871 : SKIP_UNKNOWN_FIELD(m_pabyDataCur, pabyDataLimit, FALSE);
1262 : }
1263 : }
1264 :
1265 2771 : if (poFeature == nullptr)
1266 658 : return nullptr;
1267 :
1268 2113 : READ_SIZE(m_pabyDataCur, pabyDataLimit, nFeatureLength);
1269 2113 : const GByte *pabyDataFeatureEnd = m_pabyDataCur + nFeatureLength;
1270 8392 : while (m_pabyDataCur < pabyDataFeatureEnd)
1271 : {
1272 6279 : READ_VARUINT32(m_pabyDataCur, pabyDataFeatureEnd, nKey);
1273 6279 : 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 6265 : else if (nKey == MAKE_KEY(knFEATURE_TYPE, WT_VARINT))
1280 : {
1281 2107 : READ_VARUINT32(m_pabyDataCur, pabyDataFeatureEnd,
1282 : nGeomType);
1283 : }
1284 4158 : else if (nKey == MAKE_KEY(knFEATURE_TAGS, WT_DATA))
1285 : {
1286 2051 : unsigned int nTagsSize = 0;
1287 2051 : READ_SIZE(m_pabyDataCur, pabyDataFeatureEnd, nTagsSize);
1288 2051 : const GByte *pabyDataTagsEnd = m_pabyDataCur + nTagsSize;
1289 62984 : while (m_pabyDataCur < pabyDataTagsEnd)
1290 : {
1291 60933 : unsigned int nKeyIdx = 0;
1292 60933 : unsigned int nValIdx = 0;
1293 60933 : READ_VARUINT32(m_pabyDataCur, pabyDataTagsEnd, nKeyIdx);
1294 60933 : READ_VARUINT32(m_pabyDataCur, pabyDataTagsEnd, nValIdx);
1295 121866 : if (nKeyIdx < m_aosKeys.size() &&
1296 60933 : nValIdx < m_asValues.size())
1297 : {
1298 : const int nFieldIdx =
1299 60933 : m_poFeatureDefn->GetFieldIndex(
1300 60933 : m_aosKeys[nKeyIdx]);
1301 60933 : if (nFieldIdx >= 0)
1302 : {
1303 60933 : if (m_asValues[nValIdx].eType == OFTString)
1304 : {
1305 33101 : poFeature->SetField(
1306 : nFieldIdx,
1307 33101 : m_asValues[nValIdx].sValue.String);
1308 : }
1309 27832 : else if (m_asValues[nValIdx].eType ==
1310 : OFTInteger)
1311 : {
1312 26304 : poFeature->SetField(
1313 : nFieldIdx,
1314 26304 : m_asValues[nValIdx].sValue.Integer);
1315 : }
1316 1528 : else if (m_asValues[nValIdx].eType ==
1317 : OFTInteger64)
1318 : {
1319 456 : poFeature->SetField(
1320 : nFieldIdx,
1321 456 : m_asValues[nValIdx].sValue.Integer64);
1322 : }
1323 1072 : else if (m_asValues[nValIdx].eType == OFTReal)
1324 : {
1325 1072 : poFeature->SetField(
1326 : nFieldIdx,
1327 1072 : m_asValues[nValIdx].sValue.Real);
1328 : }
1329 : }
1330 : }
1331 : }
1332 : }
1333 2107 : else if (nKey == MAKE_KEY(knFEATURE_GEOMETRY, WT_DATA) &&
1334 2105 : nGeomType >= 1 && nGeomType <= 3)
1335 : {
1336 2105 : unsigned int nGeometrySize = 0;
1337 2105 : READ_SIZE(m_pabyDataCur, pabyDataFeatureEnd, nGeometrySize);
1338 2105 : const GByte *pabyDataGeometryEnd =
1339 2105 : m_pabyDataCur + nGeometrySize;
1340 : OGRGeometry *poGeom =
1341 2105 : ParseGeometry(nGeomType, pabyDataGeometryEnd);
1342 2105 : if (poGeom)
1343 : {
1344 : // Clip geometry to tile extent if requested
1345 2104 : if (m_poDS->m_bClip && OGRGeometryFactory::haveGEOS())
1346 : {
1347 2099 : OGREnvelope sEnvelope;
1348 2099 : poGeom->getEnvelope(&sEnvelope);
1349 2099 : if (sEnvelope.MinX >= m_dfTileMinX &&
1350 1858 : sEnvelope.MinY >= m_dfTileMinY &&
1351 1698 : sEnvelope.MaxX <= m_dfTileMaxX &&
1352 1478 : sEnvelope.MaxY <= m_dfTileMaxY)
1353 : {
1354 : // do nothing
1355 : }
1356 667 : else if (sEnvelope.MinX < m_dfTileMaxX &&
1357 663 : sEnvelope.MinY < m_dfTileMaxY &&
1358 663 : sEnvelope.MaxX > m_dfTileMinX &&
1359 663 : sEnvelope.MaxY > m_dfTileMinY)
1360 : {
1361 : OGRGeometry *poClipped =
1362 663 : poGeom->Intersection(&m_oClipPoly);
1363 663 : if (poClipped)
1364 : {
1365 663 : SanitizeClippedGeometry(poClipped);
1366 663 : if (poClipped->IsEmpty())
1367 : {
1368 0 : delete poClipped;
1369 0 : bOK = false;
1370 : }
1371 : else
1372 : {
1373 663 : poClipped->assignSpatialReference(
1374 663 : GetSpatialRef());
1375 663 : poFeature->SetGeometryDirectly(
1376 : poClipped);
1377 663 : delete poGeom;
1378 663 : poGeom = nullptr;
1379 : }
1380 663 : }
1381 : }
1382 : else
1383 : {
1384 4 : bOK = false;
1385 : }
1386 : }
1387 :
1388 2104 : if (poGeom)
1389 : {
1390 1441 : poGeom->assignSpatialReference(GetSpatialRef());
1391 1441 : poFeature->SetGeometryDirectly(poGeom);
1392 : }
1393 : }
1394 :
1395 2105 : m_pabyDataCur = pabyDataGeometryEnd;
1396 : }
1397 : else
1398 : {
1399 2 : SKIP_UNKNOWN_FIELD(m_pabyDataCur, pabyDataFeatureEnd,
1400 : FALSE);
1401 : }
1402 : }
1403 2113 : m_pabyDataCur = pabyDataFeatureEnd;
1404 :
1405 2113 : if (bOK)
1406 : {
1407 2109 : poFeature->SetFID(m_nFID);
1408 2109 : m_nFID++;
1409 2109 : 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 100 : static CPLStringList StripDummyEntries(const CPLStringList &aosInput)
1440 : {
1441 200 : CPLStringList aosOutput;
1442 373 : for (int i = 0; i < aosInput.Count(); i++)
1443 : {
1444 663 : if (aosInput[i] != CPLString(".") && aosInput[i] != CPLString("..") &&
1445 390 : CPLString(aosInput[i]).find(".properties") == std::string::npos)
1446 : {
1447 117 : aosOutput.AddString(aosInput[i]);
1448 : }
1449 : }
1450 200 : 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 141 : void OGRMVTDirectoryLayer::ResetReading()
1551 : {
1552 141 : m_bEOF = false;
1553 141 : m_nXIndex = -1;
1554 141 : m_nYIndex = -1;
1555 141 : delete m_poCurrentTile;
1556 141 : m_poCurrentTile = nullptr;
1557 141 : }
1558 :
1559 : /************************************************************************/
1560 : /* IsBetween() */
1561 : /************************************************************************/
1562 :
1563 140 : static bool IsBetween(int nVal, int nMin, int nMax)
1564 : {
1565 140 : return nVal >= nMin && nVal <= nMax;
1566 : }
1567 :
1568 : /************************************************************************/
1569 : /* ReadNewSubDir() */
1570 : /************************************************************************/
1571 :
1572 118 : void OGRMVTDirectoryLayer::ReadNewSubDir()
1573 : {
1574 118 : delete m_poCurrentTile;
1575 118 : m_poCurrentTile = nullptr;
1576 118 : if (m_bUseReadDir || !m_aosDirContent.empty())
1577 : {
1578 2 : while (
1579 178 : m_nXIndex < m_aosDirContent.Count() &&
1580 71 : (CPLGetValueType(m_aosDirContent[m_nXIndex]) != CPL_VALUE_INTEGER ||
1581 71 : !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 131 : if (m_nXIndex < ((m_bUseReadDir || !m_aosDirContent.empty())
1595 118 : ? m_aosDirContent.Count()
1596 13 : : (1 << m_nZ)))
1597 : {
1598 : m_aosSubDirName =
1599 78 : CPLFormFilenameSafe(m_osDirName,
1600 78 : (m_bUseReadDir || !m_aosDirContent.empty())
1601 69 : ? m_aosDirContent[m_nXIndex]
1602 9 : : CPLSPrintf("%d", m_nXIndex),
1603 78 : nullptr);
1604 78 : if (m_bUseReadDir)
1605 : {
1606 : m_aosSubDirContent =
1607 69 : VSIReadDirEx(m_aosSubDirName, knMAX_FILES_PER_DIR);
1608 69 : 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 69 : m_aosSubDirContent = StripDummyEntries(m_aosSubDirContent);
1615 : }
1616 78 : m_nYIndex = -1;
1617 78 : OpenTileIfNeeded();
1618 : }
1619 : else
1620 : {
1621 40 : m_bEOF = true;
1622 : }
1623 118 : }
1624 :
1625 : /************************************************************************/
1626 : /* OpenTile() */
1627 : /************************************************************************/
1628 :
1629 78 : void OGRMVTDirectoryLayer::OpenTile()
1630 : {
1631 78 : delete m_poCurrentTile;
1632 78 : m_poCurrentTile = nullptr;
1633 78 : if (m_nYIndex < (m_bUseReadDir ? m_aosSubDirContent.Count() : (1 << m_nZ)))
1634 : {
1635 78 : CPLString osFilename = CPLFormFilenameSafe(
1636 : m_aosSubDirName,
1637 78 : m_bUseReadDir ? m_aosSubDirContent[m_nYIndex]
1638 9 : : CPLSPrintf("%d.%s", m_nYIndex,
1639 9 : m_poDS->m_osTileExtension.c_str()),
1640 156 : nullptr);
1641 78 : GDALOpenInfo oOpenInfo(("MVT:" + osFilename).c_str(), GA_ReadOnly);
1642 78 : oOpenInfo.papszOpenOptions = CSLSetNameValue(
1643 : nullptr, "METADATA_FILE",
1644 78 : m_bJsonField ? "" : m_poDS->m_osMetadataMemFilename.c_str());
1645 78 : oOpenInfo.papszOpenOptions = CSLSetNameValue(
1646 : oOpenInfo.papszOpenOptions, "DO_NOT_ERROR_ON_MISSING_TILE", "YES");
1647 78 : m_poCurrentTile =
1648 78 : OGRMVTDataset::Open(&oOpenInfo, /* bRecurseAllowed = */ false);
1649 78 : CSLDestroy(oOpenInfo.papszOpenOptions);
1650 :
1651 9 : int nX = (m_bUseReadDir || !m_aosDirContent.empty())
1652 87 : ? atoi(m_aosDirContent[m_nXIndex])
1653 78 : : m_nXIndex;
1654 : int nY =
1655 78 : m_bUseReadDir ? atoi(m_aosSubDirContent[m_nYIndex]) : m_nYIndex;
1656 78 : m_nFIDBase = (static_cast<GIntBig>(nX) << m_nZ) | nY;
1657 : }
1658 78 : }
1659 :
1660 : /************************************************************************/
1661 : /* OpenTileIfNeeded() */
1662 : /************************************************************************/
1663 :
1664 228 : void OGRMVTDirectoryLayer::OpenTileIfNeeded()
1665 : {
1666 228 : if (m_nXIndex < 0)
1667 : {
1668 77 : m_nXIndex = 0;
1669 77 : ReadNewSubDir();
1670 : }
1671 575 : while ((m_poCurrentTile == nullptr && !m_bEOF) ||
1672 228 : (m_poCurrentTile != nullptr &&
1673 181 : m_poCurrentTile->GetLayerByName(GetName()) == nullptr))
1674 : {
1675 119 : m_nYIndex++;
1676 119 : if (m_bUseReadDir)
1677 : {
1678 175 : while (m_nYIndex < m_aosSubDirContent.Count() &&
1679 69 : (CPLGetValueType(
1680 175 : CPLGetBasenameSafe(m_aosSubDirContent[m_nYIndex])
1681 69 : .c_str()) != CPL_VALUE_INTEGER ||
1682 69 : !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 119 : if (m_nYIndex ==
1696 119 : (m_bUseReadDir ? m_aosSubDirContent.Count() : (1 << m_nZ)))
1697 : {
1698 41 : m_nXIndex++;
1699 41 : ReadNewSubDir();
1700 : }
1701 : else
1702 : {
1703 78 : OpenTile();
1704 : }
1705 : }
1706 228 : }
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 36 : int OGRMVTDirectoryLayer::TestCapability(const char *pszCap) const
1806 : {
1807 36 : if (EQUAL(pszCap, OLCFastGetExtent))
1808 : {
1809 2 : return TRUE;
1810 : }
1811 34 : 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 61 : OGRFeature *OGRMVTDirectoryLayer::CreateFeatureFrom(OGRFeature *poSrcFeature)
1834 : {
1835 :
1836 61 : return OGRMVTCreateFeatureFrom(poSrcFeature, m_poFeatureDefn, m_bJsonField,
1837 122 : GetSpatialRef());
1838 : }
1839 :
1840 : /************************************************************************/
1841 : /* GetNextRawFeature() */
1842 : /************************************************************************/
1843 :
1844 116 : OGRFeature *OGRMVTDirectoryLayer::GetNextRawFeature()
1845 : {
1846 : while (true)
1847 : {
1848 116 : OpenTileIfNeeded();
1849 116 : if (m_poCurrentTile == nullptr)
1850 31 : return nullptr;
1851 : OGRLayer *poUnderlyingLayer =
1852 85 : m_poCurrentTile->GetLayerByName(GetName());
1853 85 : OGRFeature *poUnderlyingFeature = poUnderlyingLayer->GetNextFeature();
1854 85 : if (poUnderlyingFeature != nullptr)
1855 : {
1856 59 : OGRFeature *poFeature = CreateFeatureFrom(poUnderlyingFeature);
1857 118 : poFeature->SetFID(m_nFIDBase +
1858 59 : (poUnderlyingFeature->GetFID() << (2 * m_nZ)));
1859 59 : delete poUnderlyingFeature;
1860 59 : return poFeature;
1861 : }
1862 : else
1863 : {
1864 26 : delete m_poCurrentTile;
1865 26 : m_poCurrentTile = nullptr;
1866 : }
1867 26 : }
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 985 : OGRMVTDataset::OGRMVTDataset(GByte *pabyData)
1924 985 : : m_pabyData(pabyData), m_poSRS(new OGRSpatialReference())
1925 : {
1926 985 : m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1927 :
1928 985 : m_bClip = CPLTestBool(CPLGetConfigOption("OGR_MVT_CLIP", "YES"));
1929 :
1930 : // Default WebMercator tiling scheme
1931 985 : InitWebMercatorTilingScheme(m_poSRS, m_dfTopXOrigin, m_dfTopYOrigin,
1932 985 : m_dfTileDim0);
1933 985 : }
1934 :
1935 : /************************************************************************/
1936 : /* ~OGRMVTDataset() */
1937 : /************************************************************************/
1938 :
1939 1970 : OGRMVTDataset::~OGRMVTDataset()
1940 : {
1941 985 : VSIFree(m_pabyData);
1942 985 : if (!m_osMetadataMemFilename.empty())
1943 17 : VSIUnlink(m_osMetadataMemFilename);
1944 985 : if (m_poSRS)
1945 982 : m_poSRS->Release();
1946 1970 : }
1947 :
1948 : /************************************************************************/
1949 : /* GetLayer() */
1950 : /************************************************************************/
1951 :
1952 1708 : const OGRLayer *OGRMVTDataset::GetLayer(int iLayer) const
1953 :
1954 : {
1955 1708 : if (iLayer < 0 || iLayer >= GetLayerCount())
1956 4 : return nullptr;
1957 1704 : return m_apoLayers[iLayer].get();
1958 : }
1959 :
1960 : /************************************************************************/
1961 : /* Identify() */
1962 : /************************************************************************/
1963 :
1964 54512 : static int OGRMVTDriverIdentify(GDALOpenInfo *poOpenInfo)
1965 :
1966 : {
1967 54512 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "MVT:"))
1968 1820 : return TRUE;
1969 :
1970 52692 : 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 52692 : if (poOpenInfo->bIsDirectory)
1980 : {
1981 718 : 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 688 : return FALSE;
2047 : }
2048 :
2049 51974 : if (poOpenInfo->nHeaderBytes <= 2)
2050 49230 : return FALSE;
2051 :
2052 : // GZip header ?
2053 2744 : 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 2708 : const GByte *pabyData = reinterpret_cast<GByte *>(poOpenInfo->pabyHeader);
2071 2708 : const GByte *const pabyDataStart = pabyData;
2072 : const GByte *pabyLayerStart;
2073 2708 : const GByte *const pabyDataLimit = pabyData + poOpenInfo->nHeaderBytes;
2074 2708 : const GByte *pabyLayerEnd = pabyDataLimit;
2075 2708 : int nKey = 0;
2076 2708 : unsigned int nLayerLength = 0;
2077 2708 : bool bLayerNameFound = false;
2078 2708 : bool bKeyFound = false;
2079 2708 : bool bFeatureFound = false;
2080 2708 : bool bVersionFound = false;
2081 :
2082 : try
2083 : {
2084 2708 : READ_FIELD_KEY(nKey);
2085 2706 : if (nKey != MAKE_KEY(knLAYER, WT_DATA))
2086 2657 : return FALSE;
2087 49 : READ_VARUINT32(pabyData, pabyDataLimit, nLayerLength);
2088 49 : pabyLayerStart = pabyData;
2089 :
2090 : // Sanity check on layer length
2091 49 : if (nLayerLength < static_cast<unsigned>(poOpenInfo->nHeaderBytes -
2092 49 : (pabyData - pabyDataStart)))
2093 : {
2094 7 : if (pabyData[nLayerLength] != MAKE_KEY(knLAYER, WT_DATA))
2095 1 : return FALSE;
2096 6 : 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 544 : while (pabyData < pabyLayerEnd)
2106 : {
2107 498 : READ_VARUINT32(pabyData, pabyLayerEnd, nKey);
2108 498 : auto nFieldNumber = GET_FIELDNUMBER(nKey);
2109 498 : auto nWireType = GET_WIRETYPE(nKey);
2110 498 : if (nFieldNumber == knLAYER_NAME)
2111 : {
2112 48 : if (nWireType != WT_DATA)
2113 : {
2114 0 : CPLDebug("MVT", "Invalid wire type for layer_name field");
2115 : }
2116 48 : char *pszLayerName = nullptr;
2117 48 : unsigned int nTextSize = 0;
2118 48 : READ_TEXT_WITH_SIZE(pabyData, pabyLayerEnd, pszLayerName,
2119 : nTextSize);
2120 48 : 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 48 : CPLFree(pszLayerName);
2127 48 : bLayerNameFound = true;
2128 : }
2129 450 : else if (nFieldNumber == knLAYER_FEATURES)
2130 : {
2131 52 : if (nWireType != WT_DATA)
2132 : {
2133 0 : CPLDebug("MVT",
2134 : "Invalid wire type for layer_features field");
2135 : }
2136 52 : unsigned int nFeatureLength = 0;
2137 52 : unsigned int nGeomType = 0;
2138 52 : READ_VARUINT32(pabyData, pabyLayerEnd, nFeatureLength);
2139 52 : if (nFeatureLength > nLayerLength - (pabyData - pabyLayerStart))
2140 : {
2141 0 : CPLDebug("MVT", "Protobuf error: line %d", __LINE__);
2142 0 : return FALSE;
2143 : }
2144 52 : bFeatureFound = true;
2145 :
2146 52 : const GByte *const pabyDataFeatureStart = pabyData;
2147 : const GByte *const pabyDataFeatureEnd =
2148 : pabyDataStart +
2149 104 : std::min(static_cast<int>(pabyData + nFeatureLength -
2150 : pabyDataStart),
2151 52 : poOpenInfo->nHeaderBytes);
2152 168 : while (pabyData < pabyDataFeatureEnd)
2153 : {
2154 118 : READ_VARUINT32(pabyData, pabyDataFeatureEnd, nKey);
2155 118 : nFieldNumber = GET_FIELDNUMBER(nKey);
2156 118 : nWireType = GET_WIRETYPE(nKey);
2157 118 : if (nFieldNumber == knFEATURE_TYPE)
2158 : {
2159 48 : if (nWireType != WT_VARINT)
2160 : {
2161 0 : CPLDebug(
2162 : "MVT",
2163 : "Invalid wire type for feature_type field");
2164 0 : return FALSE;
2165 : }
2166 48 : READ_VARUINT32(pabyData, pabyDataFeatureEnd, nGeomType);
2167 48 : if (nGeomType > knGEOM_TYPE_POLYGON)
2168 : {
2169 0 : CPLDebug("MVT", "Protobuf error: line %d",
2170 : __LINE__);
2171 0 : return FALSE;
2172 : }
2173 : }
2174 70 : 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 52 : 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 52 : else if (nKey == MAKE_KEY(knFEATURE_GEOMETRY, WT_DATA) &&
2222 48 : nGeomType >= knGEOM_TYPE_POINT &&
2223 : nGeomType <= knGEOM_TYPE_POLYGON)
2224 : {
2225 48 : unsigned int nGeometrySize = 0;
2226 48 : READ_VARUINT32(pabyData, pabyDataFeatureEnd,
2227 : nGeometrySize);
2228 48 : if (nGeometrySize == 0 ||
2229 48 : nGeometrySize >
2230 48 : nFeatureLength -
2231 48 : (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 96 : std::min(static_cast<int>(pabyData + nGeometrySize -
2240 : pabyDataStart),
2241 48 : poOpenInfo->nHeaderBytes);
2242 :
2243 48 : if (nGeomType == knGEOM_TYPE_POINT)
2244 : {
2245 14 : unsigned int nCmdCountCombined = 0;
2246 : unsigned int nCount;
2247 14 : READ_VARUINT32(pabyData, pabyDataGeometryEnd,
2248 : nCmdCountCombined);
2249 14 : nCount = GetCmdCount(nCmdCountCombined);
2250 28 : if (GetCmdId(nCmdCountCombined) != knCMD_MOVETO ||
2251 28 : nCount == 0 || nCount > 10 * 1024 * 1024)
2252 : {
2253 0 : CPLDebug("MVT", "Protobuf error: line %d",
2254 : __LINE__);
2255 0 : return FALSE;
2256 : }
2257 46 : for (unsigned i = 0; i < 2 * nCount; i++)
2258 : {
2259 32 : 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 46 : pabyData = pabyDataGeometryEnd;
2343 : }
2344 : else
2345 : {
2346 4 : SKIP_UNKNOWN_FIELD(pabyData, pabyDataFeatureEnd, FALSE);
2347 : }
2348 : }
2349 :
2350 50 : pabyData = pabyDataFeatureEnd;
2351 : }
2352 398 : 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 246 : 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 90 : else if (GET_FIELDNUMBER(nKey) == knLAYER_EXTENT &&
2389 42 : 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 90 : else if (nFieldNumber == knLAYER_VERSION)
2408 : {
2409 46 : if (nWireType != WT_VARINT)
2410 : {
2411 0 : CPLDebug("MVT", "Invalid wire type for version field");
2412 0 : return FALSE;
2413 : }
2414 46 : unsigned int nVersion = 0;
2415 46 : READ_VARUINT32(pabyData, pabyLayerEnd, nVersion);
2416 46 : if (nVersion != 1 && nVersion != 2)
2417 : {
2418 0 : CPLDebug("MVT", "Invalid version: %u", nVersion);
2419 0 : return FALSE;
2420 : }
2421 46 : bVersionFound = true;
2422 : }
2423 : else
2424 : {
2425 44 : SKIP_UNKNOWN_FIELD(pabyData, pabyLayerEnd, FALSE);
2426 : }
2427 : }
2428 : }
2429 4 : catch (const GPBException &)
2430 : {
2431 : }
2432 :
2433 50 : 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 927 : 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 1854 : CPLJSONDocument oDoc;
2464 :
2465 : bool bLoadOK;
2466 927 : if (!osMetadataContent.empty())
2467 : {
2468 3 : bLoadOK = oDoc.LoadMemory(osMetadataContent);
2469 : }
2470 1848 : else if (STARTS_WITH(osMetadataFile, "http://") ||
2471 924 : STARTS_WITH(osMetadataFile, "https://"))
2472 : {
2473 0 : bLoadOK = oDoc.LoadUrl(osMetadataFile, nullptr);
2474 : }
2475 : else
2476 : {
2477 924 : bLoadOK = oDoc.Load(osMetadataFile);
2478 : }
2479 927 : if (!bLoadOK)
2480 2 : return false;
2481 :
2482 2775 : const CPLJSONObject oCrs(oDoc.GetRoot().GetObj("crs"));
2483 : const CPLJSONObject oTopX(
2484 2775 : oDoc.GetRoot().GetObj("tile_origin_upper_left_x"));
2485 : const CPLJSONObject oTopY(
2486 2775 : oDoc.GetRoot().GetObj("tile_origin_upper_left_y"));
2487 : const CPLJSONObject oTileDim0(
2488 2775 : oDoc.GetRoot().GetObj("tile_dimension_zoom_0"));
2489 925 : nTileMatrixWidth0 = 1;
2490 925 : nTileMatrixHeight0 = 1;
2491 931 : 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 925 : oVectorLayers.Deinit();
2515 925 : oTileStatLayers.Deinit();
2516 :
2517 2775 : CPLJSONObject oJson = oDoc.GetRoot().GetObj("json");
2518 925 : if (!(oJson.IsValid() && oJson.GetType() == CPLJSONObject::Type::String))
2519 : {
2520 534 : oVectorLayers = oDoc.GetRoot().GetArray("vector_layers");
2521 :
2522 534 : oTileStatLayers = oDoc.GetRoot().GetArray("tilestats/layers");
2523 : }
2524 : else
2525 : {
2526 391 : CPLJSONDocument oJsonDoc;
2527 391 : if (!oJsonDoc.LoadMemory(oJson.ToString()))
2528 : {
2529 1 : return false;
2530 : }
2531 :
2532 390 : oVectorLayers = oJsonDoc.GetRoot().GetArray("vector_layers");
2533 :
2534 390 : oTileStatLayers = oJsonDoc.GetRoot().GetArray("tilestats/layers");
2535 : }
2536 :
2537 924 : oBounds = oDoc.GetRoot().GetObj("bounds");
2538 :
2539 924 : if (!osMetadataMemFilename.empty())
2540 : {
2541 17 : oDoc.Save(osMetadataMemFilename);
2542 : }
2543 :
2544 924 : 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 21 : CPLString osTileExtension(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
2604 42 : "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 : 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 = std::move(osTileExtension);
2898 17 : poDS->m_osMetadataMemFilename = std::move(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 904 : GDALDataset *OGRMVTDataset::Open(GDALOpenInfo *poOpenInfo)
2932 : {
2933 904 : return Open(poOpenInfo, true);
2934 : }
2935 :
2936 994 : GDALDataset *OGRMVTDataset::Open(GDALOpenInfo *poOpenInfo, bool bRecurseAllowed)
2937 :
2938 : {
2939 994 : if (!OGRMVTDriverIdentify(poOpenInfo) || poOpenInfo->eAccess == GA_Update)
2940 0 : return nullptr;
2941 :
2942 994 : VSILFILE *fp = poOpenInfo->fpL;
2943 1988 : CPLString osFilename(poOpenInfo->pszFilename);
2944 994 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "MVT:"))
2945 : {
2946 955 : osFilename = poOpenInfo->pszFilename + strlen("MVT:");
2947 1910 : if (STARTS_WITH(osFilename, "/vsigzip/http://") ||
2948 955 : 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 865 : if (bRecurseAllowed && !STARTS_WITH(osFilename, "/vsigzip/") &&
2957 864 : strchr((CPLGetFilename(osFilename)), '.') == nullptr &&
2958 1820 : 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 1814 : if (bRecurseAllowed &&
2971 862 : (STARTS_WITH(osFilename, "/vsicurl") ||
2972 862 : STARTS_WITH(osFilename, "http://") ||
2973 1814 : 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 1884 : if (!STARTS_WITH(osFilename, "http://") &&
2985 937 : !STARTS_WITH(osFilename, "https://"))
2986 : {
2987 : CPLConfigOptionSetter oSetter("CPL_VSIL_GZIP_WRITE_PROPERTIES",
2988 1874 : "NO", false);
2989 : CPLConfigOptionSetter oSetter2("CPL_VSIL_GZIP_SAVE_INFO", "NO",
2990 1874 : false);
2991 937 : fp = VSIFOpenL(osFilename, "rb");
2992 : // Is it a gzipped file ?
2993 937 : if (fp && !STARTS_WITH(osFilename, "/vsigzip/"))
2994 : {
2995 935 : GByte abyHeaderBytes[2] = {0, 0};
2996 935 : VSIFReadL(abyHeaderBytes, 2, 1, fp);
2997 935 : if (abyHeaderBytes[0] == 0x1F && abyHeaderBytes[1] == 0x8B)
2998 : {
2999 399 : VSIFCloseL(fp);
3000 399 : fp = VSIFOpenL(("/vsigzip/" + osFilename).c_str(), "rb");
3001 : }
3002 : }
3003 : }
3004 : }
3005 78 : else if (bRecurseAllowed &&
3006 39 : (poOpenInfo->bIsDirectory ||
3007 24 : (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 24 : else if (poOpenInfo->nHeaderBytes >= 2 &&
3015 24 : 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 6 : poOpenInfo->fpL = nullptr;
3025 : }
3026 972 : if (fp == nullptr && !STARTS_WITH(osFilename, "http://") &&
3027 1 : !STARTS_WITH(osFilename, "https://"))
3028 : {
3029 1 : return nullptr;
3030 : }
3031 :
3032 1940 : CPLString osY = CPLGetBasenameSafe(osFilename);
3033 2910 : CPLString osX = CPLGetBasenameSafe(CPLGetPathSafe(osFilename).c_str());
3034 1940 : CPLString osZ = CPLGetBasenameSafe(
3035 3880 : CPLGetPathSafe(CPLGetPathSafe(osFilename).c_str()).c_str());
3036 970 : size_t nPos = osY.find('.');
3037 970 : if (nPos != std::string::npos)
3038 0 : osY.resize(nPos);
3039 :
3040 1940 : CPLString osMetadataFile;
3041 970 : if (CSLFetchNameValue(poOpenInfo->papszOpenOptions, "METADATA_FILE"))
3042 : {
3043 : osMetadataFile =
3044 948 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "METADATA_FILE");
3045 : }
3046 22 : else if (CPLGetValueType(osX) == CPL_VALUE_INTEGER &&
3047 34 : 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 970 : if (CSLFetchNameValue(poOpenInfo->papszOpenOptions, "X") &&
3067 1799 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "Y") &&
3068 829 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "Z"))
3069 : {
3070 829 : osX = CSLFetchNameValue(poOpenInfo->papszOpenOptions, "X");
3071 829 : osY = CSLFetchNameValue(poOpenInfo->papszOpenOptions, "Y");
3072 829 : osZ = CSLFetchNameValue(poOpenInfo->papszOpenOptions, "Z");
3073 : }
3074 :
3075 : GByte *pabyDataMod;
3076 : size_t nFileSize;
3077 :
3078 970 : 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 960 : VSIFSeekL(fp, 0, SEEK_END);
3124 960 : vsi_l_offset nFileSizeL = VSIFTellL(fp);
3125 960 : if (nFileSizeL > 10 * 1024 * 1024)
3126 : {
3127 0 : VSIFCloseL(fp);
3128 0 : return nullptr;
3129 : }
3130 960 : nFileSize = static_cast<size_t>(nFileSizeL);
3131 960 : pabyDataMod = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nFileSize + 1));
3132 960 : if (pabyDataMod == nullptr)
3133 : {
3134 0 : VSIFCloseL(fp);
3135 0 : return nullptr;
3136 : }
3137 960 : VSIFSeekL(fp, 0, SEEK_SET);
3138 960 : VSIFReadL(pabyDataMod, 1, nFileSize, fp);
3139 960 : pabyDataMod[nFileSize] = 0;
3140 960 : VSIFCloseL(fp);
3141 : }
3142 :
3143 965 : const GByte *pabyData = pabyDataMod;
3144 :
3145 : // First scan to browse through layers
3146 965 : const GByte *pabyDataLimit = pabyData + nFileSize;
3147 965 : int nKey = 0;
3148 965 : OGRMVTDataset *poDS = new OGRMVTDataset(pabyDataMod);
3149 965 : poDS->SetDescription(poOpenInfo->pszFilename);
3150 1930 : poDS->m_bClip =
3151 965 : CPLFetchBool(poOpenInfo->papszOpenOptions, "CLIP", poDS->m_bClip);
3152 :
3153 1923 : if (!(CPLGetValueType(osX) == CPL_VALUE_INTEGER &&
3154 958 : CPLGetValueType(osY) == CPL_VALUE_INTEGER &&
3155 933 : CPLGetValueType(osZ) == CPL_VALUE_INTEGER))
3156 : {
3157 : // See
3158 : // https://github.com/mapbox/mvt-fixtures/tree/master/real-world/compressed
3159 32 : int nX = 0;
3160 32 : int nY = 0;
3161 32 : int nZ = 0;
3162 : CPLString osBasename(
3163 96 : CPLGetBasenameSafe(CPLGetBasenameSafe(osFilename).c_str()));
3164 63 : if (sscanf(osBasename, "%d-%d-%d", &nZ, &nX, &nY) == 3 ||
3165 31 : 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 1930 : CPLJSONArray oVectorLayers;
3174 965 : oVectorLayers.Deinit();
3175 :
3176 1930 : CPLJSONArray oTileStatLayers;
3177 965 : oTileStatLayers.Deinit();
3178 :
3179 965 : if (!osMetadataFile.empty())
3180 : {
3181 910 : CPLJSONObject oBounds;
3182 910 : LoadMetadata(osMetadataFile, CPLString(), oVectorLayers,
3183 : oTileStatLayers, oBounds, poDS->m_poSRS,
3184 910 : poDS->m_dfTopXOrigin, poDS->m_dfTopYOrigin,
3185 910 : poDS->m_dfTileDim0, poDS->m_nTileMatrixWidth0,
3186 1820 : poDS->m_nTileMatrixHeight0, CPLString());
3187 : }
3188 :
3189 : const char *pszGeorefTopX =
3190 965 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "GEOREF_TOPX");
3191 : const char *pszGeorefTopY =
3192 965 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "GEOREF_TOPY");
3193 : const char *pszGeorefTileDimX =
3194 965 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "GEOREF_TILEDIMX");
3195 : const char *pszGeorefTileDimY =
3196 965 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "GEOREF_TILEDIMY");
3197 965 : 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 962 : else if (CPLGetValueType(osX) == CPL_VALUE_INTEGER &&
3209 1896 : CPLGetValueType(osY) == CPL_VALUE_INTEGER &&
3210 934 : CPLGetValueType(osZ) == CPL_VALUE_INTEGER)
3211 : {
3212 934 : int nX = atoi(osX);
3213 934 : int nY = atoi(osY);
3214 934 : int nZ = atoi(osZ);
3215 934 : if (nZ >= 0 && nZ < 30 && nX >= 0 &&
3216 934 : nX < (static_cast<int64_t>(1) << nZ) * poDS->m_nTileMatrixWidth0 &&
3217 932 : nY >= 0 &&
3218 932 : nY < (static_cast<int64_t>(1) << nZ) * poDS->m_nTileMatrixHeight0)
3219 : {
3220 932 : poDS->m_bGeoreferenced = true;
3221 932 : poDS->m_dfTileDimX = poDS->m_dfTileDim0 / (1 << nZ);
3222 932 : poDS->m_dfTileDimY = poDS->m_dfTileDimX;
3223 932 : poDS->m_dfTopX = poDS->m_dfTopXOrigin + nX * poDS->m_dfTileDimX;
3224 932 : poDS->m_dfTopY = poDS->m_dfTopYOrigin - nY * poDS->m_dfTileDimY;
3225 : }
3226 : }
3227 :
3228 : try
3229 : {
3230 2013 : while (pabyData < pabyDataLimit)
3231 : {
3232 1049 : READ_FIELD_KEY(nKey);
3233 1049 : if (nKey == MAKE_KEY(knLAYER, WT_DATA))
3234 : {
3235 1048 : unsigned int nLayerSize = 0;
3236 1048 : READ_SIZE(pabyData, pabyDataLimit, nLayerSize);
3237 1048 : const GByte *pabyDataLayer = pabyData;
3238 1048 : const GByte *pabyDataLimitLayer = pabyData + nLayerSize;
3239 1296 : while (pabyData < pabyDataLimitLayer)
3240 : {
3241 1296 : READ_VARINT32(pabyData, pabyDataLimitLayer, nKey);
3242 1296 : if (nKey == MAKE_KEY(knLAYER_NAME, WT_DATA))
3243 : {
3244 1048 : char *pszLayerName = nullptr;
3245 1048 : READ_TEXT(pabyData, pabyDataLimitLayer, pszLayerName);
3246 :
3247 2096 : CPLJSONObject oFields;
3248 1048 : oFields.Deinit();
3249 1048 : if (oVectorLayers.IsValid())
3250 : {
3251 1096 : for (int i = 0; i < oVectorLayers.Size(); i++)
3252 : {
3253 : CPLJSONObject oId =
3254 2192 : oVectorLayers[i].GetObj("id");
3255 2192 : if (oId.IsValid() &&
3256 1096 : oId.GetType() ==
3257 : CPLJSONObject::Type::String)
3258 : {
3259 1096 : if (oId.ToString() == pszLayerName)
3260 : {
3261 : oFields =
3262 967 : oVectorLayers[i].GetObj("fields");
3263 967 : break;
3264 : }
3265 : }
3266 : }
3267 : }
3268 :
3269 1048 : OGRwkbGeometryType eGeomType = wkbUnknown;
3270 1048 : if (oTileStatLayers.IsValid())
3271 : {
3272 564 : eGeomType = OGRMVTFindGeomTypeFromTileStat(
3273 : oTileStatLayers, pszLayerName);
3274 : }
3275 : CPLJSONArray oAttributesFromTileStats =
3276 : OGRMVTFindAttributesFromTileStat(oTileStatLayers,
3277 2096 : pszLayerName);
3278 :
3279 1048 : poDS->m_apoLayers.push_back(
3280 2096 : std::make_unique<OGRMVTLayer>(
3281 : poDS, pszLayerName, pabyDataLayer, nLayerSize,
3282 : oFields, oAttributesFromTileStats, eGeomType));
3283 1048 : CPLFree(pszLayerName);
3284 1048 : break;
3285 : }
3286 : else
3287 : {
3288 248 : SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimitLayer, FALSE);
3289 : }
3290 : }
3291 1048 : pabyData = pabyDataLimitLayer;
3292 : }
3293 : else
3294 : {
3295 1 : if (nKey == 0 && !poDS->m_apoLayers.empty())
3296 : {
3297 : // File attached to https://github.com/OSGeo/gdal/issues/13268
3298 : // has 0-byte padding after the layer definition.
3299 1 : break;
3300 : }
3301 0 : SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, FALSE);
3302 : }
3303 : }
3304 :
3305 965 : return poDS;
3306 : }
3307 0 : catch (const GPBException &e)
3308 : {
3309 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
3310 0 : delete poDS;
3311 0 : return nullptr;
3312 : }
3313 : }
3314 :
3315 : #ifdef HAVE_MVT_WRITE_SUPPORT
3316 :
3317 : /************************************************************************/
3318 : /* OGRMVTWriterDataset */
3319 : /************************************************************************/
3320 :
3321 : class OGRMVTWriterLayer;
3322 :
3323 : struct OGRMVTFeatureContent
3324 : {
3325 : std::vector<std::pair<std::string, MVTTileLayerValue>> oValues;
3326 : GIntBig nFID;
3327 : };
3328 :
3329 : class OGRMVTWriterDataset final : public GDALDataset
3330 : {
3331 : class MVTFieldProperties
3332 : {
3333 : public:
3334 : CPLString m_osName;
3335 : std::set<MVTTileLayerValue> m_oSetValues;
3336 : std::set<MVTTileLayerValue> m_oSetAllValues;
3337 : double m_dfMinVal = 0;
3338 : double m_dfMaxVal = 0;
3339 : bool m_bAllInt = false;
3340 : MVTTileLayerValue::ValueType m_eType =
3341 : MVTTileLayerValue::ValueType::NONE;
3342 : };
3343 :
3344 : class MVTLayerProperties
3345 : {
3346 : public:
3347 : int m_nMinZoom = 0;
3348 : int m_nMaxZoom = 0;
3349 : std::map<MVTTileLayerFeature::GeomType, GIntBig> m_oCountGeomType;
3350 : std::map<CPLString, size_t> m_oMapFieldNameToIdx;
3351 : std::vector<MVTFieldProperties> m_aoFields;
3352 : std::set<CPLString> m_oSetFields;
3353 : };
3354 :
3355 : std::vector<std::unique_ptr<OGRMVTWriterLayer>> m_apoLayers;
3356 : CPLString m_osTempDB;
3357 : mutable std::mutex m_oDBMutex;
3358 : mutable bool m_bWriteFeatureError = false;
3359 : sqlite3_vfs *m_pMyVFS = nullptr;
3360 : sqlite3 *m_hDB = nullptr;
3361 : sqlite3_stmt *m_hInsertStmt = nullptr;
3362 : int m_nMinZoom = 0;
3363 : int m_nMaxZoom = 5;
3364 : double m_dfSimplification = 0.0;
3365 : double m_dfSimplificationMaxZoom = 0.0;
3366 : CPLJSONDocument m_oConf;
3367 : unsigned m_nExtent = knDEFAULT_EXTENT;
3368 : int m_nMetadataVersion = 2;
3369 : int m_nMVTVersion = 2;
3370 : int m_nBuffer = 5 * knDEFAULT_EXTENT / 256;
3371 : bool m_bGZip = true;
3372 : mutable CPLWorkerThreadPool m_oThreadPool;
3373 : bool m_bThreadPoolOK = false;
3374 : mutable GIntBig m_nTempTiles = 0;
3375 : CPLString m_osName;
3376 : CPLString m_osDescription;
3377 : CPLString m_osType{"overlay"};
3378 : sqlite3 *m_hDBMBTILES = nullptr;
3379 : OGREnvelope m_oEnvelope;
3380 : bool m_bMaxTileSizeOptSpecified = false;
3381 : bool m_bMaxFeaturesOptSpecified = false;
3382 : unsigned m_nMaxTileSize = 500000;
3383 : unsigned m_nMaxFeatures = 200000;
3384 : std::map<std::string, std::string> m_oMapLayerNameToDesc;
3385 : std::map<std::string, GIntBig> m_oMapLayerNameToFeatureCount;
3386 : CPLString m_osBounds;
3387 : CPLString m_osCenter;
3388 : CPLString m_osExtension{"pbf"};
3389 : OGRSpatialReference *m_poSRS = nullptr;
3390 : double m_dfTopX = 0.0;
3391 : double m_dfTopY = 0.0;
3392 : double m_dfTileDim0 = 0.0;
3393 : int m_nTileMatrixWidth0 =
3394 : 1; // Number of tiles along X axis at zoom level 0
3395 : int m_nTileMatrixHeight0 =
3396 : 1; // Number of tiles along Y axis at zoom level 0
3397 : bool m_bReuseTempFile = false; // debug only
3398 :
3399 : OGRErr PreGenerateForTile(
3400 : int nZ, int nX, int nY, const CPLString &osTargetName,
3401 : bool bIsMaxZoomForLayer,
3402 : const std::shared_ptr<OGRMVTFeatureContent> &poFeatureContent,
3403 : GIntBig nSerial, const std::shared_ptr<OGRGeometry> &poGeom,
3404 : const OGREnvelope &sEnvelope) const;
3405 :
3406 : static void WriterTaskFunc(void *pParam);
3407 :
3408 : OGRErr PreGenerateForTileReal(int nZ, int nX, int nY,
3409 : const CPLString &osTargetName,
3410 : bool bIsMaxZoomForLayer,
3411 : const OGRMVTFeatureContent *poFeatureContent,
3412 : GIntBig nSerial, const OGRGeometry *poGeom,
3413 : const OGREnvelope &sEnvelope) const;
3414 :
3415 : void ConvertToTileCoords(double dfX, double dfY, int &nX, int &nY,
3416 : double dfTopX, double dfTopY,
3417 : double dfTileDim) const;
3418 : bool EncodeLineString(MVTTileLayerFeature *poGPBFeature,
3419 : const OGRLineString *poLS, OGRLineString *poOutLS,
3420 : bool bWriteLastPoint, bool bReverseOrder,
3421 : GUInt32 nMinLineTo, double dfTopX, double dfTopY,
3422 : double dfTileDim, int &nLastX, int &nLastY) const;
3423 : bool EncodePolygon(MVTTileLayerFeature *poGPBFeature,
3424 : const OGRPolygon *poPoly, OGRPolygon *poOutPoly,
3425 : double dfTopX, double dfTopY, double dfTileDim,
3426 : int &nLastX, int &nLastY, double &dfArea) const;
3427 : #ifdef notdef
3428 : bool EncodeRepairedOuterRing(MVTTileLayerFeature *poGPBFeature,
3429 : OGRPolygon &oOutPoly, int &nLastX,
3430 : int &nLastY) const;
3431 : #endif
3432 :
3433 : static void UpdateLayerProperties(MVTLayerProperties *poLayerProperties,
3434 : const std::string &osKey,
3435 : const MVTTileLayerValue &oValue);
3436 :
3437 : void EncodeFeature(const void *pabyBlob, int nBlobSize,
3438 : std::shared_ptr<MVTTileLayer> &poTargetLayer,
3439 : std::map<CPLString, GUInt32> &oMapKeyToIdx,
3440 : std::map<MVTTileLayerValue, GUInt32> &oMapValueToIdx,
3441 : MVTLayerProperties *poLayerProperties, GUInt32 nExtent,
3442 : unsigned &nFeaturesInTile);
3443 :
3444 : std::string
3445 : EncodeTile(int nZ, int nX, int nY, sqlite3_stmt *hStmtLayer,
3446 : sqlite3_stmt *hStmtRows,
3447 : std::map<CPLString, MVTLayerProperties> &oMapLayerProps,
3448 : std::set<CPLString> &oSetLayers, GIntBig &nTempTilesRead);
3449 :
3450 : std::string RecodeTileLowerResolution(int nZ, int nX, int nY, int nExtent,
3451 : sqlite3_stmt *hStmtLayer,
3452 : sqlite3_stmt *hStmtRows);
3453 :
3454 : bool CreateOutput();
3455 :
3456 : bool GenerateMetadata(size_t nLayers,
3457 : const std::map<CPLString, MVTLayerProperties> &oMap);
3458 :
3459 : public:
3460 : OGRMVTWriterDataset();
3461 : ~OGRMVTWriterDataset() override;
3462 :
3463 : CPLErr Close() override;
3464 :
3465 : OGRLayer *ICreateLayer(const char *pszName,
3466 : const OGRGeomFieldDefn *poGeomFieldDefn,
3467 : CSLConstList papszOptions) override;
3468 :
3469 : int TestCapability(const char *) const override;
3470 :
3471 : OGRErr WriteFeature(OGRMVTWriterLayer *poLayer, OGRFeature *poFeature,
3472 : GIntBig nSerial, OGRGeometry *poGeom);
3473 :
3474 : static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
3475 : int nBandsIn, GDALDataType eDT,
3476 : char **papszOptions);
3477 :
3478 179 : OGRSpatialReference *GetSRS()
3479 : {
3480 179 : return m_poSRS;
3481 : }
3482 : };
3483 :
3484 : /************************************************************************/
3485 : /* OGRMVTWriterLayer */
3486 : /************************************************************************/
3487 :
3488 : class OGRMVTWriterLayer final : public OGRLayer
3489 : {
3490 : friend class OGRMVTWriterDataset;
3491 :
3492 : OGRMVTWriterDataset *m_poDS = nullptr;
3493 : OGRFeatureDefn *m_poFeatureDefn = nullptr;
3494 : OGRCoordinateTransformation *m_poCT = nullptr;
3495 : GIntBig m_nSerial = 0;
3496 : int m_nMinZoom = 0;
3497 : int m_nMaxZoom = 5;
3498 : CPLString m_osTargetName;
3499 :
3500 : public:
3501 : OGRMVTWriterLayer(OGRMVTWriterDataset *poDS, const char *pszLayerName,
3502 : OGRSpatialReference *poSRS);
3503 : ~OGRMVTWriterLayer() override;
3504 :
3505 48 : void ResetReading() override
3506 : {
3507 48 : }
3508 :
3509 48 : OGRFeature *GetNextFeature() override
3510 : {
3511 48 : return nullptr;
3512 : }
3513 :
3514 1432 : const OGRFeatureDefn *GetLayerDefn() const override
3515 : {
3516 1432 : return m_poFeatureDefn;
3517 : }
3518 :
3519 : int TestCapability(const char *) const override;
3520 : OGRErr ICreateFeature(OGRFeature *) override;
3521 : OGRErr CreateField(const OGRFieldDefn *, int) override;
3522 :
3523 49 : GDALDataset *GetDataset() override
3524 : {
3525 49 : return m_poDS;
3526 : }
3527 : };
3528 :
3529 : /************************************************************************/
3530 : /* OGRMVTWriterLayer() */
3531 : /************************************************************************/
3532 :
3533 170 : OGRMVTWriterLayer::OGRMVTWriterLayer(OGRMVTWriterDataset *poDS,
3534 : const char *pszLayerName,
3535 170 : OGRSpatialReference *poSRSIn)
3536 : {
3537 170 : m_poDS = poDS;
3538 170 : m_poFeatureDefn = new OGRFeatureDefn(pszLayerName);
3539 170 : SetDescription(m_poFeatureDefn->GetName());
3540 170 : m_poFeatureDefn->Reference();
3541 :
3542 170 : m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poDS->GetSRS());
3543 :
3544 170 : if (poSRSIn != nullptr && !poDS->GetSRS()->IsSame(poSRSIn))
3545 : {
3546 2 : m_poCT = OGRCreateCoordinateTransformation(poSRSIn, poDS->GetSRS());
3547 2 : if (m_poCT == nullptr)
3548 : {
3549 : // If we can't create a transformation, issue a warning - but
3550 : // continue the transformation.
3551 1 : CPLError(CE_Warning, CPLE_AppDefined,
3552 : "Failed to create coordinate transformation between the "
3553 : "input and target coordinate systems.");
3554 : }
3555 : }
3556 170 : }
3557 :
3558 : /************************************************************************/
3559 : /* ~OGRMVTWriterLayer() */
3560 : /************************************************************************/
3561 :
3562 340 : OGRMVTWriterLayer::~OGRMVTWriterLayer()
3563 : {
3564 170 : m_poFeatureDefn->Release();
3565 170 : delete m_poCT;
3566 340 : }
3567 :
3568 : /************************************************************************/
3569 : /* TestCapability() */
3570 : /************************************************************************/
3571 :
3572 356 : int OGRMVTWriterLayer::TestCapability(const char *pszCap) const
3573 : {
3574 :
3575 356 : if (EQUAL(pszCap, OLCSequentialWrite) || EQUAL(pszCap, OLCCreateField))
3576 96 : return true;
3577 260 : return false;
3578 : }
3579 :
3580 : /************************************************************************/
3581 : /* CreateField() */
3582 : /************************************************************************/
3583 :
3584 313 : OGRErr OGRMVTWriterLayer::CreateField(const OGRFieldDefn *poFieldDefn, int)
3585 : {
3586 313 : m_poFeatureDefn->AddFieldDefn(poFieldDefn);
3587 313 : return OGRERR_NONE;
3588 : }
3589 :
3590 : /************************************************************************/
3591 : /* ICreateFeature() */
3592 : /************************************************************************/
3593 :
3594 294 : OGRErr OGRMVTWriterLayer::ICreateFeature(OGRFeature *poFeature)
3595 : {
3596 294 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
3597 294 : if (poGeom == nullptr || poGeom->IsEmpty())
3598 102 : return OGRERR_NONE;
3599 192 : if (m_poCT)
3600 : {
3601 1 : poGeom->transform(m_poCT);
3602 : }
3603 192 : m_nSerial++;
3604 192 : return m_poDS->WriteFeature(this, poFeature, m_nSerial, poGeom);
3605 : }
3606 :
3607 : /************************************************************************/
3608 : /* OGRMVTWriterDataset() */
3609 : /************************************************************************/
3610 :
3611 132 : OGRMVTWriterDataset::OGRMVTWriterDataset()
3612 : {
3613 : // Default WebMercator tiling scheme
3614 132 : m_poSRS = new OGRSpatialReference();
3615 132 : m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
3616 :
3617 132 : InitWebMercatorTilingScheme(m_poSRS, m_dfTopX, m_dfTopY, m_dfTileDim0);
3618 132 : }
3619 :
3620 : /************************************************************************/
3621 : /* ~OGRMVTWriterDataset() */
3622 : /************************************************************************/
3623 :
3624 264 : OGRMVTWriterDataset::~OGRMVTWriterDataset()
3625 : {
3626 132 : OGRMVTWriterDataset::Close();
3627 :
3628 132 : if (m_pMyVFS)
3629 : {
3630 132 : sqlite3_vfs_unregister(m_pMyVFS);
3631 132 : CPLFree(m_pMyVFS->pAppData);
3632 132 : CPLFree(m_pMyVFS);
3633 : }
3634 :
3635 132 : m_poSRS->Release();
3636 264 : }
3637 :
3638 : /************************************************************************/
3639 : /* Close() */
3640 : /************************************************************************/
3641 :
3642 254 : CPLErr OGRMVTWriterDataset::Close()
3643 : {
3644 254 : CPLErr eErr = CE_None;
3645 254 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
3646 : {
3647 132 : if (GetDescription()[0] != '\0')
3648 : {
3649 122 : if (!CreateOutput())
3650 3 : eErr = CE_Failure;
3651 : }
3652 132 : if (m_hInsertStmt != nullptr)
3653 : {
3654 129 : sqlite3_finalize(m_hInsertStmt);
3655 : }
3656 132 : if (m_hDB)
3657 : {
3658 129 : sqlite3_close(m_hDB);
3659 : }
3660 132 : if (m_hDBMBTILES)
3661 : {
3662 76 : sqlite3_close(m_hDBMBTILES);
3663 : }
3664 260 : if (!m_osTempDB.empty() && !m_bReuseTempFile &&
3665 128 : CPLTestBool(CPLGetConfigOption("OGR_MVT_REMOVE_TEMP_FILE", "YES")))
3666 : {
3667 127 : VSIUnlink(m_osTempDB);
3668 : }
3669 :
3670 132 : if (GDALDataset::Close() != CE_None)
3671 0 : eErr = CE_Failure;
3672 : }
3673 254 : return eErr;
3674 : }
3675 :
3676 : /************************************************************************/
3677 : /* ConvertToTileCoords() */
3678 : /************************************************************************/
3679 :
3680 12384 : void OGRMVTWriterDataset::ConvertToTileCoords(double dfX, double dfY, int &nX,
3681 : int &nY, double dfTopX,
3682 : double dfTopY,
3683 : double dfTileDim) const
3684 : {
3685 12384 : if (dfTileDim == 0)
3686 : {
3687 333 : nX = static_cast<int>(dfX);
3688 333 : nY = static_cast<int>(dfY);
3689 : }
3690 : else
3691 : {
3692 12051 : nX = static_cast<int>(
3693 12051 : std::round((dfX - dfTopX) * m_nExtent / dfTileDim));
3694 12051 : nY = static_cast<int>(
3695 12051 : std::round((dfTopY - dfY) * m_nExtent / dfTileDim));
3696 : }
3697 12384 : }
3698 :
3699 : /************************************************************************/
3700 : /* GetCmdCountCombined() */
3701 : /************************************************************************/
3702 :
3703 3121 : static unsigned GetCmdCountCombined(unsigned int nCmdId, unsigned int nCmdCount)
3704 : {
3705 3121 : return (nCmdId | (nCmdCount << 3));
3706 : }
3707 :
3708 : /************************************************************************/
3709 : /* EncodeLineString() */
3710 : /************************************************************************/
3711 :
3712 2720 : bool OGRMVTWriterDataset::EncodeLineString(
3713 : MVTTileLayerFeature *poGPBFeature, const OGRLineString *poLS,
3714 : OGRLineString *poOutLS, bool bWriteLastPoint, bool bReverseOrder,
3715 : GUInt32 nMinLineTo, double dfTopX, double dfTopY, double dfTileDim,
3716 : int &nLastX, int &nLastY) const
3717 : {
3718 2720 : const GUInt32 nInitialSize = poGPBFeature->getGeometryCount();
3719 2731 : const int nLastXOri = nLastX;
3720 2731 : const int nLastYOri = nLastY;
3721 2731 : GUInt32 nLineToCount = 0;
3722 2731 : const int nPoints = poLS->getNumPoints() - (bWriteLastPoint ? 0 : 1);
3723 2723 : if (poOutLS)
3724 2739 : poOutLS->setNumPoints(nPoints);
3725 2736 : int nFirstX = 0;
3726 2736 : int nFirstY = 0;
3727 2736 : int nLastXValid = nLastX;
3728 2736 : int nLastYValid = nLastY;
3729 13760 : for (int i = 0; i < nPoints; i++)
3730 : {
3731 : int nX, nY;
3732 11031 : int nSrcIdx = bReverseOrder ? poLS->getNumPoints() - 1 - i : i;
3733 11031 : double dfX = poLS->getX(nSrcIdx);
3734 11001 : double dfY = poLS->getY(nSrcIdx);
3735 10998 : ConvertToTileCoords(dfX, dfY, nX, nY, dfTopX, dfTopY, dfTileDim);
3736 10998 : int nDiffX = nX - nLastX;
3737 10998 : int nDiffY = nY - nLastY;
3738 10998 : if (i == 0 || nDiffX != 0 || nDiffY != 0)
3739 : {
3740 3809 : if (i > 0)
3741 : {
3742 1104 : nLineToCount++;
3743 1104 : if (nLineToCount == 1)
3744 : {
3745 313 : poGPBFeature->addGeometry(
3746 : GetCmdCountCombined(knCMD_MOVETO, 1));
3747 313 : const int nLastDiffX = nLastX - nLastXOri;
3748 313 : const int nLastDiffY = nLastY - nLastYOri;
3749 313 : poGPBFeature->addGeometry(EncodeSInt(nLastDiffX));
3750 313 : poGPBFeature->addGeometry(EncodeSInt(nLastDiffY));
3751 313 : if (poOutLS)
3752 313 : poOutLS->setPoint(0, nLastX, nLastY);
3753 :
3754 : // To be modified later
3755 312 : poGPBFeature->addGeometry(
3756 : GetCmdCountCombined(knCMD_LINETO, 0));
3757 : }
3758 :
3759 1103 : poGPBFeature->addGeometry(EncodeSInt(nDiffX));
3760 1101 : poGPBFeature->addGeometry(EncodeSInt(nDiffY));
3761 1103 : if (poOutLS)
3762 1102 : poOutLS->setPoint(nLineToCount, nX, nY);
3763 : }
3764 : else
3765 : {
3766 2705 : nFirstX = nX;
3767 2705 : nFirstY = nY;
3768 : }
3769 3835 : nLastXValid = nLastX;
3770 3835 : nLastYValid = nLastY;
3771 3835 : nLastX = nX;
3772 3835 : nLastY = nY;
3773 : }
3774 : }
3775 :
3776 : // If last point of ring is identical to first one, discard it
3777 2729 : if (nMinLineTo == 2 && nLineToCount > 0 && nFirstX == nLastX &&
3778 106 : nFirstY == nLastY)
3779 : {
3780 66 : poGPBFeature->resizeGeometryArray(poGPBFeature->getGeometryCount() - 2);
3781 68 : nLineToCount--;
3782 68 : nLastX = nLastXValid;
3783 68 : nLastY = nLastYValid;
3784 : }
3785 :
3786 2731 : if (nLineToCount >= nMinLineTo)
3787 : {
3788 264 : if (poOutLS)
3789 264 : poOutLS->setNumPoints(1 + nLineToCount);
3790 : // Patch actual number of points in LINETO command
3791 263 : poGPBFeature->setGeometry(
3792 : nInitialSize + 3, GetCmdCountCombined(knCMD_LINETO, nLineToCount));
3793 264 : return true;
3794 : }
3795 : else
3796 : {
3797 2467 : poGPBFeature->resizeGeometryArray(nInitialSize);
3798 2454 : nLastX = nLastXOri;
3799 2454 : nLastY = nLastYOri;
3800 2454 : return false;
3801 : }
3802 : }
3803 :
3804 : #ifdef notdef
3805 : /************************************************************************/
3806 : /* EncodeRepairedOuterRing() */
3807 : /************************************************************************/
3808 :
3809 : bool OGRMVTWriterDataset::EncodeRepairedOuterRing(
3810 : MVTTileLayerFeature *poGPBFeature, OGRPolygon &oInPoly, int &nLastX,
3811 : int &nLastY) const
3812 : {
3813 : std::unique_ptr<OGRGeometry> poFixedGeom(oInPoly.Buffer(0));
3814 : if (!poFixedGeom.get() || poFixedGeom->IsEmpty())
3815 : {
3816 : return false;
3817 : }
3818 :
3819 : OGRPolygon *poPoly = nullptr;
3820 : if (wkbFlatten(poFixedGeom->getGeometryType()) == wkbMultiPolygon)
3821 : {
3822 : OGRMultiPolygon *poMP = poFixedGeom.get()->toMultiPolygon();
3823 : poPoly = poMP->getGeometryRef(0)->toPolygon();
3824 : }
3825 : else if (wkbFlatten(poFixedGeom->getGeometryType()) == wkbPolygon)
3826 : {
3827 : poPoly = poFixedGeom.get()->toPolygon();
3828 : }
3829 : if (!poPoly)
3830 : return false;
3831 :
3832 : OGRLinearRing *poRing = poPoly->getExteriorRing();
3833 : const bool bReverseOrder = !poRing->isClockwise();
3834 :
3835 : const GUInt32 nInitialSize = poGPBFeature->getGeometryCount();
3836 : const int nLastXOri = nLastX;
3837 : const int nLastYOri = nLastY;
3838 : GUInt32 nLineToCount = 0;
3839 : const int nPoints = poRing->getNumPoints() - 1;
3840 : auto poOutLinearRing = std::make_unique<OGRLinearRing>();
3841 : poOutLinearRing->setNumPoints(nPoints);
3842 : for (int i = 0; i < nPoints; i++)
3843 : {
3844 : int nSrcIdx = bReverseOrder ? poRing->getNumPoints() - 1 - i : i;
3845 : double dfX = poRing->getX(nSrcIdx);
3846 : double dfY = poRing->getY(nSrcIdx);
3847 : int nX = static_cast<int>(std::round(dfX));
3848 : int nY = static_cast<int>(std::round(dfY));
3849 : if (nX != dfX || nY != dfY)
3850 : continue;
3851 : int nDiffX = nX - nLastX;
3852 : int nDiffY = nY - nLastY;
3853 : if (i == 0 || nDiffX != 0 || nDiffY != 0)
3854 : {
3855 : if (i > 0)
3856 : {
3857 : nLineToCount++;
3858 : if (nLineToCount == 1)
3859 : {
3860 : poGPBFeature->addGeometry(
3861 : GetCmdCountCombined(knCMD_MOVETO, 1));
3862 : const int nLastDiffX = nLastX - nLastXOri;
3863 : const int nLastDiffY = nLastY - nLastYOri;
3864 : poGPBFeature->addGeometry(EncodeSInt(nLastDiffX));
3865 : poGPBFeature->addGeometry(EncodeSInt(nLastDiffY));
3866 : poOutLinearRing->setPoint(0, nLastX, nLastY);
3867 :
3868 : // To be modified later
3869 : poGPBFeature->addGeometry(
3870 : GetCmdCountCombined(knCMD_LINETO, 0));
3871 : }
3872 :
3873 : poGPBFeature->addGeometry(EncodeSInt(nDiffX));
3874 : poGPBFeature->addGeometry(EncodeSInt(nDiffY));
3875 : poOutLinearRing->setPoint(nLineToCount, nX, nY);
3876 : }
3877 : nLastX = nX;
3878 : nLastY = nY;
3879 : }
3880 : }
3881 : if (nLineToCount >= 2)
3882 : {
3883 : poOutLinearRing->setNumPoints(1 + nLineToCount);
3884 : OGRPolygon oOutPoly;
3885 : oOutPoly.addRingDirectly(poOutLinearRing.release());
3886 : int bIsValid;
3887 : {
3888 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
3889 : bIsValid = oOutPoly.IsValid();
3890 : }
3891 : if (bIsValid)
3892 : {
3893 : // Patch actual number of points in LINETO command
3894 : poGPBFeature->setGeometry(
3895 : nInitialSize + 3,
3896 : GetCmdCountCombined(knCMD_LINETO, nLineToCount));
3897 : poGPBFeature->addGeometry(GetCmdCountCombined(knCMD_CLOSEPATH, 1));
3898 : return true;
3899 : }
3900 : }
3901 :
3902 : poGPBFeature->resizeGeometryArray(nInitialSize);
3903 : nLastX = nLastXOri;
3904 : nLastY = nLastYOri;
3905 : return false;
3906 : }
3907 : #endif
3908 :
3909 : /************************************************************************/
3910 : /* EncodePolygon() */
3911 : /************************************************************************/
3912 :
3913 1386 : bool OGRMVTWriterDataset::EncodePolygon(MVTTileLayerFeature *poGPBFeature,
3914 : const OGRPolygon *poPoly,
3915 : OGRPolygon *poOutPoly, double dfTopX,
3916 : double dfTopY, double dfTileDim,
3917 : int &nLastX, int &nLastY,
3918 : double &dfArea) const
3919 : {
3920 1386 : dfArea = 0;
3921 2761 : auto poOutOuterRing = std::make_unique<OGRLinearRing>();
3922 1580 : for (int i = 0; i < 1 + poPoly->getNumInteriorRings(); i++)
3923 : {
3924 1389 : const OGRLinearRing *poRing = (i == 0) ? poPoly->getExteriorRing()
3925 22 : : poPoly->getInteriorRing(i - 1);
3926 2808 : if (poRing->getNumPoints() < 4 ||
3927 2784 : poRing->getX(0) != poRing->getX(poRing->getNumPoints() - 1) ||
3928 1416 : poRing->getY(0) != poRing->getY(poRing->getNumPoints() - 1))
3929 : {
3930 0 : if (i == 0)
3931 1193 : return false;
3932 69 : continue;
3933 : }
3934 1391 : const bool bWriteLastPoint = false;
3935 : // If dealing with input geometry in CRS units, exterior rings must
3936 : // be clockwise oriented.
3937 : // But if re-encoding a geometry already in tile coordinates
3938 : // (dfTileDim == 0), this is the reverse.
3939 : const bool bReverseOrder = dfTileDim != 0
3940 1477 : ? ((i == 0 && !poRing->isClockwise()) ||
3941 24 : (i > 0 && poRing->isClockwise()))
3942 63 : : ((i == 0 && poRing->isClockwise()) ||
3943 1 : (i > 0 && !poRing->isClockwise()));
3944 1405 : const GUInt32 nMinLineTo = 2;
3945 0 : std::unique_ptr<OGRLinearRing> poOutInnerRing;
3946 1405 : if (i > 0)
3947 25 : poOutInnerRing = std::make_unique<OGRLinearRing>();
3948 : OGRLinearRing *poOutRing =
3949 1405 : poOutInnerRing.get() ? poOutInnerRing.get() : poOutOuterRing.get();
3950 :
3951 1391 : bool bSuccess = EncodeLineString(
3952 : poGPBFeature, poRing, poOutRing, bWriteLastPoint, bReverseOrder,
3953 : nMinLineTo, dfTopX, dfTopY, dfTileDim, nLastX, nLastY);
3954 1388 : if (!bSuccess)
3955 : {
3956 1189 : if (i == 0)
3957 1191 : return false;
3958 0 : continue;
3959 : }
3960 :
3961 199 : if (poOutPoly == nullptr)
3962 : {
3963 58 : poGPBFeature->addGeometry(GetCmdCountCombined(knCMD_CLOSEPATH, 1));
3964 58 : continue;
3965 : }
3966 :
3967 141 : poOutRing->closeRings();
3968 :
3969 133 : poOutPoly->addRing(poOutRing);
3970 133 : if (i > 0)
3971 17 : dfArea -= poOutRing->get_Area();
3972 : else
3973 116 : dfArea = poOutRing->get_Area();
3974 :
3975 133 : poGPBFeature->addGeometry(GetCmdCountCombined(knCMD_CLOSEPATH, 1));
3976 : }
3977 :
3978 182 : return true;
3979 : }
3980 :
3981 : /************************************************************************/
3982 : /* PreGenerateForTile() */
3983 : /************************************************************************/
3984 :
3985 4013 : OGRErr OGRMVTWriterDataset::PreGenerateForTileReal(
3986 : int nZ, int nTileX, int nTileY, const CPLString &osTargetName,
3987 : bool bIsMaxZoomForLayer, const OGRMVTFeatureContent *poFeatureContent,
3988 : GIntBig nSerial, const OGRGeometry *poGeom,
3989 : const OGREnvelope &sEnvelope) const
3990 : {
3991 4013 : double dfTileDim = m_dfTileDim0 / (1 << nZ);
3992 4013 : double dfBuffer = dfTileDim * m_nBuffer / m_nExtent;
3993 4013 : double dfTopX = m_dfTopX + nTileX * dfTileDim;
3994 4013 : double dfTopY = m_dfTopY - nTileY * dfTileDim;
3995 4013 : double dfBottomRightX = dfTopX + dfTileDim;
3996 4013 : double dfBottomRightY = dfTopY - dfTileDim;
3997 4013 : double dfIntersectTopX = dfTopX - dfBuffer;
3998 4013 : double dfIntersectTopY = dfTopY + dfBuffer;
3999 4013 : double dfIntersectBottomRightX = dfBottomRightX + dfBuffer;
4000 4013 : double dfIntersectBottomRightY = dfBottomRightY - dfBuffer;
4001 :
4002 : const OGRGeometry *poIntersection;
4003 4012 : std::unique_ptr<OGRGeometry> poIntersectionHolder; // keep in that scope
4004 4013 : if (sEnvelope.MinX >= dfIntersectTopX &&
4005 3993 : sEnvelope.MinY >= dfIntersectBottomRightY &&
4006 3987 : sEnvelope.MaxX <= dfIntersectBottomRightX &&
4007 3969 : sEnvelope.MaxY <= dfIntersectTopY)
4008 : {
4009 3967 : poIntersection = poGeom;
4010 : }
4011 : else
4012 : {
4013 46 : OGRLinearRing *poLR = new OGRLinearRing();
4014 49 : poLR->addPoint(dfIntersectTopX, dfIntersectTopY);
4015 49 : poLR->addPoint(dfIntersectTopX, dfIntersectBottomRightY);
4016 49 : poLR->addPoint(dfIntersectBottomRightX, dfIntersectBottomRightY);
4017 49 : poLR->addPoint(dfIntersectBottomRightX, dfIntersectTopY);
4018 49 : poLR->addPoint(dfIntersectTopX, dfIntersectTopY);
4019 49 : OGRPolygon oPoly;
4020 49 : oPoly.addRingDirectly(poLR);
4021 :
4022 49 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
4023 49 : auto poTmp = poGeom->Intersection(&oPoly);
4024 49 : poIntersection = poTmp;
4025 49 : poIntersectionHolder.reset(poTmp);
4026 49 : if (poIntersection == nullptr || poIntersection->IsEmpty())
4027 : {
4028 2 : return OGRERR_NONE;
4029 : }
4030 : }
4031 :
4032 : // Create a layer with a single feature in it
4033 : std::shared_ptr<MVTTileLayer> poLayer =
4034 8005 : std::shared_ptr<MVTTileLayer>(new MVTTileLayer());
4035 : std::shared_ptr<MVTTileLayerFeature> poGPBFeature =
4036 7985 : std::shared_ptr<MVTTileLayerFeature>(new MVTTileLayerFeature());
4037 4000 : poLayer->addFeature(poGPBFeature);
4038 :
4039 3995 : OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType());
4040 4007 : if (eGeomType == wkbPoint || eGeomType == wkbMultiPoint)
4041 1372 : poGPBFeature->setType(MVTTileLayerFeature::GeomType::POINT);
4042 2635 : else if (eGeomType == wkbLineString || eGeomType == wkbMultiLineString)
4043 1321 : poGPBFeature->setType(MVTTileLayerFeature::GeomType::LINESTRING);
4044 1314 : else if (eGeomType == wkbPolygon || eGeomType == wkbMultiPolygon)
4045 1314 : poGPBFeature->setType(MVTTileLayerFeature::GeomType::POLYGON);
4046 : else
4047 : {
4048 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported geometry type");
4049 0 : return OGRERR_NONE;
4050 : }
4051 :
4052 : OGRwkbGeometryType eGeomToEncodeType =
4053 3986 : wkbFlatten(poIntersection->getGeometryType());
4054 :
4055 : // Simplify contour if requested by user
4056 3999 : const OGRGeometry *poGeomToEncode = poIntersection;
4057 3995 : std::unique_ptr<OGRGeometry> poGeomSimplified;
4058 3999 : const double dfSimplification =
4059 3999 : bIsMaxZoomForLayer ? m_dfSimplificationMaxZoom : m_dfSimplification;
4060 3999 : if (dfSimplification > 0 &&
4061 12 : (eGeomType == wkbLineString || eGeomType == wkbMultiLineString ||
4062 12 : eGeomType == wkbPolygon || eGeomType == wkbMultiPolygon))
4063 : {
4064 20 : const double dfTol = dfTileDim / m_nExtent;
4065 32 : poGeomSimplified = std::unique_ptr<OGRGeometry>(
4066 12 : poIntersection->SimplifyPreserveTopology(dfTol * dfSimplification));
4067 12 : if (poGeomSimplified.get())
4068 : {
4069 12 : poGeomToEncode = poGeomSimplified.get();
4070 12 : eGeomToEncodeType = wkbFlatten(poGeomSimplified->getGeometryType());
4071 : }
4072 : }
4073 :
4074 3991 : bool bGeomOK = false;
4075 3991 : double dfAreaOrLength = 0.0;
4076 :
4077 : const auto EmitValidPolygon =
4078 57 : [this, &bGeomOK, &dfAreaOrLength,
4079 182 : &poGPBFeature](const OGRGeometry *poValidGeom)
4080 : {
4081 57 : bGeomOK = false;
4082 57 : dfAreaOrLength = 0;
4083 57 : int nLastX = 0;
4084 57 : int nLastY = 0;
4085 :
4086 57 : if (wkbFlatten(poValidGeom->getGeometryType()) == wkbPolygon)
4087 : {
4088 11 : const OGRPolygon *poPoly = poValidGeom->toPolygon();
4089 11 : double dfPartArea = 0.0;
4090 11 : bGeomOK = EncodePolygon(poGPBFeature.get(), poPoly, nullptr, 0, 0,
4091 : 0, nLastX, nLastY, dfPartArea);
4092 11 : dfAreaOrLength = dfPartArea;
4093 : }
4094 46 : else if (OGR_GT_IsSubClassOf(poValidGeom->getGeometryType(),
4095 46 : wkbGeometryCollection))
4096 : {
4097 130 : for (auto &&poSubGeom : poValidGeom->toGeometryCollection())
4098 : {
4099 88 : if (wkbFlatten(poSubGeom->getGeometryType()) == wkbPolygon)
4100 : {
4101 36 : const OGRPolygon *poPoly = poSubGeom->toPolygon();
4102 36 : double dfPartArea = 0.0;
4103 36 : bGeomOK |=
4104 36 : EncodePolygon(poGPBFeature.get(), poPoly, nullptr, 0, 0,
4105 36 : 0, nLastX, nLastY, dfPartArea);
4106 36 : dfAreaOrLength += dfPartArea;
4107 : }
4108 52 : else if (wkbFlatten(poSubGeom->getGeometryType()) ==
4109 : wkbMultiPolygon)
4110 : {
4111 : const OGRMultiPolygon *poMPoly =
4112 5 : poSubGeom->toMultiPolygon();
4113 15 : for (const auto *poPoly : poMPoly)
4114 : {
4115 10 : double dfPartArea = 0.0;
4116 10 : bGeomOK |=
4117 10 : EncodePolygon(poGPBFeature.get(), poPoly, nullptr,
4118 10 : 0, 0, 0, nLastX, nLastY, dfPartArea);
4119 10 : dfAreaOrLength += dfPartArea;
4120 : }
4121 : }
4122 : }
4123 : }
4124 57 : };
4125 :
4126 3991 : if (eGeomType == wkbPoint || eGeomType == wkbMultiPoint)
4127 : {
4128 1363 : if (eGeomToEncodeType == wkbPoint)
4129 : {
4130 979 : const OGRPoint *poPoint = poIntersection->toPoint();
4131 : int nX, nY;
4132 980 : double dfX = poPoint->getX();
4133 978 : double dfY = poPoint->getY();
4134 979 : bGeomOK = true;
4135 979 : ConvertToTileCoords(dfX, dfY, nX, nY, dfTopX, dfTopY, dfTileDim);
4136 980 : poGPBFeature->addGeometry(GetCmdCountCombined(knCMD_MOVETO, 1));
4137 980 : poGPBFeature->addGeometry(EncodeSInt(nX));
4138 983 : poGPBFeature->addGeometry(EncodeSInt(nY));
4139 : }
4140 384 : else if (eGeomToEncodeType == wkbMultiPoint ||
4141 : eGeomToEncodeType == wkbGeometryCollection)
4142 : {
4143 : const OGRGeometryCollection *poGC =
4144 384 : poIntersection->toGeometryCollection();
4145 766 : std::set<std::pair<int, int>> oSetUniqueCoords;
4146 384 : poGPBFeature->addGeometry(
4147 : GetCmdCountCombined(knCMD_MOVETO, 0)); // To be modified later
4148 383 : int nLastX = 0;
4149 383 : int nLastY = 0;
4150 773 : for (auto &&poSubGeom : poGC)
4151 : {
4152 389 : if (wkbFlatten(poSubGeom->getGeometryType()) == wkbPoint)
4153 : {
4154 390 : const OGRPoint *poPoint = poSubGeom->toPoint();
4155 : int nX, nY;
4156 389 : double dfX = poPoint->getX();
4157 389 : double dfY = poPoint->getY();
4158 389 : ConvertToTileCoords(dfX, dfY, nX, nY, dfTopX, dfTopY,
4159 : dfTileDim);
4160 389 : if (oSetUniqueCoords.find(std::pair<int, int>(nX, nY)) ==
4161 777 : oSetUniqueCoords.end())
4162 : {
4163 388 : oSetUniqueCoords.insert(std::pair<int, int>(nX, nY));
4164 :
4165 389 : int nDiffX = nX - nLastX;
4166 389 : int nDiffY = nY - nLastY;
4167 389 : poGPBFeature->addGeometry(EncodeSInt(nDiffX));
4168 390 : poGPBFeature->addGeometry(EncodeSInt(nDiffY));
4169 390 : nLastX = nX;
4170 390 : nLastY = nY;
4171 : }
4172 : }
4173 : }
4174 384 : GUInt32 nPoints = static_cast<GUInt32>(oSetUniqueCoords.size());
4175 383 : bGeomOK = nPoints > 0;
4176 383 : poGPBFeature->setGeometry(
4177 : 0, GetCmdCountCombined(knCMD_MOVETO, nPoints));
4178 1366 : }
4179 : }
4180 2628 : else if (eGeomType == wkbLineString || eGeomType == wkbMultiLineString)
4181 : {
4182 1316 : const bool bWriteLastPoint = true;
4183 1316 : const bool bReverseOrder = false;
4184 1316 : const GUInt32 nMinLineTo = 1;
4185 :
4186 1316 : if (eGeomToEncodeType == wkbLineString)
4187 : {
4188 933 : const OGRLineString *poLS = poGeomToEncode->toLineString();
4189 931 : int nLastX = 0;
4190 931 : int nLastY = 0;
4191 931 : OGRLineString oOutLS;
4192 930 : bGeomOK =
4193 932 : EncodeLineString(poGPBFeature.get(), poLS, &oOutLS,
4194 : bWriteLastPoint, bReverseOrder, nMinLineTo,
4195 : dfTopX, dfTopY, dfTileDim, nLastX, nLastY);
4196 930 : dfAreaOrLength = oOutLS.get_Length();
4197 : }
4198 383 : else if (eGeomToEncodeType == wkbMultiLineString ||
4199 : eGeomToEncodeType == wkbGeometryCollection)
4200 : {
4201 : const OGRGeometryCollection *poGC =
4202 400 : poGeomToEncode->toGeometryCollection();
4203 386 : int nLastX = 0;
4204 386 : int nLastY = 0;
4205 780 : for (auto &&poSubGeom : poGC)
4206 : {
4207 393 : if (wkbFlatten(poSubGeom->getGeometryType()) == wkbLineString)
4208 : {
4209 394 : const OGRLineString *poLS = poSubGeom->toLineString();
4210 394 : OGRLineString oOutLS;
4211 394 : bool bSubGeomOK = EncodeLineString(
4212 : poGPBFeature.get(), poLS, &oOutLS, bWriteLastPoint,
4213 : bReverseOrder, nMinLineTo, dfTopX, dfTopY, dfTileDim,
4214 : nLastX, nLastY);
4215 394 : if (bSubGeomOK)
4216 18 : dfAreaOrLength += oOutLS.get_Length();
4217 394 : bGeomOK |= bSubGeomOK;
4218 : }
4219 : }
4220 1301 : }
4221 : }
4222 1312 : else if (eGeomType == wkbPolygon || eGeomType == wkbMultiPolygon)
4223 : {
4224 1319 : if (eGeomToEncodeType == wkbPolygon)
4225 : {
4226 945 : const OGRPolygon *poPoly = poGeomToEncode->toPolygon();
4227 948 : int nLastX = 0;
4228 948 : int nLastY = 0;
4229 1890 : OGRPolygon oOutPoly;
4230 940 : const GUInt32 nInitialSize = poGPBFeature->getGeometryCount();
4231 943 : CPL_IGNORE_RET_VAL(nInitialSize);
4232 940 : bGeomOK = EncodePolygon(poGPBFeature.get(), poPoly, &oOutPoly,
4233 : dfTopX, dfTopY, dfTileDim, nLastX, nLastY,
4234 : dfAreaOrLength);
4235 : int bIsValid;
4236 : {
4237 942 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
4238 940 : bIsValid = oOutPoly.IsValid();
4239 : }
4240 942 : if (!bIsValid)
4241 : {
4242 : // Build a valid geometry from the initial MVT geometry and emit
4243 : // it
4244 110 : std::unique_ptr<OGRGeometry> poPolyValid(oOutPoly.MakeValid());
4245 55 : if (poPolyValid)
4246 : {
4247 55 : poGPBFeature->resizeGeometryArray(nInitialSize);
4248 55 : EmitValidPolygon(poPolyValid.get());
4249 : }
4250 : }
4251 : }
4252 374 : else if (eGeomToEncodeType == wkbMultiPolygon ||
4253 : eGeomToEncodeType == wkbGeometryCollection)
4254 : {
4255 : const OGRGeometryCollection *poGC =
4256 337 : poGeomToEncode->toGeometryCollection();
4257 376 : int nLastX = 0;
4258 376 : int nLastY = 0;
4259 745 : OGRMultiPolygon oOutMP;
4260 372 : const GUInt32 nInitialSize = poGPBFeature->getGeometryCount();
4261 373 : CPL_IGNORE_RET_VAL(nInitialSize);
4262 745 : for (auto &&poSubGeom : poGC)
4263 : {
4264 372 : if (wkbFlatten(poSubGeom->getGeometryType()) == wkbPolygon)
4265 : {
4266 378 : const OGRPolygon *poPoly = poSubGeom->toPolygon();
4267 372 : double dfPartArea = 0.0;
4268 744 : auto poOutPoly = std::make_unique<OGRPolygon>();
4269 371 : bGeomOK |= EncodePolygon(
4270 : poGPBFeature.get(), poPoly, poOutPoly.get(), dfTopX,
4271 380 : dfTopY, dfTileDim, nLastX, nLastY, dfPartArea);
4272 380 : dfAreaOrLength += dfPartArea;
4273 380 : oOutMP.addGeometryDirectly(poOutPoly.release());
4274 : }
4275 : }
4276 : int bIsValid;
4277 : {
4278 372 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
4279 372 : bIsValid = oOutMP.IsValid();
4280 : }
4281 369 : if (!bIsValid)
4282 : {
4283 : // Build a valid geometry from the initial MVT geometry and emit
4284 : // it
4285 4 : std::unique_ptr<OGRGeometry> poMPValid(oOutMP.MakeValid());
4286 2 : if (poMPValid)
4287 : {
4288 2 : poGPBFeature->resizeGeometryArray(nInitialSize);
4289 2 : EmitValidPolygon(poMPValid.get());
4290 : }
4291 : }
4292 : }
4293 : }
4294 3991 : if (!bGeomOK)
4295 2461 : return OGRERR_NONE;
4296 :
4297 5863 : for (const auto &pair : poFeatureContent->oValues)
4298 : {
4299 4340 : GUInt32 nKey = poLayer->addKey(pair.first);
4300 4335 : GUInt32 nVal = poLayer->addValue(pair.second);
4301 4335 : poGPBFeature->addTag(nKey);
4302 4335 : poGPBFeature->addTag(nVal);
4303 : }
4304 1531 : if (poFeatureContent->nFID >= 0)
4305 : {
4306 53 : poGPBFeature->setId(poFeatureContent->nFID);
4307 : }
4308 :
4309 : #ifdef notdef
4310 : {
4311 : MVTTile oTile;
4312 : poLayer->setName("x");
4313 : oTile.addLayer(poLayer);
4314 :
4315 : CPLString oBuffer(oTile.write());
4316 :
4317 : VSILFILE *fp = VSIFOpenL(
4318 : CPLSPrintf("/tmp/%d-%d-%d.pbf", nZ, nTileX, nTileY), "wb");
4319 : VSIFWriteL(oBuffer.data(), 1, oBuffer.size(), fp);
4320 : VSIFCloseL(fp);
4321 : }
4322 : #endif
4323 :
4324 : // GPB encode the layer with our single feature
4325 3065 : CPLString oBuffer(poLayer->write());
4326 :
4327 : // Compress buffer
4328 1528 : size_t nCompressedSize = 0;
4329 1528 : void *pCompressed = CPLZLibDeflate(oBuffer.data(), oBuffer.size(), -1,
4330 : nullptr, 0, &nCompressedSize);
4331 1530 : oBuffer.assign(static_cast<char *>(pCompressed), nCompressedSize);
4332 1525 : CPLFree(pCompressed);
4333 :
4334 1534 : const auto InsertIntoDb = [&]()
4335 : {
4336 15340 : m_nTempTiles++;
4337 1534 : sqlite3_bind_int(m_hInsertStmt, 1, nZ);
4338 1534 : sqlite3_bind_int(m_hInsertStmt, 2, nTileX);
4339 1534 : sqlite3_bind_int(m_hInsertStmt, 3, nTileY);
4340 1534 : sqlite3_bind_text(m_hInsertStmt, 4, osTargetName.c_str(), -1,
4341 : SQLITE_STATIC);
4342 1534 : sqlite3_bind_int64(m_hInsertStmt, 5, nSerial);
4343 1534 : sqlite3_bind_blob(m_hInsertStmt, 6, oBuffer.data(),
4344 1534 : static_cast<int>(oBuffer.size()), SQLITE_STATIC);
4345 1534 : sqlite3_bind_int(m_hInsertStmt, 7,
4346 1534 : static_cast<int>(poGPBFeature->getType()));
4347 1534 : sqlite3_bind_double(m_hInsertStmt, 8, dfAreaOrLength);
4348 1534 : int rc = sqlite3_step(m_hInsertStmt);
4349 1534 : sqlite3_reset(m_hInsertStmt);
4350 1534 : return rc;
4351 1526 : };
4352 :
4353 : int rc;
4354 1526 : if (m_bThreadPoolOK)
4355 : {
4356 1504 : std::lock_guard<std::mutex> oLock(m_oDBMutex);
4357 1509 : rc = InsertIntoDb();
4358 : }
4359 : else
4360 : {
4361 22 : rc = InsertIntoDb();
4362 : }
4363 :
4364 1534 : if (!(rc == SQLITE_OK || rc == SQLITE_DONE))
4365 : {
4366 2 : return OGRERR_FAILURE;
4367 : }
4368 :
4369 1532 : return OGRERR_NONE;
4370 : }
4371 :
4372 : /************************************************************************/
4373 : /* MVTWriterTask() */
4374 : /************************************************************************/
4375 :
4376 : class MVTWriterTask
4377 : {
4378 : public:
4379 : const OGRMVTWriterDataset *poDS;
4380 : int nZ;
4381 : int nTileX;
4382 : int nTileY;
4383 : CPLString osTargetName;
4384 : bool bIsMaxZoomForLayer;
4385 : std::shared_ptr<OGRMVTFeatureContent> poFeatureContent;
4386 : GIntBig nSerial;
4387 : std::shared_ptr<OGRGeometry> poGeom;
4388 : OGREnvelope sEnvelope;
4389 : };
4390 :
4391 : /************************************************************************/
4392 : /* WriterTaskFunc() */
4393 : /************************************************************************/
4394 :
4395 3998 : void OGRMVTWriterDataset::WriterTaskFunc(void *pParam)
4396 : {
4397 3998 : MVTWriterTask *poTask = static_cast<MVTWriterTask *>(pParam);
4398 15951 : OGRErr eErr = poTask->poDS->PreGenerateForTileReal(
4399 3994 : poTask->nZ, poTask->nTileX, poTask->nTileY, poTask->osTargetName,
4400 3996 : poTask->bIsMaxZoomForLayer, poTask->poFeatureContent.get(),
4401 3998 : poTask->nSerial, poTask->poGeom.get(), poTask->sEnvelope);
4402 3963 : if (eErr != OGRERR_NONE)
4403 : {
4404 1 : std::lock_guard oLock(poTask->poDS->m_oDBMutex);
4405 1 : poTask->poDS->m_bWriteFeatureError = true;
4406 : }
4407 3963 : delete poTask;
4408 3999 : }
4409 :
4410 : /************************************************************************/
4411 : /* PreGenerateForTile() */
4412 : /************************************************************************/
4413 :
4414 4027 : OGRErr OGRMVTWriterDataset::PreGenerateForTile(
4415 : int nZ, int nTileX, int nTileY, const CPLString &osTargetName,
4416 : bool bIsMaxZoomForLayer,
4417 : const std::shared_ptr<OGRMVTFeatureContent> &poFeatureContent,
4418 : GIntBig nSerial, const std::shared_ptr<OGRGeometry> &poGeom,
4419 : const OGREnvelope &sEnvelope) const
4420 : {
4421 4027 : if (!m_bThreadPoolOK)
4422 : {
4423 25 : return PreGenerateForTileReal(
4424 : nZ, nTileX, nTileY, osTargetName, bIsMaxZoomForLayer,
4425 50 : poFeatureContent.get(), nSerial, poGeom.get(), sEnvelope);
4426 : }
4427 : else
4428 : {
4429 4002 : MVTWriterTask *poTask = new MVTWriterTask;
4430 4002 : poTask->poDS = this;
4431 4002 : poTask->nZ = nZ;
4432 4002 : poTask->nTileX = nTileX;
4433 4002 : poTask->nTileY = nTileY;
4434 4002 : poTask->osTargetName = osTargetName;
4435 4002 : poTask->bIsMaxZoomForLayer = bIsMaxZoomForLayer;
4436 4002 : poTask->poFeatureContent = poFeatureContent;
4437 4002 : poTask->nSerial = nSerial;
4438 4002 : poTask->poGeom = poGeom;
4439 4002 : poTask->sEnvelope = sEnvelope;
4440 4002 : m_oThreadPool.SubmitJob(OGRMVTWriterDataset::WriterTaskFunc, poTask);
4441 : // Do not queue more than 1000 jobs to avoid memory exhaustion
4442 4002 : m_oThreadPool.WaitCompletion(1000);
4443 :
4444 4002 : std::lock_guard oLock(m_oDBMutex);
4445 4002 : return m_bWriteFeatureError ? OGRERR_FAILURE : OGRERR_NONE;
4446 : }
4447 : }
4448 :
4449 : /************************************************************************/
4450 : /* UpdateLayerProperties() */
4451 : /************************************************************************/
4452 :
4453 4353 : void OGRMVTWriterDataset::UpdateLayerProperties(
4454 : MVTLayerProperties *poLayerProperties, const std::string &osKey,
4455 : const MVTTileLayerValue &oValue)
4456 : {
4457 4353 : auto oFieldIter = poLayerProperties->m_oMapFieldNameToIdx.find(osKey);
4458 4353 : MVTFieldProperties *poFieldProps = nullptr;
4459 4353 : if (oFieldIter == poLayerProperties->m_oMapFieldNameToIdx.end())
4460 : {
4461 181 : if (poLayerProperties->m_oSetFields.size() < knMAX_COUNT_FIELDS)
4462 : {
4463 181 : poLayerProperties->m_oSetFields.insert(osKey);
4464 181 : if (poLayerProperties->m_oMapFieldNameToIdx.size() <
4465 : knMAX_REPORT_FIELDS)
4466 : {
4467 362 : MVTFieldProperties oFieldProps;
4468 181 : oFieldProps.m_osName = osKey;
4469 181 : if (oValue.isNumeric())
4470 : {
4471 73 : oFieldProps.m_dfMinVal = oValue.getNumericValue();
4472 73 : oFieldProps.m_dfMaxVal = oValue.getNumericValue();
4473 73 : oFieldProps.m_bAllInt = true; // overridden just below
4474 : }
4475 181 : oFieldProps.m_eType =
4476 289 : oValue.isNumeric() ? MVTTileLayerValue::ValueType::DOUBLE
4477 108 : : oValue.isString() ? MVTTileLayerValue::ValueType::STRING
4478 : : MVTTileLayerValue::ValueType::BOOL;
4479 :
4480 181 : poLayerProperties->m_oMapFieldNameToIdx[osKey] =
4481 181 : poLayerProperties->m_aoFields.size();
4482 181 : poLayerProperties->m_aoFields.push_back(std::move(oFieldProps));
4483 181 : poFieldProps = &(poLayerProperties->m_aoFields.back());
4484 : }
4485 : }
4486 : }
4487 : else
4488 : {
4489 4172 : poFieldProps = &(poLayerProperties->m_aoFields[oFieldIter->second]);
4490 : }
4491 :
4492 4353 : if (poFieldProps)
4493 : {
4494 4353 : if (oValue.getType() == MVTTileLayerValue::ValueType::BOOL)
4495 : {
4496 24 : MVTTileLayerValue oUniqVal;
4497 12 : oUniqVal.setBoolValue(oValue.getBoolValue());
4498 12 : poFieldProps->m_oSetAllValues.insert(oUniqVal);
4499 12 : poFieldProps->m_oSetValues.insert(oUniqVal);
4500 : }
4501 4341 : else if (oValue.isNumeric())
4502 : {
4503 1780 : if (poFieldProps->m_bAllInt)
4504 : {
4505 935 : poFieldProps->m_bAllInt =
4506 1870 : oValue.getType() == MVTTileLayerValue::ValueType::INT ||
4507 2751 : oValue.getType() == MVTTileLayerValue::ValueType::SINT ||
4508 1798 : (oValue.getType() == MVTTileLayerValue::ValueType::UINT &&
4509 881 : oValue.getUIntValue() < GINT64_MAX);
4510 : }
4511 1780 : double dfVal = oValue.getNumericValue();
4512 1780 : poFieldProps->m_dfMinVal =
4513 1780 : std::min(poFieldProps->m_dfMinVal, dfVal);
4514 1780 : poFieldProps->m_dfMaxVal =
4515 1780 : std::max(poFieldProps->m_dfMaxVal, dfVal);
4516 1780 : if (poFieldProps->m_oSetAllValues.size() < knMAX_COUNT_VALUES)
4517 : {
4518 3560 : MVTTileLayerValue oUniqVal;
4519 1780 : oUniqVal.setDoubleValue(dfVal);
4520 1780 : poFieldProps->m_oSetAllValues.insert(oUniqVal);
4521 1780 : if (poFieldProps->m_oSetValues.size() < knMAX_REPORT_VALUES)
4522 : {
4523 1780 : poFieldProps->m_oSetValues.insert(oUniqVal);
4524 : }
4525 : }
4526 : }
4527 5122 : else if (oValue.isString() &&
4528 2561 : poFieldProps->m_oSetAllValues.size() < knMAX_COUNT_VALUES)
4529 : {
4530 5122 : auto osVal = oValue.getStringValue();
4531 5122 : MVTTileLayerValue oUniqVal;
4532 2561 : oUniqVal.setStringValue(osVal);
4533 2561 : poFieldProps->m_oSetAllValues.insert(oUniqVal);
4534 5122 : if (osVal.size() <= knMAX_STRING_VALUE_LENGTH &&
4535 2561 : poFieldProps->m_oSetValues.size() < knMAX_REPORT_VALUES)
4536 : {
4537 2561 : poFieldProps->m_oSetValues.insert(oUniqVal);
4538 : }
4539 : }
4540 : }
4541 4353 : }
4542 :
4543 : /************************************************************************/
4544 : /* GZIPCompress() */
4545 : /************************************************************************/
4546 :
4547 972 : static void GZIPCompress(std::string &oTileBuffer)
4548 : {
4549 972 : if (!oTileBuffer.empty())
4550 : {
4551 : const CPLString osTmpFilename(
4552 1944 : VSIMemGenerateHiddenFilename("mvt_temp.gz"));
4553 1944 : CPLString osTmpGZipFilename("/vsigzip/" + osTmpFilename);
4554 972 : VSILFILE *fpGZip = VSIFOpenL(osTmpGZipFilename, "wb");
4555 972 : if (fpGZip)
4556 : {
4557 972 : VSIFWriteL(oTileBuffer.data(), 1, oTileBuffer.size(), fpGZip);
4558 972 : VSIFCloseL(fpGZip);
4559 :
4560 972 : vsi_l_offset nCompressedSize = 0;
4561 : GByte *pabyCompressed =
4562 972 : VSIGetMemFileBuffer(osTmpFilename, &nCompressedSize, false);
4563 : oTileBuffer.assign(reinterpret_cast<char *>(pabyCompressed),
4564 972 : static_cast<size_t>(nCompressedSize));
4565 : }
4566 972 : VSIUnlink(osTmpFilename);
4567 : }
4568 972 : }
4569 :
4570 : /************************************************************************/
4571 : /* GetReducedPrecisionGeometry() */
4572 : /************************************************************************/
4573 :
4574 : static std::vector<GUInt32>
4575 167 : GetReducedPrecisionGeometry(MVTTileLayerFeature::GeomType eGeomType,
4576 : const std::vector<GUInt32> &anSrcGeometry,
4577 : GUInt32 nSrcExtent, GUInt32 nDstExtent)
4578 : {
4579 167 : std::vector<GUInt32> anDstGeometry;
4580 167 : size_t nLastMoveToIdx = 0;
4581 167 : int nX = 0;
4582 167 : int nY = 0;
4583 167 : int nFirstReducedX = 0;
4584 167 : int nFirstReducedY = 0;
4585 167 : int nLastReducedX = 0;
4586 167 : int nLastReducedY = 0;
4587 167 : int nLastReducedXValid = 0;
4588 167 : int nLastReducedYValid = 0;
4589 167 : std::unique_ptr<OGRLinearRing> poInRing;
4590 167 : std::unique_ptr<OGRLinearRing> poOutRing;
4591 167 : std::unique_ptr<OGRLinearRing> poOutOuterRing;
4592 167 : bool bDiscardInnerRings = false;
4593 167 : const bool bIsPoly = eGeomType == MVTTileLayerFeature::GeomType::POLYGON;
4594 506 : for (size_t iSrc = 0; iSrc < anSrcGeometry.size();)
4595 : {
4596 339 : const unsigned nCount = GetCmdCount(anSrcGeometry[iSrc]);
4597 339 : switch (GetCmdId(anSrcGeometry[iSrc]))
4598 : {
4599 185 : case knCMD_MOVETO:
4600 : {
4601 185 : nLastMoveToIdx = anDstGeometry.size();
4602 :
4603 185 : anDstGeometry.push_back(anSrcGeometry[iSrc]);
4604 185 : iSrc++;
4605 :
4606 185 : unsigned nDstPoints = 0;
4607 185 : for (unsigned j = 0;
4608 370 : iSrc + 1 < anSrcGeometry.size() && j < nCount;
4609 185 : j++, iSrc += 2)
4610 : {
4611 185 : nX += DecodeSInt(anSrcGeometry[iSrc]);
4612 185 : nY += DecodeSInt(anSrcGeometry[iSrc + 1]);
4613 :
4614 185 : int nReducedX = static_cast<int>(static_cast<GIntBig>(nX) *
4615 185 : nDstExtent / nSrcExtent);
4616 185 : int nReducedY = static_cast<int>(static_cast<GIntBig>(nY) *
4617 185 : nDstExtent / nSrcExtent);
4618 185 : int nDiffX = nReducedX - nLastReducedX;
4619 185 : int nDiffY = nReducedY - nLastReducedY;
4620 185 : if (j == 0)
4621 : {
4622 185 : if (bIsPoly)
4623 : {
4624 82 : poInRing = std::unique_ptr<OGRLinearRing>(
4625 82 : new OGRLinearRing());
4626 82 : poOutRing = std::unique_ptr<OGRLinearRing>(
4627 82 : new OGRLinearRing());
4628 : }
4629 185 : nFirstReducedX = nReducedX;
4630 185 : nFirstReducedY = nReducedY;
4631 : }
4632 185 : if (j == 0 || nDiffX != 0 || nDiffY != 0)
4633 : {
4634 185 : if (bIsPoly)
4635 : {
4636 41 : poInRing->addPoint(nX, nY);
4637 41 : poOutRing->addPoint(nReducedX, nReducedY);
4638 : }
4639 185 : nDstPoints++;
4640 185 : anDstGeometry.push_back(EncodeSInt(nDiffX));
4641 185 : anDstGeometry.push_back(EncodeSInt(nDiffY));
4642 185 : nLastReducedX = nReducedX;
4643 185 : nLastReducedY = nReducedY;
4644 : }
4645 : }
4646 : // Patch count of MOVETO
4647 185 : anDstGeometry[nLastMoveToIdx] = GetCmdCountCombined(
4648 185 : GetCmdId(anDstGeometry[nLastMoveToIdx]), nDstPoints);
4649 185 : break;
4650 : }
4651 113 : case knCMD_LINETO:
4652 : {
4653 113 : size_t nIdxToPatch = anDstGeometry.size();
4654 113 : anDstGeometry.push_back(anSrcGeometry[iSrc]);
4655 113 : iSrc++;
4656 113 : unsigned nDstPoints = 0;
4657 113 : int nLastReducedXBefore = nLastReducedX;
4658 113 : int nLastReducedYBefore = nLastReducedY;
4659 113 : for (unsigned j = 0;
4660 267 : iSrc + 1 < anSrcGeometry.size() && j < nCount;
4661 154 : j++, iSrc += 2)
4662 : {
4663 154 : nX += DecodeSInt(anSrcGeometry[iSrc]);
4664 154 : nY += DecodeSInt(anSrcGeometry[iSrc + 1]);
4665 :
4666 154 : int nReducedX = static_cast<int>(static_cast<GIntBig>(nX) *
4667 154 : nDstExtent / nSrcExtent);
4668 154 : int nReducedY = static_cast<int>(static_cast<GIntBig>(nY) *
4669 154 : nDstExtent / nSrcExtent);
4670 154 : int nDiffX = nReducedX - nLastReducedX;
4671 154 : int nDiffY = nReducedY - nLastReducedY;
4672 154 : if (nDiffX != 0 || nDiffY != 0)
4673 : {
4674 114 : if (bIsPoly)
4675 : {
4676 60 : CPLAssert(poInRing);
4677 60 : CPLAssert(poOutRing);
4678 60 : poInRing->addPoint(nX, nY);
4679 60 : poOutRing->addPoint(nReducedX, nReducedY);
4680 : }
4681 114 : nDstPoints++;
4682 114 : anDstGeometry.push_back(EncodeSInt(nDiffX));
4683 114 : anDstGeometry.push_back(EncodeSInt(nDiffY));
4684 114 : nLastReducedXBefore = nLastReducedX;
4685 114 : nLastReducedYBefore = nLastReducedY;
4686 114 : nLastReducedX = nReducedX;
4687 114 : nLastReducedY = nReducedY;
4688 : }
4689 : }
4690 :
4691 : // If last point of ring is identical to first one, discard it
4692 113 : if (nDstPoints > 0 && bIsPoly &&
4693 1 : nLastReducedX == nFirstReducedX &&
4694 : nLastReducedY == nFirstReducedY)
4695 : {
4696 0 : nLastReducedX = nLastReducedXBefore;
4697 0 : nLastReducedY = nLastReducedYBefore;
4698 0 : nDstPoints -= 1;
4699 0 : anDstGeometry.resize(anDstGeometry.size() - 2);
4700 0 : poOutRing->setNumPoints(poOutRing->getNumPoints() - 1);
4701 : }
4702 :
4703 : // Patch count of LINETO
4704 113 : anDstGeometry[nIdxToPatch] = GetCmdCountCombined(
4705 113 : GetCmdId(anDstGeometry[nIdxToPatch]), nDstPoints);
4706 :
4707 : // A valid linestring should have at least one MOVETO +
4708 : // one coord pair + one LINETO + one coord pair
4709 113 : if (eGeomType == MVTTileLayerFeature::GeomType::LINESTRING)
4710 : {
4711 72 : if (anDstGeometry.size() < nLastMoveToIdx + 1 + 2 + 1 + 2)
4712 : {
4713 : // Remove last linestring
4714 18 : nLastReducedX = nLastReducedXValid;
4715 18 : nLastReducedY = nLastReducedYValid;
4716 18 : anDstGeometry.resize(nLastMoveToIdx);
4717 : }
4718 : else
4719 : {
4720 54 : nLastReducedXValid = nLastReducedX;
4721 54 : nLastReducedYValid = nLastReducedY;
4722 : }
4723 : }
4724 :
4725 113 : break;
4726 : }
4727 41 : case knCMD_CLOSEPATH:
4728 : {
4729 41 : CPLAssert(bIsPoly);
4730 41 : CPLAssert(poInRing);
4731 41 : CPLAssert(poOutRing);
4732 41 : int bIsValid = true;
4733 :
4734 : // A valid ring should have at least one MOVETO + one
4735 : // coord pair + one LINETO + two coord pairs
4736 41 : if (anDstGeometry.size() < nLastMoveToIdx + 1 + 2 + 1 + 2 * 2)
4737 : {
4738 : // Remove ring. Normally if we remove an outer ring,
4739 : // its inner rings should also be removed, given they are
4740 : // smaller than the outer ring.
4741 14 : bIsValid = false;
4742 : }
4743 : else
4744 : {
4745 27 : poInRing->closeRings();
4746 27 : poOutRing->closeRings();
4747 27 : bool bIsOuterRing = !poInRing->isClockwise();
4748 : // Normally the first ring of a polygon geometry should
4749 : // be a outer ring, except when it is degenerate enough
4750 : // in which case poOutOuterRing might be null.
4751 27 : if (bIsOuterRing)
4752 : {
4753 : // if the outer ring turned out to be a inner ring
4754 : // once reduced
4755 18 : if (poOutRing->isClockwise())
4756 : {
4757 0 : bIsValid = false;
4758 0 : bDiscardInnerRings = true;
4759 : }
4760 : else
4761 : {
4762 18 : OGRPolygon oPoly;
4763 18 : oPoly.addRing(poOutRing.get());
4764 36 : poOutOuterRing = std::unique_ptr<OGRLinearRing>(
4765 18 : poOutRing.release());
4766 : {
4767 : CPLErrorStateBackuper oErrorStateBackuper(
4768 18 : CPLQuietErrorHandler);
4769 18 : bIsValid = oPoly.IsValid();
4770 : }
4771 18 : bDiscardInnerRings = !bIsValid;
4772 : }
4773 : }
4774 9 : else if (bDiscardInnerRings ||
4775 18 : poOutOuterRing.get() == nullptr ||
4776 : // if the inner ring turned out to be a outer ring
4777 : // once reduced
4778 9 : !poOutRing->isClockwise())
4779 : {
4780 0 : bIsValid = false;
4781 : }
4782 : else
4783 : {
4784 18 : OGRPolygon oPoly;
4785 9 : oPoly.addRing(poOutOuterRing.get());
4786 9 : oPoly.addRingDirectly(poOutRing.release());
4787 : {
4788 : CPLErrorStateBackuper oErrorStateBackuper(
4789 9 : CPLQuietErrorHandler);
4790 9 : bIsValid = oPoly.IsValid();
4791 : }
4792 : }
4793 : }
4794 :
4795 41 : if (bIsValid)
4796 : {
4797 24 : nLastReducedXValid = nLastReducedX;
4798 24 : nLastReducedYValid = nLastReducedY;
4799 24 : anDstGeometry.push_back(anSrcGeometry[iSrc]);
4800 : }
4801 : else
4802 : {
4803 : // Remove this ring
4804 17 : nLastReducedX = nLastReducedXValid;
4805 17 : nLastReducedY = nLastReducedYValid;
4806 17 : anDstGeometry.resize(nLastMoveToIdx);
4807 : }
4808 :
4809 41 : iSrc++;
4810 41 : break;
4811 : }
4812 0 : default:
4813 : {
4814 0 : CPLAssert(false);
4815 : break;
4816 : }
4817 : }
4818 : }
4819 :
4820 334 : return anDstGeometry;
4821 : }
4822 :
4823 : /************************************************************************/
4824 : /* EncodeFeature() */
4825 : /************************************************************************/
4826 :
4827 1700 : void OGRMVTWriterDataset::EncodeFeature(
4828 : const void *pabyBlob, int nBlobSize,
4829 : std::shared_ptr<MVTTileLayer> &poTargetLayer,
4830 : std::map<CPLString, GUInt32> &oMapKeyToIdx,
4831 : std::map<MVTTileLayerValue, GUInt32> &oMapValueToIdx,
4832 : MVTLayerProperties *poLayerProperties, GUInt32 nExtent,
4833 : unsigned &nFeaturesInTile)
4834 : {
4835 1700 : size_t nUncompressedSize = 0;
4836 : void *pCompressed =
4837 1700 : CPLZLibInflate(pabyBlob, nBlobSize, nullptr, 0, &nUncompressedSize);
4838 1700 : GByte *pabyUncompressed = static_cast<GByte *>(pCompressed);
4839 :
4840 3400 : MVTTileLayer oSrcTileLayer;
4841 1700 : if (nUncompressedSize &&
4842 1700 : oSrcTileLayer.read(pabyUncompressed,
4843 1700 : pabyUncompressed + nUncompressedSize))
4844 : {
4845 1700 : const auto &srcFeatures = oSrcTileLayer.getFeatures();
4846 1700 : if (srcFeatures.size() == 1) // should always be true !
4847 : {
4848 1700 : const auto &poSrcFeature = srcFeatures[0];
4849 : std::shared_ptr<MVTTileLayerFeature> poFeature(
4850 3400 : new MVTTileLayerFeature());
4851 :
4852 1700 : if (poSrcFeature->hasId())
4853 53 : poFeature->setId(poSrcFeature->getId());
4854 1700 : poFeature->setType(poSrcFeature->getType());
4855 1700 : if (poLayerProperties)
4856 : {
4857 1521 : poLayerProperties->m_oCountGeomType[poSrcFeature->getType()]++;
4858 : }
4859 1700 : bool bOK = true;
4860 1700 : if (nExtent < m_nExtent)
4861 : {
4862 : #ifdef for_debugging
4863 : const auto &srcKeys = oSrcTileLayer.getKeys();
4864 : const auto &srcValues = oSrcTileLayer.getValues();
4865 : const auto &anSrcTags = poSrcFeature->getTags();
4866 : for (size_t i = 0; i + 1 < anSrcTags.size(); i += 2)
4867 : {
4868 : GUInt32 nSrcIdxKey = anSrcTags[i];
4869 : GUInt32 nSrcIdxValue = anSrcTags[i + 1];
4870 : if (nSrcIdxKey < srcKeys.size() &&
4871 : nSrcIdxValue < srcValues.size())
4872 : {
4873 : auto &osKey = srcKeys[nSrcIdxKey];
4874 : auto &oValue = srcValues[nSrcIdxValue];
4875 : if (osKey == "tunnus" &&
4876 : oValue.getUIntValue() == 28799760)
4877 : {
4878 : printf("foo\n"); /* ok */
4879 : break;
4880 : }
4881 : }
4882 : }
4883 : #endif
4884 :
4885 167 : poFeature->setGeometry(GetReducedPrecisionGeometry(
4886 : poSrcFeature->getType(), poSrcFeature->getGeometry(),
4887 : m_nExtent, nExtent));
4888 167 : if (poFeature->getGeometry().empty())
4889 : {
4890 23 : bOK = false;
4891 : }
4892 : }
4893 : else
4894 : {
4895 1533 : poFeature->setGeometry(poSrcFeature->getGeometry());
4896 : }
4897 1700 : if (bOK)
4898 : {
4899 1677 : const auto &srcKeys = oSrcTileLayer.getKeys();
4900 6120 : for (const auto &osKey : srcKeys)
4901 : {
4902 4443 : auto oIter = oMapKeyToIdx.find(osKey);
4903 4443 : if (oIter == oMapKeyToIdx.end())
4904 : {
4905 3645 : oMapKeyToIdx[osKey] = poTargetLayer->addKey(osKey);
4906 : }
4907 : }
4908 :
4909 1677 : const auto &srcValues = oSrcTileLayer.getValues();
4910 6120 : for (const auto &oValue : srcValues)
4911 : {
4912 4443 : auto oIter = oMapValueToIdx.find(oValue);
4913 4443 : if (oIter == oMapValueToIdx.end())
4914 : {
4915 3771 : oMapValueToIdx[oValue] =
4916 3771 : poTargetLayer->addValue(oValue);
4917 : }
4918 : }
4919 :
4920 1677 : const auto &anSrcTags = poSrcFeature->getTags();
4921 6120 : for (size_t i = 0; i + 1 < anSrcTags.size(); i += 2)
4922 : {
4923 4443 : GUInt32 nSrcIdxKey = anSrcTags[i];
4924 4443 : GUInt32 nSrcIdxValue = anSrcTags[i + 1];
4925 8886 : if (nSrcIdxKey < srcKeys.size() &&
4926 4443 : nSrcIdxValue < srcValues.size())
4927 : {
4928 4443 : const auto &osKey = srcKeys[nSrcIdxKey];
4929 4443 : const auto &oValue = srcValues[nSrcIdxValue];
4930 :
4931 4443 : if (poLayerProperties)
4932 : {
4933 4353 : UpdateLayerProperties(poLayerProperties, osKey,
4934 : oValue);
4935 : }
4936 :
4937 4443 : poFeature->addTag(oMapKeyToIdx[osKey]);
4938 4443 : poFeature->addTag(oMapValueToIdx[oValue]);
4939 : }
4940 : }
4941 :
4942 1677 : nFeaturesInTile++;
4943 1677 : poTargetLayer->addFeature(std::move(poFeature));
4944 : }
4945 : }
4946 : }
4947 : else
4948 : {
4949 : // Shouldn't fail
4950 0 : CPLError(CE_Failure, CPLE_AppDefined, "Deserialization failure");
4951 : }
4952 :
4953 1700 : CPLFree(pabyUncompressed);
4954 1700 : }
4955 :
4956 : /************************************************************************/
4957 : /* EncodeTile() */
4958 : /************************************************************************/
4959 :
4960 865 : std::string OGRMVTWriterDataset::EncodeTile(
4961 : int nZ, int nX, int nY, sqlite3_stmt *hStmtLayer, sqlite3_stmt *hStmtRows,
4962 : std::map<CPLString, MVTLayerProperties> &oMapLayerProps,
4963 : std::set<CPLString> &oSetLayers, GIntBig &nTempTilesRead)
4964 : {
4965 1730 : MVTTile oTargetTile;
4966 :
4967 865 : sqlite3_bind_int(hStmtLayer, 1, nZ);
4968 865 : sqlite3_bind_int(hStmtLayer, 2, nX);
4969 865 : sqlite3_bind_int(hStmtLayer, 3, nY);
4970 :
4971 865 : unsigned nFeaturesInTile = 0;
4972 : const GIntBig nProgressStep =
4973 865 : std::max(static_cast<GIntBig>(1), m_nTempTiles / 10);
4974 :
4975 4330 : while (nFeaturesInTile < m_nMaxFeatures &&
4976 2159 : sqlite3_step(hStmtLayer) == SQLITE_ROW)
4977 : {
4978 : const char *pszLayerName =
4979 1306 : reinterpret_cast<const char *>(sqlite3_column_text(hStmtLayer, 0));
4980 1306 : sqlite3_bind_int(hStmtRows, 1, nZ);
4981 1306 : sqlite3_bind_int(hStmtRows, 2, nX);
4982 1306 : sqlite3_bind_int(hStmtRows, 3, nY);
4983 1306 : sqlite3_bind_text(hStmtRows, 4, pszLayerName, -1, SQLITE_STATIC);
4984 :
4985 1306 : auto oIterMapLayerProps = oMapLayerProps.find(pszLayerName);
4986 1306 : MVTLayerProperties *poLayerProperties = nullptr;
4987 1306 : if (oIterMapLayerProps == oMapLayerProps.end())
4988 : {
4989 75 : if (oSetLayers.size() < knMAX_COUNT_LAYERS)
4990 : {
4991 75 : oSetLayers.insert(pszLayerName);
4992 75 : if (oMapLayerProps.size() < knMAX_REPORT_LAYERS)
4993 : {
4994 75 : MVTLayerProperties props;
4995 75 : props.m_nMinZoom = nZ;
4996 75 : props.m_nMaxZoom = nZ;
4997 75 : oMapLayerProps[pszLayerName] = std::move(props);
4998 75 : poLayerProperties = &(oMapLayerProps[pszLayerName]);
4999 : }
5000 : }
5001 : }
5002 : else
5003 : {
5004 1231 : poLayerProperties = &(oIterMapLayerProps->second);
5005 : }
5006 1306 : if (poLayerProperties)
5007 : {
5008 1306 : poLayerProperties->m_nMinZoom =
5009 1306 : std::min(nZ, poLayerProperties->m_nMinZoom);
5010 1306 : poLayerProperties->m_nMaxZoom =
5011 1306 : std::max(nZ, poLayerProperties->m_nMaxZoom);
5012 : }
5013 :
5014 2612 : std::shared_ptr<MVTTileLayer> poTargetLayer(new MVTTileLayer());
5015 1306 : oTargetTile.addLayer(poTargetLayer);
5016 1306 : poTargetLayer->setName(pszLayerName);
5017 1306 : poTargetLayer->setVersion(m_nMVTVersion);
5018 1306 : poTargetLayer->setExtent(m_nExtent);
5019 :
5020 2612 : std::map<CPLString, GUInt32> oMapKeyToIdx;
5021 2612 : std::map<MVTTileLayerValue, GUInt32> oMapValueToIdx;
5022 :
5023 5642 : while (nFeaturesInTile < m_nMaxFeatures &&
5024 2815 : sqlite3_step(hStmtRows) == SQLITE_ROW)
5025 : {
5026 1521 : int nBlobSize = sqlite3_column_bytes(hStmtRows, 0);
5027 1521 : const void *pabyBlob = sqlite3_column_blob(hStmtRows, 0);
5028 :
5029 1521 : EncodeFeature(pabyBlob, nBlobSize, poTargetLayer, oMapKeyToIdx,
5030 : oMapValueToIdx, poLayerProperties, m_nExtent,
5031 : nFeaturesInTile);
5032 :
5033 1521 : nTempTilesRead++;
5034 1521 : if (nTempTilesRead == m_nTempTiles ||
5035 1470 : (nTempTilesRead % nProgressStep) == 0)
5036 : {
5037 529 : const int nPct =
5038 529 : static_cast<int>((100 * nTempTilesRead) / m_nTempTiles);
5039 529 : CPLDebug("MVT", "%d%%...", nPct);
5040 : }
5041 : }
5042 1306 : sqlite3_reset(hStmtRows);
5043 : }
5044 :
5045 865 : sqlite3_reset(hStmtLayer);
5046 :
5047 1730 : std::string oTileBuffer(oTargetTile.write());
5048 865 : size_t nSizeBefore = oTileBuffer.size();
5049 865 : if (m_bGZip)
5050 865 : GZIPCompress(oTileBuffer);
5051 865 : const size_t nSizeAfter = oTileBuffer.size();
5052 865 : const double dfCompressionRatio =
5053 865 : static_cast<double>(nSizeAfter) / nSizeBefore;
5054 :
5055 865 : const bool bTooManyFeatures = nFeaturesInTile >= m_nMaxFeatures;
5056 865 : if (bTooManyFeatures && !m_bMaxFeaturesOptSpecified)
5057 : {
5058 1 : m_bMaxFeaturesOptSpecified = true;
5059 1 : CPLError(CE_Warning, CPLE_AppDefined,
5060 : "At least one tile exceeded the default maximum number of "
5061 : "features per tile (%u) and was truncated to satisfy it.",
5062 : m_nMaxFeatures);
5063 : }
5064 :
5065 : // If the tile size is above the allowed values or there are too many
5066 : // features, then sort by descending area / length until we get to the
5067 : // limit.
5068 865 : bool bTooBigTile = oTileBuffer.size() > m_nMaxTileSize;
5069 865 : if (bTooBigTile && !m_bMaxTileSizeOptSpecified)
5070 : {
5071 1 : m_bMaxTileSizeOptSpecified = true;
5072 1 : CPLError(CE_Warning, CPLE_AppDefined,
5073 : "At least one tile exceeded the default maximum tile size of "
5074 : "%u bytes and was encoded at lower resolution",
5075 : m_nMaxTileSize);
5076 : }
5077 :
5078 865 : GUInt32 nExtent = m_nExtent;
5079 947 : while (bTooBigTile && !bTooManyFeatures && nExtent >= 256)
5080 : {
5081 82 : nExtent /= 2;
5082 82 : nSizeBefore = oTileBuffer.size();
5083 164 : oTileBuffer = RecodeTileLowerResolution(nZ, nX, nY, nExtent, hStmtLayer,
5084 82 : hStmtRows);
5085 82 : bTooBigTile = oTileBuffer.size() > m_nMaxTileSize;
5086 82 : CPLDebug("MVT",
5087 : "Recoding tile %d/%d/%d with extent = %u. "
5088 : "From %u to %u bytes",
5089 : nZ, nX, nY, nExtent, static_cast<unsigned>(nSizeBefore),
5090 82 : static_cast<unsigned>(oTileBuffer.size()));
5091 : }
5092 :
5093 865 : if (bTooBigTile || bTooManyFeatures)
5094 : {
5095 25 : if (bTooBigTile)
5096 : {
5097 13 : CPLDebug("MVT", "For tile %d/%d/%d, tile size is %u > %u", nZ, nX,
5098 13 : nY, static_cast<unsigned>(oTileBuffer.size()),
5099 : m_nMaxTileSize);
5100 : }
5101 25 : if (bTooManyFeatures)
5102 : {
5103 12 : CPLDebug("MVT",
5104 : "For tile %d/%d/%d, feature count limit of %u is reached",
5105 : nZ, nX, nY, m_nMaxFeatures);
5106 : }
5107 :
5108 25 : oTargetTile.clear();
5109 :
5110 : const unsigned nTotalFeaturesInTile =
5111 25 : std::min(m_nMaxFeatures, nFeaturesInTile);
5112 : char *pszSQL =
5113 25 : sqlite3_mprintf("SELECT layer, feature FROM temp "
5114 : "WHERE z = %d AND x = %d AND y = %d ORDER BY "
5115 : "area_or_length DESC LIMIT %d",
5116 : nZ, nX, nY, nTotalFeaturesInTile);
5117 25 : sqlite3_stmt *hTmpStmt = nullptr;
5118 25 : CPL_IGNORE_RET_VAL(
5119 25 : sqlite3_prepare_v2(m_hDB, pszSQL, -1, &hTmpStmt, nullptr));
5120 25 : sqlite3_free(pszSQL);
5121 25 : if (!hTmpStmt)
5122 0 : return std::string();
5123 :
5124 : class TargetTileLayerProps
5125 : {
5126 : public:
5127 : std::shared_ptr<MVTTileLayer> m_poLayer;
5128 : std::map<CPLString, GUInt32> m_oMapKeyToIdx;
5129 : std::map<MVTTileLayerValue, GUInt32> m_oMapValueToIdx;
5130 : };
5131 :
5132 50 : std::map<std::string, TargetTileLayerProps> oMapLayerNameToTargetLayer;
5133 :
5134 25 : nFeaturesInTile = 0;
5135 25 : const unsigned nCheckStep = std::max(1U, nTotalFeaturesInTile / 100);
5136 49 : while (sqlite3_step(hTmpStmt) == SQLITE_ROW)
5137 : {
5138 : const char *pszLayerName = reinterpret_cast<const char *>(
5139 37 : sqlite3_column_text(hTmpStmt, 0));
5140 37 : int nBlobSize = sqlite3_column_bytes(hTmpStmt, 1);
5141 37 : const void *pabyBlob = sqlite3_column_blob(hTmpStmt, 1);
5142 :
5143 0 : std::shared_ptr<MVTTileLayer> poTargetLayer;
5144 : std::map<CPLString, GUInt32> *poMapKeyToIdx;
5145 : std::map<MVTTileLayerValue, GUInt32> *poMapValueToIdx;
5146 37 : auto oIter = oMapLayerNameToTargetLayer.find(pszLayerName);
5147 37 : if (oIter == oMapLayerNameToTargetLayer.end())
5148 : {
5149 : poTargetLayer =
5150 25 : std::shared_ptr<MVTTileLayer>(new MVTTileLayer());
5151 25 : TargetTileLayerProps props;
5152 25 : props.m_poLayer = poTargetLayer;
5153 25 : oTargetTile.addLayer(poTargetLayer);
5154 25 : poTargetLayer->setName(pszLayerName);
5155 25 : poTargetLayer->setVersion(m_nMVTVersion);
5156 25 : poTargetLayer->setExtent(nExtent);
5157 25 : oMapLayerNameToTargetLayer[pszLayerName] = std::move(props);
5158 25 : poMapKeyToIdx =
5159 25 : &oMapLayerNameToTargetLayer[pszLayerName].m_oMapKeyToIdx;
5160 25 : poMapValueToIdx =
5161 25 : &oMapLayerNameToTargetLayer[pszLayerName].m_oMapValueToIdx;
5162 : }
5163 : else
5164 : {
5165 12 : poTargetLayer = oIter->second.m_poLayer;
5166 12 : poMapKeyToIdx = &oIter->second.m_oMapKeyToIdx;
5167 12 : poMapValueToIdx = &oIter->second.m_oMapValueToIdx;
5168 : }
5169 :
5170 37 : EncodeFeature(pabyBlob, nBlobSize, poTargetLayer, *poMapKeyToIdx,
5171 : *poMapValueToIdx, nullptr, nExtent, nFeaturesInTile);
5172 :
5173 37 : if (nFeaturesInTile == nTotalFeaturesInTile ||
5174 18 : (bTooBigTile && (nFeaturesInTile % nCheckStep == 0)))
5175 : {
5176 37 : if (oTargetTile.getSize() * dfCompressionRatio > m_nMaxTileSize)
5177 : {
5178 13 : break;
5179 : }
5180 : }
5181 : }
5182 :
5183 25 : oTileBuffer = oTargetTile.write();
5184 25 : if (m_bGZip)
5185 25 : GZIPCompress(oTileBuffer);
5186 :
5187 25 : if (bTooBigTile)
5188 : {
5189 13 : CPLDebug("MVT", "For tile %d/%d/%d, final tile size is %u", nZ, nX,
5190 13 : nY, static_cast<unsigned>(oTileBuffer.size()));
5191 : }
5192 :
5193 25 : sqlite3_finalize(hTmpStmt);
5194 : }
5195 :
5196 865 : return oTileBuffer;
5197 : }
5198 :
5199 : /************************************************************************/
5200 : /* RecodeTileLowerResolution() */
5201 : /************************************************************************/
5202 :
5203 82 : std::string OGRMVTWriterDataset::RecodeTileLowerResolution(
5204 : int nZ, int nX, int nY, int nExtent, sqlite3_stmt *hStmtLayer,
5205 : sqlite3_stmt *hStmtRows)
5206 : {
5207 164 : MVTTile oTargetTile;
5208 :
5209 82 : sqlite3_bind_int(hStmtLayer, 1, nZ);
5210 82 : sqlite3_bind_int(hStmtLayer, 2, nX);
5211 82 : sqlite3_bind_int(hStmtLayer, 3, nY);
5212 :
5213 82 : unsigned nFeaturesInTile = 0;
5214 328 : while (nFeaturesInTile < m_nMaxFeatures &&
5215 164 : sqlite3_step(hStmtLayer) == SQLITE_ROW)
5216 : {
5217 : const char *pszLayerName =
5218 82 : reinterpret_cast<const char *>(sqlite3_column_text(hStmtLayer, 0));
5219 82 : sqlite3_bind_int(hStmtRows, 1, nZ);
5220 82 : sqlite3_bind_int(hStmtRows, 2, nX);
5221 82 : sqlite3_bind_int(hStmtRows, 3, nY);
5222 82 : sqlite3_bind_text(hStmtRows, 4, pszLayerName, -1, SQLITE_STATIC);
5223 :
5224 164 : std::shared_ptr<MVTTileLayer> poTargetLayer(new MVTTileLayer());
5225 82 : oTargetTile.addLayer(poTargetLayer);
5226 82 : poTargetLayer->setName(pszLayerName);
5227 82 : poTargetLayer->setVersion(m_nMVTVersion);
5228 82 : poTargetLayer->setExtent(nExtent);
5229 :
5230 164 : std::map<CPLString, GUInt32> oMapKeyToIdx;
5231 164 : std::map<MVTTileLayerValue, GUInt32> oMapValueToIdx;
5232 :
5233 448 : while (nFeaturesInTile < m_nMaxFeatures &&
5234 224 : sqlite3_step(hStmtRows) == SQLITE_ROW)
5235 : {
5236 142 : int nBlobSize = sqlite3_column_bytes(hStmtRows, 0);
5237 142 : const void *pabyBlob = sqlite3_column_blob(hStmtRows, 0);
5238 :
5239 142 : EncodeFeature(pabyBlob, nBlobSize, poTargetLayer, oMapKeyToIdx,
5240 : oMapValueToIdx, nullptr, nExtent, nFeaturesInTile);
5241 : }
5242 82 : sqlite3_reset(hStmtRows);
5243 : }
5244 :
5245 82 : sqlite3_reset(hStmtLayer);
5246 :
5247 82 : std::string oTileBuffer(oTargetTile.write());
5248 82 : if (m_bGZip)
5249 82 : GZIPCompress(oTileBuffer);
5250 :
5251 164 : return oTileBuffer;
5252 : }
5253 :
5254 : /************************************************************************/
5255 : /* CreateOutput() */
5256 : /************************************************************************/
5257 :
5258 122 : bool OGRMVTWriterDataset::CreateOutput()
5259 : {
5260 122 : if (m_bThreadPoolOK)
5261 119 : m_oThreadPool.WaitCompletion();
5262 :
5263 244 : std::map<CPLString, MVTLayerProperties> oMapLayerProps;
5264 244 : std::set<CPLString> oSetLayers;
5265 :
5266 122 : if (!m_oEnvelope.IsInit())
5267 : {
5268 50 : return GenerateMetadata(0, oMapLayerProps);
5269 : }
5270 :
5271 72 : CPLDebug("MVT", "Building output file from temporary database...");
5272 :
5273 72 : sqlite3_stmt *hStmtZXY = nullptr;
5274 72 : CPL_IGNORE_RET_VAL(sqlite3_prepare_v2(
5275 : m_hDB, "SELECT DISTINCT z, x, y FROM temp ORDER BY z, x, y", -1,
5276 : &hStmtZXY, nullptr));
5277 72 : if (hStmtZXY == nullptr)
5278 : {
5279 2 : CPLError(CE_Failure, CPLE_AppDefined, "Prepared statement failed");
5280 2 : return false;
5281 : }
5282 :
5283 70 : sqlite3_stmt *hStmtLayer = nullptr;
5284 70 : CPL_IGNORE_RET_VAL(
5285 70 : sqlite3_prepare_v2(m_hDB,
5286 : "SELECT DISTINCT layer FROM temp "
5287 : "WHERE z = ? AND x = ? AND y = ? ORDER BY layer",
5288 : -1, &hStmtLayer, nullptr));
5289 70 : if (hStmtLayer == nullptr)
5290 : {
5291 0 : CPLError(CE_Failure, CPLE_AppDefined, "Prepared statement failed");
5292 0 : sqlite3_finalize(hStmtZXY);
5293 0 : return false;
5294 : }
5295 70 : sqlite3_stmt *hStmtRows = nullptr;
5296 70 : CPL_IGNORE_RET_VAL(sqlite3_prepare_v2(
5297 : m_hDB,
5298 : "SELECT feature FROM temp "
5299 : "WHERE z = ? AND x = ? AND y = ? AND layer = ? ORDER BY idx",
5300 : -1, &hStmtRows, nullptr));
5301 70 : if (hStmtRows == nullptr)
5302 : {
5303 0 : CPLError(CE_Failure, CPLE_AppDefined, "Prepared statement failed");
5304 0 : sqlite3_finalize(hStmtZXY);
5305 0 : sqlite3_finalize(hStmtLayer);
5306 0 : return false;
5307 : }
5308 :
5309 70 : sqlite3_stmt *hInsertStmt = nullptr;
5310 70 : if (m_hDBMBTILES)
5311 : {
5312 43 : CPL_IGNORE_RET_VAL(sqlite3_prepare_v2(
5313 : m_hDBMBTILES,
5314 : "INSERT INTO tiles(zoom_level, tile_column, tile_row, "
5315 : "tile_data) VALUES (?,?,?,?)",
5316 : -1, &hInsertStmt, nullptr));
5317 43 : if (hInsertStmt == nullptr)
5318 : {
5319 0 : CPLError(CE_Failure, CPLE_AppDefined, "Prepared statement failed");
5320 0 : sqlite3_finalize(hStmtZXY);
5321 0 : sqlite3_finalize(hStmtLayer);
5322 0 : sqlite3_finalize(hStmtRows);
5323 0 : return false;
5324 : }
5325 : }
5326 :
5327 70 : int nLastZ = -1;
5328 70 : int nLastX = -1;
5329 70 : bool bRet = true;
5330 70 : GIntBig nTempTilesRead = 0;
5331 :
5332 934 : while (sqlite3_step(hStmtZXY) == SQLITE_ROW)
5333 : {
5334 865 : int nZ = sqlite3_column_int(hStmtZXY, 0);
5335 865 : int nX = sqlite3_column_int(hStmtZXY, 1);
5336 865 : int nY = sqlite3_column_int(hStmtZXY, 2);
5337 :
5338 : std::string oTileBuffer(EncodeTile(nZ, nX, nY, hStmtLayer, hStmtRows,
5339 : oMapLayerProps, oSetLayers,
5340 865 : nTempTilesRead));
5341 :
5342 865 : if (oTileBuffer.empty())
5343 : {
5344 0 : bRet = false;
5345 : }
5346 865 : else if (hInsertStmt)
5347 : {
5348 531 : sqlite3_bind_int(hInsertStmt, 1, nZ);
5349 531 : sqlite3_bind_int(hInsertStmt, 2, nX);
5350 531 : sqlite3_bind_int(hInsertStmt, 3, (1 << nZ) - 1 - nY);
5351 531 : sqlite3_bind_blob(hInsertStmt, 4, oTileBuffer.data(),
5352 531 : static_cast<int>(oTileBuffer.size()),
5353 : SQLITE_STATIC);
5354 531 : const int rc = sqlite3_step(hInsertStmt);
5355 531 : bRet = (rc == SQLITE_OK || rc == SQLITE_DONE);
5356 531 : sqlite3_reset(hInsertStmt);
5357 : }
5358 : else
5359 : {
5360 : const std::string osZDirname(CPLFormFilenameSafe(
5361 668 : GetDescription(), CPLSPrintf("%d", nZ), nullptr));
5362 : const std::string osXDirname(CPLFormFilenameSafe(
5363 668 : osZDirname.c_str(), CPLSPrintf("%d", nX), nullptr));
5364 334 : if (nZ != nLastZ)
5365 : {
5366 113 : VSIMkdir(osZDirname.c_str(), 0755);
5367 113 : nLastZ = nZ;
5368 113 : nLastX = -1;
5369 : }
5370 334 : if (nX != nLastX)
5371 : {
5372 192 : VSIMkdir(osXDirname.c_str(), 0755);
5373 192 : nLastX = nX;
5374 : }
5375 : const std::string osTileFilename(
5376 : CPLFormFilenameSafe(osXDirname.c_str(), CPLSPrintf("%d", nY),
5377 668 : m_osExtension.c_str()));
5378 334 : VSILFILE *fpOut = VSIFOpenL(osTileFilename.c_str(), "wb");
5379 334 : if (fpOut)
5380 : {
5381 333 : const size_t nRet = VSIFWriteL(oTileBuffer.data(), 1,
5382 : oTileBuffer.size(), fpOut);
5383 333 : bRet = (nRet == oTileBuffer.size());
5384 333 : VSIFCloseL(fpOut);
5385 : }
5386 : else
5387 : {
5388 1 : bRet = false;
5389 : }
5390 : }
5391 :
5392 865 : if (!bRet)
5393 : {
5394 1 : CPLError(CE_Failure, CPLE_AppDefined,
5395 : "Error while writing tile %d/%d/%d", nZ, nX, nY);
5396 1 : break;
5397 : }
5398 : }
5399 70 : sqlite3_finalize(hStmtZXY);
5400 70 : sqlite3_finalize(hStmtLayer);
5401 70 : sqlite3_finalize(hStmtRows);
5402 70 : if (hInsertStmt)
5403 43 : sqlite3_finalize(hInsertStmt);
5404 :
5405 70 : bRet &= GenerateMetadata(oSetLayers.size(), oMapLayerProps);
5406 :
5407 70 : return bRet;
5408 : }
5409 :
5410 : /************************************************************************/
5411 : /* SphericalMercatorToLongLat() */
5412 : /************************************************************************/
5413 :
5414 232 : static void SphericalMercatorToLongLat(double *x, double *y)
5415 : {
5416 232 : double lng = *x / kmSPHERICAL_RADIUS / M_PI * 180;
5417 : double lat =
5418 232 : 2 * (atan(exp(*y / kmSPHERICAL_RADIUS)) - M_PI / 4) / M_PI * 180;
5419 232 : *x = lng;
5420 232 : *y = lat;
5421 232 : }
5422 :
5423 : /************************************************************************/
5424 : /* WriteMetadataItem() */
5425 : /************************************************************************/
5426 :
5427 : template <class T>
5428 1299 : static bool WriteMetadataItemT(const char *pszKey, T value,
5429 : const char *pszValueFormat, sqlite3 *hDBMBTILES,
5430 : CPLJSONObject &oRoot)
5431 : {
5432 1299 : if (hDBMBTILES)
5433 : {
5434 : char *pszSQL;
5435 :
5436 825 : pszSQL = sqlite3_mprintf(
5437 : CPLSPrintf("INSERT INTO metadata(name, value) VALUES('%%q', '%s')",
5438 : pszValueFormat),
5439 : pszKey, value);
5440 825 : OGRErr eErr = SQLCommand(hDBMBTILES, pszSQL);
5441 825 : sqlite3_free(pszSQL);
5442 825 : return eErr == OGRERR_NONE;
5443 : }
5444 : else
5445 : {
5446 474 : oRoot.Add(pszKey, value);
5447 474 : return true;
5448 : }
5449 : }
5450 :
5451 : /************************************************************************/
5452 : /* WriteMetadataItem() */
5453 : /************************************************************************/
5454 :
5455 919 : static bool WriteMetadataItem(const char *pszKey, const char *pszValue,
5456 : sqlite3 *hDBMBTILES, CPLJSONObject &oRoot)
5457 : {
5458 919 : return WriteMetadataItemT(pszKey, pszValue, "%q", hDBMBTILES, oRoot);
5459 : }
5460 :
5461 : /************************************************************************/
5462 : /* WriteMetadataItem() */
5463 : /************************************************************************/
5464 :
5465 368 : static bool WriteMetadataItem(const char *pszKey, int nValue,
5466 : sqlite3 *hDBMBTILES, CPLJSONObject &oRoot)
5467 : {
5468 368 : return WriteMetadataItemT(pszKey, nValue, "%d", hDBMBTILES, oRoot);
5469 : }
5470 :
5471 : /************************************************************************/
5472 : /* WriteMetadataItem() */
5473 : /************************************************************************/
5474 :
5475 12 : static bool WriteMetadataItem(const char *pszKey, double dfValue,
5476 : sqlite3 *hDBMBTILES, CPLJSONObject &oRoot)
5477 : {
5478 12 : return WriteMetadataItemT(pszKey, dfValue, "%.17g", hDBMBTILES, oRoot);
5479 : }
5480 :
5481 : /************************************************************************/
5482 : /* GenerateMetadata() */
5483 : /************************************************************************/
5484 :
5485 120 : bool OGRMVTWriterDataset::GenerateMetadata(
5486 : size_t nLayers, const std::map<CPLString, MVTLayerProperties> &oMap)
5487 : {
5488 240 : CPLJSONDocument oDoc;
5489 240 : CPLJSONObject oRoot = oDoc.GetRoot();
5490 :
5491 240 : OGRSpatialReference oSRS_EPSG3857;
5492 : double dfTopXWebMercator;
5493 : double dfTopYWebMercator;
5494 : double dfTileDim0WebMercator;
5495 120 : InitWebMercatorTilingScheme(&oSRS_EPSG3857, dfTopXWebMercator,
5496 : dfTopYWebMercator, dfTileDim0WebMercator);
5497 : const bool bIsStandardTilingScheme =
5498 236 : m_poSRS->IsSame(&oSRS_EPSG3857) && m_dfTopX == dfTopXWebMercator &&
5499 236 : m_dfTopY == dfTopYWebMercator && m_dfTileDim0 == dfTileDim0WebMercator;
5500 120 : if (bIsStandardTilingScheme)
5501 : {
5502 116 : SphericalMercatorToLongLat(&(m_oEnvelope.MinX), &(m_oEnvelope.MinY));
5503 116 : SphericalMercatorToLongLat(&(m_oEnvelope.MaxX), &(m_oEnvelope.MaxY));
5504 116 : m_oEnvelope.MinY = std::max(-85.0, m_oEnvelope.MinY);
5505 116 : m_oEnvelope.MaxY = std::min(85.0, m_oEnvelope.MaxY);
5506 : }
5507 : else
5508 : {
5509 8 : OGRSpatialReference oSRS_EPSG4326;
5510 4 : oSRS_EPSG4326.SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
5511 4 : oSRS_EPSG4326.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
5512 : OGRCoordinateTransformation *poCT =
5513 4 : OGRCreateCoordinateTransformation(m_poSRS, &oSRS_EPSG4326);
5514 4 : if (poCT)
5515 : {
5516 8 : OGRPoint oPoint1(m_oEnvelope.MinX, m_oEnvelope.MinY);
5517 4 : oPoint1.transform(poCT);
5518 8 : OGRPoint oPoint2(m_oEnvelope.MinX, m_oEnvelope.MaxY);
5519 4 : oPoint2.transform(poCT);
5520 8 : OGRPoint oPoint3(m_oEnvelope.MaxX, m_oEnvelope.MaxY);
5521 4 : oPoint3.transform(poCT);
5522 8 : OGRPoint oPoint4(m_oEnvelope.MaxX, m_oEnvelope.MinY);
5523 4 : oPoint4.transform(poCT);
5524 4 : m_oEnvelope.MinX =
5525 4 : std::min(std::min(oPoint1.getX(), oPoint2.getX()),
5526 8 : std::min(oPoint3.getX(), oPoint4.getX()));
5527 4 : m_oEnvelope.MinY =
5528 4 : std::min(std::min(oPoint1.getY(), oPoint2.getY()),
5529 8 : std::min(oPoint3.getY(), oPoint4.getY()));
5530 4 : m_oEnvelope.MaxX =
5531 4 : std::max(std::max(oPoint1.getX(), oPoint2.getX()),
5532 8 : std::max(oPoint3.getX(), oPoint4.getX()));
5533 4 : m_oEnvelope.MaxY =
5534 4 : std::max(std::max(oPoint1.getY(), oPoint2.getY()),
5535 8 : std::max(oPoint3.getY(), oPoint4.getY()));
5536 4 : delete poCT;
5537 : }
5538 : }
5539 120 : const double dfCenterX = (m_oEnvelope.MinX + m_oEnvelope.MaxX) / 2;
5540 120 : const double dfCenterY = (m_oEnvelope.MinY + m_oEnvelope.MaxY) / 2;
5541 : CPLString osCenter(
5542 240 : CPLSPrintf("%.7f,%.7f,%d", dfCenterX, dfCenterY, m_nMinZoom));
5543 : CPLString osBounds(CPLSPrintf("%.7f,%.7f,%.7f,%.7f", m_oEnvelope.MinX,
5544 : m_oEnvelope.MinY, m_oEnvelope.MaxX,
5545 240 : m_oEnvelope.MaxY));
5546 :
5547 120 : WriteMetadataItem("name", m_osName, m_hDBMBTILES, oRoot);
5548 120 : WriteMetadataItem("description", m_osDescription, m_hDBMBTILES, oRoot);
5549 120 : WriteMetadataItem("version", m_nMetadataVersion, m_hDBMBTILES, oRoot);
5550 120 : WriteMetadataItem("minzoom", m_nMinZoom, m_hDBMBTILES, oRoot);
5551 120 : WriteMetadataItem("maxzoom", m_nMaxZoom, m_hDBMBTILES, oRoot);
5552 120 : WriteMetadataItem("center", !m_osCenter.empty() ? m_osCenter : osCenter,
5553 : m_hDBMBTILES, oRoot);
5554 120 : WriteMetadataItem("bounds", !m_osBounds.empty() ? m_osBounds : osBounds,
5555 : m_hDBMBTILES, oRoot);
5556 120 : WriteMetadataItem("type", m_osType, m_hDBMBTILES, oRoot);
5557 120 : WriteMetadataItem("format", "pbf", m_hDBMBTILES, oRoot);
5558 120 : if (m_hDBMBTILES)
5559 : {
5560 75 : WriteMetadataItem("scheme", "tms", m_hDBMBTILES, oRoot);
5561 : }
5562 :
5563 : // GDAL extension for custom tiling schemes
5564 120 : if (!bIsStandardTilingScheme)
5565 : {
5566 4 : const char *pszAuthName = m_poSRS->GetAuthorityName(nullptr);
5567 4 : const char *pszAuthCode = m_poSRS->GetAuthorityCode(nullptr);
5568 4 : if (pszAuthName && pszAuthCode)
5569 : {
5570 4 : WriteMetadataItem("crs",
5571 : CPLSPrintf("%s:%s", pszAuthName, pszAuthCode),
5572 : m_hDBMBTILES, oRoot);
5573 : }
5574 : else
5575 : {
5576 0 : char *pszWKT = nullptr;
5577 0 : m_poSRS->exportToWkt(&pszWKT);
5578 0 : WriteMetadataItem("crs", pszWKT, m_hDBMBTILES, oRoot);
5579 0 : CPLFree(pszWKT);
5580 : }
5581 4 : WriteMetadataItem("tile_origin_upper_left_x", m_dfTopX, m_hDBMBTILES,
5582 : oRoot);
5583 4 : WriteMetadataItem("tile_origin_upper_left_y", m_dfTopY, m_hDBMBTILES,
5584 : oRoot);
5585 4 : WriteMetadataItem("tile_dimension_zoom_0", m_dfTileDim0, m_hDBMBTILES,
5586 : oRoot);
5587 4 : WriteMetadataItem("tile_matrix_width_zoom_0", m_nTileMatrixWidth0,
5588 : m_hDBMBTILES, oRoot);
5589 4 : WriteMetadataItem("tile_matrix_height_zoom_0", m_nTileMatrixHeight0,
5590 : m_hDBMBTILES, oRoot);
5591 : }
5592 :
5593 240 : CPLJSONDocument oJsonDoc;
5594 240 : CPLJSONObject oJsonRoot = oJsonDoc.GetRoot();
5595 :
5596 240 : CPLJSONArray oVectorLayers;
5597 120 : oJsonRoot.Add("vector_layers", oVectorLayers);
5598 240 : std::set<std::string> oAlreadyVisited;
5599 287 : for (const auto &poLayer : m_apoLayers)
5600 : {
5601 167 : auto oIter = oMap.find(poLayer->m_osTargetName);
5602 242 : if (oIter != oMap.end() &&
5603 75 : oAlreadyVisited.find(poLayer->m_osTargetName) ==
5604 242 : oAlreadyVisited.end())
5605 : {
5606 75 : oAlreadyVisited.insert(poLayer->m_osTargetName);
5607 :
5608 150 : CPLJSONObject oLayerObj;
5609 75 : oLayerObj.Add("id", poLayer->m_osTargetName);
5610 75 : oLayerObj.Add("description",
5611 75 : m_oMapLayerNameToDesc[poLayer->m_osTargetName]);
5612 75 : oLayerObj.Add("minzoom", oIter->second.m_nMinZoom);
5613 75 : oLayerObj.Add("maxzoom", oIter->second.m_nMaxZoom);
5614 :
5615 150 : CPLJSONObject oFields;
5616 75 : oLayerObj.Add("fields", oFields);
5617 75 : auto poFDefn = poLayer->GetLayerDefn();
5618 279 : for (int i = 0; i < poFDefn->GetFieldCount(); i++)
5619 : {
5620 204 : auto poFieldDefn = poFDefn->GetFieldDefn(i);
5621 204 : auto eType = poFieldDefn->GetType();
5622 239 : if (eType == OFTInteger &&
5623 35 : poFieldDefn->GetSubType() == OFSTBoolean)
5624 : {
5625 1 : oFields.Add(poFieldDefn->GetNameRef(), "Boolean");
5626 : }
5627 203 : else if (eType == OFTInteger || eType == OFTInteger64 ||
5628 : eType == OFTReal)
5629 : {
5630 73 : oFields.Add(poFieldDefn->GetNameRef(), "Number");
5631 : }
5632 : else
5633 : {
5634 130 : oFields.Add(poFieldDefn->GetNameRef(), "String");
5635 : }
5636 : }
5637 :
5638 75 : oVectorLayers.Add(oLayerObj);
5639 : }
5640 : }
5641 :
5642 240 : CPLJSONObject oTileStats;
5643 120 : oJsonRoot.Add("tilestats", oTileStats);
5644 120 : oTileStats.Add("layerCount", static_cast<int>(nLayers));
5645 240 : CPLJSONArray oTileStatsLayers;
5646 120 : oTileStats.Add("layers", oTileStatsLayers);
5647 120 : oAlreadyVisited.clear();
5648 287 : for (const auto &poLayer : m_apoLayers)
5649 : {
5650 167 : auto oIter = oMap.find(poLayer->m_osTargetName);
5651 242 : if (oIter != oMap.end() &&
5652 75 : oAlreadyVisited.find(poLayer->m_osTargetName) ==
5653 242 : oAlreadyVisited.end())
5654 : {
5655 75 : oAlreadyVisited.insert(poLayer->m_osTargetName);
5656 75 : auto &oLayerProps = oIter->second;
5657 150 : CPLJSONObject oLayerObj;
5658 :
5659 150 : std::string osName(poLayer->m_osTargetName);
5660 75 : osName.resize(std::min(knMAX_LAYER_NAME_LENGTH, osName.size()));
5661 75 : oLayerObj.Add("layer", osName);
5662 75 : oLayerObj.Add(
5663 : "count",
5664 75 : m_oMapLayerNameToFeatureCount[poLayer->m_osTargetName]);
5665 :
5666 : // Find majority geometry type
5667 75 : MVTTileLayerFeature::GeomType eMaxGeomType =
5668 : MVTTileLayerFeature::GeomType::UNKNOWN;
5669 75 : GIntBig nMaxCountGeom = 0;
5670 300 : for (int i = static_cast<int>(MVTTileLayerFeature::GeomType::POINT);
5671 300 : i <= static_cast<int>(MVTTileLayerFeature::GeomType::POLYGON);
5672 : i++)
5673 : {
5674 225 : MVTTileLayerFeature::GeomType eGeomType =
5675 225 : static_cast<MVTTileLayerFeature::GeomType>(i);
5676 : auto oIterCountGeom =
5677 225 : oLayerProps.m_oCountGeomType.find(eGeomType);
5678 225 : if (oIterCountGeom != oLayerProps.m_oCountGeomType.end())
5679 : {
5680 79 : if (oIterCountGeom->second >= nMaxCountGeom)
5681 : {
5682 78 : eMaxGeomType = eGeomType;
5683 78 : nMaxCountGeom = oIterCountGeom->second;
5684 : }
5685 : }
5686 : }
5687 75 : if (eMaxGeomType == MVTTileLayerFeature::GeomType::POINT)
5688 63 : oLayerObj.Add("geometry", "Point");
5689 12 : else if (eMaxGeomType == MVTTileLayerFeature::GeomType::LINESTRING)
5690 6 : oLayerObj.Add("geometry", "LineString");
5691 6 : else if (eMaxGeomType == MVTTileLayerFeature::GeomType::POLYGON)
5692 6 : oLayerObj.Add("geometry", "Polygon");
5693 :
5694 75 : oLayerObj.Add("attributeCount",
5695 75 : static_cast<int>(oLayerProps.m_oSetFields.size()));
5696 150 : CPLJSONArray oAttributes;
5697 75 : oLayerObj.Add("attributes", oAttributes);
5698 256 : for (const auto &oFieldProps : oLayerProps.m_aoFields)
5699 : {
5700 362 : CPLJSONObject oFieldObj;
5701 181 : oAttributes.Add(oFieldObj);
5702 362 : std::string osFieldNameTruncated(oFieldProps.m_osName);
5703 181 : osFieldNameTruncated.resize(std::min(
5704 181 : knMAX_FIELD_NAME_LENGTH, osFieldNameTruncated.size()));
5705 181 : oFieldObj.Add("attribute", osFieldNameTruncated);
5706 181 : oFieldObj.Add("count", static_cast<int>(
5707 181 : oFieldProps.m_oSetAllValues.size()));
5708 181 : oFieldObj.Add("type",
5709 181 : oFieldProps.m_eType ==
5710 : MVTTileLayerValue::ValueType::DOUBLE
5711 : ? "number"
5712 108 : : oFieldProps.m_eType ==
5713 : MVTTileLayerValue::ValueType::STRING
5714 108 : ? "string"
5715 : : "boolean");
5716 :
5717 362 : CPLJSONArray oValues;
5718 181 : oFieldObj.Add("values", oValues);
5719 408 : for (const auto &oIterValue : oFieldProps.m_oSetValues)
5720 : {
5721 227 : if (oIterValue.getType() ==
5722 : MVTTileLayerValue::ValueType::BOOL)
5723 : {
5724 1 : oValues.Add(oIterValue.getBoolValue());
5725 : }
5726 226 : else if (oIterValue.isNumeric())
5727 : {
5728 104 : if (oFieldProps.m_bAllInt)
5729 : {
5730 53 : oValues.Add(static_cast<GInt64>(
5731 53 : oIterValue.getNumericValue()));
5732 : }
5733 : else
5734 : {
5735 51 : oValues.Add(oIterValue.getNumericValue());
5736 : }
5737 : }
5738 122 : else if (oIterValue.isString())
5739 : {
5740 122 : oValues.Add(oIterValue.getStringValue());
5741 : }
5742 : }
5743 :
5744 181 : if (oFieldProps.m_eType == MVTTileLayerValue::ValueType::DOUBLE)
5745 : {
5746 73 : if (oFieldProps.m_bAllInt)
5747 : {
5748 37 : oFieldObj.Add(
5749 37 : "min", static_cast<GInt64>(oFieldProps.m_dfMinVal));
5750 37 : oFieldObj.Add(
5751 37 : "max", static_cast<GInt64>(oFieldProps.m_dfMaxVal));
5752 : }
5753 : else
5754 : {
5755 36 : oFieldObj.Add("min", oFieldProps.m_dfMinVal);
5756 36 : oFieldObj.Add("max", oFieldProps.m_dfMaxVal);
5757 : }
5758 : }
5759 : }
5760 :
5761 75 : oTileStatsLayers.Add(oLayerObj);
5762 : }
5763 : }
5764 :
5765 120 : WriteMetadataItem("json", oJsonDoc.SaveAsString().c_str(), m_hDBMBTILES,
5766 : oRoot);
5767 :
5768 120 : if (m_hDBMBTILES)
5769 : {
5770 75 : return true;
5771 : }
5772 :
5773 45 : return oDoc.Save(
5774 90 : CPLFormFilenameSafe(GetDescription(), "metadata.json", nullptr));
5775 : }
5776 :
5777 : /************************************************************************/
5778 : /* WriteFeature() */
5779 : /************************************************************************/
5780 :
5781 250 : OGRErr OGRMVTWriterDataset::WriteFeature(OGRMVTWriterLayer *poLayer,
5782 : OGRFeature *poFeature, GIntBig nSerial,
5783 : OGRGeometry *poGeom)
5784 : {
5785 250 : if (poFeature->GetGeometryRef() == poGeom)
5786 : {
5787 192 : m_oMapLayerNameToFeatureCount[poLayer->m_osTargetName]++;
5788 : }
5789 :
5790 250 : OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType());
5791 250 : if (eGeomType == wkbGeometryCollection)
5792 : {
5793 21 : OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
5794 77 : for (int i = 0; i < poGC->getNumGeometries(); i++)
5795 : {
5796 58 : if (WriteFeature(poLayer, poFeature, nSerial,
5797 58 : poGC->getGeometryRef(i)) != OGRERR_NONE)
5798 : {
5799 2 : return OGRERR_FAILURE;
5800 : }
5801 : }
5802 19 : return OGRERR_NONE;
5803 : }
5804 :
5805 229 : OGREnvelope sExtent;
5806 229 : poGeom->getEnvelope(&sExtent);
5807 :
5808 229 : if (!m_oEnvelope.IsInit())
5809 : {
5810 72 : CPLDebug("MVT", "Creating temporary database...");
5811 : }
5812 :
5813 229 : m_oEnvelope.Merge(sExtent);
5814 :
5815 229 : if (!m_bReuseTempFile)
5816 : {
5817 : auto poFeatureContent =
5818 228 : std::shared_ptr<OGRMVTFeatureContent>(new OGRMVTFeatureContent());
5819 228 : auto poSharedGeom = std::shared_ptr<OGRGeometry>(poGeom->clone());
5820 :
5821 228 : poFeatureContent->nFID = poFeature->GetFID();
5822 :
5823 228 : const OGRFeatureDefn *poFDefn = poFeature->GetDefnRef();
5824 995 : for (int i = 0; i < poFeature->GetFieldCount(); i++)
5825 : {
5826 767 : if (poFeature->IsFieldSetAndNotNull(i))
5827 : {
5828 666 : MVTTileLayerValue oValue;
5829 666 : const OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn(i);
5830 666 : OGRFieldType eFieldType = poFieldDefn->GetType();
5831 666 : if (eFieldType == OFTInteger || eFieldType == OFTInteger64)
5832 : {
5833 145 : if (poFieldDefn->GetSubType() == OFSTBoolean)
5834 : {
5835 2 : oValue.setBoolValue(poFeature->GetFieldAsInteger(i) !=
5836 : 0);
5837 : }
5838 : else
5839 : {
5840 143 : oValue.setValue(poFeature->GetFieldAsInteger64(i));
5841 : }
5842 : }
5843 521 : else if (eFieldType == OFTReal)
5844 : {
5845 140 : oValue.setValue(poFeature->GetFieldAsDouble(i));
5846 : }
5847 381 : else if (eFieldType == OFTDate || eFieldType == OFTDateTime)
5848 : {
5849 : int nYear, nMonth, nDay, nHour, nMin, nTZ;
5850 : float fSec;
5851 238 : poFeature->GetFieldAsDateTime(i, &nYear, &nMonth, &nDay,
5852 : &nHour, &nMin, &fSec, &nTZ);
5853 476 : CPLString osFormatted;
5854 238 : if (eFieldType == OFTDate)
5855 : {
5856 119 : osFormatted.Printf("%04d-%02d-%02d", nYear, nMonth,
5857 119 : nDay);
5858 : }
5859 : else
5860 : {
5861 : char *pszFormatted =
5862 119 : OGRGetXMLDateTime(poFeature->GetRawFieldRef(i));
5863 119 : osFormatted = pszFormatted;
5864 119 : CPLFree(pszFormatted);
5865 : }
5866 476 : oValue.setStringValue(osFormatted);
5867 : }
5868 : else
5869 : {
5870 143 : oValue.setStringValue(
5871 286 : std::string(poFeature->GetFieldAsString(i)));
5872 : }
5873 :
5874 666 : poFeatureContent->oValues.emplace_back(
5875 666 : std::pair<std::string, MVTTileLayerValue>(
5876 1332 : poFieldDefn->GetNameRef(), oValue));
5877 : }
5878 : }
5879 :
5880 1556 : for (int nZ = poLayer->m_nMinZoom; nZ <= poLayer->m_nMaxZoom; nZ++)
5881 : {
5882 1330 : double dfTileDim = m_dfTileDim0 / (1 << nZ);
5883 1330 : double dfBuffer = dfTileDim * m_nBuffer / m_nExtent;
5884 : const int nTileMinX = std::max(
5885 2660 : 0, static_cast<int>((sExtent.MinX - m_dfTopX - dfBuffer) /
5886 1330 : dfTileDim));
5887 : const int nTileMinY = std::max(
5888 2660 : 0, static_cast<int>((m_dfTopY - sExtent.MaxY - dfBuffer) /
5889 1330 : dfTileDim));
5890 : const int nTileMaxX =
5891 2660 : std::min(static_cast<int>((sExtent.MaxX - m_dfTopX + dfBuffer) /
5892 : dfTileDim),
5893 2660 : static_cast<int>(std::min<int64_t>(
5894 2660 : INT_MAX, (static_cast<int64_t>(1) << nZ) *
5895 2660 : m_nTileMatrixWidth0 -
5896 1330 : 1)));
5897 : const int nTileMaxY =
5898 2660 : std::min(static_cast<int>((m_dfTopY - sExtent.MinY + dfBuffer) /
5899 : dfTileDim),
5900 2660 : static_cast<int>(std::min<int64_t>(
5901 2660 : INT_MAX, (static_cast<int64_t>(1) << nZ) *
5902 2660 : m_nTileMatrixHeight0 -
5903 1330 : 1)));
5904 3569 : for (int iX = nTileMinX; iX <= nTileMaxX; iX++)
5905 : {
5906 6266 : for (int iY = nTileMinY; iY <= nTileMaxY; iY++)
5907 : {
5908 8054 : if (PreGenerateForTile(
5909 4027 : nZ, iX, iY, poLayer->m_osTargetName,
5910 4027 : (nZ == poLayer->m_nMaxZoom), poFeatureContent,
5911 4027 : nSerial, poSharedGeom, sExtent) != OGRERR_NONE)
5912 : {
5913 2 : return OGRERR_FAILURE;
5914 : }
5915 : }
5916 : }
5917 : }
5918 : }
5919 :
5920 227 : return OGRERR_NONE;
5921 : }
5922 :
5923 : /************************************************************************/
5924 : /* TestCapability() */
5925 : /************************************************************************/
5926 :
5927 204 : int OGRMVTWriterDataset::TestCapability(const char *pszCap) const
5928 : {
5929 204 : if (EQUAL(pszCap, ODsCCreateLayer))
5930 116 : return true;
5931 88 : return false;
5932 : }
5933 :
5934 : /************************************************************************/
5935 : /* ValidateMinMaxZoom() */
5936 : /************************************************************************/
5937 :
5938 299 : static bool ValidateMinMaxZoom(int nMinZoom, int nMaxZoom)
5939 : {
5940 299 : if (nMinZoom < 0 || nMinZoom > 22)
5941 : {
5942 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid MINZOOM");
5943 2 : return false;
5944 : }
5945 297 : if (nMaxZoom < 0 || nMaxZoom > 22)
5946 : {
5947 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid MAXZOOM");
5948 1 : return false;
5949 : }
5950 296 : if (nMaxZoom < nMinZoom)
5951 : {
5952 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid MAXZOOM < MINZOOM");
5953 1 : return false;
5954 : }
5955 295 : return true;
5956 : }
5957 :
5958 : /************************************************************************/
5959 : /* ICreateLayer() */
5960 : /************************************************************************/
5961 :
5962 : OGRLayer *
5963 170 : OGRMVTWriterDataset::ICreateLayer(const char *pszLayerName,
5964 : const OGRGeomFieldDefn *poGeomFieldDefn,
5965 : CSLConstList papszOptions)
5966 : {
5967 170 : OGRSpatialReference *poSRSClone = nullptr;
5968 : const auto poSRS =
5969 170 : poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
5970 170 : if (poSRS)
5971 : {
5972 7 : poSRSClone = poSRS->Clone();
5973 7 : poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
5974 : }
5975 : OGRMVTWriterLayer *poLayer =
5976 170 : new OGRMVTWriterLayer(this, pszLayerName, poSRSClone);
5977 170 : if (poSRSClone)
5978 7 : poSRSClone->Release();
5979 170 : poLayer->m_nMinZoom = m_nMinZoom;
5980 170 : poLayer->m_nMaxZoom = m_nMaxZoom;
5981 170 : poLayer->m_osTargetName = pszLayerName;
5982 :
5983 : /*
5984 :
5985 : {
5986 : "src_layer":
5987 : { "target_name": "",
5988 : "description": "",
5989 : "minzoom": 0,
5990 : "maxzoom": 0
5991 : }
5992 : }
5993 : */
5994 :
5995 510 : CPLJSONObject oObj = m_oConf.GetRoot().GetObj(pszLayerName);
5996 340 : CPLString osDescription;
5997 170 : if (oObj.IsValid())
5998 : {
5999 4 : std::string osTargetName = oObj.GetString("target_name");
6000 2 : if (!osTargetName.empty())
6001 2 : poLayer->m_osTargetName = std::move(osTargetName);
6002 2 : int nMinZoom = oObj.GetInteger("minzoom", -1);
6003 2 : if (nMinZoom >= 0)
6004 2 : poLayer->m_nMinZoom = nMinZoom;
6005 2 : int nMaxZoom = oObj.GetInteger("maxzoom", -1);
6006 2 : if (nMaxZoom >= 0)
6007 2 : poLayer->m_nMaxZoom = nMaxZoom;
6008 2 : osDescription = oObj.GetString("description");
6009 : }
6010 :
6011 170 : poLayer->m_nMinZoom = atoi(CSLFetchNameValueDef(
6012 : papszOptions, "MINZOOM", CPLSPrintf("%d", poLayer->m_nMinZoom)));
6013 170 : poLayer->m_nMaxZoom = atoi(CSLFetchNameValueDef(
6014 : papszOptions, "MAXZOOM", CPLSPrintf("%d", poLayer->m_nMaxZoom)));
6015 170 : if (!ValidateMinMaxZoom(poLayer->m_nMinZoom, poLayer->m_nMaxZoom))
6016 : {
6017 1 : delete poLayer;
6018 1 : return nullptr;
6019 : }
6020 : poLayer->m_osTargetName = CSLFetchNameValueDef(
6021 169 : papszOptions, "NAME", poLayer->m_osTargetName.c_str());
6022 : osDescription =
6023 169 : CSLFetchNameValueDef(papszOptions, "DESCRIPTION", osDescription);
6024 169 : if (!osDescription.empty())
6025 4 : m_oMapLayerNameToDesc[poLayer->m_osTargetName] =
6026 2 : std::move(osDescription);
6027 :
6028 169 : m_apoLayers.push_back(std::unique_ptr<OGRMVTWriterLayer>(poLayer));
6029 169 : return m_apoLayers.back().get();
6030 : }
6031 :
6032 : /************************************************************************/
6033 : /* Create() */
6034 : /************************************************************************/
6035 :
6036 139 : GDALDataset *OGRMVTWriterDataset::Create(const char *pszFilename, int nXSize,
6037 : int nYSize, int nBandsIn,
6038 : GDALDataType eDT, char **papszOptions)
6039 : {
6040 139 : if (nXSize != 0 || nYSize != 0 || nBandsIn != 0 || eDT != GDT_Unknown)
6041 : {
6042 1 : CPLError(CE_Failure, CPLE_NotSupported,
6043 : "Only vector creation supported");
6044 1 : return nullptr;
6045 : }
6046 :
6047 138 : const char *pszFormat = CSLFetchNameValue(papszOptions, "FORMAT");
6048 : const bool bMBTILESExt =
6049 138 : EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "mbtiles");
6050 138 : if (pszFormat == nullptr && bMBTILESExt)
6051 : {
6052 4 : pszFormat = "MBTILES";
6053 : }
6054 138 : const bool bMBTILES = pszFormat != nullptr && EQUAL(pszFormat, "MBTILES");
6055 :
6056 : // For debug only
6057 : bool bReuseTempFile =
6058 138 : CPLTestBool(CPLGetConfigOption("OGR_MVT_REUSE_TEMP_FILE", "NO"));
6059 :
6060 138 : if (bMBTILES)
6061 : {
6062 80 : if (!bMBTILESExt)
6063 : {
6064 1 : CPLError(CE_Failure, CPLE_FileIO,
6065 : "%s should have mbtiles extension", pszFilename);
6066 1 : return nullptr;
6067 : }
6068 :
6069 79 : VSIUnlink(pszFilename);
6070 : }
6071 : else
6072 : {
6073 : VSIStatBufL sStat;
6074 58 : if (VSIStatL(pszFilename, &sStat) == 0)
6075 : {
6076 3 : CPLError(CE_Failure, CPLE_FileIO, "%s already exists", pszFilename);
6077 5 : return nullptr;
6078 : }
6079 :
6080 55 : if (VSIMkdir(pszFilename, 0755) != 0)
6081 : {
6082 2 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create directory %s",
6083 : pszFilename);
6084 2 : return nullptr;
6085 : }
6086 : }
6087 :
6088 132 : OGRMVTWriterDataset *poDS = new OGRMVTWriterDataset();
6089 132 : poDS->m_pMyVFS = OGRSQLiteCreateVFS(nullptr, poDS);
6090 132 : sqlite3_vfs_register(poDS->m_pMyVFS, 0);
6091 :
6092 396 : CPLString osTempDBDefault = CPLString(pszFilename) + ".temp.db";
6093 132 : if (STARTS_WITH(osTempDBDefault, "/vsizip/"))
6094 : {
6095 : osTempDBDefault =
6096 0 : CPLString(pszFilename + strlen("/vsizip/")) + ".temp.db";
6097 : }
6098 : CPLString osTempDB = CSLFetchNameValueDef(papszOptions, "TEMPORARY_DB",
6099 264 : osTempDBDefault.c_str());
6100 132 : if (!bReuseTempFile)
6101 131 : VSIUnlink(osTempDB);
6102 :
6103 132 : sqlite3 *hDB = nullptr;
6104 132 : if (sqlite3_open_v2(osTempDB, &hDB,
6105 : SQLITE_OPEN_READWRITE |
6106 : (bReuseTempFile ? 0 : SQLITE_OPEN_CREATE) |
6107 : SQLITE_OPEN_NOMUTEX,
6108 393 : poDS->m_pMyVFS->zName) != SQLITE_OK ||
6109 129 : hDB == nullptr)
6110 : {
6111 3 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", osTempDB.c_str());
6112 3 : delete poDS;
6113 3 : sqlite3_close(hDB);
6114 3 : return nullptr;
6115 : }
6116 129 : poDS->m_osTempDB = osTempDB;
6117 129 : poDS->m_hDB = hDB;
6118 129 : poDS->m_bReuseTempFile = bReuseTempFile;
6119 :
6120 : // For Unix
6121 257 : if (!poDS->m_bReuseTempFile &&
6122 128 : CPLTestBool(CPLGetConfigOption("OGR_MVT_REMOVE_TEMP_FILE", "YES")))
6123 : {
6124 125 : VSIUnlink(osTempDB);
6125 : }
6126 :
6127 129 : if (poDS->m_bReuseTempFile)
6128 : {
6129 1 : poDS->m_nTempTiles =
6130 1 : SQLGetInteger64(hDB, "SELECT COUNT(*) FROM temp", nullptr);
6131 : }
6132 : else
6133 : {
6134 128 : CPL_IGNORE_RET_VAL(SQLCommand(
6135 : hDB,
6136 : "PRAGMA page_size = 4096;" // 4096: default since sqlite 3.12
6137 : "PRAGMA synchronous = OFF;"
6138 : "PRAGMA journal_mode = OFF;"
6139 : "PRAGMA temp_store = MEMORY;"
6140 : "CREATE TABLE temp(z INTEGER, x INTEGER, y INTEGER, layer TEXT, "
6141 : "idx INTEGER, feature BLOB, geomtype INTEGER, area_or_length "
6142 : "DOUBLE);"
6143 : "CREATE INDEX temp_index ON temp (z, x, y, layer, idx);"));
6144 : }
6145 :
6146 129 : sqlite3_stmt *hInsertStmt = nullptr;
6147 129 : CPL_IGNORE_RET_VAL(sqlite3_prepare_v2(
6148 : hDB,
6149 : "INSERT INTO temp (z,x,y,layer,idx,feature,geomtype,area_or_length) "
6150 : "VALUES (?,?,?,?,?,?,?,?)",
6151 : -1, &hInsertStmt, nullptr));
6152 129 : if (hInsertStmt == nullptr)
6153 : {
6154 0 : delete poDS;
6155 0 : return nullptr;
6156 : }
6157 129 : poDS->m_hInsertStmt = hInsertStmt;
6158 :
6159 129 : poDS->m_nMinZoom = atoi(CSLFetchNameValueDef(
6160 : papszOptions, "MINZOOM", CPLSPrintf("%d", poDS->m_nMinZoom)));
6161 129 : poDS->m_nMaxZoom = atoi(CSLFetchNameValueDef(
6162 : papszOptions, "MAXZOOM", CPLSPrintf("%d", poDS->m_nMaxZoom)));
6163 129 : if (!ValidateMinMaxZoom(poDS->m_nMinZoom, poDS->m_nMaxZoom))
6164 : {
6165 3 : delete poDS;
6166 3 : return nullptr;
6167 : }
6168 :
6169 126 : const char *pszConf = CSLFetchNameValue(papszOptions, "CONF");
6170 126 : if (pszConf)
6171 : {
6172 : VSIStatBufL sStat;
6173 : bool bSuccess;
6174 3 : if (VSIStatL(pszConf, &sStat) == 0)
6175 : {
6176 2 : bSuccess = poDS->m_oConf.Load(pszConf);
6177 : }
6178 : else
6179 : {
6180 1 : bSuccess = poDS->m_oConf.LoadMemory(pszConf);
6181 : }
6182 3 : if (!bSuccess)
6183 : {
6184 1 : delete poDS;
6185 1 : return nullptr;
6186 : }
6187 : }
6188 :
6189 125 : poDS->m_dfSimplification =
6190 125 : CPLAtof(CSLFetchNameValueDef(papszOptions, "SIMPLIFICATION", "0"));
6191 125 : poDS->m_dfSimplificationMaxZoom = CPLAtof(
6192 : CSLFetchNameValueDef(papszOptions, "SIMPLIFICATION_MAX_ZOOM",
6193 : CPLSPrintf("%g", poDS->m_dfSimplification)));
6194 125 : poDS->m_nExtent = static_cast<unsigned>(atoi(CSLFetchNameValueDef(
6195 : papszOptions, "EXTENT", CPLSPrintf("%u", poDS->m_nExtent))));
6196 125 : poDS->m_nBuffer = static_cast<unsigned>(atoi(CSLFetchNameValueDef(
6197 125 : papszOptions, "BUFFER", CPLSPrintf("%u", 5 * poDS->m_nExtent / 256))));
6198 :
6199 : {
6200 125 : const char *pszMaxSize = CSLFetchNameValue(papszOptions, "MAX_SIZE");
6201 125 : poDS->m_bMaxTileSizeOptSpecified = pszMaxSize != nullptr;
6202 : // This is used by unit tests
6203 125 : pszMaxSize = CSLFetchNameValueDef(papszOptions, "@MAX_SIZE_FOR_TEST",
6204 : pszMaxSize);
6205 125 : if (pszMaxSize)
6206 : {
6207 3 : poDS->m_nMaxTileSize =
6208 3 : std::max(100U, static_cast<unsigned>(atoi(pszMaxSize)));
6209 : }
6210 : }
6211 :
6212 : {
6213 : const char *pszMaxFeatures =
6214 125 : CSLFetchNameValue(papszOptions, "MAX_FEATURES");
6215 125 : poDS->m_bMaxFeaturesOptSpecified = pszMaxFeatures != nullptr;
6216 125 : pszMaxFeatures = CSLFetchNameValueDef(
6217 : // This is used by unit tests
6218 : papszOptions, "@MAX_FEATURES_FOR_TEST", pszMaxFeatures);
6219 125 : if (pszMaxFeatures)
6220 : {
6221 2 : poDS->m_nMaxFeatures =
6222 2 : std::max(1U, static_cast<unsigned>(atoi(pszMaxFeatures)));
6223 : }
6224 : }
6225 :
6226 : poDS->m_osName = CSLFetchNameValueDef(
6227 125 : papszOptions, "NAME", CPLGetBasenameSafe(pszFilename).c_str());
6228 : poDS->m_osDescription = CSLFetchNameValueDef(papszOptions, "DESCRIPTION",
6229 125 : poDS->m_osDescription.c_str());
6230 : poDS->m_osType =
6231 125 : CSLFetchNameValueDef(papszOptions, "TYPE", poDS->m_osType.c_str());
6232 125 : poDS->m_bGZip = CPLFetchBool(papszOptions, "COMPRESS", poDS->m_bGZip);
6233 125 : poDS->m_osBounds = CSLFetchNameValueDef(papszOptions, "BOUNDS", "");
6234 125 : poDS->m_osCenter = CSLFetchNameValueDef(papszOptions, "CENTER", "");
6235 : poDS->m_osExtension = CSLFetchNameValueDef(papszOptions, "TILE_EXTENSION",
6236 125 : poDS->m_osExtension);
6237 :
6238 : const char *pszTilingScheme =
6239 125 : CSLFetchNameValue(papszOptions, "TILING_SCHEME");
6240 125 : if (pszTilingScheme)
6241 : {
6242 6 : if (bMBTILES)
6243 : {
6244 1 : CPLError(CE_Failure, CPLE_NotSupported,
6245 : "Custom TILING_SCHEME not supported with MBTILES output");
6246 1 : delete poDS;
6247 2 : return nullptr;
6248 : }
6249 :
6250 5 : const CPLStringList aoList(CSLTokenizeString2(pszTilingScheme, ",", 0));
6251 5 : if (aoList.Count() >= 4)
6252 : {
6253 4 : poDS->m_poSRS->SetFromUserInput(aoList[0]);
6254 4 : poDS->m_dfTopX = CPLAtof(aoList[1]);
6255 4 : poDS->m_dfTopY = CPLAtof(aoList[2]);
6256 4 : poDS->m_dfTileDim0 = CPLAtof(aoList[3]);
6257 4 : if (aoList.Count() == 6)
6258 : {
6259 2 : poDS->m_nTileMatrixWidth0 = std::max(1, atoi(aoList[4]));
6260 2 : poDS->m_nTileMatrixHeight0 = std::max(1, atoi(aoList[5]));
6261 : }
6262 2 : else if (poDS->m_dfTopX == -180 && poDS->m_dfTileDim0 == 180)
6263 : {
6264 : // Assumes WorldCRS84Quad with 2 tiles in width
6265 : // cf https://github.com/OSGeo/gdal/issues/11749
6266 1 : poDS->m_nTileMatrixWidth0 = 2;
6267 : }
6268 : }
6269 : else
6270 : {
6271 1 : CPLError(CE_Failure, CPLE_AppDefined,
6272 : "Wrong format for TILING_SCHEME. "
6273 : "Expecting EPSG:XXXX,tile_origin_upper_left_x,"
6274 : "tile_origin_upper_left_y,tile_dimension_zoom_0[,tile_"
6275 : "matrix_width_zoom_0,tile_matrix_height_zoom_0]");
6276 1 : delete poDS;
6277 1 : return nullptr;
6278 : }
6279 : }
6280 :
6281 123 : if (bMBTILES)
6282 : {
6283 228 : if (sqlite3_open_v2(pszFilename, &poDS->m_hDBMBTILES,
6284 : SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
6285 : SQLITE_OPEN_NOMUTEX,
6286 151 : poDS->m_pMyVFS->zName) != SQLITE_OK ||
6287 75 : poDS->m_hDBMBTILES == nullptr)
6288 : {
6289 1 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", pszFilename);
6290 1 : delete poDS;
6291 1 : return nullptr;
6292 : }
6293 :
6294 75 : if (SQLCommand(
6295 : poDS->m_hDBMBTILES,
6296 : "PRAGMA page_size = 4096;" // 4096: default since sqlite 3.12
6297 : "PRAGMA synchronous = OFF;"
6298 : "PRAGMA journal_mode = OFF;"
6299 : "PRAGMA temp_store = MEMORY;"
6300 : "CREATE TABLE metadata (name text, value text);"
6301 : "CREATE TABLE tiles (zoom_level integer, tile_column integer, "
6302 : "tile_row integer, tile_data blob, "
6303 75 : "UNIQUE (zoom_level, tile_column, tile_row))") != OGRERR_NONE)
6304 : {
6305 0 : delete poDS;
6306 0 : return nullptr;
6307 : }
6308 : }
6309 :
6310 122 : int nThreads = CPLGetNumCPUs();
6311 122 : const char *pszNumThreads = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
6312 122 : if (pszNumThreads && CPLGetValueType(pszNumThreads) == CPL_VALUE_INTEGER)
6313 : {
6314 3 : nThreads = atoi(pszNumThreads);
6315 : }
6316 122 : if (nThreads > 1)
6317 : {
6318 119 : poDS->m_bThreadPoolOK =
6319 119 : poDS->m_oThreadPool.Setup(nThreads, nullptr, nullptr);
6320 : }
6321 :
6322 122 : poDS->SetDescription(pszFilename);
6323 122 : poDS->poDriver = GDALDriver::FromHandle(GDALGetDriverByName("MVT"));
6324 :
6325 122 : return poDS;
6326 : }
6327 :
6328 75 : GDALDataset *OGRMVTWriterDatasetCreate(const char *pszFilename, int nXSize,
6329 : int nYSize, int nBandsIn,
6330 : GDALDataType eDT, char **papszOptions)
6331 : {
6332 75 : return OGRMVTWriterDataset::Create(pszFilename, nXSize, nYSize, nBandsIn,
6333 75 : eDT, papszOptions);
6334 : }
6335 :
6336 : #endif // HAVE_MVT_WRITE_SUPPORT
6337 :
6338 : /************************************************************************/
6339 : /* RegisterOGRMVT() */
6340 : /************************************************************************/
6341 :
6342 2038 : void RegisterOGRMVT()
6343 :
6344 : {
6345 2038 : if (GDALGetDriverByName("MVT") != nullptr)
6346 283 : return;
6347 :
6348 1755 : GDALDriver *poDriver = new GDALDriver();
6349 :
6350 1755 : poDriver->SetDescription("MVT");
6351 1755 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
6352 1755 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Mapbox Vector Tiles");
6353 1755 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/mvt.html");
6354 1755 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
6355 1755 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "mvt mvt.gz pbf");
6356 1755 : poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "SQLITE OGRSQL");
6357 :
6358 1755 : poDriver->SetMetadataItem(
6359 : GDAL_DMD_OPENOPTIONLIST,
6360 : "<OpenOptionList>"
6361 : " <Option name='X' type='int' description='X coordinate of tile'/>"
6362 : " <Option name='Y' type='int' description='Y coordinate of tile'/>"
6363 : " <Option name='Z' type='int' description='Z coordinate of tile'/>"
6364 : //" <Option name='@GEOREF_TOPX' type='float' description='X coordinate
6365 : // of top-left corner of tile'/>" " <Option name='@GEOREF_TOPY'
6366 : // type='float' description='Y coordinate of top-left corner of tile'/>"
6367 : //" <Option name='@GEOREF_TILEDIMX' type='float' description='Tile
6368 : // width in georeferenced units'/>" " <Option name='@GEOREF_TILEDIMY'
6369 : // type='float' description='Tile height in georeferenced units'/>"
6370 : " <Option name='METADATA_FILE' type='string' "
6371 : "description='Path to metadata.json'/>"
6372 : " <Option name='CLIP' type='boolean' "
6373 : "description='Whether to clip geometries to tile extent' "
6374 : "default='YES'/>"
6375 : " <Option name='TILE_EXTENSION' type='string' default='pbf' "
6376 : "description="
6377 : "'For tilesets, extension of tiles'/>"
6378 : " <Option name='TILE_COUNT_TO_ESTABLISH_FEATURE_DEFN' type='int' "
6379 : "description="
6380 : "'For tilesets without metadata file, maximum number of tiles to use "
6381 : "to "
6382 : "establish the layer schemas' default='1000'/>"
6383 : " <Option name='JSON_FIELD' type='boolean' description='For tilesets, "
6384 : "whether to put all attributes as a serialized JSon dictionary'/>"
6385 1755 : "</OpenOptionList>");
6386 :
6387 1755 : poDriver->pfnIdentify = OGRMVTDriverIdentify;
6388 1755 : poDriver->pfnOpen = OGRMVTDataset::Open;
6389 : #ifdef HAVE_MVT_WRITE_SUPPORT
6390 1755 : poDriver->pfnCreate = OGRMVTWriterDataset::Create;
6391 1755 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
6392 1755 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
6393 1755 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
6394 1755 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES,
6395 1755 : "Integer Integer64 Real String");
6396 1755 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES,
6397 1755 : "Boolean Float32");
6398 1755 : poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "SQLITE OGRSQL");
6399 :
6400 1755 : poDriver->SetMetadataItem(GDAL_DS_LAYER_CREATIONOPTIONLIST, MVT_LCO);
6401 :
6402 1755 : poDriver->SetMetadataItem(
6403 : GDAL_DMD_CREATIONOPTIONLIST,
6404 : "<CreationOptionList>"
6405 : " <Option name='NAME' type='string' description='Tileset name'/>"
6406 : " <Option name='DESCRIPTION' type='string' "
6407 : "description='A description of the tileset'/>"
6408 : " <Option name='TYPE' type='string-select' description='Layer type' "
6409 : "default='overlay'>"
6410 : " <Value>overlay</Value>"
6411 : " <Value>baselayer</Value>"
6412 : " </Option>"
6413 : " <Option name='FORMAT' type='string-select' description='Format'>"
6414 : " <Value>DIRECTORY</Value>"
6415 : " <Value>MBTILES</Value>"
6416 : " </Option>"
6417 : " <Option name='TILE_EXTENSION' type='string' default='pbf' "
6418 : "description="
6419 : "'For tilesets as directories of files, extension of "
6420 : "tiles'/>" MVT_MBTILES_COMMON_DSCO
6421 : " <Option name='BOUNDS' type='string' "
6422 : "description='Override default value for bounds metadata item'/>"
6423 : " <Option name='CENTER' type='string' "
6424 : "description='Override default value for center metadata item'/>"
6425 : " <Option name='TILING_SCHEME' type='string' "
6426 : "description='Custom tiling scheme with following format "
6427 : "\"EPSG:XXXX,tile_origin_upper_left_x,tile_origin_upper_left_y,"
6428 : "tile_dimension_zoom_0[,tile_matrix_width_zoom_0,tile_matrix_height_"
6429 : "zoom_0]\"'/>"
6430 1755 : "</CreationOptionList>");
6431 : #endif // HAVE_MVT_WRITE_SUPPORT
6432 :
6433 1755 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
6434 :
6435 1755 : GetGDALDriverManager()->RegisterDriver(poDriver);
6436 : }
|