Coverage Report

Created: 2025-06-09 08:44

/src/gdal/ogr/ogrsf_frmts/pmtiles/ogr_pmtiles.h
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  Implementation of PMTiles
5
 * Author:   Even Rouault <even.rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2023, Planet Labs
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#ifndef OGR_PMTILES_H_INCLUDED
14
#define OGR_PMTILES_H_INCLUDED
15
16
#include "gdal_priv.h"
17
#include "ogrsf_frmts.h"
18
19
#include "cpl_compressor.h"
20
#include "cpl_vsi_virtual.h"
21
22
#include "include_pmtiles.h"
23
24
#include <limits>
25
#include <set>
26
#include <stack>
27
28
// #define DEBUG_PMTILES
29
30
0
#define SPHERICAL_RADIUS 6378137.0
31
0
#define MAX_GM (SPHERICAL_RADIUS * M_PI)  // 20037508.342789244
32
33
#if defined(HAVE_SQLITE) && defined(HAVE_GEOS)
34
// Needed by mvtutils.h
35
#define HAVE_MVT_WRITE_SUPPORT
36
#endif
37
38
/************************************************************************/
39
/*                          OGRPMTilesDataset                           */
40
/************************************************************************/
41
42
class OGRPMTilesDataset final : public GDALDataset
43
{
44
  public:
45
114
    OGRPMTilesDataset() = default;
46
47
    ~OGRPMTilesDataset() override;
48
49
    bool Open(GDALOpenInfo *poOpenInfo);
50
51
    int GetLayerCount() override
52
0
    {
53
0
        return static_cast<int>(m_apoLayers.size());
54
0
    }
55
56
    OGRLayer *GetLayer(int) override;
57
58
    inline int GetMinZoomLevel() const
59
0
    {
60
0
        return m_nMinZoomLevel;
61
0
    }
62
63
    inline int GetMaxZoomLevel() const
64
0
    {
65
0
        return m_nMaxZoomLevel;
66
0
    }
67
68
    inline const pmtiles::headerv3 &GetHeader() const
69
0
    {
70
0
        return m_sHeader;
71
0
    }
72
73
    static const char *GetCompression(uint8_t nVal);
74
75
    static const char *GetTileType(const pmtiles::headerv3 &sHeader);
76
77
    inline const std::string &GetMetadataContent() const
78
0
    {
79
0
        return m_osMetadata;
80
0
    }
81
82
    inline const std::string &GetMetadataFilename() const
83
0
    {
84
0
        return m_osMetadataFilename;
85
0
    }
86
87
    inline const std::string &GetClipOpenOption() const
88
0
    {
89
0
        return m_osClipOpenOption;
90
0
    }
91
92
    /** Return a short-lived decompressed buffer for metadata or directory
93
     * entries or nullptr in case of error.
94
     */
95
    const std::string *ReadInternal(uint64_t nOffset, uint64_t nSize,
96
                                    const char *pszDataType);
97
98
    /** Return a short-lived decompressed buffer for tile data.
99
     *  or nullptr in case of error.
100
     */
101
    const std::string *ReadTileData(uint64_t nOffset, uint64_t nSize);
102
103
  private:
104
    VSIVirtualHandleUniquePtr m_poFile{};
105
106
    //! PMTiles header
107
    pmtiles::headerv3 m_sHeader{};
108
109
    //! JSON serialized metadata
110
    std::string m_osMetadata{};
111
112
    //! /vsimem/ filename with the m_osMetadata content
113
    std::string m_osMetadataFilename{};
114
115
    //! Value of the CLIP open option
116
    std::string m_osClipOpenOption{};
117
118
    //! Decompressor for metadata and directories
119
    const CPLCompressor *m_psInternalDecompressor = nullptr;
120
121
    //! Decompressor for tile
122
    const CPLCompressor *m_psTileDataDecompressor = nullptr;
123
124
    //! Last raw data read by Read()
125
    std::string m_osBuffer{};
126
127
    //! Last uncompressed data read by Read(). Only used if compression
128
    std::string m_osDecompressedBuffer{};
129
130
    std::vector<std::unique_ptr<OGRLayer>> m_apoLayers{};
131
132
    //! Minimum zoom level got from header
133
    int m_nMinZoomLevel = 0;
134
135
    //! Maximum zoom level got from header
136
    int m_nMaxZoomLevel = 0;
137
138
    /** Return a short-lived decompressed buffer, or nullptr in case of error
139
     */
140
    const std::string *Read(const CPLCompressor *psDecompressor,
141
                            uint64_t nOffset, uint64_t nSize,
142
                            const char *pszDataType);
143
144
    CPL_DISALLOW_COPY_ASSIGN(OGRPMTilesDataset)
145
};
146
147
/************************************************************************/
148
/*                        OGRPMTilesTileIterator                        */
149
/************************************************************************/
150
151
//! Iterator to browse through tiles
152
class OGRPMTilesTileIterator
153
{
154
  public:
155
    //! Constructor to iterate over all tiles (possibly limited to a zoom level
156
    explicit OGRPMTilesTileIterator(OGRPMTilesDataset *poDS,
157
                                    int nZoomLevel = -1)
158
0
        : m_poDS(poDS), m_nZoomLevel(nZoomLevel)
159
0
    {
160
0
    }
161
162
    //! Constructor with a window of interest in tile coordinates
163
    OGRPMTilesTileIterator(OGRPMTilesDataset *poDS, int nZoomLevel, int nMinX,
164
                           int nMinY, int nMaxX, int nMaxY)
165
0
        : m_poDS(poDS), m_nZoomLevel(nZoomLevel), m_nMinX(nMinX),
166
0
          m_nMinY(nMinY), m_nMaxX(nMaxX), m_nMaxY(nMaxY)
167
0
    {
168
0
    }
169
170
    /** Return the (z, x, y, offset, length) of the next tile.
171
     *
172
     * If entry_zxy.offset == 0, the iteration has stopped.
173
     */
174
    pmtiles::entry_zxy GetNextTile(uint32_t *pnRunLength = nullptr);
175
176
    void SkipRunLength();
177
178
#ifdef DEBUG_PMTILES
179
    void DumpTiles();
180
#endif
181
182
  private:
183
    // Input parameters
184
    OGRPMTilesDataset *m_poDS = nullptr;
185
    int m_nZoomLevel = -1;
186
    int m_nMinX = -1;
187
    int m_nMinY = -1;
188
    int m_nMaxX = -1;
189
    int m_nMaxY = -1;
190
191
    // Used when iterating over tile id is inefficient
192
    int m_nCurX = -1;
193
    int m_nCurY = -1;
194
195
    // for sanity checks. Must be increasing when walking through entries
196
    static constexpr uint64_t INVALID_LAST_TILE_ID =
197
        std::numeric_limits<uint64_t>::max();
198
    uint64_t m_nLastTileId = INVALID_LAST_TILE_ID;
199
200
    // Computed values from zoom leven and min/max x/y
201
    uint64_t m_nMinTileId = std::numeric_limits<uint64_t>::max();
202
    uint64_t m_nMaxTileId = 0;
203
204
    bool m_bEOF = false;
205
206
    // State of exploration of a directory
207
    struct DirectoryContext
208
    {
209
        // Entries, either tiles (sEntry.run_length > 0) or subdiretories
210
        // (sEntry.run_length == 0)
211
        std::vector<pmtiles::entryv3> sEntries{};
212
213
        // Next index of sEntries[] to explore
214
        uint32_t nIdxInEntries = 0;
215
216
        // For tiles, value between 0 and
217
        // sEntries[nIdxInEntries].run_length - 1
218
        uint32_t nIdxInRunLength = 0;
219
    };
220
221
    // Stack of directories: bottom is root directory, and then we
222
    // push subdiretories we browse throw
223
    std::stack<DirectoryContext> m_aoStack{};
224
225
    bool LoadRootDirectory();
226
227
    CPL_DISALLOW_COPY_ASSIGN(OGRPMTilesTileIterator)
228
};
229
230
/************************************************************************/
231
/*                        OGRPMTilesVectorLayer                         */
232
/************************************************************************/
233
234
class OGRPMTilesVectorLayer final
235
    : public OGRLayer,
