1 /******************************************************************************
2 *
3 * Project: GDAL Utilities
4 * Purpose: Command line application to list info about a multidimensional raster
5 * Author: Even Rouault,<even.rouault at spatialys.com>
6 *
7 * ****************************************************************************
8 * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a
11 * copy of this software and associated documentation files (the "Software"),
12 * to deal in the Software without restriction, including without limitation
13 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 * and/or sell copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included
18 * in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 * DEALINGS IN THE SOFTWARE.
27 ****************************************************************************/
28
29 #include "cpl_port.h"
30 #include "gdal_utils.h"
31 #include "gdal_utils_priv.h"
32
33 #include "cpl_json_streaming_writer.h"
34 #include "gdal_priv.h"
35 #include <limits>
36 #include <set>
37
38 CPL_CVSID("$Id: gdalmdiminfo_lib.cpp 2e6733101e6356653e8c999cb7fe808abc22ed11 2020-08-11 14:57:51 +0200 Even Rouault $")
39
40 /************************************************************************/
41 /* GDALMultiDimInfoOptions */
42 /************************************************************************/
43
44 struct GDALMultiDimInfoOptions
45 {
46 bool bStdoutOutput = false;
47 bool bDetailed = false;
48 bool bPretty = true;
49 size_t nLimitValuesByDim = 0;
50 CPLStringList aosArrayOptions{};
51 std::string osArrayName{};
52 bool bStats = false;
53 };
54
55 /************************************************************************/
56 /* HasUniqueNames() */
57 /************************************************************************/
58
HasUniqueNames(const std::vector<std::string> & oNames)59 static bool HasUniqueNames(const std::vector<std::string>& oNames)
60 {
61 std::set<std::string> oSetNames;
62 for( const auto& subgroupName: oNames )
63 {
64 if( oSetNames.find(subgroupName) != oSetNames.end() )
65 {
66 return false;
67 }
68 oSetNames.insert(subgroupName);
69 }
70 return true;
71 }
72
73 /************************************************************************/
74 /* DumpDataType() */
75 /************************************************************************/
76
DumpDataType(const GDALExtendedDataType & dt,CPLJSonStreamingWriter & serializer)77 static void DumpDataType(const GDALExtendedDataType& dt,
78 CPLJSonStreamingWriter& serializer)
79 {
80 switch( dt.GetClass() )
81 {
82 case GEDTC_STRING:
83 serializer.Add("String");
84 break;
85
86 case GEDTC_NUMERIC:
87 serializer.Add( GDALGetDataTypeName(dt.GetNumericDataType()) );
88 break;
89
90 case GEDTC_COMPOUND:
91 {
92 auto compoundContext(serializer.MakeObjectContext());
93 serializer.AddObjKey("name");
94 serializer.Add(dt.GetName());
95 serializer.AddObjKey("size");
96 serializer.Add(static_cast<unsigned>(dt.GetSize()));
97 serializer.AddObjKey("components");
98 const auto& components = dt.GetComponents();
99 auto componentsContext(serializer.MakeArrayContext());
100 for( const auto& comp: components )
101 {
102 auto compContext(serializer.MakeObjectContext());
103 serializer.AddObjKey("name");
104 serializer.Add(comp->GetName());
105 serializer.AddObjKey("offset");
106 serializer.Add(static_cast<unsigned>(comp->GetOffset()));
107 serializer.AddObjKey("type");
108 DumpDataType(comp->GetType(), serializer);
109 }
110 break;
111 }
112 }
113 }
114
115 /************************************************************************/
116 /* DumpValue() */
117 /************************************************************************/
118
119 template<typename T>
DumpValue(CPLJSonStreamingWriter & serializer,const void * bytes)120 static void DumpValue(CPLJSonStreamingWriter& serializer,
121 const void* bytes)
122 {
123 T tmp;
124 memcpy(&tmp, bytes, sizeof(T));
125 serializer.Add(tmp);
126 }
127
128 /************************************************************************/
129 /* DumpComplexValue() */
130 /************************************************************************/
131
132 template<typename T>
DumpComplexValue(CPLJSonStreamingWriter & serializer,const GByte * bytes)133 static void DumpComplexValue(CPLJSonStreamingWriter& serializer,
134 const GByte* bytes)
135 {
136 auto objectContext(serializer.MakeObjectContext());
137 serializer.AddObjKey("real");
138 DumpValue<T>(serializer, bytes);
139 serializer.AddObjKey("imag");
140 DumpValue<T>(serializer, bytes + sizeof(T));
141 }
142
143 /************************************************************************/
144 /* DumpValue() */
145 /************************************************************************/
146
DumpValue(CPLJSonStreamingWriter & serializer,const GByte * bytes,const GDALDataType & eDT)147 static void DumpValue(CPLJSonStreamingWriter& serializer,
148 const GByte* bytes,
149 const GDALDataType& eDT)
150 {
151 switch(eDT)
152 {
153 case GDT_Byte:
154 DumpValue<GByte>(serializer, bytes);
155 break;
156 case GDT_Int16:
157 DumpValue<GInt16>(serializer, bytes);
158 break;
159 case GDT_UInt16:
160 DumpValue<GUInt16>(serializer, bytes);
161 break;
162 case GDT_Int32:
163 DumpValue<GInt32>(serializer, bytes);
164 break;
165 case GDT_UInt32:
166 DumpValue<GUInt32>(serializer, bytes);
167 break;
168 case GDT_Float32:
169 DumpValue<float>(serializer, bytes);
170 break;
171 case GDT_Float64:
172 DumpValue<double>(serializer, bytes);
173 break;
174 case GDT_CInt16:
175 DumpComplexValue<GInt16>(serializer, bytes);
176 break;
177 case GDT_CInt32:
178 DumpComplexValue<GInt32>(serializer, bytes);
179 break;
180 case GDT_CFloat32:
181 DumpComplexValue<float>(serializer, bytes);
182 break;
183 case GDT_CFloat64:
184 DumpComplexValue<double>(serializer, bytes);
185 break;
186 default:
187 CPLAssert(false);
188 break;
189 }
190 }
191
192 static void DumpValue(CPLJSonStreamingWriter& serializer,
193 const GByte* values,
194 const GDALExtendedDataType& dt);
195
196 /************************************************************************/
197 /* DumpCompound() */
198 /************************************************************************/
199
DumpCompound(CPLJSonStreamingWriter & serializer,const GByte * values,const GDALExtendedDataType & dt)200 static void DumpCompound(CPLJSonStreamingWriter& serializer,
201 const GByte* values,
202 const GDALExtendedDataType& dt)
203 {
204 CPLAssert(dt.GetClass() == GEDTC_COMPOUND);
205 const auto& components = dt.GetComponents();
206 auto objectContext(serializer.MakeObjectContext());
207 for( const auto& comp: components )
208 {
209 serializer.AddObjKey(comp->GetName());
210 DumpValue(serializer, values + comp->GetOffset(), comp->GetType());
211 }
212 }
213
214 /************************************************************************/
215 /* DumpValue() */
216 /************************************************************************/
217
DumpValue(CPLJSonStreamingWriter & serializer,const GByte * values,const GDALExtendedDataType & dt)218 static void DumpValue(CPLJSonStreamingWriter& serializer,
219 const GByte* values,
220 const GDALExtendedDataType& dt)
221 {
222 switch( dt.GetClass() )
223 {
224 case GEDTC_NUMERIC:
225 DumpValue(serializer, values, dt.GetNumericDataType());
226 break;
227 case GEDTC_COMPOUND:
228 DumpCompound(serializer, values, dt);
229 break;
230 case GEDTC_STRING:
231 {
232 const char* pszStr;
233 // cppcheck-suppress pointerSize
234 memcpy(&pszStr, values, sizeof(const char*));
235 if( pszStr )
236 serializer.Add(pszStr);
237 else
238 serializer.AddNull();
239 break;
240 }
241 }
242 }
243
244 /************************************************************************/
245 /* DumpAttrValue() */
246 /************************************************************************/
247
DumpAttrValue(std::shared_ptr<GDALAttribute> attr,CPLJSonStreamingWriter & serializer)248 static void DumpAttrValue(std::shared_ptr<GDALAttribute> attr,
249 CPLJSonStreamingWriter& serializer)
250 {
251 const auto dt(attr->GetDataType());
252 const size_t nEltCount(static_cast<size_t>(attr->GetTotalElementsCount()));
253 switch( dt.GetClass() )
254 {
255 case GEDTC_STRING:
256 {
257 if( nEltCount == 1 )
258 {
259 const char* pszStr = attr->ReadAsString();
260 if( pszStr )
261 serializer.Add(pszStr);
262 else
263 serializer.AddNull();
264 }
265 else
266 {
267 CPLStringList aosValues(attr->ReadAsStringArray());
268 {
269 auto arrayContextValues(serializer.MakeArrayContext(nEltCount < 10));
270 for( int i = 0; i < aosValues.size(); ++i )
271 {
272 serializer.Add(aosValues[i]);
273 }
274 }
275 }
276 break;
277 }
278
279 case GEDTC_NUMERIC:
280 {
281 auto eDT = dt.GetNumericDataType();
282 const auto rawValues(attr->ReadAsRaw());
283 const GByte* bytePtr = rawValues.data();
284 if( bytePtr )
285 {
286 const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
287 if( nEltCount == 1 )
288 {
289 serializer.SetNewline(false);
290 DumpValue(serializer, rawValues.data(), eDT);
291 serializer.SetNewline(true);
292 }
293 else
294 {
295 auto arrayContextValues(serializer.MakeArrayContext(nEltCount < 10));
296 for( size_t i = 0; i < nEltCount; i++ )
297 {
298 DumpValue(serializer, bytePtr, eDT);
299 bytePtr += nDTSize;
300 }
301 }
302 }
303 else
304 {
305 serializer.AddNull();
306 }
307 break;
308 }
309
310 case GEDTC_COMPOUND:
311 {
312 auto rawValues(attr->ReadAsRaw());
313 const GByte* bytePtr = rawValues.data();
314 if( bytePtr )
315 {
316 if( nEltCount == 1 )
317 {
318 serializer.SetNewline(false);
319 DumpCompound(serializer, bytePtr, dt);
320 serializer.SetNewline(true);
321 }
322 else
323 {
324 auto arrayContextValues(serializer.MakeArrayContext());
325 for( size_t i = 0; i < nEltCount; i++ )
326 {
327 DumpCompound(serializer, bytePtr, dt);
328 bytePtr += dt.GetSize();
329 }
330 }
331 }
332 else
333 {
334 serializer.AddNull();
335 }
336 break;
337 }
338 }
339 }
340
341 /************************************************************************/
342 /* DumpAttr() */
343 /************************************************************************/
344
DumpAttr(std::shared_ptr<GDALAttribute> attr,CPLJSonStreamingWriter & serializer,const GDALMultiDimInfoOptions * psOptions,bool bOutputObjType,bool bOutputName)345 static void DumpAttr(std::shared_ptr<GDALAttribute> attr,
346 CPLJSonStreamingWriter& serializer,
347 const GDALMultiDimInfoOptions *psOptions,
348 bool bOutputObjType, bool bOutputName)
349 {
350 if( !bOutputObjType && !bOutputName && !psOptions->bDetailed )
351 {
352 DumpAttrValue(attr, serializer);
353 return;
354 }
355
356 const auto dt(attr->GetDataType());
357 auto objectContext(serializer.MakeObjectContext());
358 if( bOutputObjType )
359 {
360 serializer.AddObjKey("type");
361 serializer.Add("attribute");
362 }
363 if( bOutputName )
364 {
365 serializer.AddObjKey("name");
366 serializer.Add(attr->GetName());
367 }
368
369 if( psOptions->bDetailed )
370 {
371 serializer.AddObjKey("datatype");
372 DumpDataType(dt, serializer);
373
374 serializer.AddObjKey("value");
375 }
376
377 DumpAttrValue(attr, serializer);
378 }
379
380 /************************************************************************/
381 /* DumpAttrs() */
382 /************************************************************************/
383
DumpAttrs(const std::vector<std::shared_ptr<GDALAttribute>> & attrs,CPLJSonStreamingWriter & serializer,const GDALMultiDimInfoOptions * psOptions)384 static void DumpAttrs(const std::vector<std::shared_ptr<GDALAttribute>>& attrs,
385 CPLJSonStreamingWriter& serializer,
386 const GDALMultiDimInfoOptions *psOptions)
387 {
388 std::vector<std::string> attributeNames;
389 for( const auto& poAttr: attrs )
390 attributeNames.emplace_back(poAttr->GetName());
391 if( HasUniqueNames(attributeNames) )
392 {
393 auto objectContext(serializer.MakeObjectContext());
394 for( const auto& poAttr: attrs )
395 {
396 serializer.AddObjKey(poAttr->GetName());
397 DumpAttr( poAttr, serializer, psOptions, false, false );
398 }
399 }
400 else
401 {
402 auto arrayContext(serializer.MakeArrayContext());
403 for( const auto& poAttr: attrs )
404 {
405 DumpAttr( poAttr, serializer, psOptions, false, true );
406 }
407 }
408 }
409
410 /************************************************************************/
411 /* DumpArrayRec() */
412 /************************************************************************/
413
DumpArrayRec(std::shared_ptr<GDALMDArray> array,CPLJSonStreamingWriter & serializer,size_t nCurDim,const std::vector<GUInt64> & dimSizes,std::vector<GUInt64> & startIdx,const GDALMultiDimInfoOptions * psOptions)414 static void DumpArrayRec(std::shared_ptr<GDALMDArray> array,
415 CPLJSonStreamingWriter& serializer,
416 size_t nCurDim,
417 const std::vector<GUInt64>& dimSizes,
418 std::vector<GUInt64>& startIdx,
419 const GDALMultiDimInfoOptions *psOptions)
420 {
421 do
422 {
423 auto arrayContext(serializer.MakeArrayContext());
424 if( nCurDim + 1 == dimSizes.size() )
425 {
426 const auto dt(array->GetDataType());
427 const auto nDTSize(dt.GetSize());
428 const auto lambdaDumpValue = [&serializer, &dt, nDTSize](std::vector<GByte>& abyTmp, size_t nCount)
429 {
430 GByte* pabyPtr = &abyTmp[0];
431 for( size_t i = 0; i < nCount; ++i )
432 {
433 DumpValue(serializer, pabyPtr, dt);
434 dt.FreeDynamicMemory(pabyPtr);
435 pabyPtr += nDTSize;
436 }
437 };
438
439 serializer.SetNewline(false);
440 std::vector<size_t> count(dimSizes.size(), 1);
441 if( psOptions->nLimitValuesByDim == 0 ||
442 dimSizes.back() <= psOptions->nLimitValuesByDim )
443 {
444 const size_t nCount = static_cast<size_t>(dimSizes.back());
445 if( nCount > 0 )
446 {
447 if( nCount != dimSizes.back() ||
448 nDTSize > std::numeric_limits<size_t>::max() / nCount )
449 {
450 serializer.Add("[too many values]");
451 break;
452 }
453 std::vector<GByte> abyTmp(nDTSize * nCount);
454 count.back() = nCount;
455 if( !array->Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &abyTmp[0]) )
456 break;
457 lambdaDumpValue(abyTmp, count.back());
458 }
459 }
460 else
461 {
462 std::vector<GByte> abyTmp(nDTSize * (psOptions->nLimitValuesByDim + 1) / 2);
463 startIdx.back() = 0;
464 size_t nStartCount = (psOptions->nLimitValuesByDim + 1) / 2;
465 count.back() = nStartCount;
466 if( !array->Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &abyTmp[0]) )
467 break;
468 lambdaDumpValue(abyTmp, count.back());
469 serializer.Add("[...]");
470
471 count.back() = psOptions->nLimitValuesByDim / 2;
472 if( count.back() )
473 {
474 startIdx.back() = dimSizes.back() - count.back();
475 if( !array->Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &abyTmp[0]) )
476 break;
477 lambdaDumpValue(abyTmp, count.back());
478 }
479 }
480 }
481 else
482 {
483 if( psOptions->nLimitValuesByDim == 0 ||
484 dimSizes[nCurDim] <= psOptions->nLimitValuesByDim )
485 {
486 for( startIdx[nCurDim] = 0;
487 startIdx[nCurDim] < dimSizes[nCurDim];
488 ++startIdx[nCurDim] )
489 {
490 DumpArrayRec(array, serializer, nCurDim + 1, dimSizes,
491 startIdx, psOptions);
492 }
493 }
494 else
495 {
496 size_t nStartCount = (psOptions->nLimitValuesByDim + 1) / 2;
497 for( startIdx[nCurDim] = 0;
498 startIdx[nCurDim] < nStartCount;
499 ++startIdx[nCurDim] )
500 {
501 DumpArrayRec(array, serializer, nCurDim + 1, dimSizes,
502 startIdx, psOptions);
503 }
504 serializer.Add("[...]");
505 size_t nEndCount = psOptions->nLimitValuesByDim / 2;
506 for( startIdx[nCurDim] = dimSizes[nCurDim] - nEndCount;
507 startIdx[nCurDim] < dimSizes[nCurDim];
508 ++startIdx[nCurDim] )
509 {
510 DumpArrayRec(array, serializer, nCurDim + 1, dimSizes,
511 startIdx, psOptions);
512 }
513 }
514 }
515 } while(false);
516 serializer.SetNewline(true);
517 }
518
519 /************************************************************************/
520 /* DumpDimensions() */
521 /************************************************************************/
522
DumpDimensions(const std::vector<std::shared_ptr<GDALDimension>> & dims,CPLJSonStreamingWriter & serializer,const GDALMultiDimInfoOptions *,std::set<std::string> & alreadyDumpedDimensions)523 static void DumpDimensions(const std::vector<std::shared_ptr<GDALDimension>>& dims,
524 CPLJSonStreamingWriter& serializer,
525 const GDALMultiDimInfoOptions *,
526 std::set<std::string>& alreadyDumpedDimensions)
527 {
528 auto arrayContext(serializer.MakeArrayContext());
529 for( const auto& dim: dims )
530 {
531 const std::string osFullname(dim->GetFullName());
532 if( alreadyDumpedDimensions.find(osFullname) !=
533 alreadyDumpedDimensions.end() )
534 {
535 serializer.Add(osFullname);
536 continue;
537 }
538
539 auto dimObjectContext(serializer.MakeObjectContext());
540 if( !osFullname.empty() && osFullname[0] == '/' )
541 alreadyDumpedDimensions.insert(osFullname);
542
543 serializer.AddObjKey("name");
544 serializer.Add(dim->GetName());
545
546 serializer.AddObjKey("full_name");
547 serializer.Add(osFullname);
548
549 serializer.AddObjKey("size");
550 serializer.Add(dim->GetSize());
551
552 const auto& type(dim->GetType());
553 if( !type.empty() )
554 {
555 serializer.AddObjKey("type");
556 serializer.Add(type);
557 }
558
559 const auto& direction(dim->GetDirection());
560 if( !direction.empty() )
561 {
562 serializer.AddObjKey("direction");
563 serializer.Add(direction);
564 }
565
566 auto poIndexingVariable(dim->GetIndexingVariable());
567 if( poIndexingVariable )
568 {
569 serializer.AddObjKey("indexing_variable");
570 serializer.Add(poIndexingVariable->GetFullName());
571 }
572 }
573 }
574
575 /************************************************************************/
576 /* DumpStructuralInfo() */
577 /************************************************************************/
578
DumpStructuralInfo(CSLConstList papszStructuralInfo,CPLJSonStreamingWriter & serializer)579 static void DumpStructuralInfo(CSLConstList papszStructuralInfo,
580 CPLJSonStreamingWriter& serializer)
581 {
582 auto objectContext(serializer.MakeObjectContext());
583 for(int i = 0; papszStructuralInfo && papszStructuralInfo[i]; ++i )
584 {
585 char* pszKey = nullptr;
586 const char* pszValue = CPLParseNameValue(papszStructuralInfo[i], &pszKey);
587 if( pszKey )
588 {
589 serializer.AddObjKey(pszKey);
590 }
591 else
592 {
593 serializer.AddObjKey(CPLSPrintf("metadata_%d", i+1));
594 }
595 serializer.Add(pszValue);
596 CPLFree(pszKey);
597 }
598 }
599
600 /************************************************************************/
601 /* DumpArray() */
602 /************************************************************************/
603
DumpArray(GDALDataset * poDS,std::shared_ptr<GDALMDArray> array,CPLJSonStreamingWriter & serializer,const GDALMultiDimInfoOptions * psOptions,std::set<std::string> & alreadyDumpedDimensions,bool bOutputObjType,bool bOutputName)604 static void DumpArray(GDALDataset* poDS,
605 std::shared_ptr<GDALMDArray> array,
606 CPLJSonStreamingWriter& serializer,
607 const GDALMultiDimInfoOptions *psOptions,
608 std::set<std::string>& alreadyDumpedDimensions,
609 bool bOutputObjType, bool bOutputName)
610 {
611 auto objectContext(serializer.MakeObjectContext());
612 if( bOutputObjType )
613 {
614 serializer.AddObjKey("type");
615 serializer.Add("array");
616 }
617 if( bOutputName )
618 {
619 serializer.AddObjKey("name");
620 serializer.Add(array->GetName());
621 }
622
623 serializer.AddObjKey("datatype");
624 const auto dt(array->GetDataType());
625 DumpDataType(dt, serializer);
626
627 auto dims = array->GetDimensions();
628 if( !dims.empty() )
629 {
630 serializer.AddObjKey("dimensions");
631 DumpDimensions(dims, serializer, psOptions, alreadyDumpedDimensions);
632 }
633
634 CPLStringList aosOptions;
635 if( psOptions->bDetailed )
636 aosOptions.SetNameValue("SHOW_ALL", "YES");
637 auto attrs = array->GetAttributes(aosOptions.List());
638 if( !attrs.empty() )
639 {
640 serializer.AddObjKey("attributes");
641 DumpAttrs(attrs, serializer, psOptions);
642 }
643
644 auto unit = array->GetUnit();
645 if( !unit.empty() )
646 {
647 serializer.AddObjKey("unit");
648 serializer.Add(unit);
649 }
650
651 auto nodata = array->GetRawNoDataValue();
652 if( nodata )
653 {
654 serializer.AddObjKey("nodata_value");
655 DumpValue(serializer, static_cast<const GByte*>(nodata), dt);
656 }
657
658 bool bValid = false;
659 double dfOffset = array->GetOffset(&bValid);
660 if( bValid )
661 {
662 serializer.AddObjKey("offset");
663 serializer.Add(dfOffset);
664 }
665 double dfScale = array->GetScale(&bValid);
666 if( bValid )
667 {
668 serializer.AddObjKey("scale");
669 serializer.Add(dfScale);
670 }
671
672 auto srs = array->GetSpatialRef();
673 if( srs )
674 {
675 char* pszWKT = nullptr;
676 CPLStringList wktOptions;
677 wktOptions.SetNameValue("FORMAT", "WKT2_2018");
678 if( srs->exportToWkt(&pszWKT, wktOptions.List()) == OGRERR_NONE )
679 {
680 serializer.AddObjKey("srs");
681 {
682 auto srsContext(serializer.MakeObjectContext());
683 serializer.AddObjKey("wkt");
684 serializer.Add(pszWKT);
685 serializer.AddObjKey("data_axis_to_srs_axis_mapping");
686 {
687 auto dataAxisContext(serializer.MakeArrayContext(true));
688 auto mapping = srs->GetDataAxisToSRSAxisMapping();
689 for( const auto& axisNumber: mapping )
690 serializer.Add(axisNumber);
691 }
692 }
693 }
694 CPLFree(pszWKT);
695 }
696
697 auto papszStructuralInfo = array->GetStructuralInfo();
698 if( papszStructuralInfo )
699 {
700 serializer.AddObjKey("structural_info");
701 DumpStructuralInfo(papszStructuralInfo, serializer);
702 }
703
704 if( psOptions->bDetailed )
705 {
706 serializer.AddObjKey("values");
707 if( dims.empty() )
708 {
709 std::vector<GByte> abyTmp(dt.GetSize());
710 array->Read(nullptr, nullptr, nullptr, nullptr, dt, &abyTmp[0]);
711 DumpValue(serializer, &abyTmp[0], dt);
712 }
713 else
714 {
715 std::vector<GUInt64> startIdx(dims.size());
716 std::vector<GUInt64> dimSizes;
717 for( const auto& dim: dims )
718 dimSizes.emplace_back(dim->GetSize());
719 DumpArrayRec(array, serializer, 0, dimSizes, startIdx, psOptions);
720 }
721 }
722
723 if( psOptions->bStats )
724 {
725 double dfMin = 0.0;
726 double dfMax = 0.0;
727 double dfMean = 0.0;
728 double dfStdDev = 0.0;
729 GUInt64 nValidCount = 0;
730 if( array->GetStatistics( poDS, false, true,
731 &dfMin, &dfMax, &dfMean, &dfStdDev,
732 &nValidCount,
733 nullptr, nullptr ) == CE_None )
734 {
735 serializer.AddObjKey("statistics");
736 auto statContext(serializer.MakeObjectContext());
737 if( nValidCount > 0 )
738 {
739 serializer.AddObjKey("min");
740 serializer.Add(dfMin);
741
742 serializer.AddObjKey("max");
743 serializer.Add(dfMax);
744
745 serializer.AddObjKey("mean");
746 serializer.Add(dfMean);
747
748 serializer.AddObjKey("stddev");
749 serializer.Add(dfStdDev);
750 }
751
752 serializer.AddObjKey("valid_sample_count");
753 serializer.Add(nValidCount);
754 }
755 }
756 }
757
758 /************************************************************************/
759 /* DumpArrays() */
760 /************************************************************************/
761
DumpArrays(GDALDataset * poDS,std::shared_ptr<GDALGroup> group,const std::vector<std::string> & arrayNames,CPLJSonStreamingWriter & serializer,const GDALMultiDimInfoOptions * psOptions,std::set<std::string> & alreadyDumpedDimensions)762 static void DumpArrays(GDALDataset* poDS,
763 std::shared_ptr<GDALGroup> group,
764 const std::vector<std::string>& arrayNames,
765 CPLJSonStreamingWriter& serializer,
766 const GDALMultiDimInfoOptions *psOptions,
767 std::set<std::string>& alreadyDumpedDimensions)
768 {
769 std::set<std::string> oSetNames;
770 auto objectContext(serializer.MakeObjectContext());
771 for( const auto& name: arrayNames )
772 {
773 if( oSetNames.find(name) != oSetNames.end() )
774 continue; // should not happen on well behaved drivers
775 oSetNames.insert(name);
776 auto array = group->OpenMDArray(name);
777 if( array )
778 {
779 serializer.AddObjKey(array->GetName());
780 DumpArray( poDS, array, serializer, psOptions,
781 alreadyDumpedDimensions, false, false );
782 }
783 }
784 }
785
786 /************************************************************************/
787 /* DumpGroup() */
788 /************************************************************************/
789
DumpGroup(GDALDataset * poDS,std::shared_ptr<GDALGroup> group,const char * pszDriverName,CPLJSonStreamingWriter & serializer,const GDALMultiDimInfoOptions * psOptions,std::set<std::string> & alreadyDumpedDimensions,bool bOutputObjType,bool bOutputName)790 static void DumpGroup(GDALDataset* poDS,
791 std::shared_ptr<GDALGroup> group,
792 const char* pszDriverName,
793 CPLJSonStreamingWriter& serializer,
794 const GDALMultiDimInfoOptions *psOptions,
795 std::set<std::string>& alreadyDumpedDimensions,
796 bool bOutputObjType, bool bOutputName)
797 {
798 auto objectContext(serializer.MakeObjectContext());
799 if( bOutputObjType )
800 {
801 serializer.AddObjKey("type");
802 serializer.Add("group");
803 }
804 if( pszDriverName )
805 {
806 serializer.AddObjKey("driver");
807 serializer.Add(pszDriverName);
808 }
809 if( bOutputName )
810 {
811 serializer.AddObjKey("name");
812 serializer.Add(group->GetName());
813 }
814
815 CPLStringList aosOptionsGetAttr;
816 if( psOptions->bDetailed )
817 aosOptionsGetAttr.SetNameValue("SHOW_ALL", "YES");
818 auto attrs = group->GetAttributes(aosOptionsGetAttr.List());
819 if( !attrs.empty() )
820 {
821 serializer.AddObjKey("attributes");
822 DumpAttrs(attrs, serializer, psOptions);
823 }
824
825 auto dims = group->GetDimensions();
826 if( !dims.empty() )
827 {
828 serializer.AddObjKey("dimensions");
829 DumpDimensions(dims, serializer, psOptions, alreadyDumpedDimensions);
830 }
831
832 CPLStringList aosOptionsGetArray(psOptions->aosArrayOptions);
833 if( psOptions->bDetailed )
834 aosOptionsGetArray.SetNameValue("SHOW_ALL", "YES");
835 auto arrayNames = group->GetMDArrayNames(aosOptionsGetArray.List());
836 if( !arrayNames.empty() )
837 {
838 serializer.AddObjKey("arrays");
839 DumpArrays(poDS, group, arrayNames, serializer, psOptions,
840 alreadyDumpedDimensions);
841 }
842
843 auto papszStructuralInfo = group->GetStructuralInfo();
844 if( papszStructuralInfo )
845 {
846 serializer.AddObjKey("structural_info");
847 DumpStructuralInfo(papszStructuralInfo, serializer);
848 }
849
850 auto subgroupNames = group->GetGroupNames();
851 if( !subgroupNames.empty() )
852 {
853 serializer.AddObjKey("groups");
854 if( HasUniqueNames(subgroupNames) )
855 {
856 auto groupContext(serializer.MakeObjectContext());
857 for( const auto& subgroupName: subgroupNames )
858 {
859 auto subgroup = group->OpenGroup(subgroupName);
860 if( subgroup )
861 {
862 serializer.AddObjKey(subgroupName);
863 DumpGroup( poDS, subgroup, nullptr, serializer, psOptions,
864 alreadyDumpedDimensions, false, false );
865 }
866 }
867 }
868 else
869 {
870 auto arrayContext(serializer.MakeArrayContext());
871 for( const auto& subgroupName: subgroupNames )
872 {
873 auto subgroup = group->OpenGroup(subgroupName);
874 if( subgroup )
875 {
876 DumpGroup( poDS, subgroup, nullptr, serializer, psOptions,
877 alreadyDumpedDimensions, false, true );
878 }
879 }
880 }
881 }
882 }
883
884 /************************************************************************/
885 /* WriteToStdout() */
886 /************************************************************************/
887
WriteToStdout(const char * pszText,void *)888 static void WriteToStdout(const char* pszText, void*)
889 {
890 printf("%s", pszText);
891 }
892
893 /************************************************************************/
894 /* GDALMultiDimInfo() */
895 /************************************************************************/
896
897 /**
898 * Lists various information about a GDAL multidimensional dataset.
899 *
900 * This is the equivalent of the <a href="/programs/gdalmdiminfo.html">gdalmdiminfo</a>utility.
901 *
902 * GDALMultiDimInfoOptions* must be allocated and freed with GDALMultiDimInfoOptionsNew()
903 * and GDALMultiDimInfoOptionsFree() respectively.
904 *
905 * @param hDataset the dataset handle.
906 * @param psOptionsIn the options structure returned by GDALMultiDimInfoOptionsNew() or NULL.
907 * @return string corresponding to the information about the raster dataset (must be freed with CPLFree()), or NULL in case of error.
908 *
909 * @since GDAL 3.1
910 */
911
GDALMultiDimInfo(GDALDatasetH hDataset,const GDALMultiDimInfoOptions * psOptionsIn)912 char *GDALMultiDimInfo( GDALDatasetH hDataset,
913 const GDALMultiDimInfoOptions *psOptionsIn )
914 {
915 if( hDataset == nullptr )
916 return nullptr;
917
918 GDALMultiDimInfoOptions oOptionsDefault;
919 const GDALMultiDimInfoOptions* psOptions = psOptionsIn ? psOptionsIn : &oOptionsDefault;
920 CPLJSonStreamingWriter serializer(
921 psOptions->bStdoutOutput ? WriteToStdout : nullptr ,
922 nullptr);
923 serializer.SetPrettyFormatting(psOptions->bPretty);
924 GDALDataset* poDS = GDALDataset::FromHandle(hDataset);
925 auto group = poDS->GetRootGroup();
926 if( !group )
927 return nullptr;
928
929 std::set<std::string> alreadyDumpedDimensions;
930 try
931 {
932 if( psOptions->osArrayName.empty() )
933 {
934 const char* pszDriverName = nullptr;
935 auto poDriver = poDS->GetDriver();
936 if( poDriver )
937 pszDriverName = poDriver->GetDescription();
938 DumpGroup(poDS, group, pszDriverName, serializer, psOptions,
939 alreadyDumpedDimensions, true, true);
940 }
941 else
942 {
943 auto curGroup = group;
944 CPLStringList aosTokens(CSLTokenizeString2(
945 psOptions->osArrayName.c_str(), "/", 0));
946 for( int i = 0; i < aosTokens.size() - 1; i++ )
947 {
948 curGroup = curGroup->OpenGroup(aosTokens[i]);
949 if( !curGroup )
950 {
951 CPLError(CE_Failure, CPLE_AppDefined,
952 "Cannot find group %s", aosTokens[i]);
953 return nullptr;
954 }
955 }
956 const char* pszArrayName = aosTokens[aosTokens.size()-1];
957 auto array(curGroup->OpenMDArray(pszArrayName));
958 if( !array )
959 {
960 CPLError(CE_Failure, CPLE_AppDefined,
961 "Cannot find array %s", pszArrayName);
962 return nullptr;
963 }
964 DumpArray(poDS, array, serializer, psOptions,
965 alreadyDumpedDimensions, true, true);
966 }
967 }
968 catch( const std::exception& e)
969 {
970 CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
971 return nullptr;
972 }
973
974 if( psOptions->bStdoutOutput)
975 {
976 printf("\n");
977 }
978 else
979 {
980 return VSIStrdup(serializer.GetString().c_str());
981 }
982 return nullptr;
983 }
984
985 /************************************************************************/
986 /* GDALMultiDimInfoOptionsNew() */
987 /************************************************************************/
988
989 /**
990 * Allocates a GDALMultiDimInfo struct.
991 *
992 * @param papszArgv NULL terminated list of options (potentially including filename and open options too), or NULL.
993 * The accepted options are the ones of the <a href="/programs/gdalmdiminfo.html">gdalmdiminfo</a> utility.
994 * @param psOptionsForBinary (output) may be NULL (and should generally be NULL),
995 * otherwise (gdalmultidiminfo_bin.cpp use case) must be allocated with
996 * GDALMultiDimInfoOptionsForBinaryNew() prior to this function. Will be
997 * filled with potentially present filename, open options, subdataset number...
998 * @return pointer to the allocated GDALMultiDimInfoOptions struct. Must be freed with GDALMultiDimInfoOptionsFree().
999 *
1000 * @since GDAL 3.1
1001 */
1002
GDALMultiDimInfoOptionsNew(char ** papszArgv,GDALMultiDimInfoOptionsForBinary * psOptionsForBinary)1003 GDALMultiDimInfoOptions *GDALMultiDimInfoOptionsNew(
1004 char** papszArgv,
1005 GDALMultiDimInfoOptionsForBinary* psOptionsForBinary )
1006 {
1007 bool bGotFilename = false;
1008 GDALMultiDimInfoOptions *psOptions = new GDALMultiDimInfoOptions;
1009
1010 /* -------------------------------------------------------------------- */
1011 /* Parse arguments. */
1012 /* -------------------------------------------------------------------- */
1013 for( int i = 0; papszArgv != nullptr && papszArgv[i] != nullptr; i++ )
1014 {
1015 if( EQUAL(papszArgv[i], "-oo") && papszArgv[i+1] != nullptr )
1016 {
1017 i++;
1018 if( psOptionsForBinary )
1019 {
1020 psOptionsForBinary->papszOpenOptions = CSLAddString(
1021 psOptionsForBinary->papszOpenOptions, papszArgv[i] );
1022 }
1023 }
1024 /* Not documented: used by gdalinfo_bin.cpp only */
1025 else if( EQUAL(papszArgv[i], "-stdout") )
1026 psOptions->bStdoutOutput = true;
1027 else if( EQUAL(papszArgv[i], "-detailed") )
1028 psOptions->bDetailed = true;
1029 else if( EQUAL(papszArgv[i], "-nopretty") )
1030 psOptions->bPretty = false;
1031 else if( EQUAL(papszArgv[i], "-array") && papszArgv[i+1] != nullptr )
1032 {
1033 ++i;
1034 psOptions->osArrayName = papszArgv[i];
1035 }
1036 else if( EQUAL(papszArgv[i], "-arrayoption") && papszArgv[i+1] != nullptr )
1037 {
1038 ++i;
1039 psOptions->aosArrayOptions.AddString(papszArgv[i]);
1040 }
1041 else if( EQUAL(papszArgv[i], "-limit") && papszArgv[i+1] != nullptr )
1042 {
1043 ++i;
1044 psOptions->nLimitValuesByDim = atoi(papszArgv[i]);
1045 }
1046 else if( EQUAL(papszArgv[i], "-stats") )
1047 {
1048 psOptions->bStats = true;
1049 }
1050 else if( papszArgv[i][0] == '-' )
1051 {
1052 CPLError(CE_Failure, CPLE_NotSupported,
1053 "Unknown option name '%s'", papszArgv[i]);
1054 GDALMultiDimInfoOptionsFree(psOptions);
1055 return nullptr;
1056 }
1057 else if( !bGotFilename )
1058 {
1059 bGotFilename = true;
1060 if( psOptionsForBinary )
1061 psOptionsForBinary->pszFilename = CPLStrdup(papszArgv[i]);
1062 }
1063 else
1064 {
1065 CPLError(CE_Failure, CPLE_NotSupported,
1066 "Too many command options '%s'", papszArgv[i]);
1067 GDALMultiDimInfoOptionsFree(psOptions);
1068 return nullptr;
1069 }
1070 }
1071
1072 return psOptions;
1073 }
1074
1075 /************************************************************************/
1076 /* GDALMultiDimInfoOptionsFree() */
1077 /************************************************************************/
1078
1079 /**
1080 * Frees the GDALMultiDimInfoOptions struct.
1081 *
1082 * @param psOptions the options struct for GDALMultiDimInfo().
1083 *
1084 * @since GDAL 3.1
1085 */
1086
GDALMultiDimInfoOptionsFree(GDALMultiDimInfoOptions * psOptions)1087 void GDALMultiDimInfoOptionsFree( GDALMultiDimInfoOptions *psOptions )
1088 {
1089 delete psOptions;
1090 }
1091
1092