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 6608 : OGRFeatureDefn *GetLayerDefn() override
254 : {
255 6608 : return m_poFeatureDefn;
256 : }
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 34 : 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
|