236
      public OGRGetNextFeatureThroughRaw<OGRPMTilesVectorLayer>
237
{
238
  public:
239
    OGRPMTilesVectorLayer(OGRPMTilesDataset *poDS, const char *pszLayerName,
240
                          const CPLJSONObject &oFields,
241
                          const CPLJSONArray &oAttributesFromTileStats,
242
                          bool bJsonField, double dfMinX, double dfMinY,
243
                          double dfMaxX, double dfMaxY,
244
                          OGRwkbGeometryType eGeomType, int nZoomLevel,
245
                          bool bZoomLevelFromSpatialFilter);
246
    ~OGRPMTilesVectorLayer();
247
248
    void ResetReading() override;
249
250
    OGRFeature *GetNextRawFeature();
251
    DEFINE_GET_NEXT_FEATURE_THROUGH_RAW(OGRPMTilesVectorLayer)
252
253
    OGRFeatureDefn *GetLayerDefn() override
254
0
    {
255
0
        return m_poFeatureDefn;
256
0
    }
257
258
    int TestCapability(const char *) override;
259
260
    OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
261
                      bool bForce) override;
262
263
    OGRErr ISetSpatialFilter(int iGeomField,
264
                             const OGRGeometry *poGeom) override;
265
266
    GIntBig GetFeatureCount(int bForce) override;
