Line data Source code
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 1670 : static bool CheckExistenceOfOneZarrFile(const char *pszFilename) 26 : { 27 : 28 : CPLString osMDFilename = 29 3340 : CPLFormFilenameSafe(pszFilename, ".zarray", nullptr); 30 : 31 : VSIStatBufL sStat; 32 1670 : if (VSIStatL(osMDFilename, &sStat) == 0) 33 496 : return true; 34 : 35 1174 : osMDFilename = CPLFormFilenameSafe(pszFilename, ".zgroup", nullptr); 36 1174 : if (VSIStatL(osMDFilename, &sStat) == 0) 37 460 : return true; 38 : 39 : // Zarr V3 40 714 : osMDFilename = CPLFormFilenameSafe(pszFilename, "zarr.json", nullptr); 41 714 : if (VSIStatL(osMDFilename, &sStat) == 0) 42 436 : return true; 43 : 44 278 : return false; 45 : } 46 : 47 : /************************************************************************/ 48 : /* ZARRIsLikelyKerchunkJSONRef() */ 49 : /************************************************************************/ 50 : 51 57952 : bool ZARRIsLikelyKerchunkJSONRef(const GDALOpenInfo *poOpenInfo) 52 : { 53 60840 : if (poOpenInfo->nHeaderBytes > 0 && poOpenInfo->eAccess == GA_ReadOnly && 54 2888 : (poOpenInfo->IsExtensionEqualToCI("json") || 55 : // e.g. like in https://noaa-nodd-kerchunk-pds.s3.amazonaws.com/nos/cbofs/cbofs.fields.best.nc.zarr 56 1416 : poOpenInfo->IsExtensionEqualToCI("zarr"))) 57 : { 58 65 : const char *pszHeader = 59 : reinterpret_cast<const char *>(poOpenInfo->pabyHeader); 60 65 : if (ZARRIsLikelyStreamableKerchunkJSONRefContent( 61 130 : std::string_view(pszHeader, poOpenInfo->nHeaderBytes))) 62 : { 63 57 : return true; 64 : } 65 : } 66 57895 : return false; 67 : } 68 : 69 : /************************************************************************/ 70 : /* ZARRDriverIdentify() */ 71 : /************************************************************************/ 72 : 73 57273 : int ZARRDriverIdentify(GDALOpenInfo *poOpenInfo) 74 : 75 : { 76 57273 : const std::string_view osvFilename(poOpenInfo->pszFilename); 77 114444 : if (cpl::starts_with(osvFilename, "ZARR:") || 78 57171 : cpl::starts_with(osvFilename, "ZARR_DUMMY:")) 79 : { 80 104 : return TRUE; 81 : } 82 : 83 57169 : if (ZARRIsLikelyKerchunkJSONRef(poOpenInfo)) 84 : { 85 38 : return TRUE; 86 : } 87 57131 : if (cpl::starts_with(osvFilename, JSON_REF_FS_PREFIX)) 88 : { 89 4 : return -1; 90 : } 91 228486 : for (const char *pszFile : 92 285613 : {".zarray", ".zgroup", ".zmetadata", "zarr.json"}) 93 : { 94 228493 : if (cpl::ends_with(osvFilename, pszFile)) 95 : { 96 8 : return TRUE; 97 : } 98 : } 99 57119 : if (!poOpenInfo->bIsDirectory) 100 : { 101 55448 : return FALSE; 102 : } 103 : 104 1671 : return CheckExistenceOfOneZarrFile(poOpenInfo->pszFilename); 105 : } 106 : 107 : /************************************************************************/ 108 : /* ZARRDriverSetCommonMetadata() */ 109 : /************************************************************************/ 110 : 111 1755 : void ZARRDriverSetCommonMetadata(GDALDriver *poDriver) 112 : { 113 1755 : poDriver->SetDescription(DRIVER_NAME); 114 1755 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); 115 1755 : poDriver->SetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER, "YES"); 116 1755 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Zarr"); 117 1755 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "zarr"); 118 1755 : poDriver->SetMetadataItem( 119 : GDAL_DMD_CREATIONDATATYPES, 120 : "Int8 Byte Int16 UInt16 Int32 UInt32 Int64 UInt64 " 121 1755 : "Float16 Float32 Float64 CFLoat16 CFloat32 CFloat64"); 122 1755 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); 123 1755 : poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES"); 124 1755 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_SUBDATASETS, "YES"); 125 : 126 1755 : poDriver->SetMetadataItem( 127 : GDAL_DMD_OPENOPTIONLIST, 128 : "<OpenOptionList>" 129 : " <Option name='LIST_ALL_ARRAYS' type='boolean' " 130 : "description='Whether to list all arrays, and not only those whose " 131 : "dimension count is 2 or more' default='NO'/>" 132 : " <Option name='USE_ZMETADATA' type='boolean' description='Whether " 133 : "to use consolidated metadata from .zmetadata' default='YES'/>" 134 : " <Option name='CACHE_TILE_PRESENCE' type='boolean' " 135 : "description='Whether to establish an initial listing of present " 136 : "tiles' default='NO'/>" 137 : " <Option name='CACHE_KERCHUNK_JSON' type='boolean' " 138 : "description='Whether to transform Kerchunk JSON reference files into " 139 : "Kerchunk Parquet reference files in a local cache' default='NO'/>" 140 : " <Option name='MULTIBAND' type='boolean' default='YES' " 141 : "description='Whether to expose >= 3D arrays as GDAL multiband " 142 : "datasets " 143 : "(when using the classic 2D API)'/>" 144 : " <Option name='DIM_X' type='string' description=" 145 : "'Name or index of the X dimension (only used when MULTIBAND=YES)'/>" 146 : " <Option name='DIM_Y' type='string' description=" 147 : "'Name or index of the Y dimension (only used when MULTIBAND=YES)'/>" 148 : " <Option name='LOAD_EXTRA_DIM_METADATA_DELAY' type='string' " 149 : "description=" 150 : "'Maximum delay in seconds allowed to set the DIM_{dimname}_VALUE band " 151 : "metadata items'/>" 152 1755 : "</OpenOptionList>"); 153 : 154 1755 : poDriver->SetMetadataItem( 155 : GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST, 156 : "<MultiDimDatasetCreationOptionList>" 157 : " <Option name='FORMAT' type='string-select' default='ZARR_V2'>" 158 : " <Value>ZARR_V2</Value>" 159 : " <Value>ZARR_V3</Value>" 160 : " </Option>" 161 : " <Option name='CREATE_ZMETADATA' type='boolean' " 162 : "description='Whether to create consolidated metadata into .zmetadata " 163 : "(Zarr V2 only)' default='YES'/>" 164 1755 : "</MultiDimDatasetCreationOptionList>"); 165 : 166 1755 : poDriver->pfnIdentify = ZARRDriverIdentify; 167 1755 : poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES"); 168 1755 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE, "YES"); 169 1755 : poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES"); 170 1755 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_MULTIDIMENSIONAL, "YES"); 171 : 172 1755 : poDriver->SetMetadataItem(GDAL_DCAP_UPDATE, "YES"); 173 1755 : poDriver->SetMetadataItem(GDAL_DMD_UPDATE_ITEMS, 174 : "GeoTransform SRS NoData " 175 : "RasterValues " 176 1755 : "DatasetMetadata BandMetadata"); 177 1755 : } 178 : 179 : /************************************************************************/ 180 : /* DeclareDeferredZarrPlugin() */ 181 : /************************************************************************/ 182 : 183 : #ifdef PLUGIN_FILENAME 184 : void DeclareDeferredZarrPlugin() 185 : { 186 : if (GDALGetDriverByName(DRIVER_NAME) != nullptr) 187 : { 188 : return; 189 : } 190 : auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME); 191 : #ifdef PLUGIN_INSTALLATION_MESSAGE 192 : poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE, 193 : PLUGIN_INSTALLATION_MESSAGE); 194 : #endif 195 : ZARRDriverSetCommonMetadata(poDriver); 196 : GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver); 197 : } 198 : #endif