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