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 <algorithm>
16 : #include <cassert>
17 : #include <map>
18 :
19 : constexpr const char *ATTRIBUTE_GROUP_SUFFIX = "/_GLOBAL_";
20 :
21 : /************************************************************************/
22 : /* ZarrAttributeGroup::ZarrAttributeGroup() */
23 : /************************************************************************/
24 :
25 2586 : ZarrAttributeGroup::ZarrAttributeGroup(const std::string &osParentName,
26 2586 : bool bContainerIsGroup)
27 : : m_bContainerIsGroup(bContainerIsGroup),
28 : m_poGroup(MEMGroup::Create(
29 : bContainerIsGroup
30 5172 : ? (osParentName == "/" ? ATTRIBUTE_GROUP_SUFFIX
31 : : osParentName + ATTRIBUTE_GROUP_SUFFIX)
32 : : osParentName,
33 5172 : nullptr))
34 : {
35 2586 : }
36 :
37 : /************************************************************************/
38 : /* ZarrAttributeGroup::Init() */
39 : /************************************************************************/
40 :
41 809 : void ZarrAttributeGroup::Init(const CPLJSONObject &obj, bool bUpdatable)
42 : {
43 809 : if (obj.GetType() != CPLJSONObject::Type::Object)
44 28 : return;
45 1562 : const auto children = obj.GetChildren();
46 1122 : for (const auto &item : children)
47 : {
48 341 : const auto itemType = item.GetType();
49 341 : bool bDone = false;
50 341 : std::shared_ptr<GDALAttribute> poAttr;
51 341 : switch (itemType)
52 : {
53 192 : case CPLJSONObject::Type::String:
54 : {
55 192 : bDone = true;
56 768 : poAttr = m_poGroup->CreateAttribute(
57 384 : item.GetName(), {}, GDALExtendedDataType::CreateString(),
58 384 : nullptr);
59 192 : if (poAttr)
60 : {
61 192 : const GUInt64 arrayStartIdx = 0;
62 192 : const size_t count = 1;
63 192 : const GInt64 arrayStep = 0;
64 192 : const GPtrDiff_t bufferStride = 0;
65 576 : const std::string str = item.ToString();
66 192 : const char *c_str = str.c_str();
67 384 : poAttr->Write(&arrayStartIdx, &count, &arrayStep,
68 192 : &bufferStride, poAttr->GetDataType(), &c_str);
69 : }
70 192 : break;
71 : }
72 17 : case CPLJSONObject::Type::Integer:
73 : {
74 17 : bDone = true;
75 68 : poAttr = m_poGroup->CreateAttribute(
76 34 : item.GetName(), {}, GDALExtendedDataType::Create(GDT_Int32),
77 34 : nullptr);
78 17 : if (poAttr)
79 : {
80 17 : const GUInt64 arrayStartIdx = 0;
81 17 : const size_t count = 1;
82 17 : const GInt64 arrayStep = 0;
83 17 : const GPtrDiff_t bufferStride = 0;
84 17 : const int val = item.ToInteger();
85 34 : poAttr->Write(
86 : &arrayStartIdx, &count, &arrayStep, &bufferStride,
87 34 : GDALExtendedDataType::Create(GDT_Int32), &val);
88 : }
89 17 : break;
90 : }
91 20 : case CPLJSONObject::Type::Long:
92 : {
93 20 : bDone = true;
94 80 : poAttr = m_poGroup->CreateAttribute(
95 40 : item.GetName(), {}, GDALExtendedDataType::Create(GDT_Int64),
96 40 : nullptr);
97 20 : if (poAttr)
98 : {
99 20 : const GUInt64 arrayStartIdx = 0;
100 20 : const size_t count = 1;
101 20 : const GInt64 arrayStep = 0;
102 20 : const GPtrDiff_t bufferStride = 0;
103 20 : const int64_t val = item.ToLong();
104 40 : poAttr->Write(
105 : &arrayStartIdx, &count, &arrayStep, &bufferStride,
106 40 : GDALExtendedDataType::Create(GDT_Int64), &val);
107 : }
108 20 : break;
109 : }
110 32 : case CPLJSONObject::Type::Double:
111 : {
112 32 : bDone = true;
113 128 : poAttr = m_poGroup->CreateAttribute(
114 64 : item.GetName(), {},
115 96 : GDALExtendedDataType::Create(GDT_Float64), nullptr);
116 32 : if (poAttr)
117 : {
118 32 : const GUInt64 arrayStartIdx = 0;
119 32 : const size_t count = 1;
120 32 : const GInt64 arrayStep = 0;
121 32 : const GPtrDiff_t bufferStride = 0;
122 32 : const double val = item.ToDouble();
123 64 : poAttr->Write(
124 : &arrayStartIdx, &count, &arrayStep, &bufferStride,
125 64 : GDALExtendedDataType::Create(GDT_Float64), &val);
126 : }
127 32 : break;
128 : }
129 54 : case CPLJSONObject::Type::Array:
130 : {
131 108 : const auto array = item.ToArray();
132 54 : bool isFirst = true;
133 54 : bool isString = false;
134 54 : bool isNumeric = false;
135 54 : bool foundInt64 = false;
136 54 : bool foundDouble = false;
137 54 : bool mixedType = false;
138 54 : size_t countItems = 0;
139 165 : for (const auto &subItem : array)
140 : {
141 111 : const auto subItemType = subItem.GetType();
142 111 : if (subItemType == CPLJSONObject::Type::String)
143 : {
144 27 : if (isFirst)
145 : {
146 13 : isString = true;
147 : }
148 14 : else if (!isString)
149 : {
150 0 : mixedType = true;
151 0 : break;
152 : }
153 27 : countItems++;
154 : }
155 84 : else if (subItemType == CPLJSONObject::Type::Integer ||
156 20 : subItemType == CPLJSONObject::Type::Long ||
157 : subItemType == CPLJSONObject::Type::Double)
158 : {
159 84 : if (isFirst)
160 : {
161 41 : isNumeric = true;
162 : }
163 43 : else if (!isNumeric)
164 : {
165 2 : mixedType = true;
166 2 : break;
167 : }
168 82 : if (subItemType == CPLJSONObject::Type::Double)
169 20 : foundDouble = true;
170 62 : else if (subItemType == CPLJSONObject::Type::Long)
171 34 : foundInt64 = true;
172 82 : countItems++;
173 : }
174 : else
175 : {
176 0 : mixedType = true;
177 0 : break;
178 : }
179 109 : isFirst = false;
180 : }
181 :
182 54 : if (!mixedType && !isFirst)
183 : {
184 52 : bDone = true;
185 260 : poAttr = m_poGroup->CreateAttribute(
186 104 : item.GetName(), {countItems},
187 134 : isString ? GDALExtendedDataType::CreateString()
188 : : GDALExtendedDataType::Create(
189 : foundDouble ? GDT_Float64
190 30 : : foundInt64 ? GDT_Int64
191 : : GDT_Int32),
192 104 : nullptr);
193 52 : if (poAttr)
194 : {
195 52 : size_t idx = 0;
196 159 : for (const auto &subItem : array)
197 : {
198 107 : const GUInt64 arrayStartIdx = idx;
199 107 : const size_t count = 1;
200 107 : const GInt64 arrayStep = 0;
201 107 : const GPtrDiff_t bufferStride = 0;
202 107 : const auto subItemType = subItem.GetType();
203 : switch (subItemType)
204 : {
205 25 : case CPLJSONObject::Type::String:
206 : {
207 75 : const std::string str = subItem.ToString();
208 25 : const char *c_str = str.c_str();
209 50 : poAttr->Write(&arrayStartIdx, &count,
210 : &arrayStep, &bufferStride,
211 25 : poAttr->GetDataType(),
212 : &c_str);
213 25 : break;
214 : }
215 28 : case CPLJSONObject::Type::Integer:
216 : {
217 28 : const int val = subItem.ToInteger();
218 56 : poAttr->Write(
219 : &arrayStartIdx, &count, &arrayStep,
220 : &bufferStride,
221 56 : GDALExtendedDataType::Create(GDT_Int32),
222 : &val);
223 28 : break;
224 : }
225 34 : case CPLJSONObject::Type::Long:
226 : {
227 34 : const int64_t val = subItem.ToLong();
228 68 : poAttr->Write(
229 : &arrayStartIdx, &count, &arrayStep,
230 : &bufferStride,
231 68 : GDALExtendedDataType::Create(GDT_Int64),
232 : &val);
233 34 : break;
234 : }
235 20 : case CPLJSONObject::Type::Double:
236 : {
237 20 : const double val = subItem.ToDouble();
238 40 : poAttr->Write(&arrayStartIdx, &count,
239 : &arrayStep, &bufferStride,
240 40 : GDALExtendedDataType::Create(
241 : GDT_Float64),
242 : &val);
243 20 : break;
244 : }
245 0 : default:
246 : // Ignore other JSON object types
247 0 : break;
248 : }
249 107 : ++idx;
250 : }
251 : }
252 : }
253 54 : break;
254 : }
255 26 : default:
256 : // Ignore other JSON object types
257 26 : break;
258 : }
259 :
260 341 : if (!bDone)
261 : {
262 28 : constexpr size_t nMaxStringLength = 0;
263 : const auto eDT = GDALExtendedDataType::CreateString(
264 56 : nMaxStringLength, GEDTST_JSON);
265 : poAttr =
266 28 : m_poGroup->CreateAttribute(item.GetName(), {}, eDT, nullptr);
267 28 : if (poAttr)
268 : {
269 28 : const GUInt64 arrayStartIdx = 0;
270 28 : const size_t count = 1;
271 28 : const GInt64 arrayStep = 0;
272 28 : const GPtrDiff_t bufferStride = 0;
273 84 : const std::string str = item.ToString();
274 28 : const char *c_str = str.c_str();
275 56 : poAttr->Write(&arrayStartIdx, &count, &arrayStep, &bufferStride,
276 28 : poAttr->GetDataType(), &c_str);
277 : }
278 : }
279 :
280 682 : auto poMemAttr = std::dynamic_pointer_cast<MEMAttribute>(poAttr);
281 341 : if (poMemAttr)
282 341 : poMemAttr->SetModified(false);
283 : }
284 781 : SetUpdatable(bUpdatable);
285 : }
286 :
287 : /************************************************************************/
288 : /* ZarrAttributeGroup::Serialize() */
289 : /************************************************************************/
290 :
291 443 : CPLJSONObject ZarrAttributeGroup::Serialize() const
292 : {
293 443 : CPLJSONObject o;
294 886 : const auto attrs = m_poGroup->GetAttributes(nullptr);
295 685 : for (const auto &attr : attrs)
296 : {
297 242 : const auto &oType = attr->GetDataType();
298 242 : if (oType.GetClass() == GEDTC_STRING)
299 : {
300 332 : const auto anDims = attr->GetDimensionsSize();
301 166 : if (anDims.size() == 0)
302 : {
303 157 : const char *pszStr = attr->ReadAsString();
304 157 : if (pszStr)
305 : {
306 314 : CPLJSONDocument oDoc;
307 163 : if (oType.GetSubType() == GEDTST_JSON &&
308 163 : oDoc.LoadMemory(pszStr))
309 : {
310 6 : o.Add(attr->GetName(), oDoc.GetRoot());
311 : }
312 : else
313 : {
314 151 : o.Add(attr->GetName(), pszStr);
315 : }
316 : }
317 : else
318 : {
319 0 : o.AddNull(attr->GetName());
320 : }
321 : }
322 9 : else if (anDims.size() == 1)
323 : {
324 18 : const auto list = attr->ReadAsStringArray();
325 18 : CPLJSONArray arr;
326 30 : for (int i = 0; i < list.size(); ++i)
327 : {
328 21 : arr.Add(list[i]);
329 : }
330 9 : o.Add(attr->GetName(), arr);
331 : }
332 : else
333 : {
334 0 : CPLError(
335 : CE_Warning, CPLE_AppDefined,
336 : "Cannot serialize attribute %s of dimension count >= 2",
337 0 : attr->GetName().c_str());
338 : }
339 : }
340 76 : else if (oType.GetClass() == GEDTC_NUMERIC)
341 : {
342 152 : const auto anDims = attr->GetDimensionsSize();
343 76 : const auto eDT = oType.GetNumericDataType();
344 76 : if (anDims.size() == 0)
345 : {
346 45 : if (eDT == GDT_Int8 || eDT == GDT_Int16 || eDT == GDT_Int32 ||
347 : eDT == GDT_Int64)
348 : {
349 18 : const int64_t nVal = attr->ReadAsInt64();
350 18 : o.Add(attr->GetName(), static_cast<GInt64>(nVal));
351 : }
352 27 : else if (eDT == GDT_Byte || eDT == GDT_UInt16 ||
353 9 : eDT == GDT_UInt32 || eDT == GDT_UInt64)
354 : {
355 21 : const int64_t nVal = attr->ReadAsInt64();
356 21 : o.Add(attr->GetName(), static_cast<uint64_t>(nVal));
357 : }
358 : else
359 : {
360 6 : const double dfVal = attr->ReadAsDouble();
361 6 : o.Add(attr->GetName(), dfVal);
362 : }
363 : }
364 31 : else if (anDims.size() == 1)
365 : {
366 62 : CPLJSONArray arr;
367 31 : if (eDT == GDT_Int8 || eDT == GDT_Int16 || eDT == GDT_Int32 ||
368 : eDT == GDT_Int64)
369 : {
370 36 : const auto list = attr->ReadAsInt64Array();
371 54 : for (const auto nVal : list)
372 : {
373 36 : arr.Add(static_cast<GInt64>(nVal));
374 18 : }
375 : }
376 13 : else if (eDT == GDT_Byte || eDT == GDT_UInt16 ||
377 10 : eDT == GDT_UInt32 || eDT == GDT_UInt64)
378 : {
379 12 : const auto list = attr->ReadAsInt64Array();
380 18 : for (const auto nVal : list)
381 : {
382 12 : arr.Add(static_cast<uint64_t>(nVal));
383 6 : }
384 : }
385 : else
386 : {
387 14 : const auto list = attr->ReadAsDoubleArray();
388 21 : for (const auto dfVal : list)
389 : {
390 14 : arr.Add(dfVal);
391 : }
392 : }
393 31 : o.Add(attr->GetName(), arr);
394 : }
395 : else
396 : {
397 0 : CPLError(
398 : CE_Warning, CPLE_AppDefined,
399 : "Cannot serialize attribute %s of dimension count >= 2",
400 0 : attr->GetName().c_str());
401 : }
402 : }
403 : }
404 886 : return o;
405 : }
406 :
407 : /************************************************************************/
408 : /* ParentRenamed() */
409 : /************************************************************************/
410 :
411 25 : void ZarrAttributeGroup::ParentRenamed(const std::string &osNewParentFullName)
412 : {
413 25 : if (m_bContainerIsGroup)
414 10 : m_poGroup->SetFullName(osNewParentFullName + ATTRIBUTE_GROUP_SUFFIX);
415 : else
416 15 : m_poGroup->SetFullName(osNewParentFullName);
417 50 : const auto attrs = m_poGroup->GetAttributes(nullptr);
418 52 : for (auto &attr : attrs)
419 : {
420 27 : attr->ParentRenamed(m_poGroup->GetFullName());
421 : }
422 25 : }
423 :
424 : /************************************************************************/
425 : /* ParentDeleted() */
426 : /************************************************************************/
427 :
428 14 : void ZarrAttributeGroup::ParentDeleted()
429 : {
430 14 : m_poGroup->Deleted();
431 14 : }
|