Coverage Report

Created: 2026-02-14 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/pmtiles/ogrpmtilesdriver.cpp
Line
Count
Source
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
#include "ogr_pmtiles.h"
14
15
#include "vsipmtiles.h"
16
17
#include "ogrpmtilesfrommbtiles.h"
18
19
#ifdef HAVE_MVT_WRITE_SUPPORT
20
#include "mvtutils.h"
21
#endif
22
23
/************************************************************************/
24
/*                      OGRPMTilesDriverIdentify()                      */
25
/************************************************************************/
26
27
static int OGRPMTilesDriverIdentify(GDALOpenInfo *poOpenInfo)
28
79.9k
{
29
79.9k
    if (poOpenInfo->nHeaderBytes < 127 || !poOpenInfo->fpL)
30
45.0k
        return FALSE;
31
34.9k
    return memcmp(poOpenInfo->pabyHeader, "PMTiles\x03", 8) == 0;
32
79.9k
}
33
34
/************************************************************************/
35
/*                        OGRPMTilesDriverOpen()                        */
36
/************************************************************************/
37
38
static GDALDataset *OGRPMTilesDriverOpen(GDALOpenInfo *poOpenInfo)
39
137
{
40
137
    if (!OGRPMTilesDriverIdentify(poOpenInfo))
41
0
        return nullptr;
42
137
    auto poDS = std::make_unique<OGRPMTilesDataset>();
43
137
    if (!poDS->Open(poOpenInfo))
44
115
        return nullptr;
45
22
    return poDS.release();
46
137
}
47
48
/************************************************************************/
49
/*               OGRPMTilesDriverCanVectorTranslateFrom()               */
50
/************************************************************************/
51
52
static bool OGRPMTilesDriverCanVectorTranslateFrom(
53
    const char * /*pszDestName*/, GDALDataset *poSourceDS,
54
    CSLConstList papszVectorTranslateArguments, char ***ppapszFailureReasons)
55
0
{
56
0
    auto poSrcDriver = poSourceDS->GetDriver();
57
0
    if (!(poSrcDriver && EQUAL(poSrcDriver->GetDescription(), "MBTiles")))
58
0
    {
59
0
        if (ppapszFailureReasons)
60
0
            *ppapszFailureReasons = CSLAddString(
61
0
                *ppapszFailureReasons, "Source driver is not MBTiles");
62
0
        return false;
63
0
    }
64
65
0
    if (papszVectorTranslateArguments)
66
0
    {
67
0
        const int nArgs = CSLCount(papszVectorTranslateArguments);
68
0
        for (int i = 0; i < nArgs; ++i)
69
0
        {
70
0
            if (i + 1 < nArgs &&
71
0
                (strcmp(papszVectorTranslateArguments[i], "-f") == 0 ||
72
0
                 strcmp(papszVectorTranslateArguments[i], "-of") == 0))
73
0
            {
74
0
                ++i;
75
0
            }
76
0
            else
77
0
            {
78
0
                if (ppapszFailureReasons)
79
0
                    *ppapszFailureReasons =
80
0
                        CSLAddString(*ppapszFailureReasons,
81
0
                                     "Direct copy from MBTiles does not "
82
0
                                     "support GDALVectorTranslate() options");
83
0
                return false;
84
0
            }
85
0
        }
86
0
    }
87
88
0
    return true;
89
0
}
90
91
/************************************************************************/
92
/*                OGRPMTilesDriverVectorTranslateFrom()                 */
93
/************************************************************************/
94
95
static GDALDataset *OGRPMTilesDriverVectorTranslateFrom(
96
    const char *pszDestName, GDALDataset *poSourceDS,
97
    CSLConstList papszVectorTranslateArguments,
98
    GDALProgressFunc /* pfnProgress */, void * /* pProgressData */)
99
0
{
100
0
    if (!OGRPMTilesDriverCanVectorTranslateFrom(
101
0
            pszDestName, poSourceDS, papszVectorTranslateArguments, nullptr))
102
0
    {
103
0
        return nullptr;
104
0
    }
105
106
0
    if (!OGRPMTilesConvertFromMBTiles(pszDestName,
107
0
                                      poSourceDS->GetDescription()))
108
0
    {
109
0
        return nullptr;
110
0
    }
111
112
0
    GDALOpenInfo oOpenInfo(pszDestName, GA_ReadOnly);
113
0
    return OGRPMTilesDriverOpen(&oOpenInfo);
114
0
}
115
116
#ifdef HAVE_MVT_WRITE_SUPPORT
117
/************************************************************************/
118
/*                               Create()                               */
119
/************************************************************************/
120
121
static GDALDataset *OGRPMTilesDriverCreate(const char *pszFilename, int nXSize,
122
                                           int nYSize, int nBandsIn,
123
                                           GDALDataType eDT,
124
                                           CSLConstList papszOptions)
