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