Coverage Report

Created: 2026-02-14 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/zarr/zarrdrivercore.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  Zarr driver
5
 * Author:   Even Rouault <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2021, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "zarrdrivercore.h"
14
#include "gdal_frmts.h"
15
#include "gdalplugindriverproxy.h"
16
17
#include "cpl_string.h"
18
#include "vsikerchunk.h"
19
#include "vsikerchunk_inline.hpp"
20
21
/************************************************************************/
22
/*                    CheckExistenceOfOneZarrFile()                     */
23
/************************************************************************/
24
25
static bool CheckExistenceOfOneZarrFile(const char *pszFilename)
26
3.38k
{
27
28
3.38k
    CPLString osMDFilename =
29
3.38k
        CPLFormFilenameSafe(pszFilename, ".zarray", nullptr);
30
31
3.38k
    VSIStatBufL sStat;
32
3.38k
    if (VSIStatL(osMDFilename, &sStat) == 0)
33
0
        return true;
34
35
3.38k
    osMDFilename = CPLFormFilenameSafe(pszFilename, ".zgroup", nullptr);
36
3.38k
    if (VSIStatL(osMDFilename, &sStat) == 0)
37
0
        return true;
38
39
    // Zarr V3
40
3.38k
    osMDFilename = CPLFormFilenameSafe(pszFilename, "zarr.json", nullptr);
41
3.38k
    if (VSIStatL(osMDFilename, &sStat) == 0)
42
0
        return true;
43
44
3.38k
    return false;
45
3.38k
}
46
47
/************************************************************************/
48
/*                    ZARRIsLikelyKerchunkJSONRef()                     */
49
/************************************************************************/
50
51
bool ZARRIsLikelyKerchunkJSONRef(const GDALOpenInfo *poOpenInfo)
52
321k
{
53
321k
    if (poOpenInfo->nHeaderBytes > 0 && poOpenInfo->eAccess == GA_ReadOnly &&
54
53.6k
        (poOpenInfo->IsExtensionEqualToCI("json") ||
55
         // e.g. like in https://noaa-nodd-kerchunk-pds.s3.amazonaws.com/nos/cbofs/cbofs.fields.best.nc.zarr
56
53.5k
         poOpenInfo->IsExtensionEqualToCI("zarr")))
57
82
    {
58
82
        const char *pszHeader =
59
82
            reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
60
82
        if (ZARRIsLikelyStreamableKerchunkJSONRefContent(
61
82
                std::string_view(pszHeader, poOpenInfo->nHeaderBytes)))
62
0
        {
63
0
            return true;
64
0
        }
65
82
    }
66
321k
    return false;
67
321k
}
68
69
/************************************************************************/
70
/*                         ZARRDriverIdentify()                         */
71
/************************************************************************/
72
73
int ZARRDriverIdentify(GDALOpenInfo *poOpenInfo)
74
75
321k
{
76
321k
    const std::string_view osvFilename(poOpenInfo->pszFilename);
77
321k
    if (cpl::starts_with(osvFilename, "ZARR:") ||
78
321k
        cpl::starts_with(osvFilename, "ZARR_DUMMY:"))
79
99
    {
80
99
        return TRUE;
81
99
    }
82
83
321k
    if (ZARRIsLikelyKerchunkJSONRef(poOpenInfo))
84
0
    {
85
0
        return TRUE;
86
0
    }
87
321k
    if (cpl::starts_with(osvFilename, JSON_REF_FS_PREFIX))
88
6
    {
89
6
        return -1;
90
6
    }
91
321k
    for (const char *pszFile :
92
321k
         {".zarray", ".zgroup", ".zmetadata", "zarr.json"})
93
1.28M
    {
94
1.28M
        if (cpl::ends_with(osvFilename, pszFile))
95
261
        {
96
261
            return TRUE;
97
261
        }
98
1.28M
    }
99
321k
    if (!poOpenInfo->bIsDirectory)
100
317k
    {
101
317k
        return FALSE;
102
317k
    }
103
104
3.38k
    return CheckExistenceOfOneZarrFile(poOpenInfo->pszFilename);
105
321k
}
106
107
/************************************************************************/
108
/*                    ZARRDriverSetCommonMetadata()                     */
109
/************************************************************************/
110
111
void ZARRDriverSetCommonMetadata(GDALDriver *poDriver)
112
22
{
113
22
    poDriver->SetDescription(DRIVER_NAME);
114
22
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
115
22
    poDriver->SetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER, "YES");
