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 "zarr.h"
14 :
15 : #include "cpl_json.h"
16 :
17 : /************************************************************************/
18 : /* ZarrSharedResource::ZarrSharedResource() */
19 : /************************************************************************/
20 :
21 985 : ZarrSharedResource::ZarrSharedResource(const std::string &osRootDirectoryName,
22 985 : bool bUpdatable)
23 985 : : m_bUpdatable(bUpdatable)
24 : {
25 985 : m_oObj.Add("zarr_consolidated_format", 1);
26 985 : m_oObj.Add("metadata", CPLJSONObject());
27 :
28 985 : m_osRootDirectoryName = osRootDirectoryName;
29 985 : if (!m_osRootDirectoryName.empty() && m_osRootDirectoryName.back() == '/')
30 : {
31 0 : m_osRootDirectoryName.pop_back();
32 : }
33 985 : m_poPAM = std::make_shared<GDALPamMultiDim>(
34 985 : CPLFormFilename(m_osRootDirectoryName.c_str(), "pam", nullptr));
35 985 : }
36 :
37 : /************************************************************************/
38 : /* ZarrSharedResource::Create() */
39 : /************************************************************************/
40 :
41 : std::shared_ptr<ZarrSharedResource>
42 985 : ZarrSharedResource::Create(const std::string &osRootDirectoryName,
43 : bool bUpdatable)
44 : {
45 : return std::shared_ptr<ZarrSharedResource>(
46 985 : new ZarrSharedResource(osRootDirectoryName, bUpdatable));
47 : }
48 :
49 : /************************************************************************/
50 : /* ZarrSharedResource::~ZarrSharedResource() */
51 : /************************************************************************/
52 :
53 985 : ZarrSharedResource::~ZarrSharedResource()
54 : {
55 985 : if (m_bZMetadataModified)
56 : {
57 178 : CPLJSONDocument oDoc;
58 178 : oDoc.SetRoot(m_oObj);
59 178 : oDoc.Save(CPLFormFilename(m_osRootDirectoryName.c_str(), ".zmetadata",
60 : nullptr));
61 : }
62 985 : }
63 :
64 : /************************************************************************/
65 : /* ZarrSharedResource::OpenRootGroup() */
66 : /************************************************************************/
67 :
68 677 : std::shared_ptr<ZarrGroupBase> ZarrSharedResource::OpenRootGroup()
69 : {
70 : {
71 1354 : auto poRG = ZarrV2Group::Create(shared_from_this(), std::string(), "/");
72 677 : poRG->SetUpdatable(m_bUpdatable);
73 677 : poRG->SetDirectoryName(m_osRootDirectoryName);
74 :
75 : const std::string osZarrayFilename(
76 677 : CPLFormFilename(m_osRootDirectoryName.c_str(), ".zarray", nullptr));
77 : VSIStatBufL sStat;
78 677 : if (VSIStatL(osZarrayFilename.c_str(), &sStat) == 0)
79 : {
80 494 : CPLJSONDocument oDoc;
81 247 : if (!oDoc.Load(osZarrayFilename))
82 0 : return nullptr;
83 494 : const auto oRoot = oDoc.GetRoot();
84 247 : if (oRoot["_NCZARR_ARRAY"].IsValid())
85 : {
86 : // If opening a NCZarr array, initialize its group from NCZarr
87 : // metadata.
88 : const std::string osGroupFilename(CPLFormFilename(
89 : CPLGetDirname(m_osRootDirectoryName.c_str()), ".zgroup",
90 3 : nullptr));
91 3 : if (VSIStatL(osGroupFilename.c_str(), &sStat) == 0)
92 : {
93 2 : CPLJSONDocument oDocGroup;
94 2 : if (oDocGroup.Load(osGroupFilename))
95 : {
96 2 : if (!poRG->InitFromZGroup(oDocGroup.GetRoot()))
97 1 : return nullptr;
98 : }
99 : }
100 : }
101 : const std::string osArrayName(
102 492 : CPLGetBasename(m_osRootDirectoryName.c_str()));
103 738 : if (!poRG->LoadArray(osArrayName, osZarrayFilename, oRoot, false,
104 738 : CPLJSONObject()))
105 39 : return nullptr;
106 :
107 207 : return poRG;
108 : }
109 :
110 : const std::string osZmetadataFilename(CPLFormFilename(
111 430 : m_osRootDirectoryName.c_str(), ".zmetadata", nullptr));
112 430 : if (CPLTestBool(CSLFetchNameValueDef(GetOpenOptions(), "USE_ZMETADATA",
113 854 : "YES")) &&
114 424 : VSIStatL(osZmetadataFilename.c_str(), &sStat) == 0)
115 : {
116 166 : if (!m_bZMetadataEnabled)
117 : {
118 166 : CPLJSONDocument oDoc;
119 166 : if (!oDoc.Load(osZmetadataFilename))
120 0 : return nullptr;
121 :
122 166 : m_bZMetadataEnabled = true;
123 166 : m_oObj = oDoc.GetRoot();
124 : }
125 166 : poRG->InitFromZMetadata(m_oObj);
126 :
127 166 : return poRG;
128 : }
129 :
130 : const std::string osGroupFilename(
131 264 : CPLFormFilename(m_osRootDirectoryName.c_str(), ".zgroup", nullptr));
132 264 : if (VSIStatL(osGroupFilename.c_str(), &sStat) == 0)
133 : {
134 110 : CPLJSONDocument oDoc;
135 55 : if (!oDoc.Load(osGroupFilename))
136 0 : return nullptr;
137 :
138 55 : if (!poRG->InitFromZGroup(oDoc.GetRoot()))
139 3 : return nullptr;
140 52 : return poRG;
141 : }
142 : }
143 :
144 : // Zarr v3
145 418 : auto poRG_V3 = ZarrV3Group::Create(shared_from_this(), std::string(), "/",
146 836 : m_osRootDirectoryName);
147 209 : poRG_V3->SetUpdatable(m_bUpdatable);
148 :
149 : const std::string osZarrJsonFilename(
150 418 : CPLFormFilename(m_osRootDirectoryName.c_str(), "zarr.json", nullptr));
151 : VSIStatBufL sStat;
152 209 : if (VSIStatL(osZarrJsonFilename.c_str(), &sStat) == 0)
153 : {
154 414 : CPLJSONDocument oDoc;
155 207 : if (!oDoc.Load(osZarrJsonFilename))
156 0 : return nullptr;
157 414 : const auto oRoot = oDoc.GetRoot();
158 207 : if (oRoot.GetInteger("zarr_format") != 3)
159 : {
160 0 : CPLError(CE_Failure, CPLE_AppDefined,
161 : "Unhandled zarr_format value");
162 0 : return nullptr;
163 : }
164 621 : const std::string osNodeType = oRoot.GetString("node_type");
165 207 : if (osNodeType == "array")
166 : {
167 : const std::string osArrayName(
168 106 : CPLGetBasename(m_osRootDirectoryName.c_str()));
169 53 : poRG_V3->SetExplored();
170 53 : if (!poRG_V3->LoadArray(osArrayName, osZarrJsonFilename, oRoot))
171 32 : return nullptr;
172 :
173 21 : return poRG_V3;
174 : }
175 154 : else if (osNodeType == "group")
176 : {
177 154 : return poRG_V3;
178 : }
179 : else
180 : {
181 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unhandled node_type value");
182 0 : return nullptr;
183 : }
184 : }
185 :
186 : // No explicit zarr.json in root directory ? Then recurse until we find
187 : // one.
188 2 : auto psDir = VSIOpenDir(m_osRootDirectoryName.c_str(), -1, nullptr);
189 2 : if (!psDir)
190 2 : return nullptr;
191 0 : bool bZarrJsonFound = false;
192 0 : while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
193 : {
194 0 : if (!VSI_ISDIR(psEntry->nMode) &&
195 0 : strcmp(CPLGetFilename(psEntry->pszName), "zarr.json") == 0)
196 : {
197 0 : bZarrJsonFound = true;
198 0 : break;
199 : }
200 0 : }
201 0 : VSICloseDir(psDir);
202 0 : if (bZarrJsonFound)
203 0 : return poRG_V3;
204 :
205 0 : return nullptr;
206 : }
207 :
208 : /************************************************************************/
209 : /* ZarrSharedResource::SetZMetadataItem() */
210 : /************************************************************************/
211 :
212 944 : void ZarrSharedResource::SetZMetadataItem(const std::string &osFilename,
213 : const CPLJSONObject &obj)
214 : {
215 944 : if (m_bZMetadataEnabled)
216 : {
217 1650 : CPLString osNormalizedFilename(osFilename);
218 825 : osNormalizedFilename.replaceAll('\\', '/');
219 825 : CPLAssert(STARTS_WITH(osNormalizedFilename.c_str(),
220 : (m_osRootDirectoryName + '/').c_str()));
221 825 : m_bZMetadataModified = true;
222 : const char *pszKey =
223 825 : osNormalizedFilename.c_str() + m_osRootDirectoryName.size() + 1;
224 1650 : auto oMetadata = m_oObj["metadata"];
225 825 : oMetadata.DeleteNoSplitName(pszKey);
226 825 : oMetadata.AddNoSplitName(pszKey, obj);
227 : }
228 944 : }
229 :
230 : /************************************************************************/
231 : /* ZarrSharedResource::DeleteZMetadataItemRecursive() */
232 : /************************************************************************/
233 :
234 12 : void ZarrSharedResource::DeleteZMetadataItemRecursive(
235 : const std::string &osFilename)
236 : {
237 12 : if (m_bZMetadataEnabled)
238 : {
239 8 : CPLString osNormalizedFilename(osFilename);
240 4 : osNormalizedFilename.replaceAll('\\', '/');
241 4 : CPLAssert(STARTS_WITH(osNormalizedFilename.c_str(),
242 : (m_osRootDirectoryName + '/').c_str()));
243 4 : m_bZMetadataModified = true;
244 : const char *pszKey =
245 4 : osNormalizedFilename.c_str() + m_osRootDirectoryName.size() + 1;
246 :
247 12 : auto oMetadata = m_oObj["metadata"];
248 28 : for (auto &item : oMetadata.GetChildren())
249 : {
250 24 : if (STARTS_WITH(item.GetName().c_str(), pszKey))
251 : {
252 14 : oMetadata.DeleteNoSplitName(item.GetName());
253 : }
254 : }
255 : }
256 12 : }
257 :
258 : /************************************************************************/
259 : /* ZarrSharedResource::RenameZMetadataRecursive() */
260 : /************************************************************************/
261 :
262 12 : void ZarrSharedResource::RenameZMetadataRecursive(
263 : const std::string &osOldFilename, const std::string &osNewFilename)
264 : {
265 12 : if (m_bZMetadataEnabled)
266 : {
267 8 : CPLString osNormalizedOldFilename(osOldFilename);
268 4 : osNormalizedOldFilename.replaceAll('\\', '/');
269 4 : CPLAssert(STARTS_WITH(osNormalizedOldFilename.c_str(),
270 : (m_osRootDirectoryName + '/').c_str()));
271 :
272 8 : CPLString osNormalizedNewFilename(osNewFilename);
273 4 : osNormalizedNewFilename.replaceAll('\\', '/');
274 4 : CPLAssert(STARTS_WITH(osNormalizedNewFilename.c_str(),
275 : (m_osRootDirectoryName + '/').c_str()));
276 :
277 4 : m_bZMetadataModified = true;
278 :
279 : const char *pszOldKeyRadix =
280 4 : osNormalizedOldFilename.c_str() + m_osRootDirectoryName.size() + 1;
281 : const char *pszNewKeyRadix =
282 4 : osNormalizedNewFilename.c_str() + m_osRootDirectoryName.size() + 1;
283 :
284 12 : auto oMetadata = m_oObj["metadata"];
285 32 : for (auto &item : oMetadata.GetChildren())
286 : {
287 28 : if (STARTS_WITH(item.GetName().c_str(), pszOldKeyRadix))
288 : {
289 15 : oMetadata.DeleteNoSplitName(item.GetName());
290 30 : std::string osNewKey(pszNewKeyRadix);
291 15 : osNewKey += (item.GetName().c_str() + strlen(pszOldKeyRadix));
292 15 : oMetadata.AddNoSplitName(osNewKey, item);
293 : }
294 : }
295 : }
296 12 : }
297 :
298 : /************************************************************************/
299 : /* ZarrSharedResource::UpdateDimensionSize() */
300 : /************************************************************************/
301 :
302 7 : void ZarrSharedResource::UpdateDimensionSize(
303 : const std::shared_ptr<GDALDimension> &poDim)
304 : {
305 14 : auto poRG = m_poWeakRootGroup.lock();
306 7 : if (!poRG)
307 0 : poRG = OpenRootGroup();
308 7 : if (poRG)
309 : {
310 7 : poRG->UpdateDimensionSize(poDim);
311 : }
312 : else
313 : {
314 0 : CPLError(CE_Failure, CPLE_AppDefined, "UpdateDimensionSize() failed");
315 : }
316 7 : poRG.reset();
317 7 : }
318 :
319 : /************************************************************************/
320 : /* ZarrSharedResource::AddArrayInLoading() */
321 : /************************************************************************/
322 :
323 816 : bool ZarrSharedResource::AddArrayInLoading(const std::string &osZarrayFilename)
324 : {
325 : // Prevent too deep or recursive array loading
326 816 : if (m_oSetArrayInLoading.find(osZarrayFilename) !=
327 1632 : m_oSetArrayInLoading.end())
328 : {
329 1 : CPLError(CE_Failure, CPLE_AppDefined,
330 : "Attempt at recursively loading %s", osZarrayFilename.c_str());
331 1 : return false;
332 : }
333 815 : if (m_oSetArrayInLoading.size() == 32)
334 : {
335 1 : CPLError(CE_Failure, CPLE_AppDefined,
336 : "Too deep call stack in LoadArray()");
337 1 : return false;
338 : }
339 814 : m_oSetArrayInLoading.insert(osZarrayFilename);
340 814 : return true;
341 : }
342 :
343 : /************************************************************************/
344 : /* ZarrSharedResource::RemoveArrayInLoading() */
345 : /************************************************************************/
346 :
347 814 : void ZarrSharedResource::RemoveArrayInLoading(
348 : const std::string &osZarrayFilename)
349 : {
350 814 : m_oSetArrayInLoading.erase(osZarrayFilename);
351 814 : }
|