Line data Source code
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 : #define SPHERICAL_RADIUS 6378137.0
31 : #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 96 : OGRPMTilesDataset() = default;
46 :
47 : ~OGRPMTilesDataset() override;
48 :
49 : bool Open(GDALOpenInfo *poOpenInfo);
50 :
51 230 : int GetLayerCount() override
52 : {
53 230 : return static_cast<int>(m_apoLayers.size());
54 : }
55 :
56 : OGRLayer *GetLayer(int) override;
57 :
58 3 : inline int GetMinZoomLevel() const
59 : {
60 3 : return m_nMinZoomLevel;
61 : }
62 :
63 8 : inline int GetMaxZoomLevel() const
64 : {
65 8 : return m_nMaxZoomLevel;
66 : }
67 :
68 1141 : inline const pmtiles::headerv3 &GetHeader() const
69 : {
70 1141 : return m_sHeader;
71 : }
72 :
73 : static const char *GetCompression(uint8_t nVal);
74 :
75 : static const char *GetTileType(const pmtiles::headerv3 &sHeader);
76 :
77 6 : inline const std::string &GetMetadataContent() const
78 : {
79 6 : return m_osMetadata;
80 : }
81 :
82 673 : inline const std::string &GetMetadataFilename() const
83 : {
84 673 : return m_osMetadataFilename;
85 : }
86 :
87 667 : inline const std::string &GetClipOpenOption() const
88 : {
89 667 : return m_osClipOpenOption;
90 : }
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 15 : explicit OGRPMTilesTileIterator(OGRPMTilesDataset *poDS,
157 : int nZoomLevel = -1)
158 15 : : m_poDS(poDS), m_nZoomLevel(nZoomLevel)
159 : {
160 15 : }
161 :
162 : //! Constructor with a window of interest in tile coordinates
163 237 : OGRPMTilesTileIterator(OGRPMTilesDataset *poDS, int nZoomLevel, int nMinX,
164 : int nMinY, int nMaxX, int nMaxY)
165 237 : : m_poDS(poDS), m_nZoomLevel(nZoomLevel), m_nMinX(nMinX),
166 237 : m_nMinY(nMinY), m_nMaxX(nMaxX), m_nMaxY(nMaxY)
167 : {
168 237 : }
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 1330 : DEFINE_GET_NEXT_FEATURE_THROUGH_RAW(OGRPMTilesVectorLayer)
252 :
253 6586 : OGRFeatureDefn *GetLayerDefn() override
254 : {
255 6586 : return m_poFeatureDefn;
256 : }
257 :
258 : int TestCapability(const char *) override;
259 :
260 : OGRErr GetExtent(OGREnvelope *psExtent, int bForce) override;
261 :
262 23 : OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce) override
263 : {
264 23 : return OGRLayer::GetExtent(iGeomField, psExtent, bForce);
265 : }
266 :
267 : void SetSpatialFilter(OGRGeometry *) override;
268 :
269 52 : void SetSpatialFilter(int iGeomField, OGRGeometry *poGeom) override
270 : {
271 52 : OGRLayer::SetSpatialFilter(iGeomField, poGeom);
272 52 : }
273 :
274 : GIntBig GetFeatureCount(int bForce) override;
275 :
276 : OGRFeature *GetFeature(GIntBig nFID) override;
277 :
278 : static OGRwkbGeometryType GuessGeometryType(OGRPMTilesDataset *poDS,
279 : const char *pszLayerName,
280 : int nZoomLevel);
281 :
282 : private:
283 : OGRPMTilesDataset *m_poDS = nullptr;
284 : OGRFeatureDefn *m_poFeatureDefn = nullptr;
285 :
286 : //! Iterator over tiles
287 : std::unique_ptr<OGRPMTilesTileIterator> m_poTileIterator{};
288 :
289 : //! Total feature count (may over-estimate due to not applying clipping)
290 : GIntBig m_nFeatureCount = -1;
291 :
292 : //! X tile value of currently opened tile
293 : uint32_t m_nX = 0;
294 :
295 : //! Y tile value of currently opened tile
296 : uint32_t m_nY = 0;
297 :
298 : //! Offset of the currently opened tile
299 : uint64_t m_nLastTileOffset = 0;
300 :
301 : //! Uncompressed MVT tile
302 : std::string m_osTileData{};
303 :
304 : //! In-memory MVT dataset of the currently opened tile
305 : std::unique_ptr<GDALDataset> m_poTileDS{};
306 :
307 : //! Layer of m_poTileDS
308 : OGRLayer *m_poTileLayer = nullptr;
309 :
310 : //! Layer extent
311 : OGREnvelope m_sExtent{};
312 :
313 : //! Minimum X tile value corresponding to m_sFilterEnvelope
314 : int m_nFilterMinX = 0;
315 :
316 : //! Minimum Y tile value corresponding to m_sFilterEnvelope
317 : int m_nFilterMinY = 0;
318 :
319 : //! Maximum X tile value corresponding to m_sFilterEnvelope
320 : int m_nFilterMaxX = 0;
321 :
322 : //! Maximum Y tile value corresponding to m_sFilterEnvelope
323 : int m_nFilterMaxY = 0;
324 :
325 : //! Currently used zoom level
326 : int m_nZoomLevel = 0;
327 :
328 : //! Whether we should auto-adapt m_nZoomLevel from the spatial filter extent
329 : bool m_bZoomLevelAuto = false;
330 :
331 : //! Whether we should expose the tile fields in a "json" field
332 : bool m_bJsonField = false;
333 :
334 : std::unique_ptr<OGRFeature> GetNextSrcFeature();
335 : std::unique_ptr<OGRFeature> CreateFeatureFrom(OGRFeature *poSrcFeature);
336 : GIntBig GetTotalFeatureCount() const;
337 : void ExtentToTileExtent(const OGREnvelope &sEnvelope, int &nTileMinX,
338 : int &nTileMinY, int &nTileMaxX,
339 : int &nTileMaxY) const;
340 :
341 : CPL_DISALLOW_COPY_ASSIGN(OGRPMTilesVectorLayer)
342 : };
343 :
344 : #ifdef HAVE_MVT_WRITE_SUPPORT
345 :
346 : /************************************************************************/
347 : /* OGRPMTilesWriterDataset */
348 : /************************************************************************/
349 :
350 : class OGRPMTilesWriterDataset final : public GDALDataset
351 : {
352 : std::unique_ptr<GDALDataset> m_poMBTilesWriterDataset{};
353 :
354 : public:
355 34 : OGRPMTilesWriterDataset() = default;
356 :
357 : ~OGRPMTilesWriterDataset() override;
358 :
359 : bool Create(const char *pszFilename, CSLConstList papszOptions);
360 :
361 : CPLErr Close() override;
362 :
363 : OGRLayer *ICreateLayer(const char *pszName,
364 : const OGRGeomFieldDefn *poGeomFieldDefn,
365 : CSLConstList papszOptions) override;
366 :
367 : int TestCapability(const char *) override;
368 : };
369 :
370 : #endif // HAVE_MVT_WRITE_SUPPORT
371 :
372 : #endif // OGR_PMTILES_H_INCLUDED
|