267
268
    OGRFeature *GetFeature(GIntBig nFID) override;
269
270
    static OGRwkbGeometryType GuessGeometryType(OGRPMTilesDataset *poDS,
271
                                                const char *pszLayerName,
272
                                                int nZoomLevel);
273
274
  private:
275
    OGRPMTilesDataset *m_poDS = nullptr;
276
    OGRFeatureDefn *m_poFeatureDefn = nullptr;
277
278
    //! Iterator over tiles
279
    std::unique_ptr<OGRPMTilesTileIterator> m_poTileIterator{};
280
281
    //! Total feature count (may over-estimate due to not applying clipping)
282
    GIntBig m_nFeatureCount = -1;
283
284
    //! X tile value of currently opened tile
285
    uint32_t m_nX = 0;
286
287
    //! Y tile value of currently opened tile
288
    uint32_t m_nY = 0;
289
290
    //! Offset of the currently opened tile
291
    uint64_t m_nLastTileOffset = 0;
292
293
    //! Uncompressed MVT tile
294
    std::string m_osTileData{};
295
296
    //! In-memory MVT dataset of the currently opened tile
297
    std::unique_ptr<GDALDataset> m_poTileDS{};
298
299
    //! Layer of m_poTileDS
300
    OGRLayer *m_poTileLayer = nullptr;
301
302
    //! Layer extent
303
    OGREnvelope m_sExtent{};
304
305
    //! Minimum X tile value corresponding to m_sFilterEnvelope
306
    int m_nFilterMinX = 0;
307
308
    //! Minimum Y tile value corresponding to m_sFilterEnvelope
309
    int m_nFilterMinY = 0;
310
311
    //! Maximum X tile value corresponding to m_sFilterEnvelope
312
    int m_nFilterMaxX = 0;
313
314
    //! Maximum Y tile value corresponding to m_sFilterEnvelope
315
    int m_nFilterMaxY = 0;
316
317
    //! Currently used zoom level
318
    int m_nZoomLevel = 0;
319
320
    //! Whether we should auto-adapt m_nZoomLevel from the spatial filter extent
321
    bool m_bZoomLevelAuto = false;
322
323
    //! Whether we should expose the tile fields in a "json" field
324
    bool m_bJsonField = false;
325
326
    std::unique_ptr<OGRFeature> GetNextSrcFeature();
327
    std::unique_ptr<OGRFeature> CreateFeatureFrom(OGRFeature *poSrcFeature);
328
    GIntBig GetTotalFeatureCount() const;
329
    void ExtentToTileExtent(const OGREnvelope &sEnvelope, int &nTileMinX,
330
                            int &nTileMinY, int &nTileMaxX,
331
                            int &nTileMaxY) const;
332
333
    CPL_DISALLOW_COPY_ASSIGN(OGRPMTilesVectorLayer)
334
};
335
336
#ifdef HAVE_MVT_WRITE_SUPPORT
337
338
/************************************************************************/
339
/*                     OGRPMTilesWriterDataset                          */
340
/************************************************************************/
341
342
class OGRPMTilesWriterDataset final : public GDALDataset
343
{
344
    std::unique_ptr<GDALDataset> m_poMBTilesWriterDataset{};
345
346
  public:
347
    OGRPMTilesWriterDataset() = default;
348
349
    ~OGRPMTilesWriterDataset() override;
350
351
    bool Create(const char *pszFilename, CSLConstList papszOptions);
352
353
    CPLErr Close() override;
354
355
    OGRLayer *ICreateLayer(const char *pszName,
356
                           const OGRGeomFieldDefn *poGeomFieldDefn,
357
                           CSLConstList papszOptions) override;
358
359
    int TestCapability(const char *) override;
360
};
361
362
#endif  // HAVE_MVT_WRITE_SUPPORT
363
364
#endif  // OGR_PMTILES_H_INCLUDED