/src/gdal/apps/gdalmdiminfo_lib.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: GDAL Utilities |
4 | | * Purpose: Command line application to list info about a multidimensional |
5 | | *raster Author: Even Rouault,<even.rouault at spatialys.com> |
6 | | * |
7 | | * **************************************************************************** |
8 | | * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com> |
9 | | * |
10 | | * SPDX-License-Identifier: MIT |
11 | | ****************************************************************************/ |
12 | | |
13 | | #include "cpl_port.h" |
14 | | #include "gdal_utils.h" |
15 | | #include "gdal_utils_priv.h" |
16 | | |
17 | | #include "cpl_enumerate.h" |
18 | | #include "cpl_json.h" |
19 | | #include "cpl_json_streaming_writer.h" |
20 | | #include "gdal_priv.h" |
21 | | #include "gdal_rat.h" |
22 | | #include "gdalargumentparser.h" |
23 | | |
24 | | #include <algorithm> |
25 | | #include <limits> |
26 | | #include <set> |
27 | | |
28 | | static void DumpArray(const std::shared_ptr<GDALGroup> &rootGroup, |
29 | | const std::shared_ptr<GDALMDArray> &array, |
30 | | CPLJSonStreamingWriter &serializer, |
31 | | const GDALMultiDimInfoOptions *psOptions, |
32 | | std::set<std::string> &alreadyDumpedDimensions, |
33 | | std::set<std::string> &alreadyDumpedArrays, |
34 | | bool bOutputObjType, bool bOutputName, |
35 | | bool bOutputOverviews); |
36 | | |
37 | | /************************************************************************/ |
38 | | /* GDALMultiDimInfoOptions */ |
39 | | /************************************************************************/ |
40 | | |
41 | | struct GDALMultiDimInfoOptions |
42 | | { |
43 | | bool bStdoutOutput = false; |
44 | | bool bSummary = false; |
45 | | bool bDetailed = false; |
46 | | bool bPretty = true; |
47 | | size_t nLimitValuesByDim = 0; |
48 | | CPLStringList aosArrayOptions{}; |
49 | | std::string osArrayName{}; |
50 | | bool bStats = false; |
51 | | std::string osFormat = "json"; |
52 | | }; |
53 | | |
54 | | /************************************************************************/ |
55 | | /* HasUniqueNames() */ |
56 | | /************************************************************************/ |
57 | | |
58 | | static bool HasUniqueNames(const std::vector<std::string> &oNames) |
59 | 0 | { |
60 | 0 | std::set<std::string> oSetNames; |
61 | 0 | for (const auto &subgroupName : oNames) |
62 | 0 | { |
63 | 0 | if (oSetNames.find(subgroupName) != oSetNames.end()) |
64 | 0 | { |
65 | 0 | return false; |
66 | 0 | } |
67 | 0 | oSetNames.insert(subgroupName); |
68 | 0 | } |
69 | 0 | return true; |
70 | 0 | } |
71 | | |
72 | | /************************************************************************/ |
73 | | /* DumpDataType() */ |
74 | | /************************************************************************/ |
75 | | |
76 | | static void DumpDataType(const GDALExtendedDataType &dt, |
77 | | CPLJSonStreamingWriter &serializer) |
78 | 0 | { |
79 | 0 | switch (dt.GetClass()) |
80 | 0 | { |
81 | 0 | case GEDTC_STRING: |
82 | 0 | serializer.Add("String"); |
83 | 0 | break; |
84 | | |
85 | 0 | case GEDTC_NUMERIC: |
86 | 0 | { |
87 | 0 | auto poRAT = dt.GetRAT(); |
88 | 0 | if (poRAT) |
89 | 0 | { |
90 | 0 | auto objContext(serializer.MakeObjectContext()); |
91 | 0 | serializer.AddObjKey("name"); |
92 | 0 | serializer.Add(dt.GetName()); |
93 | 0 | serializer.AddObjKey("type"); |
94 | 0 | serializer.Add(GDALGetDataTypeName(dt.GetNumericDataType())); |
95 | 0 | serializer.AddObjKey("attribute_table"); |
96 | 0 | auto arrayContext(serializer.MakeArrayContext()); |
97 | 0 | const int nRows = poRAT->GetRowCount(); |
98 | 0 | const int nCols = poRAT->GetColumnCount(); |
99 | 0 | for (int iRow = 0; iRow < nRows; ++iRow) |
100 | 0 | { |
101 | 0 | auto obj2Context(serializer.MakeObjectContext()); |
102 | 0 | for (int iCol = 0; iCol < nCols; ++iCol) |
103 | 0 | { |
104 | 0 | serializer.AddObjKey(poRAT->GetNameOfCol(iCol)); |
105 | 0 | switch (poRAT->GetTypeOfCol(iCol)) |
106 | 0 | { |
107 | 0 | case GFT_Integer: |
108 | 0 | serializer.Add( |
109 | 0 | poRAT->GetValueAsInt(iRow, iCol)); |
110 | 0 | break; |
111 | 0 | case GFT_Real: |
112 | 0 | serializer.Add( |
113 | 0 | poRAT->GetValueAsDouble(iRow, iCol)); |
114 | 0 | break; |
115 | 0 | case GFT_String: |
116 | 0 | serializer.Add( |
117 | 0 | poRAT->GetValueAsString(iRow, iCol)); |
118 | 0 | break; |
119 | 0 | case GFT_Boolean: |
120 | 0 | serializer.Add( |
121 | 0 | poRAT->GetValueAsBoolean(iRow, iCol)); |
122 | 0 | break; |
123 | 0 | case GFT_DateTime: |
124 | 0 | { |
125 | 0 | const auto sDateTime = |
126 | 0 | poRAT->GetValueAsDateTime(iRow, iCol); |
127 | 0 | serializer.Add( |
128 | 0 | GDALRasterAttributeTable::DateTimeToString( |
129 | 0 | sDateTime)); |
130 | 0 | break; |
131 | 0 | } |
132 | 0 | case GFT_WKBGeometry: |
133 | 0 | { |
134 | 0 | size_t nWKBSize = 0; |
135 | 0 | const GByte *pabyWKB = |
136 | 0 | poRAT->GetValueAsWKBGeometry(iRow, iCol, |
137 | 0 | nWKBSize); |
138 | 0 | std::string osWKT = |
139 | 0 | GDALRasterAttributeTable::WKBGeometryToWKT( |
140 | 0 | pabyWKB, nWKBSize); |
141 | 0 | if (osWKT.empty()) |
142 | 0 | serializer.AddNull(); |
143 | 0 | else |
144 | 0 | serializer.Add(osWKT); |
145 | 0 | break; |
146 | 0 | } |
147 | 0 | } |
148 | 0 | } |
149 | 0 | } |
150 | 0 | } |
151 | 0 | else |
152 | 0 | { |
153 | 0 | serializer.Add(GDALGetDataTypeName(dt.GetNumericDataType())); |
154 | 0 | } |
155 | 0 | break; |
156 | 0 | } |
157 | | |
158 | 0 | case GEDTC_COMPOUND: |
159 | 0 | { |
160 | 0 | auto compoundContext(serializer.MakeObjectContext()); |
161 | 0 | serializer.AddObjKey("name"); |
162 | 0 | serializer.Add(dt.GetName()); |
163 | 0 | serializer.AddObjKey("size"); |
164 | 0 | serializer.Add(static_cast<unsigned>(dt.GetSize())); |
165 | 0 | serializer.AddObjKey("components"); |
166 | 0 | const auto &components = dt.GetComponents(); |
167 | 0 | auto componentsContext(serializer.MakeArrayContext()); |
168 | 0 | for (const auto &comp : components) |
169 | 0 | { |
170 | 0 | auto compContext(serializer.MakeObjectContext()); |
171 | 0 | serializer.AddObjKey("name"); |
172 | 0 | serializer.Add(comp->GetName()); |
173 | 0 | serializer.AddObjKey("offset"); |
174 | 0 | serializer.Add(static_cast<unsigned>(comp->GetOffset())); |
175 | 0 | serializer.AddObjKey("type"); |
176 | 0 | DumpDataType(comp->GetType(), serializer); |
177 | 0 | } |
178 | 0 | break; |
179 | 0 | } |
180 | 0 | } |
181 | 0 | } |
182 | | |
183 | | /************************************************************************/ |
184 | | /* DumpValue() */ |
185 | | /************************************************************************/ |
186 | | |
187 | | template <typename T> |
188 | | static void DumpValue(CPLJSonStreamingWriter &serializer, const void *bytes) |
189 | 0 | { |
190 | 0 | T tmp; |
191 | 0 | memcpy(&tmp, bytes, sizeof(T)); |
192 | 0 | serializer.Add(tmp); |
193 | 0 | } Unexecuted instantiation: gdalmdiminfo_lib.cpp:void DumpValue<unsigned char>(CPLJSonStreamingWriter&, void const*) Unexecuted instantiation: gdalmdiminfo_lib.cpp:void DumpValue<signed char>(CPLJSonStreamingWriter&, void const*) Unexecuted instantiation: gdalmdiminfo_lib.cpp:void DumpValue<short>(CPLJSonStreamingWriter&, void const*) Unexecuted instantiation: gdalmdiminfo_lib.cpp:void DumpValue<unsigned short>(CPLJSonStreamingWriter&, void const*) Unexecuted instantiation: gdalmdiminfo_lib.cpp:void DumpValue<int>(CPLJSonStreamingWriter&, void const*) Unexecuted instantiation: gdalmdiminfo_lib.cpp:void DumpValue<unsigned int>(CPLJSonStreamingWriter&, void const*) Unexecuted instantiation: gdalmdiminfo_lib.cpp:void DumpValue<long>(CPLJSonStreamingWriter&, void const*) Unexecuted instantiation: gdalmdiminfo_lib.cpp:void DumpValue<unsigned long>(CPLJSonStreamingWriter&, void const*) Unexecuted instantiation: gdalmdiminfo_lib.cpp:void DumpValue<cpl::Float16>(CPLJSonStreamingWriter&, void const*) Unexecuted instantiation: gdalmdiminfo_lib.cpp:void DumpValue<float>(CPLJSonStreamingWriter&, void const*) Unexecuted instantiation: gdalmdiminfo_lib.cpp:void DumpValue<double>(CPLJSonStreamingWriter&, void const*) |
194 | | |
195 | | /************************************************************************/ |
196 | | /* DumpComplexValue() */ |
197 | | /************************************************************************/ |
198 | | |
199 | | template <typename T> |
200 | | static void DumpComplexValue(CPLJSonStreamingWriter &serializer, |
201 | | const GByte *bytes) |
202 | 0 | { |
203 | 0 | auto objectContext(serializer.MakeObjectContext()); |
204 | 0 | serializer.AddObjKey("real"); |
205 | 0 | DumpValue<T>(serializer, bytes); |
206 | 0 | serializer.AddObjKey("imag"); |
207 | 0 | DumpValue<T>(serializer, bytes + sizeof(T)); |
208 | 0 | } Unexecuted instantiation: gdalmdiminfo_lib.cpp:void DumpComplexValue<short>(CPLJSonStreamingWriter&, unsigned char const*) Unexecuted instantiation: gdalmdiminfo_lib.cpp:void DumpComplexValue<int>(CPLJSonStreamingWriter&, unsigned char const*) Unexecuted instantiation: gdalmdiminfo_lib.cpp:void DumpComplexValue<cpl::Float16>(CPLJSonStreamingWriter&, unsigned char const*) Unexecuted instantiation: gdalmdiminfo_lib.cpp:void DumpComplexValue<float>(CPLJSonStreamingWriter&, unsigned char const*) Unexecuted instantiation: gdalmdiminfo_lib.cpp:void DumpComplexValue<double>(CPLJSonStreamingWriter&, unsigned char const*) |
209 | | |
210 | | /************************************************************************/ |
211 | | /* DumpValue() */ |
212 | | /************************************************************************/ |
213 | | |
214 | | static void DumpValue(CPLJSonStreamingWriter &serializer, const GByte *bytes, |
215 | | const GDALDataType &eDT) |
216 | 0 | { |
217 | 0 | switch (eDT) |
218 | 0 | { |
219 | 0 | case GDT_UInt8: |
220 | 0 | DumpValue<GByte>(serializer, bytes); |
221 | 0 | break; |
222 | 0 | case GDT_Int8: |
223 | 0 | DumpValue<GInt8>(serializer, bytes); |
224 | 0 | break; |
225 | 0 | case GDT_Int16: |
226 | 0 | DumpValue<GInt16>(serializer, bytes); |
227 | 0 | break; |
228 | 0 | case GDT_UInt16: |
229 | 0 | DumpValue<GUInt16>(serializer, bytes); |
230 | 0 | break; |
231 | 0 | case GDT_Int32: |
232 | 0 | DumpValue<GInt32>(serializer, bytes); |
233 | 0 | break; |
234 | 0 | case GDT_UInt32: |
235 | 0 | DumpValue<GUInt32>(serializer, bytes); |
236 | 0 | break; |
237 | 0 | case GDT_Int64: |
238 | 0 | DumpValue<std::int64_t>(serializer, bytes); |
239 | 0 | break; |
240 | 0 | case GDT_UInt64: |
241 | 0 | DumpValue<std::uint64_t>(serializer, bytes); |
242 | 0 | break; |
243 | 0 | case GDT_Float16: |
244 | 0 | DumpValue<GFloat16>(serializer, bytes); |
245 | 0 | break; |
246 | 0 | case GDT_Float32: |
247 | 0 | DumpValue<float>(serializer, bytes); |
248 | 0 | break; |
249 | 0 | case GDT_Float64: |
250 | 0 | DumpValue<double>(serializer, bytes); |
251 | 0 | break; |
252 | 0 | case GDT_CInt16: |
253 | 0 | DumpComplexValue<GInt16>(serializer, bytes); |
254 | 0 | break; |
255 | 0 | case GDT_CInt32: |
256 | 0 | DumpComplexValue<GInt32>(serializer, bytes); |
257 | 0 | break; |
258 | 0 | case GDT_CFloat16: |
259 | 0 | DumpComplexValue<GFloat16>(serializer, bytes); |
260 | 0 | break; |
261 | 0 | case GDT_CFloat32: |
262 | 0 | DumpComplexValue<float>(serializer, bytes); |
263 | 0 | break; |
264 | 0 | case GDT_CFloat64: |
265 | 0 | DumpComplexValue<double>(serializer, bytes); |
266 | 0 | break; |
267 | 0 | case GDT_Unknown: |
268 | 0 | case GDT_TypeCount: |
269 | 0 | CPLAssert(false); |
270 | 0 | break; |
271 | 0 | } |
272 | 0 | } |
273 | | |
274 | | static void DumpValue(CPLJSonStreamingWriter &serializer, const GByte *values, |
275 | | const GDALExtendedDataType &dt); |
276 | | |
277 | | /************************************************************************/ |
278 | | /* DumpCompound() */ |
279 | | /************************************************************************/ |
280 | | |
281 | | static void DumpCompound(CPLJSonStreamingWriter &serializer, |
282 | | const GByte *values, const GDALExtendedDataType &dt) |
283 | 0 | { |
284 | 0 | CPLAssert(dt.GetClass() == GEDTC_COMPOUND); |
285 | 0 | const auto &components = dt.GetComponents(); |
286 | 0 | auto objectContext(serializer.MakeObjectContext()); |
287 | 0 | for (const auto &comp : components) |
288 | 0 | { |
289 | 0 | serializer.AddObjKey(comp->GetName()); |
290 | 0 | DumpValue(serializer, values + comp->GetOffset(), comp->GetType()); |
291 | 0 | } |
292 | 0 | } |
293 | | |
294 | | /************************************************************************/ |
295 | | /* DumpValue() */ |
296 | | /************************************************************************/ |
297 | | |
298 | | static void DumpValue(CPLJSonStreamingWriter &serializer, const GByte *values, |
299 | | const GDALExtendedDataType &dt) |
300 | 0 | { |
301 | 0 | switch (dt.GetClass()) |
302 | 0 | { |
303 | 0 | case GEDTC_NUMERIC: |
304 | 0 | DumpValue(serializer, values, dt.GetNumericDataType()); |
305 | 0 | break; |
306 | 0 | case GEDTC_COMPOUND: |
307 | 0 | DumpCompound(serializer, values, dt); |
308 | 0 | break; |
309 | 0 | case GEDTC_STRING: |
310 | 0 | { |
311 | 0 | const char *pszStr; |
312 | | // cppcheck-suppress pointerSize |
313 | 0 | memcpy(&pszStr, values, sizeof(const char *)); |
314 | 0 | if (pszStr) |
315 | 0 | serializer.Add(pszStr); |
316 | 0 | else |
317 | 0 | serializer.AddNull(); |
318 | 0 | break; |
319 | 0 | } |
320 | 0 | } |
321 | 0 | } |
322 | | |
323 | | /************************************************************************/ |
324 | | /* SerializeJSON() */ |
325 | | /************************************************************************/ |
326 | | |
327 | | static void SerializeJSON(const CPLJSONObject &obj, |
328 | | CPLJSonStreamingWriter &serializer) |
329 | 0 | { |
330 | 0 | switch (obj.GetType()) |
331 | 0 | { |
332 | 0 | case CPLJSONObject::Type::Unknown: |
333 | 0 | { |
334 | 0 | CPLAssert(false); |
335 | 0 | break; |
336 | 0 | } |
337 | | |
338 | 0 | case CPLJSONObject::Type::Null: |
339 | 0 | { |
340 | 0 | serializer.AddNull(); |
341 | 0 | break; |
342 | 0 | } |
343 | | |
344 | 0 | case CPLJSONObject::Type::Object: |
345 | 0 | { |
346 | 0 | auto objectContext(serializer.MakeObjectContext()); |
347 | 0 | for (const auto &subobj : obj.GetChildren()) |
348 | 0 | { |
349 | 0 | serializer.AddObjKey(subobj.GetName()); |
350 | 0 | SerializeJSON(subobj, serializer); |
351 | 0 | } |
352 | 0 | break; |
353 | 0 | } |
354 | | |
355 | 0 | case CPLJSONObject::Type::Array: |
356 | 0 | { |
357 | 0 | auto arrayContext(serializer.MakeArrayContext()); |
358 | 0 | const CPLJSONArray array = obj.ToArray(); |
359 | 0 | for (const auto &subobj : array) |
360 | 0 | { |
361 | 0 | SerializeJSON(subobj, serializer); |
362 | 0 | } |
363 | 0 | break; |
364 | 0 | } |
365 | | |
366 | 0 | case CPLJSONObject::Type::Boolean: |
367 | 0 | { |
368 | 0 | serializer.Add(obj.ToBool()); |
369 | 0 | break; |
370 | 0 | } |
371 | | |
372 | 0 | case CPLJSONObject::Type::String: |
373 | 0 | { |
374 | 0 | serializer.Add(obj.ToString()); |
375 | 0 | break; |
376 | 0 | } |
377 | | |
378 | 0 | case CPLJSONObject::Type::Integer: |
379 | 0 | { |
380 | 0 | serializer.Add(obj.ToInteger()); |
381 | 0 | break; |
382 | 0 | } |
383 | | |
384 | 0 | case CPLJSONObject::Type::Long: |
385 | 0 | { |
386 | 0 | serializer.Add(static_cast<int64_t>(obj.ToLong())); |
387 | 0 | break; |
388 | 0 | } |
389 | | |
390 | 0 | case CPLJSONObject::Type::Double: |
391 | 0 | { |
392 | 0 | serializer.Add(obj.ToDouble()); |
393 | 0 | break; |
394 | 0 | } |
395 | 0 | } |
396 | 0 | } |
397 | | |
398 | | /************************************************************************/ |
399 | | /* DumpAttrValue() */ |
400 | | /************************************************************************/ |
401 | | |
402 | | static void DumpAttrValue(const std::shared_ptr<GDALAttribute> &attr, |
403 | | CPLJSonStreamingWriter &serializer) |
404 | 0 | { |
405 | 0 | const auto &dt = attr->GetDataType(); |
406 | 0 | const size_t nEltCount(static_cast<size_t>(attr->GetTotalElementsCount())); |
407 | 0 | switch (dt.GetClass()) |
408 | 0 | { |
409 | 0 | case GEDTC_STRING: |
410 | 0 | { |
411 | 0 | if (nEltCount == 1) |
412 | 0 | { |
413 | 0 | const char *pszStr = attr->ReadAsString(); |
414 | 0 | if (pszStr) |
415 | 0 | { |
416 | 0 | if (dt.GetSubType() == GEDTST_JSON) |
417 | 0 | { |
418 | 0 | CPLJSONDocument oDoc; |
419 | 0 | if (oDoc.LoadMemory(std::string(pszStr))) |
420 | 0 | { |
421 | 0 | SerializeJSON(oDoc.GetRoot(), serializer); |
422 | 0 | } |
423 | 0 | else |
424 | 0 | { |
425 | 0 | serializer.Add(pszStr); |
426 | 0 | } |
427 | 0 | } |
428 | 0 | else |
429 | 0 | { |
430 | 0 | serializer.Add(pszStr); |
431 | 0 | } |
432 | 0 | } |
433 | 0 | else |
434 | 0 | { |
435 | 0 | serializer.AddNull(); |
436 | 0 | } |
437 | 0 | } |
438 | 0 | else |
439 | 0 | { |
440 | 0 | CPLStringList aosValues(attr->ReadAsStringArray()); |
441 | 0 | { |
442 | 0 | auto arrayContextValues( |
443 | 0 | serializer.MakeArrayContext(nEltCount < 10)); |
444 | 0 | for (int i = 0; i < aosValues.size(); ++i) |
445 | 0 | { |
446 | 0 | serializer.Add(aosValues[i]); |
447 | 0 | } |
448 | 0 | } |
449 | 0 | } |
450 | 0 | break; |
451 | 0 | } |
452 | | |
453 | 0 | case GEDTC_NUMERIC: |
454 | 0 | { |
455 | 0 | auto eDT = dt.GetNumericDataType(); |
456 | 0 | const auto rawValues(attr->ReadAsRaw()); |
457 | 0 | const GByte *bytePtr = rawValues.data(); |
458 | 0 | if (bytePtr) |
459 | 0 | { |
460 | 0 | const int nDTSize = GDALGetDataTypeSizeBytes(eDT); |
461 | 0 | if (nEltCount == 1) |
462 | 0 | { |
463 | 0 | serializer.SetNewline(false); |
464 | 0 | DumpValue(serializer, rawValues.data(), eDT); |
465 | 0 | serializer.SetNewline(true); |
466 | 0 | } |
467 | 0 | else |
468 | 0 | { |
469 | 0 | auto arrayContextValues( |
470 | 0 | serializer.MakeArrayContext(nEltCount < 10)); |
471 | 0 | for (size_t i = 0; i < nEltCount; i++) |
472 | 0 | { |
473 | 0 | DumpValue(serializer, bytePtr, eDT); |
474 | 0 | bytePtr += nDTSize; |
475 | 0 | } |
476 | 0 | } |
477 | 0 | } |
478 | 0 | else |
479 | 0 | { |
480 | 0 | serializer.AddNull(); |
481 | 0 | } |
482 | 0 | break; |
483 | 0 | } |
484 | | |
485 | 0 | case GEDTC_COMPOUND: |
486 | 0 | { |
487 | 0 | auto rawValues(attr->ReadAsRaw()); |
488 | 0 | const GByte *bytePtr = rawValues.data(); |
489 | 0 | if (bytePtr) |
490 | 0 | { |
491 | 0 | if (nEltCount == 1) |
492 | 0 | { |
493 | 0 | serializer.SetNewline(false); |
494 | 0 | DumpCompound(serializer, bytePtr, dt); |
495 | 0 | serializer.SetNewline(true); |
496 | 0 | } |
497 | 0 | else |
498 | 0 | { |
499 | 0 | auto arrayContextValues(serializer.MakeArrayContext()); |
500 | 0 | for (size_t i = 0; i < nEltCount; i++) |
501 | 0 | { |
502 | 0 | DumpCompound(serializer, bytePtr, dt); |
503 | 0 | bytePtr += dt.GetSize(); |
504 | 0 | } |
505 | 0 | } |
506 | 0 | } |
507 | 0 | else |
508 | 0 | { |
509 | 0 | serializer.AddNull(); |
510 | 0 | } |
511 | 0 | break; |
512 | 0 | } |
513 | 0 | } |
514 | 0 | } |
515 | | |
516 | | /************************************************************************/ |
517 | | /* DumpAttr() */ |
518 | | /************************************************************************/ |
519 | | |
520 | | static void DumpAttr(std::shared_ptr<GDALAttribute> attr, |
521 | | CPLJSonStreamingWriter &serializer, |
522 | | const GDALMultiDimInfoOptions *psOptions, |
523 | | bool bOutputObjType, bool bOutputName) |
524 | 0 | { |
525 | 0 | if (!bOutputObjType && !bOutputName && !psOptions->bDetailed) |
526 | 0 | { |
527 | 0 | DumpAttrValue(attr, serializer); |
528 | 0 | return; |
529 | 0 | } |
530 | | |
531 | 0 | const auto &dt = attr->GetDataType(); |
532 | 0 | auto objectContext(serializer.MakeObjectContext()); |
533 | 0 | if (bOutputObjType) |
534 | 0 | { |
535 | 0 | serializer.AddObjKey("type"); |
536 | 0 | serializer.Add("attribute"); |
537 | 0 | } |
538 | 0 | if (bOutputName) |
539 | 0 | { |
540 | 0 | serializer.AddObjKey("name"); |
541 | 0 | serializer.Add(attr->GetName()); |
542 | 0 | } |
543 | |
|
544 | 0 | if (psOptions->bDetailed) |
545 | 0 | { |
546 | 0 | serializer.AddObjKey("datatype"); |
547 | 0 | DumpDataType(dt, serializer); |
548 | |
|
549 | 0 | switch (dt.GetSubType()) |
550 | 0 | { |
551 | 0 | case GEDTST_NONE: |
552 | 0 | break; |
553 | 0 | case GEDTST_JSON: |
554 | 0 | { |
555 | 0 | serializer.AddObjKey("subtype"); |
556 | 0 | serializer.Add("JSON"); |
557 | 0 | break; |
558 | 0 | } |
559 | 0 | } |
560 | | |
561 | 0 | serializer.AddObjKey("value"); |
562 | 0 | } |
563 | | |
564 | 0 | DumpAttrValue(attr, serializer); |
565 | 0 | } |
566 | | |
567 | | /************************************************************************/ |
568 | | /* DumpAttrs() */ |
569 | | /************************************************************************/ |
570 | | |
571 | | static void DumpAttrs(const std::vector<std::shared_ptr<GDALAttribute>> &attrs, |
572 | | CPLJSonStreamingWriter &serializer, |
573 | | const GDALMultiDimInfoOptions *psOptions) |
574 | 0 | { |
575 | 0 | std::vector<std::string> attributeNames; |
576 | 0 | for (const auto &poAttr : attrs) |
577 | 0 | attributeNames.emplace_back(poAttr->GetName()); |
578 | 0 | if (HasUniqueNames(attributeNames)) |
579 | 0 | { |
580 | 0 | auto objectContext(serializer.MakeObjectContext()); |
581 | 0 | for (const auto &poAttr : attrs) |
582 | 0 | { |
583 | 0 | serializer.AddObjKey(poAttr->GetName()); |
584 | 0 | DumpAttr(poAttr, serializer, psOptions, false, false); |
585 | 0 | } |
586 | 0 | } |
587 | 0 | else |
588 | 0 | { |
589 | 0 | auto arrayContext(serializer.MakeArrayContext()); |
590 | 0 | for (const auto &poAttr : attrs) |
591 | 0 | { |
592 | 0 | DumpAttr(poAttr, serializer, psOptions, false, true); |
593 | 0 | } |
594 | 0 | } |
595 | 0 | } |
596 | | |
597 | | /************************************************************************/ |
598 | | /* DumpArrayRec() */ |
599 | | /************************************************************************/ |
600 | | |
601 | | static void DumpArrayRec(std::shared_ptr<GDALMDArray> array, |
602 | | CPLJSonStreamingWriter &serializer, size_t nCurDim, |
603 | | const std::vector<GUInt64> &dimSizes, |
604 | | std::vector<GUInt64> &startIdx, |
605 | | const GDALMultiDimInfoOptions *psOptions) |
606 | 0 | { |
607 | 0 | do |
608 | 0 | { |
609 | 0 | auto arrayContext(serializer.MakeArrayContext()); |
610 | 0 | if (nCurDim + 1 == dimSizes.size()) |
611 | 0 | { |
612 | 0 | const auto &dt(array->GetDataType()); |
613 | 0 | const auto nDTSize(dt.GetSize()); |
614 | 0 | const auto lambdaDumpValue = |
615 | 0 | [&serializer, &dt, nDTSize](std::vector<GByte> &abyTmp, |
616 | 0 | size_t nCount) |
617 | 0 | { |
618 | 0 | GByte *pabyPtr = &abyTmp[0]; |
619 | 0 | for (size_t i = 0; i < nCount; ++i) |
620 | 0 | { |
621 | 0 | DumpValue(serializer, pabyPtr, dt); |
622 | 0 | dt.FreeDynamicMemory(pabyPtr); |
623 | 0 | pabyPtr += nDTSize; |
624 | 0 | } |
625 | 0 | }; |
626 | |
|
627 | 0 | serializer.SetNewline(false); |
628 | 0 | std::vector<size_t> count(dimSizes.size(), 1); |
629 | 0 | if (psOptions->nLimitValuesByDim == 0 || |
630 | 0 | dimSizes.back() <= psOptions->nLimitValuesByDim) |
631 | 0 | { |
632 | 0 | const size_t nCount = static_cast<size_t>(dimSizes.back()); |
633 | 0 | if (nCount > 0) |
634 | 0 | { |
635 | 0 | if (nCount != dimSizes.back() || |
636 | 0 | nDTSize > std::numeric_limits<size_t>::max() / nCount) |
637 | 0 | { |
638 | 0 | serializer.Add("[too many values]"); |
639 | 0 | break; |
640 | 0 | } |
641 | 0 | std::vector<GByte> abyTmp(nDTSize * nCount); |
642 | 0 | count.back() = nCount; |
643 | 0 | if (!array->Read(startIdx.data(), count.data(), nullptr, |
644 | 0 | nullptr, dt, &abyTmp[0])) |
645 | 0 | break; |
646 | 0 | lambdaDumpValue(abyTmp, count.back()); |
647 | 0 | } |
648 | 0 | } |
649 | 0 | else |
650 | 0 | { |
651 | 0 | std::vector<GByte> abyTmp( |
652 | 0 | nDTSize * (psOptions->nLimitValuesByDim + 1) / 2); |
653 | 0 | startIdx.back() = 0; |
654 | 0 | size_t nStartCount = (psOptions->nLimitValuesByDim + 1) / 2; |
655 | 0 | count.back() = nStartCount; |
656 | 0 | if (!array->Read(startIdx.data(), count.data(), nullptr, |
657 | 0 | nullptr, dt, &abyTmp[0])) |
658 | 0 | break; |
659 | 0 | lambdaDumpValue(abyTmp, count.back()); |
660 | 0 | serializer.Add("[...]"); |
661 | |
|
662 | 0 | count.back() = psOptions->nLimitValuesByDim / 2; |
663 | 0 | if (count.back()) |
664 | 0 | { |
665 | 0 | startIdx.back() = dimSizes.back() - count.back(); |
666 | 0 | if (!array->Read(startIdx.data(), count.data(), nullptr, |
667 | 0 | nullptr, dt, &abyTmp[0])) |
668 | 0 | break; |
669 | 0 | lambdaDumpValue(abyTmp, count.back()); |
670 | 0 | } |
671 | 0 | } |
672 | 0 | } |
673 | 0 | else |
674 | 0 | { |
675 | 0 | if (psOptions->nLimitValuesByDim == 0 || |
676 | 0 | dimSizes[nCurDim] <= psOptions->nLimitValuesByDim) |
677 | 0 | { |
678 | 0 | for (startIdx[nCurDim] = 0; |
679 | 0 | startIdx[nCurDim] < dimSizes[nCurDim]; ++startIdx[nCurDim]) |
680 | 0 | { |
681 | 0 | DumpArrayRec(array, serializer, nCurDim + 1, dimSizes, |
682 | 0 | startIdx, psOptions); |
683 | 0 | } |
684 | 0 | } |
685 | 0 | else |
686 | 0 | { |
687 | 0 | size_t nStartCount = (psOptions->nLimitValuesByDim + 1) / 2; |
688 | 0 | for (startIdx[nCurDim] = 0; startIdx[nCurDim] < nStartCount; |
689 | 0 | ++startIdx[nCurDim]) |
690 | 0 | { |
691 | 0 | DumpArrayRec(array, serializer, nCurDim + 1, dimSizes, |
692 | 0 | startIdx, psOptions); |
693 | 0 | } |
694 | 0 | serializer.Add("[...]"); |
695 | 0 | size_t nEndCount = psOptions->nLimitValuesByDim / 2; |
696 | 0 | for (startIdx[nCurDim] = dimSizes[nCurDim] - nEndCount; |
697 | 0 | startIdx[nCurDim] < dimSizes[nCurDim]; ++startIdx[nCurDim]) |
698 | 0 | { |
699 | 0 | DumpArrayRec(array, serializer, nCurDim + 1, dimSizes, |
700 | 0 | startIdx, psOptions); |
701 | 0 | } |
702 | 0 | } |
703 | 0 | } |
704 | 0 | } while (false); |
705 | 0 | serializer.SetNewline(true); |
706 | 0 | } |
707 | | |
708 | | /************************************************************************/ |
709 | | /* DumpDimensions() */ |
710 | | /************************************************************************/ |
711 | | |
712 | | static void |
713 | | DumpDimensions(const std::shared_ptr<GDALGroup> &rootGroup, |
714 | | const std::vector<std::shared_ptr<GDALDimension>> &dims, |
715 | | CPLJSonStreamingWriter &serializer, |
716 | | const GDALMultiDimInfoOptions *psOptions, |
717 | | std::set<std::string> &alreadyDumpedDimensions) |
718 | 0 | { |
719 | 0 | auto arrayContext(serializer.MakeArrayContext()); |
720 | 0 | for (const auto &dim : dims) |
721 | 0 | { |
722 | 0 | std::string osFullname(dim->GetFullName()); |
723 | 0 | if (alreadyDumpedDimensions.find(osFullname) != |
724 | 0 | alreadyDumpedDimensions.end()) |
725 | 0 | { |
726 | 0 | serializer.Add(osFullname); |
727 | 0 | continue; |
728 | 0 | } |
729 | | |
730 | 0 | auto dimObjectContext(serializer.MakeObjectContext()); |
731 | 0 | if (!osFullname.empty() && osFullname[0] == '/') |
732 | 0 | alreadyDumpedDimensions.insert(osFullname); |
733 | |
|
734 | 0 | serializer.AddObjKey("name"); |
735 | 0 | serializer.Add(dim->GetName()); |
736 | |
|
737 | 0 | serializer.AddObjKey("full_name"); |
738 | 0 | serializer.Add(osFullname); |
739 | |
|
740 | 0 | serializer.AddObjKey("size"); |
741 | 0 | serializer.Add(static_cast<std::uint64_t>(dim->GetSize())); |
742 | |
|
743 | 0 | const auto &type(dim->GetType()); |
744 | 0 | if (!type.empty()) |
745 | 0 | { |
746 | 0 | serializer.AddObjKey("type"); |
747 | 0 | serializer.Add(type); |
748 | 0 | } |
749 | |
|
750 | 0 | const auto &direction(dim->GetDirection()); |
751 | 0 | if (!direction.empty()) |
752 | 0 | { |
753 | 0 | serializer.AddObjKey("direction"); |
754 | 0 | serializer.Add(direction); |
755 | 0 | } |
756 | |
|
757 | 0 | auto poIndexingVariable(dim->GetIndexingVariable()); |
758 | 0 | if (poIndexingVariable) |
759 | 0 | { |
760 | 0 | serializer.AddObjKey("indexing_variable"); |
761 | 0 | bool isKnownFromRoot; |
762 | 0 | { |
763 | | // For autotest/gdrivers/tiledb_multidim.py::test_tiledb_multidim_array_read_dim_label_and_spatial_ref |
764 | 0 | CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler); |
765 | 0 | isKnownFromRoot = |
766 | 0 | rootGroup->OpenMDArrayFromFullname( |
767 | 0 | poIndexingVariable->GetFullName()) != nullptr; |
768 | 0 | } |
769 | 0 | if (isKnownFromRoot) |
770 | 0 | { |
771 | 0 | serializer.Add(poIndexingVariable->GetFullName()); |
772 | 0 | } |
773 | 0 | else |
774 | 0 | { |
775 | 0 | std::set<std::string> alreadyDumpedDimensionsLocal( |
776 | 0 | alreadyDumpedDimensions); |
777 | 0 | alreadyDumpedDimensionsLocal.insert(std::move(osFullname)); |
778 | 0 | std::set<std::string> alreadyDumpedArrays; |
779 | |
|
780 | 0 | auto indexingVariableContext(serializer.MakeObjectContext()); |
781 | 0 | serializer.AddObjKey(poIndexingVariable->GetName()); |
782 | 0 | DumpArray(rootGroup, poIndexingVariable, serializer, psOptions, |
783 | 0 | alreadyDumpedDimensionsLocal, alreadyDumpedArrays, |
784 | 0 | /* bOutputObjType = */ false, |
785 | 0 | /* bOutputName = */ false, |
786 | 0 | /* bOutputOverviews = */ false); |
787 | 0 | } |
788 | 0 | } |
789 | 0 | } |
790 | 0 | } |
791 | | |
792 | | /************************************************************************/ |
793 | | /* DumpStructuralInfo() */ |
794 | | /************************************************************************/ |
795 | | |
796 | | static void DumpStructuralInfo(CSLConstList papszStructuralInfo, |
797 | | CPLJSonStreamingWriter &serializer) |
798 | 0 | { |
799 | 0 | auto objectContext(serializer.MakeObjectContext()); |
800 | 0 | int i = 1; |
801 | 0 | for (const auto &[pszKey, pszValue] : cpl::IterateNameValue( |
802 | 0 | papszStructuralInfo, /* bReturnNullKeyIfNotNameValue = */ true)) |
803 | 0 | { |
804 | 0 | if (pszKey) |
805 | 0 | { |
806 | 0 | serializer.AddObjKey(pszKey); |
807 | 0 | } |
808 | 0 | else |
809 | 0 | { |
810 | 0 | serializer.AddObjKey(CPLSPrintf("metadata_%d", i)); |
811 | 0 | ++i; |
812 | 0 | } |
813 | 0 | serializer.Add(pszValue); |
814 | 0 | } |
815 | 0 | } |
816 | | |
817 | | /************************************************************************/ |
818 | | /* DumpArray() */ |
819 | | /************************************************************************/ |
820 | | |
821 | | static void DumpArray(const std::shared_ptr<GDALGroup> &rootGroup, |
822 | | const std::shared_ptr<GDALMDArray> &array, |
823 | | CPLJSonStreamingWriter &serializer, |
824 | | const GDALMultiDimInfoOptions *psOptions, |
825 | | std::set<std::string> &alreadyDumpedDimensions, |
826 | | std::set<std::string> &alreadyDumpedArrays, |
827 | | bool bOutputObjType, bool bOutputName, |
828 | | bool bOutputOverviews) |
829 | 0 | { |
830 | | // Protection against infinite recursion |
831 | 0 | if (cpl::contains(alreadyDumpedArrays, array->GetFullName())) |
832 | 0 | { |
833 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "%s already visited", |
834 | 0 | array->GetFullName().c_str()); |
835 | 0 | return; |
836 | 0 | } |
837 | 0 | alreadyDumpedArrays.insert(array->GetFullName()); |
838 | |
|
839 | 0 | auto objectContext(serializer.MakeObjectContext()); |
840 | 0 | if (bOutputObjType) |
841 | 0 | { |
842 | 0 | serializer.AddObjKey("type"); |
843 | 0 | serializer.Add("array"); |
844 | 0 | } |
845 | 0 | if (bOutputName) |
846 | 0 | { |
847 | 0 | serializer.AddObjKey("name"); |
848 | 0 | serializer.Add(array->GetName()); |
849 | 0 | } |
850 | 0 | else |
851 | 0 | { |
852 | 0 | serializer.AddObjKey("full_name"); |
853 | 0 | serializer.Add(array->GetFullName()); |
854 | 0 | } |
855 | |
|
856 | 0 | if (psOptions->bSummary) |
857 | 0 | return; |
858 | | |
859 | 0 | serializer.AddObjKey("datatype"); |
860 | 0 | const auto &dt(array->GetDataType()); |
861 | 0 | DumpDataType(dt, serializer); |
862 | |
|
863 | 0 | auto dims = array->GetDimensions(); |
864 | 0 | if (!dims.empty()) |
865 | 0 | { |
866 | 0 | serializer.AddObjKey("dimensions"); |
867 | 0 | DumpDimensions(rootGroup, dims, serializer, psOptions, |
868 | 0 | alreadyDumpedDimensions); |
869 | |
|
870 | 0 | serializer.AddObjKey("dimension_size"); |
871 | 0 | auto arrayContext(serializer.MakeArrayContext()); |
872 | 0 | for (const auto &poDim : dims) |
873 | 0 | { |
874 | 0 | serializer.Add(static_cast<uint64_t>(poDim->GetSize())); |
875 | 0 | } |
876 | 0 | } |
877 | |
|
878 | 0 | bool hasNonNullBlockSize = false; |
879 | 0 | const auto blockSize = array->GetBlockSize(); |
880 | 0 | for (auto v : blockSize) |
881 | 0 | { |
882 | 0 | if (v != 0) |
883 | 0 | { |
884 | 0 | hasNonNullBlockSize = true; |
885 | 0 | break; |
886 | 0 | } |
887 | 0 | } |
888 | 0 | if (hasNonNullBlockSize) |
889 | 0 | { |
890 | 0 | serializer.AddObjKey("block_size"); |
891 | 0 | auto arrayContext(serializer.MakeArrayContext()); |
892 | 0 | for (auto v : blockSize) |
893 | 0 | { |
894 | 0 | serializer.Add(static_cast<uint64_t>(v)); |
895 | 0 | } |
896 | 0 | } |
897 | |
|
898 | 0 | CPLStringList aosOptions; |
899 | 0 | if (psOptions->bDetailed) |
900 | 0 | aosOptions.SetNameValue("SHOW_ALL", "YES"); |
901 | 0 | auto attrs = array->GetAttributes(aosOptions.List()); |
902 | 0 | if (!attrs.empty()) |
903 | 0 | { |
904 | 0 | serializer.AddObjKey("attributes"); |
905 | 0 | DumpAttrs(attrs, serializer, psOptions); |
906 | 0 | } |
907 | |
|
908 | 0 | const auto &unit = array->GetUnit(); |
909 | 0 | if (!unit.empty()) |
910 | 0 | { |
911 | 0 | serializer.AddObjKey("unit"); |
912 | 0 | serializer.Add(unit); |
913 | 0 | } |
914 | |
|
915 | 0 | auto nodata = array->GetRawNoDataValue(); |
916 | 0 | if (nodata) |
917 | 0 | { |
918 | 0 | serializer.AddObjKey("nodata_value"); |
919 | 0 | DumpValue(serializer, static_cast<const GByte *>(nodata), dt); |
920 | 0 | } |
921 | |
|
922 | 0 | bool bValid = false; |
923 | 0 | double dfOffset = array->GetOffset(&bValid); |
924 | 0 | if (bValid) |
925 | 0 | { |
926 | 0 | serializer.AddObjKey("offset"); |
927 | 0 | serializer.Add(dfOffset); |
928 | 0 | } |
929 | 0 | double dfScale = array->GetScale(&bValid); |
930 | 0 | if (bValid) |
931 | 0 | { |
932 | 0 | serializer.AddObjKey("scale"); |
933 | 0 | serializer.Add(dfScale); |
934 | 0 | } |
935 | |
|
936 | 0 | auto srs = array->GetSpatialRef(); |
937 | 0 | if (srs) |
938 | 0 | { |
939 | 0 | char *pszWKT = nullptr; |
940 | 0 | CPLStringList wktOptions; |
941 | 0 | wktOptions.SetNameValue("FORMAT", "WKT2_2018"); |
942 | 0 | if (srs->exportToWkt(&pszWKT, wktOptions.List()) == OGRERR_NONE) |
943 | 0 | { |
944 | 0 | serializer.AddObjKey("srs"); |
945 | 0 | { |
946 | 0 | auto srsContext(serializer.MakeObjectContext()); |
947 | 0 | serializer.AddObjKey("wkt"); |
948 | 0 | serializer.Add(pszWKT); |
949 | 0 | serializer.AddObjKey("data_axis_to_srs_axis_mapping"); |
950 | 0 | { |
951 | 0 | auto dataAxisContext(serializer.MakeArrayContext(true)); |
952 | 0 | auto mapping = srs->GetDataAxisToSRSAxisMapping(); |
953 | 0 | for (const auto &axisNumber : mapping) |
954 | 0 | serializer.Add(axisNumber); |
955 | 0 | } |
956 | 0 | } |
957 | 0 | } |
958 | 0 | CPLFree(pszWKT); |
959 | 0 | } |
960 | |
|
961 | 0 | auto papszStructuralInfo = array->GetStructuralInfo(); |
962 | 0 | if (papszStructuralInfo) |
963 | 0 | { |
964 | 0 | serializer.AddObjKey("structural_info"); |
965 | 0 | DumpStructuralInfo(papszStructuralInfo, serializer); |
966 | 0 | } |
967 | |
|
968 | 0 | if (psOptions->bDetailed) |
969 | 0 | { |
970 | 0 | serializer.AddObjKey("values"); |
971 | 0 | if (dims.empty()) |
972 | 0 | { |
973 | 0 | std::vector<GByte> abyTmp(dt.GetSize()); |
974 | 0 | array->Read(nullptr, nullptr, nullptr, nullptr, dt, &abyTmp[0]); |
975 | 0 | DumpValue(serializer, &abyTmp[0], dt); |
976 | 0 | } |
977 | 0 | else |
978 | 0 | { |
979 | 0 | std::vector<GUInt64> startIdx(dims.size()); |
980 | 0 | std::vector<GUInt64> dimSizes; |
981 | 0 | for (const auto &dim : dims) |
982 | 0 | dimSizes.emplace_back(dim->GetSize()); |
983 | 0 | DumpArrayRec(array, serializer, 0, dimSizes, startIdx, psOptions); |
984 | 0 | } |
985 | 0 | } |
986 | |
|
987 | 0 | if (psOptions->bStats) |
988 | 0 | { |
989 | 0 | double dfMin = 0.0; |
990 | 0 | double dfMax = 0.0; |
991 | 0 | double dfMean = 0.0; |
992 | 0 | double dfStdDev = 0.0; |
993 | 0 | GUInt64 nValidCount = 0; |
994 | 0 | if (array->GetStatistics(false, true, &dfMin, &dfMax, &dfMean, |
995 | 0 | &dfStdDev, &nValidCount, nullptr, |
996 | 0 | nullptr) == CE_None) |
997 | 0 | { |
998 | 0 | serializer.AddObjKey("statistics"); |
999 | 0 | auto statContext(serializer.MakeObjectContext()); |
1000 | 0 | if (nValidCount > 0) |
1001 | 0 | { |
1002 | 0 | serializer.AddObjKey("min"); |
1003 | 0 | serializer.Add(dfMin); |
1004 | |
|
1005 | 0 | serializer.AddObjKey("max"); |
1006 | 0 | serializer.Add(dfMax); |
1007 | |
|
1008 | 0 | serializer.AddObjKey("mean"); |
1009 | 0 | serializer.Add(dfMean); |
1010 | |
|
1011 | 0 | serializer.AddObjKey("stddev"); |
1012 | 0 | serializer.Add(dfStdDev); |
1013 | 0 | } |
1014 | |
|
1015 | 0 | serializer.AddObjKey("valid_sample_count"); |
1016 | 0 | serializer.Add(static_cast<std::uint64_t>(nValidCount)); |
1017 | 0 | } |
1018 | 0 | } |
1019 | |
|
1020 | 0 | if (bOutputOverviews) |
1021 | 0 | { |
1022 | 0 | const int nOverviews = array->GetOverviewCount(); |
1023 | 0 | if (nOverviews > 0) |
1024 | 0 | { |
1025 | 0 | serializer.AddObjKey("overviews"); |
1026 | 0 | auto overviewsContext(serializer.MakeArrayContext()); |
1027 | 0 | for (int i = 0; i < nOverviews; ++i) |
1028 | 0 | { |
1029 | 0 | if (auto poOvrArray = array->GetOverview(i)) |
1030 | 0 | { |
1031 | 0 | bool bIsStandalone = false; |
1032 | 0 | { |
1033 | 0 | CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler); |
1034 | 0 | bIsStandalone = |
1035 | 0 | rootGroup->OpenMDArrayFromFullname( |
1036 | 0 | poOvrArray->GetFullName()) == nullptr; |
1037 | 0 | } |
1038 | 0 | if (bIsStandalone) |
1039 | 0 | { |
1040 | 0 | DumpArray(rootGroup, poOvrArray, serializer, psOptions, |
1041 | 0 | alreadyDumpedDimensions, alreadyDumpedArrays, |
1042 | 0 | bOutputObjType, bOutputName, |
1043 | 0 | bOutputOverviews); |
1044 | 0 | } |
1045 | 0 | else |
1046 | 0 | { |
1047 | 0 | serializer.Add(poOvrArray->GetFullName()); |
1048 | 0 | } |
1049 | 0 | } |
1050 | 0 | } |
1051 | 0 | } |
1052 | 0 | } |
1053 | 0 | } |
1054 | | |
1055 | | /************************************************************************/ |
1056 | | /* DumpArrays() */ |
1057 | | /************************************************************************/ |
1058 | | |
1059 | | static void DumpArrays(const std::shared_ptr<GDALGroup> &rootGroup, |
1060 | | const std::shared_ptr<GDALGroup> &group, |
1061 | | const std::vector<std::string> &arrayNames, |
1062 | | CPLJSonStreamingWriter &serializer, |
1063 | | const GDALMultiDimInfoOptions *psOptions, |
1064 | | std::set<std::string> &alreadyDumpedDimensions, |
1065 | | std::set<std::string> &alreadyDumpedArrays) |
1066 | 0 | { |
1067 | 0 | std::set<std::string> oSetNames; |
1068 | 0 | auto objectContext(serializer.MakeObjectContext()); |
1069 | 0 | for (const auto &name : arrayNames) |
1070 | 0 | { |
1071 | 0 | if (oSetNames.find(name) != oSetNames.end()) |
1072 | 0 | continue; // should not happen on well behaved drivers |
1073 | 0 | oSetNames.insert(name); |
1074 | 0 | auto array = group->OpenMDArray(name); |
1075 | 0 | if (array) |
1076 | 0 | { |
1077 | 0 | serializer.AddObjKey(array->GetName()); |
1078 | 0 | DumpArray(rootGroup, array, serializer, psOptions, |
1079 | 0 | alreadyDumpedDimensions, alreadyDumpedArrays, false, |
1080 | 0 | false, /* bOutputOverviews = */ true); |
1081 | 0 | } |
1082 | 0 | } |
1083 | 0 | } |
1084 | | |
1085 | | /************************************************************************/ |
1086 | | /* DumpGroup() */ |
1087 | | /************************************************************************/ |
1088 | | |
1089 | | static void DumpGroup(const std::shared_ptr<GDALGroup> &rootGroup, |
1090 | | const std::shared_ptr<GDALGroup> &group, |
1091 | | const char *pszDriverName, |
1092 | | CPLJSonStreamingWriter &serializer, |
1093 | | const GDALMultiDimInfoOptions *psOptions, |
1094 | | std::set<std::string> &alreadyDumpedDimensions, |
1095 | | std::set<std::string> &alreadyDumpedArrays, |
1096 | | bool bOutputObjType, bool bOutputName) |
1097 | 0 | { |
1098 | 0 | auto objectContext(serializer.MakeObjectContext()); |
1099 | 0 | if (bOutputObjType) |
1100 | 0 | { |
1101 | 0 | serializer.AddObjKey("type"); |
1102 | 0 | serializer.Add("group"); |
1103 | 0 | } |
1104 | 0 | if (pszDriverName) |
1105 | 0 | { |
1106 | 0 | serializer.AddObjKey("driver"); |
1107 | 0 | serializer.Add(pszDriverName); |
1108 | 0 | } |
1109 | 0 | if (bOutputName) |
1110 | 0 | { |
1111 | 0 | serializer.AddObjKey("name"); |
1112 | 0 | serializer.Add(group->GetName()); |
1113 | | |
1114 | | // If the root group is not actually the root, print its full path |
1115 | 0 | if (pszDriverName != nullptr && group->GetName() != "/") |
1116 | 0 | { |
1117 | 0 | serializer.AddObjKey("full_name"); |
1118 | 0 | serializer.Add(group->GetFullName()); |
1119 | 0 | } |
1120 | 0 | } |
1121 | 0 | else if (psOptions->bSummary) |
1122 | 0 | { |
1123 | 0 | serializer.AddObjKey("full_name"); |
1124 | 0 | serializer.Add(group->GetFullName()); |
1125 | 0 | } |
1126 | |
|
1127 | 0 | CPLStringList aosOptionsGetAttr; |
1128 | 0 | if (psOptions->bDetailed) |
1129 | 0 | aosOptionsGetAttr.SetNameValue("SHOW_ALL", "YES"); |
1130 | 0 | auto attrs = group->GetAttributes(aosOptionsGetAttr.List()); |
1131 | 0 | if (!psOptions->bSummary && !attrs.empty()) |
1132 | 0 | { |
1133 | 0 | serializer.AddObjKey("attributes"); |
1134 | 0 | DumpAttrs(attrs, serializer, psOptions); |
1135 | 0 | } |
1136 | |
|
1137 | 0 | auto dims = group->GetDimensions(); |
1138 | 0 | if (!psOptions->bSummary && !dims.empty()) |
1139 | 0 | { |
1140 | 0 | serializer.AddObjKey("dimensions"); |
1141 | 0 | DumpDimensions(rootGroup, dims, serializer, psOptions, |
1142 | 0 | alreadyDumpedDimensions); |
1143 | 0 | } |
1144 | |
|
1145 | 0 | const auto &types = group->GetDataTypes(); |
1146 | 0 | if (!psOptions->bSummary && !types.empty()) |
1147 | 0 | { |
1148 | 0 | serializer.AddObjKey("datatypes"); |
1149 | 0 | auto arrayContext(serializer.MakeArrayContext()); |
1150 | 0 | for (const auto &dt : types) |
1151 | 0 | { |
1152 | 0 | DumpDataType(*(dt.get()), serializer); |
1153 | 0 | } |
1154 | 0 | } |
1155 | |
|
1156 | 0 | CPLStringList aosOptionsGetArray(psOptions->aosArrayOptions); |
1157 | 0 | if (psOptions->bDetailed) |
1158 | 0 | aosOptionsGetArray.SetNameValue("SHOW_ALL", "YES"); |
1159 | 0 | auto arrayNames = group->GetMDArrayNames(aosOptionsGetArray.List()); |
1160 | 0 | if (!arrayNames.empty()) |
1161 | 0 | { |
1162 | 0 | serializer.AddObjKey("arrays"); |
1163 | 0 | DumpArrays(rootGroup, group, arrayNames, serializer, psOptions, |
1164 | 0 | alreadyDumpedDimensions, alreadyDumpedArrays); |
1165 | 0 | } |
1166 | |
|
1167 | 0 | auto papszStructuralInfo = group->GetStructuralInfo(); |
1168 | 0 | if (!psOptions->bSummary && papszStructuralInfo) |
1169 | 0 | { |
1170 | 0 | serializer.AddObjKey("structural_info"); |
1171 | 0 | DumpStructuralInfo(papszStructuralInfo, serializer); |
1172 | 0 | } |
1173 | |
|
1174 | 0 | auto subgroupNames = group->GetGroupNames(); |
1175 | 0 | if (!subgroupNames.empty()) |
1176 | 0 | { |
1177 | 0 | serializer.AddObjKey("groups"); |
1178 | 0 | if (HasUniqueNames(subgroupNames)) |
1179 | 0 | { |
1180 | 0 | auto groupContext(serializer.MakeObjectContext()); |
1181 | 0 | for (const auto &subgroupName : subgroupNames) |
1182 | 0 | { |
1183 | 0 | auto subgroup = group->OpenGroup(subgroupName); |
1184 | 0 | if (subgroup) |
1185 | 0 | { |
1186 | 0 | serializer.AddObjKey(subgroupName); |
1187 | 0 | DumpGroup(rootGroup, subgroup, nullptr, serializer, |
1188 | 0 | psOptions, alreadyDumpedDimensions, |
1189 | 0 | alreadyDumpedArrays, false, false); |
1190 | 0 | } |
1191 | 0 | } |
1192 | 0 | } |
1193 | 0 | else |
1194 | 0 | { |
1195 | 0 | auto arrayContext(serializer.MakeArrayContext()); |
1196 | 0 | for (const auto &subgroupName : subgroupNames) |
1197 | 0 | { |
1198 | 0 | auto subgroup = group->OpenGroup(subgroupName); |
1199 | 0 | if (subgroup) |
1200 | 0 | { |
1201 | 0 | DumpGroup(rootGroup, subgroup, nullptr, serializer, |
1202 | 0 | psOptions, alreadyDumpedDimensions, |
1203 | 0 | alreadyDumpedArrays, false, true); |
1204 | 0 | } |
1205 | 0 | } |
1206 | 0 | } |
1207 | 0 | } |
1208 | 0 | } |
1209 | | |
1210 | | /************************************************************************/ |
1211 | | /* GDALMultiDimTextOutputDumper */ |
1212 | | /************************************************************************/ |
1213 | | |
1214 | | using TableType = std::vector<std::vector<std::string>>; |
1215 | | |
1216 | | struct GDALMultiDimTextOutputDumper |
1217 | | { |
1218 | | const GDALMultiDimInfoOptions &m_sOptions; |
1219 | | std::string m_osOutput{}; |
1220 | | |
1221 | | explicit GDALMultiDimTextOutputDumper( |
1222 | | const GDALMultiDimInfoOptions *psOptions) |
1223 | 0 | : m_sOptions(*psOptions) |
1224 | 0 | { |
1225 | 0 | } |
1226 | | |
1227 | | void AddLine(const std::string &osLine = std::string()) |
1228 | 0 | { |
1229 | 0 | if (m_sOptions.bStdoutOutput) |
1230 | 0 | { |
1231 | 0 | printf("%s\n", osLine.c_str()); |
1232 | 0 | } |
1233 | 0 | else |
1234 | 0 | { |
1235 | 0 | m_osOutput += osLine; |
1236 | 0 | m_osOutput += '\n'; |
1237 | 0 | } |
1238 | 0 | } |
1239 | | |
1240 | | void DumpTable(const TableType &lines, int nIndentSpaces = 2, |
1241 | | const std::vector<size_t> &anColSizeIn = {}, |
1242 | | bool headerLineSeparator = true, |
1243 | | bool bLeftPadNumbers = true); |
1244 | | |
1245 | | void |
1246 | | DumpAttributes(const std::vector<std::shared_ptr<GDALAttribute>> &apoAttrs, |
1247 | | int nIndentSpaces); |
1248 | | |
1249 | | void DumpStructuralInfo(CSLConstList papszStructuralInfo, |
1250 | | int nIndentSpaces); |
1251 | | |
1252 | | void DumpArray(const std::shared_ptr<GDALMDArray> &poArray); |
1253 | | |
1254 | | std::set<std::string> |
1255 | | DumpDimensionsSummary(const std::shared_ptr<GDALGroup> &group); |
1256 | | |
1257 | | void DumpArraysSummary( |
1258 | | const std::shared_ptr<GDALGroup> &group, |
1259 | | const std::vector<std::shared_ptr<GDALMDArray>> &apoArrays); |
1260 | | |
1261 | | void DrumpGroupsSummary(const std::shared_ptr<GDALGroup> &group); |
1262 | | }; |
1263 | | |
1264 | | /************************************************************************/ |
1265 | | /* DumpTable() */ |
1266 | | /************************************************************************/ |
1267 | | |
1268 | | void GDALMultiDimTextOutputDumper::DumpTable( |
1269 | | const TableType &lines, int nIndentSpaces, |
1270 | | const std::vector<size_t> &anColSizeIn, bool headerLineSeparator, |
1271 | | bool bLeftPadNumbers) |
1272 | 0 | { |
1273 | | // Compute column max size if needed |
1274 | 0 | std::vector<size_t> anColSizeTmp; |
1275 | 0 | if (anColSizeIn.empty()) |
1276 | 0 | { |
1277 | 0 | for (const auto &line : lines) |
1278 | 0 | { |
1279 | 0 | if (line.size() > anColSizeTmp.size()) |
1280 | 0 | anColSizeTmp.resize(line.size()); |
1281 | 0 | for (const auto [idxCol, col] : cpl::enumerate(line)) |
1282 | 0 | { |
1283 | 0 | anColSizeTmp[idxCol] = |
1284 | 0 | std::max(anColSizeTmp[idxCol], col.size()); |
1285 | 0 | } |
1286 | 0 | } |
1287 | 0 | } |
1288 | 0 | const auto &anColSize = anColSizeIn.empty() ? anColSizeTmp : anColSizeIn; |
1289 | | |
1290 | | // Check which columns are numeric-only |
1291 | 0 | std::vector<bool> abIsNumbersOnly(anColSize.size(), true); |
1292 | 0 | for (const auto [idxLine, line] : cpl::enumerate(lines)) |
1293 | 0 | { |
1294 | 0 | CPLAssert(abIsNumbersOnly.size() >= line.size()); |
1295 | 0 | if (!headerLineSeparator || idxLine > 0) |
1296 | 0 | { |
1297 | 0 | for (const auto [idxCol, col] : cpl::enumerate(line)) |
1298 | 0 | { |
1299 | 0 | abIsNumbersOnly[idxCol] = |
1300 | 0 | abIsNumbersOnly[idxCol] && |
1301 | 0 | CPLGetValueType(col.c_str()) != CPL_VALUE_STRING; |
1302 | 0 | } |
1303 | 0 | } |
1304 | 0 | } |
1305 | | |
1306 | | // Now actually print table |
1307 | 0 | for (const auto [idxLine, line] : cpl::enumerate(lines)) |
1308 | 0 | { |
1309 | 0 | CPLAssert(anColSize.size() >= line.size()); |
1310 | 0 | std::string osFormattedLine(nIndentSpaces, ' '); |
1311 | 0 | for (const auto [idxCol, col] : cpl::enumerate(line)) |
1312 | 0 | { |
1313 | 0 | if (idxCol > 0) |
1314 | 0 | osFormattedLine += " "; |
1315 | 0 | if (idxLine == 0 && headerLineSeparator) |
1316 | 0 | { |
1317 | 0 | const size_t nLeftPadding = |
1318 | 0 | (anColSize[idxCol] - col.size()) / 2; |
1319 | 0 | osFormattedLine += std::string(nLeftPadding, ' '); |
1320 | 0 | osFormattedLine += col; |
1321 | 0 | osFormattedLine += std::string( |
1322 | 0 | anColSize[idxCol] - col.size() - nLeftPadding, ' '); |
1323 | 0 | } |
1324 | 0 | else if (!bLeftPadNumbers || !abIsNumbersOnly[idxCol]) |
1325 | 0 | { |
1326 | 0 | osFormattedLine += col; |
1327 | 0 | osFormattedLine += |
1328 | 0 | std::string(anColSize[idxCol] - col.size(), ' '); |
1329 | 0 | } |
1330 | 0 | else |
1331 | 0 | { |
1332 | 0 | osFormattedLine += |
1333 | 0 | std::string(anColSize[idxCol] - col.size(), ' '); |
1334 | 0 | osFormattedLine += col; |
1335 | 0 | } |
1336 | 0 | } |
1337 | 0 | AddLine(osFormattedLine); |
1338 | |
|
1339 | 0 | if (idxLine == 0 && headerLineSeparator) |
1340 | 0 | { |
1341 | 0 | osFormattedLine = std::string(nIndentSpaces, ' '); |
1342 | 0 | for (size_t idxCol = 0; idxCol < line.size(); ++idxCol) |
1343 | 0 | { |
1344 | 0 | if (idxCol > 0) |
1345 | 0 | osFormattedLine += " "; |
1346 | 0 | osFormattedLine += std::string(anColSize[idxCol], '-'); |
1347 | 0 | } |
1348 | 0 | AddLine(osFormattedLine); |
1349 | 0 | } |
1350 | 0 | } |
1351 | 0 | } |
1352 | | |
1353 | | /************************************************************************/ |
1354 | | /* TypeToString() */ |
1355 | | /************************************************************************/ |
1356 | | |
1357 | | static std::string TypeToString(const GDALExtendedDataType &dt, |
1358 | | bool emitCompoundName = true) |
1359 | 0 | { |
1360 | 0 | std::string ret; |
1361 | 0 | switch (dt.GetClass()) |
1362 | 0 | { |
1363 | 0 | case GEDTC_STRING: |
1364 | 0 | ret = "String"; |
1365 | 0 | break; |
1366 | | |
1367 | 0 | case GEDTC_NUMERIC: |
1368 | 0 | ret = GDALGetDataTypeName(dt.GetNumericDataType()); |
1369 | 0 | break; |
1370 | | |
1371 | 0 | case GEDTC_COMPOUND: |
1372 | 0 | { |
1373 | 0 | if (emitCompoundName && !dt.GetName().empty()) |
1374 | 0 | { |
1375 | 0 | ret = dt.GetName(); |
1376 | 0 | ret += ": "; |
1377 | 0 | } |
1378 | 0 | ret += '{'; |
1379 | 0 | bool firstComp = true; |
1380 | 0 | const auto &components = dt.GetComponents(); |
1381 | 0 | for (const auto &comp : components) |
1382 | 0 | { |
1383 | 0 | if (!firstComp) |
1384 | 0 | ret += ", "; |
1385 | 0 | firstComp = false; |
1386 | 0 | ret += comp->GetName(); |
1387 | 0 | ret += ": "; |
1388 | 0 | ret += TypeToString(comp->GetType(), false); |
1389 | 0 | } |
1390 | 0 | ret += '}'; |
1391 | 0 | break; |
1392 | 0 | } |
1393 | 0 | } |
1394 | 0 | return ret; |
1395 | 0 | } |
1396 | | |
1397 | | /************************************************************************/ |
1398 | | /* DumpAttributes() */ |
1399 | | /************************************************************************/ |
1400 | | |
1401 | | void GDALMultiDimTextOutputDumper::DumpAttributes( |
1402 | | const std::vector<std::shared_ptr<GDALAttribute>> &apoAttrs, |
1403 | | int nIndentSpaces) |
1404 | 0 | { |
1405 | 0 | if (!apoAttrs.empty()) |
1406 | 0 | { |
1407 | 0 | AddLine(); |
1408 | 0 | AddLine(std::string(nIndentSpaces, ' ').append("Attributes:")); |
1409 | 0 | TableType attrs; |
1410 | 0 | attrs.push_back({"Name", "Type", "Value"}); |
1411 | |
|
1412 | 0 | for (const auto &poAttr : apoAttrs) |
1413 | 0 | { |
1414 | 0 | CPLJSonStreamingWriter serializer(nullptr, nullptr); |
1415 | 0 | serializer.SetPrettyFormatting(false); |
1416 | 0 | DumpAttrValue(poAttr, serializer); |
1417 | 0 | std::string osAttrVal = serializer.GetString(); |
1418 | 0 | constexpr size_t MAX_COLS = 80; |
1419 | 0 | if (osAttrVal.size() <= MAX_COLS) |
1420 | 0 | { |
1421 | 0 | attrs.push_back({poAttr->GetName(), |
1422 | 0 | TypeToString(poAttr->GetDataType()), |
1423 | 0 | std::move(osAttrVal)}); |
1424 | 0 | } |
1425 | 0 | else |
1426 | 0 | { |
1427 | 0 | bool bFirstLineAttr = true; |
1428 | 0 | while (osAttrVal.size() > MAX_COLS) |
1429 | 0 | { |
1430 | 0 | auto nLastBreak = osAttrVal.find_last_of(" ,\n", MAX_COLS); |
1431 | 0 | if (nLastBreak == std::string::npos) |
1432 | 0 | nLastBreak = osAttrVal.find_first_of(" ,\n"); |
1433 | 0 | if (nLastBreak != std::string::npos) |
1434 | 0 | { |
1435 | 0 | auto osVal = osAttrVal.substr(0, nLastBreak); |
1436 | 0 | if (osAttrVal[nLastBreak] != ' ' && |
1437 | 0 | osAttrVal[nLastBreak] != '\n') |
1438 | 0 | osVal += osAttrVal[nLastBreak]; |
1439 | 0 | attrs.push_back( |
1440 | 0 | {bFirstLineAttr ? poAttr->GetName() : std::string(), |
1441 | 0 | bFirstLineAttr |
1442 | 0 | ? TypeToString(poAttr->GetDataType()) |
1443 | 0 | : std::string(), |
1444 | 0 | std::move(osVal)}); |
1445 | 0 | osAttrVal = osAttrVal.substr(nLastBreak + 1); |
1446 | 0 | } |
1447 | 0 | else |
1448 | 0 | { |
1449 | 0 | break; |
1450 | 0 | } |
1451 | 0 | bFirstLineAttr = false; |
1452 | 0 | } |
1453 | 0 | attrs.push_back( |
1454 | 0 | {bFirstLineAttr ? poAttr->GetName() : std::string(), |
1455 | 0 | bFirstLineAttr ? TypeToString(poAttr->GetDataType()) |
1456 | 0 | : std::string(), |
1457 | 0 | std::move(osAttrVal)}); |
1458 | 0 | } |
1459 | 0 | } |
1460 | 0 | DumpTable(attrs, nIndentSpaces + 2); |
1461 | 0 | } |
1462 | 0 | } |
1463 | | |
1464 | | /************************************************************************/ |
1465 | | /* DimsToShapeString() */ |
1466 | | /************************************************************************/ |
1467 | | |
1468 | | static std::string |
1469 | | DimsToShapeString(const std::vector<std::shared_ptr<GDALDimension>> &dims) |
1470 | 0 | { |
1471 | 0 | std::string s("["); |
1472 | 0 | for (const auto &poDim : dims) |
1473 | 0 | { |
1474 | 0 | if (s.size() > 1) |
1475 | 0 | s += ", "; |
1476 | 0 | s += std::to_string(poDim->GetSize()); |
1477 | 0 | } |
1478 | 0 | s += ']'; |
1479 | 0 | return s; |
1480 | 0 | } |
1481 | | |
1482 | | /************************************************************************/ |
1483 | | /* DimsToString() */ |
1484 | | /************************************************************************/ |
1485 | | |
1486 | | static std::string |
1487 | | DimsToString(const std::vector<std::shared_ptr<GDALDimension>> &dims, |
1488 | | const std::string &osSameDimGroup) |
1489 | 0 | { |
1490 | 0 | std::string s("("); |
1491 | 0 | for (const auto &poDim : dims) |
1492 | 0 | { |
1493 | 0 | if (s.size() > 1) |
1494 | 0 | s += ", "; |
1495 | 0 | else if (!osSameDimGroup.empty()) |
1496 | 0 | { |
1497 | 0 | s += osSameDimGroup; |
1498 | 0 | s += '/'; |
1499 | 0 | } |
1500 | 0 | const std::string &osFullname = poDim->GetFullName(); |
1501 | 0 | if (!osFullname.empty()) |
1502 | 0 | { |
1503 | 0 | s += osSameDimGroup.empty() |
1504 | 0 | ? osFullname |
1505 | 0 | : osFullname.substr(osSameDimGroup.size() + 1); |
1506 | 0 | } |
1507 | 0 | else |
1508 | 0 | { |
1509 | 0 | s += "(unnamed)"; |
1510 | 0 | } |
1511 | 0 | } |
1512 | 0 | s += ')'; |
1513 | 0 | return s; |
1514 | 0 | } |
1515 | | |
1516 | | /************************************************************************/ |
1517 | | /* DimsToSameDimGroup() */ |
1518 | | /************************************************************************/ |
1519 | | |
1520 | | /** Return the prefix shared by all dimensions if there is one, or empty string |
1521 | | * otherwise. |
1522 | | */ |
1523 | | static std::string |
1524 | | DimsToSameDimGroup(const std::vector<std::shared_ptr<GDALDimension>> &dims) |
1525 | 0 | { |
1526 | 0 | std::string osSameDimGroup; |
1527 | 0 | for (const auto &poDim : dims) |
1528 | 0 | { |
1529 | 0 | std::string osDimGroup = poDim->GetFullName(); |
1530 | 0 | const auto nPos = osDimGroup.rfind('/'); |
1531 | 0 | if (nPos != std::string::npos) |
1532 | 0 | { |
1533 | 0 | osDimGroup.resize(nPos); |
1534 | 0 | if (osSameDimGroup.empty()) |
1535 | 0 | { |
1536 | 0 | osSameDimGroup = std::move(osDimGroup); |
1537 | 0 | } |
1538 | 0 | else if (osSameDimGroup != osDimGroup) |
1539 | 0 | { |
1540 | 0 | return {}; |
1541 | 0 | } |
1542 | 0 | } |
1543 | 0 | else |
1544 | 0 | { |
1545 | 0 | return {}; |
1546 | 0 | } |
1547 | 0 | } |
1548 | 0 | return osSameDimGroup; |
1549 | 0 | } |
1550 | | |
1551 | | /************************************************************************/ |
1552 | | /* BlockSizeToString() */ |
1553 | | /************************************************************************/ |
1554 | | |
1555 | | template <class T> |
1556 | | static std::string BlockSizeToString(const std::vector<T> &anBlockSize, |
1557 | | bool *pbAllZero = nullptr) |
1558 | 0 | { |
1559 | 0 | std::string s("["); |
1560 | 0 | if (pbAllZero) |
1561 | 0 | *pbAllZero = true; |
1562 | 0 | for (const auto nSize : anBlockSize) |
1563 | 0 | { |
1564 | 0 | if (s.size() > 1) |
1565 | 0 | s += ", "; |
1566 | 0 | s += std::to_string(nSize); |
1567 | 0 | if (pbAllZero) |
1568 | 0 | *pbAllZero = *pbAllZero && nSize == 0; |
1569 | 0 | } |
1570 | 0 | s += ']'; |
1571 | 0 | return s; |
1572 | 0 | } |
1573 | | |
1574 | | /************************************************************************/ |
1575 | | /* WriteToStdoutWithSpaceIndent() */ |
1576 | | /************************************************************************/ |
1577 | | |
1578 | | static void WriteToStdoutWithSpaceIndent(const char *pszText, void *pUserData) |
1579 | 0 | { |
1580 | 0 | const int *pnIndent = static_cast<const int *>(pUserData); |
1581 | 0 | printf("%s", pszText); |
1582 | 0 | if (pszText[0] == '\n') |
1583 | 0 | printf("%s", std::string(*pnIndent, ' ').c_str()); |
1584 | 0 | } |
1585 | | |
1586 | | /************************************************************************/ |
1587 | | /* GDALMultiDimTextOutputDumper::DumpStructuralInfo() */ |
1588 | | /************************************************************************/ |
1589 | | |
1590 | | void GDALMultiDimTextOutputDumper::DumpStructuralInfo( |
1591 | | CSLConstList papszStructuralInfo, int nIndentSpaces) |
1592 | 0 | { |
1593 | 0 | AddLine(); |
1594 | 0 | AddLine(std::string(nIndentSpaces, ' ').append("Structural metadata:")); |
1595 | |
|
1596 | 0 | TableType info; |
1597 | |
|
1598 | 0 | int i = 1; |
1599 | 0 | for (const auto &[pszKey, pszValue] : |
1600 | 0 | cpl::IterateNameValue(papszStructuralInfo, |
1601 | 0 | /* bReturnNullKeyIfNotNameValue = */ true)) |
1602 | 0 | { |
1603 | 0 | std::vector<std::string> line; |
1604 | 0 | if (pszKey) |
1605 | 0 | { |
1606 | 0 | line.push_back(pszKey); |
1607 | 0 | } |
1608 | 0 | else |
1609 | 0 | { |
1610 | 0 | line.push_back(CPLSPrintf("metadata_%d", i)); |
1611 | 0 | ++i; |
1612 | 0 | } |
1613 | 0 | line.push_back(pszValue); |
1614 | 0 | info.push_back(std::move(line)); |
1615 | 0 | } |
1616 | 0 | DumpTable(info, nIndentSpaces + 2, {}, false); |
1617 | 0 | } |
1618 | | |
1619 | | /************************************************************************/ |
1620 | | /* GDALMultiDimTextOutputDumper::DumpArray() */ |
1621 | | /************************************************************************/ |
1622 | | |
1623 | | void GDALMultiDimTextOutputDumper::DumpArray( |
1624 | | const std::shared_ptr<GDALMDArray> &poArray) |
1625 | 0 | { |
1626 | 0 | AddLine(); |
1627 | 0 | AddLine(" - " + poArray->GetFullName() + ":"); |
1628 | |
|
1629 | 0 | constexpr int INDENT_LEVEL = 6; |
1630 | |
|
1631 | 0 | TableType props; |
1632 | 0 | const auto &dims = poArray->GetDimensions(); |
1633 | 0 | props.push_back( |
1634 | 0 | {"Dimensions:", DimsToString(dims, DimsToSameDimGroup(dims))}); |
1635 | 0 | props.push_back({"Shape:", DimsToShapeString(dims)}); |
1636 | 0 | bool bAllZero = true; |
1637 | 0 | std::string osChunkSize = |
1638 | 0 | BlockSizeToString(poArray->GetBlockSize(), &bAllZero); |
1639 | 0 | if (!bAllZero) |
1640 | 0 | props.push_back({"Chunk size:", std::move(osChunkSize)}); |
1641 | |
|
1642 | 0 | const auto &dt = poArray->GetDataType(); |
1643 | 0 | props.push_back({"Type:", TypeToString(dt)}); |
1644 | 0 | const std::string &osUnit = poArray->GetUnit(); |
1645 | 0 | if (!osUnit.empty()) |
1646 | 0 | props.push_back({"Unit:", osUnit}); |
1647 | |
|
1648 | 0 | if (const auto nodata = poArray->GetRawNoDataValue()) |
1649 | 0 | { |
1650 | 0 | CPLJSonStreamingWriter serializer(nullptr, nullptr); |
1651 | 0 | serializer.SetPrettyFormatting(false); |
1652 | 0 | DumpValue(serializer, static_cast<const GByte *>(nodata), dt); |
1653 | 0 | props.push_back({"Nodata value:", serializer.GetString()}); |
1654 | 0 | } |
1655 | 0 | DumpTable(props, INDENT_LEVEL, {}, |
1656 | 0 | /* headerLineSeparator = */ false, |
1657 | 0 | /* bLeftPadNumbers = */ false); |
1658 | |
|
1659 | 0 | DumpAttributes(poArray->GetAttributes(), INDENT_LEVEL); |
1660 | |
|
1661 | 0 | if (const auto poSRS = poArray->GetSpatialRef()) |
1662 | 0 | { |
1663 | 0 | AddLine(); |
1664 | 0 | std::string osCRS; |
1665 | 0 | EmitTextDisplayOfCRS(poSRS.get(), "AUTO", "Coordinate Reference System", |
1666 | 0 | [&osCRS](const std::string &s) { osCRS += s; }); |
1667 | 0 | const CPLStringList aosLines( |
1668 | 0 | CSLTokenizeString2(osCRS.c_str(), "\n", 0)); |
1669 | 0 | for (const char *pszLine : aosLines) |
1670 | 0 | AddLine(std::string(INDENT_LEVEL, ' ').append(pszLine)); |
1671 | 0 | } |
1672 | |
|
1673 | 0 | if (CSLConstList papszStructuralInfo = poArray->GetStructuralInfo()) |
1674 | 0 | { |
1675 | 0 | DumpStructuralInfo(papszStructuralInfo, INDENT_LEVEL); |
1676 | 0 | } |
1677 | |
|
1678 | 0 | if (m_sOptions.bStats) |
1679 | 0 | { |
1680 | 0 | double dfMin = 0.0; |
1681 | 0 | double dfMax = 0.0; |
1682 | 0 | double dfMean = 0.0; |
1683 | 0 | double dfStdDev = 0.0; |
1684 | 0 | GUInt64 nValidCount = 0; |
1685 | 0 | if (poArray->GetStatistics(false, true, &dfMin, &dfMax, &dfMean, |
1686 | 0 | &dfStdDev, &nValidCount, nullptr, |
1687 | 0 | nullptr) == CE_None) |
1688 | 0 | { |
1689 | 0 | AddLine(); |
1690 | 0 | AddLine(std::string(INDENT_LEVEL, ' ').append("Statistics:")); |
1691 | |
|
1692 | 0 | TableType stats; |
1693 | 0 | if (nValidCount > 0) |
1694 | 0 | { |
1695 | 0 | stats.push_back({"min", std::to_string(dfMin)}); |
1696 | 0 | stats.push_back({"max", std::to_string(dfMax)}); |
1697 | 0 | stats.push_back({"mean", std::to_string(dfMean)}); |
1698 | 0 | stats.push_back({"stddev", std::to_string(dfStdDev)}); |
1699 | 0 | } |
1700 | 0 | stats.push_back( |
1701 | 0 | {"valid sample count", std::to_string(nValidCount)}); |
1702 | |
|
1703 | 0 | DumpTable(stats, INDENT_LEVEL + 2, {}, false); |
1704 | 0 | } |
1705 | 0 | } |
1706 | |
|
1707 | 0 | if (m_sOptions.bDetailed) |
1708 | 0 | { |
1709 | 0 | if (dims.empty()) |
1710 | 0 | { |
1711 | 0 | std::vector<GByte> abyTmp(dt.GetSize()); |
1712 | 0 | poArray->Read(nullptr, nullptr, nullptr, nullptr, dt, &abyTmp[0]); |
1713 | 0 | CPLJSonStreamingWriter serializer(nullptr, nullptr); |
1714 | 0 | DumpValue(serializer, &abyTmp[0], dt); |
1715 | |
|
1716 | 0 | AddLine(); |
1717 | 0 | AddLine(std::string(INDENT_LEVEL, ' ') |
1718 | 0 | .append("Value: ") |
1719 | 0 | .append(serializer.GetString())); |
1720 | 0 | } |
1721 | 0 | else |
1722 | 0 | { |
1723 | 0 | AddLine(); |
1724 | 0 | AddLine(std::string(INDENT_LEVEL, ' ').append("Values:")); |
1725 | |
|
1726 | 0 | int nIndent = INDENT_LEVEL; |
1727 | 0 | CPLJSonStreamingWriter serializer(m_sOptions.bStdoutOutput |
1728 | 0 | ? WriteToStdoutWithSpaceIndent |
1729 | 0 | : nullptr, |
1730 | 0 | &nIndent); |
1731 | 0 | std::vector<GUInt64> startIdx(dims.size()); |
1732 | 0 | std::vector<GUInt64> dimSizes; |
1733 | 0 | for (const auto &dim : dims) |
1734 | 0 | dimSizes.emplace_back(dim->GetSize()); |
1735 | 0 | if (m_sOptions.bStdoutOutput) |
1736 | 0 | printf("%s", std::string(INDENT_LEVEL, ' ').c_str()); |
1737 | 0 | DumpArrayRec(poArray, serializer, 0, dimSizes, startIdx, |
1738 | 0 | &m_sOptions); |
1739 | 0 | if (!m_sOptions.bStdoutOutput) |
1740 | 0 | { |
1741 | 0 | AddLine(std::string(INDENT_LEVEL, ' ') |
1742 | 0 | .append(CPLString(serializer.GetString()) |
1743 | 0 | .replaceAll( |
1744 | 0 | '\n', std::string("\n").append( |
1745 | 0 | std::string(INDENT_LEVEL, |
1746 | 0 | ' '))))); |
1747 | 0 | } |
1748 | 0 | else |
1749 | 0 | { |
1750 | 0 | AddLine(); |
1751 | 0 | } |
1752 | 0 | } |
1753 | 0 | } |
1754 | 0 | } |
1755 | | |
1756 | | /************************************************************************/ |
1757 | | /* GDALMultiDimTextOutputDumper:: DumpDimensionsSummary() */ |
1758 | | /************************************************************************/ |
1759 | | |
1760 | | std::set<std::string> GDALMultiDimTextOutputDumper::DumpDimensionsSummary( |
1761 | | const std::shared_ptr<GDALGroup> &group) |
1762 | 0 | { |
1763 | 0 | std::set<std::string> oSetIndexingVariablePaths; |
1764 | |
|
1765 | 0 | const auto dims = group->GetDimensionsRecursive(); |
1766 | 0 | if (!dims.empty()) |
1767 | 0 | { |
1768 | 0 | AddLine(); |
1769 | 0 | AddLine("Dimensions:"); |
1770 | 0 | TableType lines; |
1771 | 0 | lines.push_back({"Name (path)", "Size", "Type", "Direction"}); |
1772 | 0 | for (const auto &poDim : dims) |
1773 | 0 | { |
1774 | 0 | std::vector<std::string> line{ |
1775 | 0 | poDim->GetFullName(), std::to_string(poDim->GetSize()), |
1776 | 0 | poDim->GetType(), poDim->GetDirection()}; |
1777 | 0 | lines.push_back(std::move(line)); |
1778 | 0 | const auto poIndexingVariable = poDim->GetIndexingVariable(); |
1779 | 0 | if (poIndexingVariable) |
1780 | 0 | { |
1781 | 0 | const auto &osIndexingVarPath = |
1782 | 0 | poIndexingVariable->GetFullName(); |
1783 | 0 | if (!osIndexingVarPath.empty() && osIndexingVarPath[0] == '/') |
1784 | 0 | { |
1785 | 0 | oSetIndexingVariablePaths.insert(osIndexingVarPath); |
1786 | 0 | } |
1787 | 0 | } |
1788 | 0 | } |
1789 | 0 | DumpTable(lines); |
1790 | 0 | } |
1791 | |
|
1792 | 0 | return oSetIndexingVariablePaths; |
1793 | 0 | } |
1794 | | |
1795 | | /************************************************************************/ |
1796 | | /* GDALMultiDimTextOutputDumper:: DumpArraysSummary() */ |
1797 | | /************************************************************************/ |
1798 | | |
1799 | | void GDALMultiDimTextOutputDumper::DumpArraysSummary( |
1800 | | const std::shared_ptr<GDALGroup> &group, |
1801 | | const std::vector<std::shared_ptr<GDALMDArray>> &apoArrays) |
1802 | 0 | { |
1803 | 0 | const std::set<std::string> oSetIndexingVariablePaths = |
1804 | 0 | DumpDimensionsSummary(group); |
1805 | |
|
1806 | 0 | TableType linesCoordinateArrays; |
1807 | 0 | TableType linesScalarArrays; |
1808 | 0 | std::map<std::string, TableType> linesDataArrays; |
1809 | 0 | for (const auto &poArray : apoArrays) |
1810 | 0 | { |
1811 | 0 | const auto &dims = poArray->GetDimensions(); |
1812 | 0 | if (cpl::contains(oSetIndexingVariablePaths, poArray->GetFullName())) |
1813 | 0 | { |
1814 | 0 | if (linesCoordinateArrays.empty()) |
1815 | 0 | linesCoordinateArrays.push_back( |
1816 | 0 | {"Name (path)", "Dimension", "Type", "Unit"}); |
1817 | 0 | std::string osDimension; |
1818 | 0 | for (const auto &poDim : dims) |
1819 | 0 | { |
1820 | 0 | if (osDimension.empty()) |
1821 | 0 | osDimension = '('; |
1822 | 0 | else |
1823 | 0 | osDimension += ", "; |
1824 | 0 | osDimension += poDim->GetName(); |
1825 | 0 | } |
1826 | 0 | if (!osDimension.empty()) |
1827 | 0 | osDimension += ')'; |
1828 | 0 | std::vector<std::string> line{ |
1829 | 0 | poArray->GetFullName(), std::move(osDimension), |
1830 | 0 | TypeToString(poArray->GetDataType()), poArray->GetUnit()}; |
1831 | 0 | linesCoordinateArrays.push_back(std::move(line)); |
1832 | 0 | } |
1833 | 0 | else if (dims.empty()) |
1834 | 0 | { |
1835 | 0 | if (linesScalarArrays.empty()) |
1836 | 0 | linesScalarArrays.push_back({"Name (path)", "Type", "Unit"}); |
1837 | 0 | std::vector<std::string> line{poArray->GetFullName(), |
1838 | 0 | TypeToString(poArray->GetDataType()), |
1839 | 0 | poArray->GetUnit()}; |
1840 | 0 | linesScalarArrays.push_back(std::move(line)); |
1841 | 0 | } |
1842 | 0 | else |
1843 | 0 | { |
1844 | 0 | const std::string osSameDimGroup = DimsToSameDimGroup(dims); |
1845 | |
|
1846 | 0 | bool bAllZero = true; |
1847 | 0 | std::string osChunkSize = |
1848 | 0 | BlockSizeToString(poArray->GetBlockSize(), &bAllZero); |
1849 | 0 | if (bAllZero) |
1850 | 0 | osChunkSize = "(unknown)"; |
1851 | |
|
1852 | 0 | std::vector<std::string> line{ |
1853 | 0 | poArray->GetFullName(), TypeToString(poArray->GetDataType()), |
1854 | 0 | poArray->GetUnit(), DimsToShapeString(dims), |
1855 | 0 | std::move(osChunkSize)}; |
1856 | 0 | linesDataArrays[DimsToString(dims, osSameDimGroup)].push_back( |
1857 | 0 | std::move(line)); |
1858 | 0 | } |
1859 | 0 | } |
1860 | |
|
1861 | 0 | if (!linesCoordinateArrays.empty()) |
1862 | 0 | { |
1863 | 0 | AddLine(); |
1864 | 0 | AddLine("Coordinates (indexing variables):"); |
1865 | 0 | DumpTable(linesCoordinateArrays); |
1866 | 0 | } |
1867 | |
|
1868 | 0 | if (!linesDataArrays.empty()) |
1869 | 0 | { |
1870 | 0 | AddLine(); |
1871 | 0 | AddLine("Data variables:"); |
1872 | |
|
1873 | 0 | TableType headerLine; |
1874 | 0 | headerLine.push_back( |
1875 | 0 | {"Name (path)", "Type", "Unit", "Shape", "Chunk size"}); |
1876 | |
|
1877 | 0 | std::vector<size_t> anColSizeDataVars; |
1878 | 0 | anColSizeDataVars.resize(headerLine[0].size()); |
1879 | 0 | for (const auto [iCol, col] : cpl::enumerate(headerLine[0])) |
1880 | 0 | anColSizeDataVars[iCol] = col.size(); |
1881 | 0 | for (const auto &[_, lines] : linesDataArrays) |
1882 | 0 | { |
1883 | 0 | for (const auto &line : lines) |
1884 | 0 | { |
1885 | 0 | for (const auto [iCol, col] : cpl::enumerate(line)) |
1886 | 0 | { |
1887 | 0 | anColSizeDataVars[iCol] = |
1888 | 0 | std::max(anColSizeDataVars[iCol], col.size()); |
1889 | 0 | } |
1890 | 0 | } |
1891 | 0 | } |
1892 | |
|
1893 | 0 | DumpTable(headerLine, 2, anColSizeDataVars); |
1894 | 0 | for (const auto &[path, lines] : linesDataArrays) |
1895 | 0 | { |
1896 | 0 | AddLine(); |
1897 | 0 | AddLine(" " + path + ":"); |
1898 | 0 | DumpTable(lines, 2, anColSizeDataVars, false); |
1899 | 0 | } |
1900 | 0 | } |
1901 | |
|
1902 | 0 | if (!linesScalarArrays.empty()) |
1903 | 0 | { |
1904 | 0 | AddLine(); |
1905 | 0 | AddLine("Scalar arrays:"); |
1906 | 0 | DumpTable(linesScalarArrays); |
1907 | 0 | } |
1908 | 0 | } |
1909 | | |
1910 | | /************************************************************************/ |
1911 | | /* GDALMultiDimTextOutputDumper::DrumpGroupsSummary() */ |
1912 | | /************************************************************************/ |
1913 | | |
1914 | | void GDALMultiDimTextOutputDumper::DrumpGroupsSummary( |
1915 | | const std::shared_ptr<GDALGroup> &group) |
1916 | 0 | { |
1917 | 0 | const auto apoGroups = group->GetGroupsRecursive(); |
1918 | 0 | if (!apoGroups.empty()) |
1919 | 0 | { |
1920 | 0 | AddLine(); |
1921 | 0 | AddLine("Groups:"); |
1922 | 0 | std::vector<std::vector<std::string>> groupNames; |
1923 | 0 | for (auto &poGroup : apoGroups) |
1924 | 0 | groupNames.push_back(CPLStringList( |
1925 | 0 | CSLTokenizeString2(poGroup->GetFullName().c_str(), "/", 0))); |
1926 | 0 | std::sort(groupNames.begin(), groupNames.end()); |
1927 | 0 | for (const auto &groupName : groupNames) |
1928 | 0 | { |
1929 | 0 | std::string osLine(" "); |
1930 | 0 | for (const auto &part : groupName) |
1931 | 0 | { |
1932 | 0 | osLine += '/'; |
1933 | 0 | osLine += part; |
1934 | 0 | } |
1935 | 0 | AddLine(osLine); |
1936 | 0 | } |
1937 | 0 | } |
1938 | 0 | } |
1939 | | |
1940 | | /************************************************************************/ |
1941 | | /* DumpAsText() */ |
1942 | | /************************************************************************/ |
1943 | | |
1944 | | static char *DumpAsText(const std::shared_ptr<GDALGroup> &group, |
1945 | | const char *pszDriverName, |
1946 | | const GDALMultiDimInfoOptions *psOptions) |
1947 | 0 | { |
1948 | 0 | GDALMultiDimTextOutputDumper dumper(psOptions); |
1949 | |
|
1950 | 0 | CPLStringList aosOptionsGetArray(psOptions->aosArrayOptions); |
1951 | 0 | if (psOptions->bDetailed) |
1952 | 0 | aosOptionsGetArray.SetNameValue("SHOW_ALL", "YES"); |
1953 | |
|
1954 | 0 | const auto apoArrays = |
1955 | 0 | group->GetMDArraysRecursive(nullptr, aosOptionsGetArray.List()); |
1956 | |
|
1957 | 0 | if (psOptions->osArrayName.empty()) |
1958 | 0 | { |
1959 | 0 | dumper.AddLine( |
1960 | 0 | std::string("Driver: ") |
1961 | 0 | .append(pszDriverName ? pszDriverName : "(unknown)")); |
1962 | |
|
1963 | 0 | if (CSLConstList papszStructuralInfo = group->GetStructuralInfo()) |
1964 | 0 | { |
1965 | 0 | dumper.DumpStructuralInfo(papszStructuralInfo, 0); |
1966 | 0 | } |
1967 | |
|
1968 | 0 | dumper.DumpArraysSummary(group, apoArrays); |
1969 | |
|
1970 | 0 | dumper.DrumpGroupsSummary(group); |
1971 | 0 | } |
1972 | |
|
1973 | 0 | if ((!psOptions->bSummary || !psOptions->osArrayName.empty()) && |
1974 | 0 | !apoArrays.empty()) |
1975 | 0 | { |
1976 | 0 | if (psOptions->osArrayName.empty()) |
1977 | 0 | { |
1978 | 0 | dumper.DumpAttributes(group->GetAttributes(), 0); |
1979 | 0 | dumper.AddLine(); |
1980 | 0 | } |
1981 | |
|
1982 | 0 | dumper.AddLine("Arrays:"); |
1983 | 0 | for (const auto &poArray : apoArrays) |
1984 | 0 | { |
1985 | 0 | if (psOptions->osArrayName.empty() || |
1986 | 0 | poArray->GetName() == psOptions->osArrayName || |
1987 | 0 | poArray->GetFullName() == psOptions->osArrayName) |
1988 | 0 | { |
1989 | 0 | dumper.DumpArray(poArray); |
1990 | 0 | } |
1991 | 0 | } |
1992 | 0 | } |
1993 | |
|
1994 | 0 | if (psOptions->bStdoutOutput) |
1995 | 0 | { |
1996 | 0 | return VSIStrdup("ok"); |
1997 | 0 | } |
1998 | 0 | else |
1999 | 0 | { |
2000 | 0 | return VSIStrdup(dumper.m_osOutput.c_str()); |
2001 | 0 | } |
2002 | 0 | } |
2003 | | |
2004 | | /************************************************************************/ |
2005 | | /* WriteToStdout() */ |
2006 | | /************************************************************************/ |
2007 | | |
2008 | | static void WriteToStdout(const char *pszText, void *) |
2009 | 0 | { |
2010 | 0 | printf("%s", pszText); |
2011 | 0 | } |
2012 | | |
2013 | | /************************************************************************/ |
2014 | | /* GDALMultiDimInfoAppOptionsGetParser() */ |
2015 | | /************************************************************************/ |
2016 | | |
2017 | | static std::unique_ptr<GDALArgumentParser> GDALMultiDimInfoAppOptionsGetParser( |
2018 | | GDALMultiDimInfoOptions *psOptions, |
2019 | | GDALMultiDimInfoOptionsForBinary *psOptionsForBinary) |
2020 | 0 | { |
2021 | 0 | auto argParser = std::make_unique<GDALArgumentParser>( |
2022 | 0 | "gdalmdiminfo", /* bForBinary=*/psOptionsForBinary != nullptr); |
2023 | |
|
2024 | 0 | argParser->add_description( |
2025 | 0 | _("Lists various information about a GDAL multidimensional dataset.")); |
2026 | |
|
2027 | 0 | argParser->add_epilog(_("For more details, consult " |
2028 | 0 | "https://gdal.org/programs/gdalmdiminfo.html")); |
2029 | 0 | { |
2030 | 0 | auto &group = argParser->add_mutually_exclusive_group(); |
2031 | |
|
2032 | 0 | group.add_argument("-summary") |
2033 | 0 | .flag() |
2034 | 0 | .store_into(psOptions->bSummary) |
2035 | 0 | .help(_("Report only group and array hierarchy, without detailed " |
2036 | 0 | "information on attributes or dimensions.")); |
2037 | |
|
2038 | 0 | group.add_argument("-detailed") |
2039 | 0 | .flag() |
2040 | 0 | .store_into(psOptions->bDetailed) |
2041 | 0 | .help( |
2042 | 0 | _("Most verbose output. Report attribute data types and array " |
2043 | 0 | "values.")); |
2044 | 0 | } |
2045 | |
|
2046 | 0 | argParser->add_inverted_logic_flag( |
2047 | 0 | "-nopretty", &psOptions->bPretty, |
2048 | 0 | _("Outputs on a single line without any indentation.")); |
2049 | |
|
2050 | 0 | argParser->add_argument("-array") |
2051 | 0 | .metavar("<array_name>") |
2052 | 0 | .store_into(psOptions->osArrayName) |
2053 | 0 | .help(_("Name of the array, used to restrict the output to the " |
2054 | 0 | "specified array.")); |
2055 | |
|
2056 | 0 | argParser->add_argument("-limit") |
2057 | 0 | .metavar("<number>") |
2058 | 0 | .scan<'i', int>() |
2059 | 0 | .store_into(psOptions->nLimitValuesByDim) |
2060 | 0 | .help(_("Number of values in each dimension that is used to limit the " |
2061 | 0 | "display of array values.")); |
2062 | |
|
2063 | 0 | if (psOptionsForBinary) |
2064 | 0 | { |
2065 | 0 | argParser->add_open_options_argument( |
2066 | 0 | psOptionsForBinary->aosOpenOptions); |
2067 | |
|
2068 | 0 | argParser->add_input_format_argument( |
2069 | 0 | &psOptionsForBinary->aosAllowInputDrivers); |
2070 | |
|
2071 | 0 | argParser->add_argument("dataset_name") |
2072 | 0 | .metavar("<dataset_name>") |
2073 | 0 | .store_into(psOptionsForBinary->osFilename) |
2074 | 0 | .help("Input dataset."); |
2075 | 0 | } |
2076 | |
|
2077 | 0 | argParser->add_argument("-arrayoption") |
2078 | 0 | .metavar("<NAME>=<VALUE>") |
2079 | 0 | .append() |
2080 | 0 | .action([psOptions](const std::string &s) |
2081 | 0 | { psOptions->aosArrayOptions.AddString(s.c_str()); }) |
2082 | 0 | .help(_("Option passed to GDALGroup::GetMDArrayNames() to filter " |
2083 | 0 | "reported arrays.")); |
2084 | |
|
2085 | 0 | argParser->add_argument("-stats") |
2086 | 0 | .flag() |
2087 | 0 | .store_into(psOptions->bStats) |
2088 | 0 | .help(_("Read and display image statistics.")); |
2089 | |
|
2090 | 0 | argParser->add_argument("-format") |
2091 | 0 | .hidden() |
2092 | 0 | .store_into(psOptions->osFormat) |
2093 | 0 | .help(_("Output format.")); |
2094 | | |
2095 | | // Only used by gdalmdiminfo binary to write output to stdout instead of in a string, in JSON mode |
2096 | 0 | argParser->add_argument("-stdout").flag().hidden().store_into( |
2097 | 0 | psOptions->bStdoutOutput); |
2098 | |
|
2099 | 0 | return argParser; |
2100 | 0 | } |
2101 | | |
2102 | | /************************************************************************/ |
2103 | | /* GDALMultiDimInfoAppGetParserUsage() */ |
2104 | | /************************************************************************/ |
2105 | | |
2106 | | std::string GDALMultiDimInfoAppGetParserUsage() |
2107 | 0 | { |
2108 | 0 | try |
2109 | 0 | { |
2110 | 0 | GDALMultiDimInfoOptions sOptions; |
2111 | 0 | GDALMultiDimInfoOptionsForBinary sOptionsForBinary; |
2112 | 0 | auto argParser = |
2113 | 0 | GDALMultiDimInfoAppOptionsGetParser(&sOptions, &sOptionsForBinary); |
2114 | 0 | return argParser->usage(); |
2115 | 0 | } |
2116 | 0 | catch (const std::exception &err) |
2117 | 0 | { |
2118 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s", |
2119 | 0 | err.what()); |
2120 | 0 | return std::string(); |
2121 | 0 | } |
2122 | 0 | } |
2123 | | |
2124 | | /************************************************************************/ |
2125 | | /* GDALMultiDimInfo() */ |
2126 | | /************************************************************************/ |
2127 | | |
2128 | | /* clang-format off */ |
2129 | | /** |
2130 | | * Lists various information about a GDAL multidimensional dataset. |
2131 | | * |
2132 | | * This is the equivalent of the |
2133 | | * <a href="/programs/gdalmdiminfo.html">gdalmdiminfo</a>utility. |
2134 | | * |
2135 | | * GDALMultiDimInfoOptions* must be allocated and freed with |
2136 | | * GDALMultiDimInfoOptionsNew() and GDALMultiDimInfoOptionsFree() respectively. |
2137 | | * |
2138 | | * @param hDataset the dataset handle. |
2139 | | * @param psOptionsIn the options structure returned by |
2140 | | * GDALMultiDimInfoOptionsNew() or NULL. |
2141 | | * @return string corresponding to the information about the raster dataset |
2142 | | * (must be freed with CPLFree()), or NULL in case of error. |
2143 | | * |
2144 | | * @since GDAL 3.1 |
2145 | | */ |
2146 | | /* clang-format on */ |
2147 | | |
2148 | | char *GDALMultiDimInfo(GDALDatasetH hDataset, |
2149 | | const GDALMultiDimInfoOptions *psOptionsIn) |
2150 | 0 | { |
2151 | 0 | if (hDataset == nullptr) |
2152 | 0 | return nullptr; |
2153 | | |
2154 | 0 | GDALMultiDimInfoOptions oOptionsDefault; |
2155 | 0 | const GDALMultiDimInfoOptions *psOptions = |
2156 | 0 | psOptionsIn ? psOptionsIn : &oOptionsDefault; |
2157 | 0 | CPLJSonStreamingWriter serializer( |
2158 | 0 | psOptions->bStdoutOutput ? WriteToStdout : nullptr, nullptr); |
2159 | 0 | serializer.SetPrettyFormatting(psOptions->bPretty); |
2160 | 0 | GDALDataset *poDS = GDALDataset::FromHandle(hDataset); |
2161 | 0 | auto group = poDS->GetRootGroup(); |
2162 | 0 | if (!group) |
2163 | 0 | return nullptr; |
2164 | | |
2165 | 0 | std::set<std::string> alreadyDumpedDimensions; |
2166 | 0 | std::set<std::string> alreadyDumpedArrays; |
2167 | |
|
2168 | 0 | try |
2169 | 0 | { |
2170 | 0 | const char *pszDriverName = nullptr; |
2171 | 0 | auto poDriver = poDS->GetDriver(); |
2172 | 0 | if (poDriver) |
2173 | 0 | pszDriverName = poDriver->GetDescription(); |
2174 | |
|
2175 | 0 | if (psOptions->osFormat == "text") |
2176 | 0 | { |
2177 | 0 | return DumpAsText(group, pszDriverName, psOptions); |
2178 | 0 | } |
2179 | 0 | else |
2180 | 0 | { |
2181 | 0 | if (psOptions->osArrayName.empty()) |
2182 | 0 | { |
2183 | 0 | DumpGroup(group, group, pszDriverName, serializer, psOptions, |
2184 | 0 | alreadyDumpedDimensions, alreadyDumpedArrays, true, |
2185 | 0 | true); |
2186 | 0 | } |
2187 | 0 | else |
2188 | 0 | { |
2189 | 0 | auto curGroup = group; |
2190 | 0 | CPLStringList aosTokens( |
2191 | 0 | CSLTokenizeString2(psOptions->osArrayName.c_str(), "/", 0)); |
2192 | 0 | for (int i = 0; i < aosTokens.size() - 1; i++) |
2193 | 0 | { |
2194 | 0 | auto curGroupNew = curGroup->OpenGroup(aosTokens[i]); |
2195 | 0 | if (!curGroupNew) |
2196 | 0 | { |
2197 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
2198 | 0 | "Cannot find group %s", aosTokens[i]); |
2199 | 0 | return nullptr; |
2200 | 0 | } |
2201 | 0 | curGroup = std::move(curGroupNew); |
2202 | 0 | } |
2203 | 0 | const char *pszArrayName = aosTokens.back(); |
2204 | 0 | auto array(curGroup->OpenMDArray(pszArrayName)); |
2205 | 0 | if (!array) |
2206 | 0 | { |
2207 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
2208 | 0 | "Cannot find array %s", pszArrayName); |
2209 | 0 | return nullptr; |
2210 | 0 | } |
2211 | 0 | DumpArray(group, array, serializer, psOptions, |
2212 | 0 | alreadyDumpedDimensions, alreadyDumpedArrays, true, |
2213 | 0 | true, true); |
2214 | 0 | } |
2215 | | |
2216 | 0 | if (psOptions->bStdoutOutput) |
2217 | 0 | { |
2218 | 0 | printf("\n"); |
2219 | 0 | return VSIStrdup("ok"); |
2220 | 0 | } |
2221 | 0 | else |
2222 | 0 | { |
2223 | 0 | return VSIStrdup(serializer.GetString().c_str()); |
2224 | 0 | } |
2225 | 0 | } |
2226 | 0 | } |
2227 | 0 | catch (const std::exception &e) |
2228 | 0 | { |
2229 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what()); |
2230 | 0 | return nullptr; |
2231 | 0 | } |
2232 | 0 | } |
2233 | | |
2234 | | /************************************************************************/ |
2235 | | /* GDALMultiDimInfoOptionsNew() */ |
2236 | | /************************************************************************/ |
2237 | | |
2238 | | /** |
2239 | | * Allocates a GDALMultiDimInfo struct. |
2240 | | * |
2241 | | * @param papszArgv NULL terminated list of options (potentially including |
2242 | | * filename and open options too), or NULL. The accepted options are the ones of |
2243 | | * the <a href="/programs/gdalmdiminfo.html">gdalmdiminfo</a> utility. |
2244 | | * @param psOptionsForBinary should be nullptr, unless called from |
2245 | | * gdalmultidiminfo_bin.cpp |
2246 | | * @return pointer to the allocated GDALMultiDimInfoOptions struct. Must be |
2247 | | * freed with GDALMultiDimInfoOptionsFree(). |
2248 | | * |
2249 | | * @since GDAL 3.1 |
2250 | | */ |
2251 | | |
2252 | | GDALMultiDimInfoOptions * |
2253 | | GDALMultiDimInfoOptionsNew(char **papszArgv, |
2254 | | GDALMultiDimInfoOptionsForBinary *psOptionsForBinary) |
2255 | 0 | { |
2256 | 0 | auto psOptions = std::make_unique<GDALMultiDimInfoOptions>(); |
2257 | | |
2258 | | /* -------------------------------------------------------------------- */ |
2259 | | /* Parse arguments. */ |
2260 | | /* -------------------------------------------------------------------- */ |
2261 | |
|
2262 | 0 | CPLStringList aosArgv; |
2263 | |
|
2264 | 0 | if (papszArgv) |
2265 | 0 | { |
2266 | 0 | const int nArgc = CSLCount(papszArgv); |
2267 | 0 | for (int i = 0; i < nArgc; i++) |
2268 | 0 | aosArgv.AddString(papszArgv[i]); |
2269 | 0 | } |
2270 | |
|
2271 | 0 | try |
2272 | 0 | { |
2273 | 0 | auto argParser = GDALMultiDimInfoAppOptionsGetParser( |
2274 | 0 | psOptions.get(), psOptionsForBinary); |
2275 | 0 | argParser->parse_args_without_binary_name(aosArgv); |
2276 | 0 | } |
2277 | 0 | catch (const std::exception &err) |
2278 | 0 | { |
2279 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s", |
2280 | 0 | err.what()); |
2281 | 0 | return nullptr; |
2282 | 0 | } |
2283 | | |
2284 | 0 | return psOptions.release(); |
2285 | 0 | } |
2286 | | |
2287 | | /************************************************************************/ |
2288 | | /* GDALMultiDimInfoOptionsFree() */ |
2289 | | /************************************************************************/ |
2290 | | |
2291 | | /** |
2292 | | * Frees the GDALMultiDimInfoOptions struct. |
2293 | | * |
2294 | | * @param psOptions the options struct for GDALMultiDimInfo(). |
2295 | | * |
2296 | | * @since GDAL 3.1 |
2297 | | */ |
2298 | | |
2299 | | void GDALMultiDimInfoOptionsFree(GDALMultiDimInfoOptions *psOptions) |
2300 | 0 | { |
2301 | 0 | delete psOptions; |
2302 | 0 | } |