116
22
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Zarr");
117
22
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "zarr");
118
22
    poDriver->SetMetadataItem(
119
22
        GDAL_DMD_CREATIONDATATYPES,
120
22
        "Int8 Byte Int16 UInt16 Int32 UInt32 Int64 UInt64 "
121
22
        "Float16 Float32 Float64 CFLoat16 CFloat32 CFloat64");
122
22
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
123
22
    poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
124
22
    poDriver->SetMetadataItem(GDAL_DCAP_CREATE_SUBDATASETS, "YES");
125
126
22
    poDriver->SetMetadataItem(
127
22
        GDAL_DMD_OPENOPTIONLIST,
128
22
        "<OpenOptionList>"
129
22
        "   <Option name='LIST_ALL_ARRAYS' type='boolean' "
130
22
        "description='Whether to list all arrays, and not only those whose "
131
22
        "dimension count is 2 or more' default='NO'/>"
132
22
        "   <Option name='USE_CONSOLIDATED_METADATA' alias='USE_ZMETADATA' "
133
22
        "type='boolean' description='Whether "
134
22
        "to use consolidated metadata' default='YES'/>"
135
22
        "   <Option name='CACHE_TILE_PRESENCE' type='boolean' "
136
22
        "description='Whether to establish an initial listing of present "
137
22
        "tiles' default='NO'/>"
138
22
        "   <Option name='CACHE_KERCHUNK_JSON' type='boolean' "
139
22
        "description='Whether to transform Kerchunk JSON reference files into "
140
22
        "Kerchunk Parquet reference files in a local cache' default='NO'/>"
141
22
        "   <Option name='MULTIBAND' type='boolean' default='YES' "
142
22
        "description='Whether to expose >= 3D arrays as GDAL multiband "
143
22
        "datasets "
144
22
        "(when using the classic 2D API)'/>"
145
22
        "   <Option name='DIM_X' type='string' description="
146
22
        "'Name or index of the X dimension (only used when MULTIBAND=YES)'/>"
147
22
        "   <Option name='DIM_Y' type='string' description="
148
22
        "'Name or index of the Y dimension (only used when MULTIBAND=YES)'/>"
149
22
        "   <Option name='LOAD_EXTRA_DIM_METADATA_DELAY' type='string' "
150
22
        "description="
151
22
        "'Maximum delay in seconds allowed to set the DIM_{dimname}_VALUE band "
152
22
        "metadata items'/>"
153
22
        "</OpenOptionList>");
154
155
22
    poDriver->SetMetadataItem(
156
22
        GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST,
157
22
        "<MultiDimDatasetCreationOptionList>"
158
22
        "   <Option name='FORMAT' type='string-select' default='ZARR_V2'>"
159
22
        "     <Value>ZARR_V2</Value>"
160
22
        "     <Value>ZARR_V3</Value>"
161
22
        "   </Option>"
162
22
        "   <Option name='CREATE_CONSOLIDATED_METADATA' "
163
22
        "alias='CREATE_ZMETADATA' type='boolean' "
164
22
        "description='Whether to create consolidated metadata' default='YES'/>"
165
22
        "</MultiDimDatasetCreationOptionList>");
166
167
22
    poDriver->pfnIdentify = ZARRDriverIdentify;
168
22
    poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES");
169
22
    poDriver->SetMetadataItem(GDAL_DCAP_CREATE, "YES");
170
22
    poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES");
171
22
    poDriver->SetMetadataItem(GDAL_DCAP_CREATE_MULTIDIMENSIONAL, "YES");
172
173
22
    poDriver->SetMetadataItem(GDAL_DCAP_UPDATE, "YES");
174
22
    poDriver->SetMetadataItem(GDAL_DMD_UPDATE_ITEMS,
175
22
                              "GeoTransform SRS NoData "
176
22
                              "RasterValues "
177
22
                              "DatasetMetadata BandMetadata");
178
22
}
179
180
/************************************************************************/
181
/*                     DeclareDeferredZarrPlugin()                      */
182
/************************************************************************/
183
184
#ifdef PLUGIN_FILENAME
185
void DeclareDeferredZarrPlugin()
186
{
187
    if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
188
    {
189
        return;
190
    }
191
    auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME);
192
#ifdef PLUGIN_INSTALLATION_MESSAGE
193
    poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE,
194
                              PLUGIN_INSTALLATION_MESSAGE);
195
#endif
196
    ZARRDriverSetCommonMetadata(poDriver);
197
    GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver);
198
}
199
#endif