/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 |