125
{
126
    if (nXSize == 0 && nYSize == 0 && nBandsIn == 0 && eDT == GDT_Unknown)
127
    {
128
        auto poDS = std::make_unique<OGRPMTilesWriterDataset>();
129
        if (!poDS->Create(pszFilename, papszOptions))
130
            return nullptr;
131
        return poDS.release();
132
    }
133
    return nullptr;
134
}
135
#endif
136
137
/************************************************************************/
138
/*                         RegisterOGRPMTiles()                         */
139
/************************************************************************/
140
141
void RegisterOGRPMTiles()
142
22
{
143
22
    if (GDALGetDriverByName("PMTiles") != nullptr)
144
0
        return;
145
146
22
    VSIPMTilesRegister();
147
148
22
    GDALDriver *poDriver = new GDALDriver();
149
150
22
    poDriver->SetDescription("PMTiles");
151
22
    poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
152
22
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "ProtoMap Tiles");
153
22
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "pmtiles");
154
22
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
155
22
                              "drivers/vector/pmtiles.html");
156
157
22
    poDriver->SetMetadataItem(
158
22
        GDAL_DMD_OPENOPTIONLIST,
159
22
        "<OpenOptionList>"
160
22
        "  <Option name='ZOOM_LEVEL' type='integer' "
161
22
        "description='Zoom level of full resolution. If not specified, maximum "
162
22
        "non-empty zoom level'/>"
163
22
        "  <Option name='CLIP' type='boolean' "
164
22
        "description='Whether to clip geometries to tile extent' "
165
22
        "default='YES'/>"
166
22
        "  <Option name='ZOOM_LEVEL_AUTO' type='boolean' "
167
22
        "description='Whether to auto-select the zoom level for vector layers "
168
22
        "according to spatial filter extent. Only for display purpose' "
169
22
        "default='NO'/>"
170
22
        "  <Option name='JSON_FIELD' type='boolean' "
171
22
        "description='For vector layers, "
172
22
        "whether to put all attributes as a serialized JSon dictionary'/>"
173
22
        "</OpenOptionList>");
174
175
22
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
176
177
22
    poDriver->pfnOpen = OGRPMTilesDriverOpen;
178
22
    poDriver->pfnIdentify = OGRPMTilesDriverIdentify;
179
22
    poDriver->pfnCanVectorTranslateFrom =
180
22
        OGRPMTilesDriverCanVectorTranslateFrom;
181
22
    poDriver->pfnVectorTranslateFrom = OGRPMTilesDriverVectorTranslateFrom;
182
183
#ifdef HAVE_MVT_WRITE_SUPPORT
184
    poDriver->SetMetadataItem(
185
        GDAL_DMD_CREATIONOPTIONLIST,
186
        "<CreationOptionList>"
187
        "  <Option name='NAME' scope='raster,vector' type='string' "
188
        "description='Tileset name'/>"
189
        "  <Option name='DESCRIPTION' scope='raster,vector' type='string' "
190
        "description='A description of the layer'/>"
191
        "  <Option name='TYPE' scope='raster,vector' type='string-select' "
192
        "description='Layer type' default='overlay'>"
193
        "    <Value>overlay</Value>"
194
        "    <Value>baselayer</Value>"
195
        "  </Option>" MVT_MBTILES_PMTILES_COMMON_DSCO "</CreationOptionList>");
196
197
    poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
198
    poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
199
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES,
200
                              "Integer Integer64 Real String");
201
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES,
202
                              "Boolean Float32");
203
204
    poDriver->SetMetadataItem(GDAL_DS_LAYER_CREATIONOPTIONLIST, MVT_LCO);
205
206
    poDriver->pfnCreate = OGRPMTilesDriverCreate;
207
#endif
208
209
22
    GetGDALDriverManager()->RegisterDriver(poDriver);
210
22
}