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 3000 : static bool CheckExistenceOfOneZarrFile(const char *pszFilename) 26 : { 27 : 28 : CPLString osMDFilename = 29 6000 : CPLFormFilenameSafe(pszFilename, ".zarray", nullptr); 30 : 31 : VSIStatBufL sStat; 32 3000 : if (VSIStatL(osMDFilename, &sStat) == 0) 33 498 : return true; 34 : 35 2502 : osMDFilename = CPLFormFilenameSafe(pszFilename, ".zgroup", nullptr); 36 2502 : if (VSIStatL(osMDFilename, &sStat) == 0) 37 490 : return true; 38 : 39 : // Zarr V3 40 2012 : osMDFilename = CPLFormFilenameSafe(pszFilename, "zarr.json", nullptr); 41 2012 : if (VSIStatL(osMDFilename, &sStat) == 0) 42 1734 : return true; 43 : 44 278 : return false; 45 : } 46 : 47 : /************************************************************************/ 48 : /* ZARRIsLikelyKerchunkJSONRef() */ 49 : /************************************************************************/ 50 : 51 61595 : bool ZARRIsLikelyKerchunkJSONRef(const GDALOpenInfo *poOpenInfo) 52 : { 53 64636 : if (poOpenInfo->nHeaderBytes > 0 && poOpenInfo->eAccess == GA_ReadOnly && 54 3041 : (poOpenInfo->IsExtensionEqualToCI("json") || 55 : // e.g. like in https://noaa-nodd-kerchunk-pds.s3.amazonaws.com/nos/cbofs/cbofs.fields.best.nc.zarr 56 1431 : poOpenInfo->IsExtensionEqualToCI("zarr"))) 57 : { 58 188 : const char *pszHeader = 59 : reinterpret_cast<const char *>(poOpenInfo->pabyHeader); 60 188 : if (ZARRIsLikelyStreamableKerchunkJSONRefContent( 61 376 : std::string_view(pszHeader, poOpenInfo->nHeaderBytes))) 62 : { 63 57 : return true; 64 : } 65 : } 66 61538 : return false; 67 : } 68 : 69 : /************************************************************************/ 70 : /* ZARRDriverIdentify() */ 71 : /************************************************************************/ 72 : 73 60216 : int ZARRDriverIdentify(GDALOpenInfo *poOpenInfo) 74 : 75 : { 76 60216 : const std::string_view osvFilename(poOpenInfo->pszFilename); 77 120318 : if (cpl::starts_with(osvFilename, "ZARR:") || 78 60102 : cpl::starts_with(osvFilename, "ZARR_DUMMY:")) 79 : { 80 116 : return TRUE; 81 : } 82 : 83 60100 : if (ZARRIsLikelyKerchunkJSONRef(poOpenInfo)) 84 : { 85 38 : return TRUE; 86 : } 87 60062 : if (cpl::starts_with(osvFilename, JSON_REF_FS_PREFIX)) 88 : { 89 4 : return -1; 90 : } 91 240130 : for (const char *pszFile : 92 300188 : {".zarray", ".zgroup", ".zmetadata", "zarr.json"}) 93 : { 94 240220 : if (cpl::ends_with(osvFilename, pszFile)) 95 : { 96 90 : return TRUE; 97 : } 98 : } 99 59968 : if (!poOpenInfo->bIsDirectory) 100 : { 101 56968 : return FALSE; 102 : } 103 : 104 3000 : return CheckExistenceOfOneZarrFile(poOpenInfo->pszFilename); 105 : } 106 : 107 : /************************************************************************/ 108 : /* ZARRDriverSetCommonMetadata() */ 109 : /************************************************************************/ 110 : 111 1776 : void ZARRDriverSetCommonMetadata(GDALDriver *poDriver) 112 : { 113 1776 : poDriver->SetDescription(DRIVER_NAME); 114 1776 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); 115 1776 : poDriver->SetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER, "YES"); 116 1776 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Zarr"); 117 1776 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "zarr"); 118 1776 : poDriver->SetMetadataItem( 119 : GDAL_DMD_CREATIONDATATYPES, 120 : "Int8 Byte Int16 UInt16 Int32 UInt32 Int64 UInt64 " 121 1776 : "Float16 Float32 Float64 CFLoat16 CFloat32 CFloat64"); 122 1776 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); 123 1776 : poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES"); 124 1776 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_SUBDATASETS, "YES"); 125 : 126 1776 : 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_CONSOLIDATED_METADATA' alias='USE_ZMETADATA' " 133 : "type='boolean' description='Whether " 134 : "to use consolidated metadata' default='YES'/>" 135 : " <Option name='CACHE_TILE_PRESENCE' type='boolean' " 136 : "description='Whether to establish an initial listing of present " 137 : "tiles' default='NO'/>" 138 : " <Option name='CACHE_KERCHUNK_JSON' type='boolean' " 139 : "description='Whether to transform Kerchunk JSON reference files into " 140 : "Kerchunk Parquet reference files in a local cache' default='NO'/>" 141 : " <Option name='MULTIBAND' type='boolean' default='YES' " 142 : "description='Whether to expose >= 3D arrays as GDAL multiband " 143 : "datasets " 144 : "(when using the classic 2D API)'/>" 145 : " <Option name='DIM_X' type='string' description=" 146 : "'Name or index of the X dimension (only used when MULTIBAND=YES)'/>" 147 : " <Option name='DIM_Y' type='string' description=" 148 : "'Name or index of the Y dimension (only used when MULTIBAND=YES)'/>" 149 : " <Option name='LOAD_EXTRA_DIM_METADATA_DELAY' type='string' " 150 : "description=" 151 : "'Maximum delay in seconds allowed to set the DIM_{dimname}_VALUE band " 152 : "metadata items'/>" 153 1776 : "</OpenOptionList>"); 154 : 155 1776 : poDriver->SetMetadataItem( 156 : GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST, 157 : "<MultiDimDatasetCreationOptionList>" 158 : " <Option name='FORMAT' type='string-select' default='ZARR_V2'>" 159 : " <Value>ZARR_V2</Value>" 160 : " <Value>ZARR_V3</Value>" 161 : " </Option>" 162 : " <Option name='CREATE_CONSOLIDATED_METADATA' " 163 : "alias='CREATE_ZMETADATA' type='boolean' " 164 : "description='Whether to create consolidated metadata' default='YES'/>" 165 1776 : "</MultiDimDatasetCreationOptionList>"); 166 : 167 1776 : poDriver->pfnIdentify = ZARRDriverIdentify; 168 1776 : poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES"); 169 1776 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE, "YES"); 170 1776 : poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES"); 171 1776 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_MULTIDIMENSIONAL, "YES"); 172 : 173 1776 : poDriver->SetMetadataItem(GDAL_DCAP_UPDATE, "YES"); 174 1776 : poDriver->SetMetadataItem(GDAL_DMD_UPDATE_ITEMS, 175 : "GeoTransform SRS NoData " 176 : "RasterValues " 177 1776 : "DatasetMetadata BandMetadata"); 178 1776 : } 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