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