Loading...
Searching...
No Matches
Data.h
1/*
2
3MIT License
4
5Copyright (c) 2017 FMI Open Development / Markus Peura, first.last@fmi.fi
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to deal
9in the Software without restriction, including without limitation the rights
10to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in all
15copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23SOFTWARE.
24
25*/
26/*
27Part of Rack development has been done in the BALTRAD projects part-financed
28by the European Union (European Regional Development Fund and European
29Neighbourhood Partnership Instrument, Baltic Sea Region Programme 2007-2013)
30*/
31// Thanks to: Mikael Kilpeläinen
32
33#ifndef DATA_H_
34#define DATA_H_
35
36#include <data/QuantitySelector.h>
37#include <set>
38#include <list>
39#include <map>
40
41#include <drain/RegExp.h>
42#include <drain/util/ReferenceMap.h>
43#include <drain/util/StringMatcherList.h>
44
45#include "DataSelector.h"
46#include "DataTools.h"
47#include "PolarODIM.h"
48#include "CartesianODIM.h"
49#include "VerticalODIM.h"
50
51
52using namespace drain::image;
53
54namespace rack {
55
79
85template <typename T, typename TI, typename D>
86struct DataType {
87 typedef T tree_t;
88 typedef TI tree_iter_t;
89 typedef D image_t;
90};
91
92
93/*
94template <class T, size_t N>
95struct TypeName<UniTuple<T,N> > {
96
97 static const std::string & str(){
98 static std::string name = drain::StringBuilder<>("UniTuple<", drain::TypeName<T>::str(), ',', N, ">");
99 return name;
100 }
101
102};
103*/
104
106
109template <typename M = PolarODIM const>
110struct SrcType : public DataType<Hi5Tree const, Hi5Tree::const_iterator, Image const> {
111 typedef M odim_t;
112};
113
114
116
119template <typename M = PolarODIM>
120struct DstType : public DataType<Hi5Tree, Hi5Tree::iterator, Image> {
121 typedef M odim_t;
122};
123
126
129
132
133
134typedef DstType<ODIM> BasicDst;
135
138
141
144
147
148
149
151
157template <typename DT>
159public:
160
161 typedef DT datatype_t;
162 typedef typename DT::tree_t tree_t;
163 typedef typename DT::tree_iter_t tree_iter_t;
164
165 inline
166 const drain::VariableMap & getWhat() const {
167 return getAttr<ODIMPathElem::WHAT>();
168 }
169
170 inline
171 drain::VariableMap & getWhat() {
172 return getAttr<ODIMPathElem::WHAT>();
173 }
174
175 inline
176 const drain::VariableMap & getWhere() const {
177 return getAttr<ODIMPathElem::WHERE>();
178 }
179
180 inline
181 drain::VariableMap & getWhere() {
182 return getAttr<ODIMPathElem::WHERE>();
183 }
184
185
186 inline
187 const drain::VariableMap & getHow() const {
188 return getAttr<ODIMPathElem::HOW>();
189 }
190
191 inline
192 drain::VariableMap & getHow() { // ODIMPathElem(ODIMPathElem::HOW)
193 return getAttr<ODIMPathElem::HOW>();
194 }
195
196 // Mark this data temporary so that it will not be save by Hi5::write().
197 inline
198 void setExcluded(bool exclude = true){
199 this->tree.data.exclude = exclude;
200 };
201
202 // Mark this data temporary so that it will not be save by Hi5::write().
203 inline
204 bool isExcluded() const {
205 return this->tree.data.exclude;
206 };
207
208 //typename DT::odim_t odim;
209
210 virtual inline
211 const tree_t & getTree() const { return this->tree; };
212
213 // expected public, at least by DetectorOp::storeDebugData()
214 virtual inline
215 tree_t & getTree(){ return this->tree; } ;
216
217
218protected:
219
221 tree_t & tree;
222
223 TreeWrapper(tree_t & tree) : tree(tree){
224 };
225
226 //TreeWrapper(tree_t & tree, const odim_t & odim) tree(tree), odim(odim) {};
227 TreeWrapper(const TreeWrapper & d) : tree(d.tree){}; //, image(d.data), odim(d.odim) {};
228
229
230 ~TreeWrapper(){
231 /*
232 drain::Logger mout("TreeWrapper", __FUNCTION__);
233 if (this->tree.data.exclude){
234 mout.note("deleting (children only?)" );
235 this->tree.clear();
236 }
237 */
238 };
239
240
241 template <ODIMPathElem::group_t G>
242 const drain::VariableMap & getAttr() const {
243 return this->tree[ODIMPathElem(G)].data.attributes;
244 }
245
246 template <ODIMPathElem::group_t G>
247 drain::VariableMap & getAttr(){
248 return this->tree[ODIMPathElem(G)].data.attributes;
249 }
250
251
252};
253
254// In future, could be the interface for wrapping the whole structure?
255
257
263template <typename DT>
264class RootData : public TreeWrapper<DT> {
265
266public:
267
268 RootData(typename DT::tree_t & tree) : TreeWrapper<DT>(tree) { // This could be good: odim(ODIMPathElem::ROOT) ... but failed with current design
269 // experimental
270 this->odim.copyFrom(tree.data.image); // <- works (only) if updateInternalAttributes() has been called?
271 };
272
273 virtual inline
274 ~RootData(){
275
276 drain::Logger mout(__FILE__, __FUNCTION__);
277 mout.experimental<LOG_DEBUG>("invoking ODIM::updateH5AttributeGroups<ODIMPathElem::ROOT>()");
278 mout.experimental<LOG_DEBUG+1>("root odim: ", this->odim);
279
280 ODIM::updateH5AttributeGroups<ODIMPathElem::ROOT>(this->odim, this->tree);
281 DataTools::updateInternalAttributes(this->tree); // overrides anything?
282 };
283
284 // Metadata structure
285 typename DT::odim_t odim;
286
287};
288
289
291
299template <typename DT>
300class PlainData : public TreeWrapper<DT> {
301public:
302
303 typedef typename DT::tree_t tree_t;
304 typedef typename DT::image_t image_t;
305 typedef typename DT::odim_t odim_t;
306
307 // NEW
308 inline
309 PlainData(typename DT::tree_t & tree) :
311 data(tree[ODIMPathElem::ARRAY].data.image), // "data"
312 odim(data, data.properties.get("what:quantity",""))
313 {
314 //data.setScaling(odim.scaling.scale, odim.scaling.offset);
315 }
316
318 PlainData(typename DT::tree_t & tree, const std::string & quantity) :
319 TreeWrapper<DT>(tree),
320 data(tree[ODIMPathElem::ARRAY].data.image), // "data"
321 odim(data, quantity) // reads data.properties?
322 {
323 //data.setScaling(odim.scaling.scale, odim.scaling.offset);
324 }
325
327
336 template <typename DT2>
337 PlainData(const PlainData<DT2> & d) : TreeWrapper<DT>(d.getTree()),
338 data(this->tree[ODIMPathElem::ARRAY].data.image), // "data"
339 // odim(d.odim) // OLD
340 odim(data) // NEW
341 {
342 //odim.updateFromMap(d.odim); // NEW
343 //odim.updateFromCastableMap(d.odim); // NEW
344 //data.setScaling(odim.scaling.scale, odim.scaling.offset);
345 }
346
347 inline
348 ~PlainData(){
349 //drain::Logger mout("PlainData", __FUNCTION__);
350 //mout.debug2("calling updateTree2, odim: " , odim );
351 updateTree2();
352 }
353
355 template <class T>
356 inline
357 void setEncoding(const T & type, const std::string & values = ""){
358 //odim.type = drain::Type::getTypeChar(type);
359 this->odim.type = drain::Type::getTypeChar(drain::Type(type));
360 this->odim.setTypeDefaults(type, values);
361 data.setType(type);
362 // TODO: data.setScaling(odim.scaling); ??
363 }
364
366 inline
367 void copyEncoding(const EncodingODIM & odim){
368 this->odim.importMap(odim);
369 data.setType(this->odim.type);
370 data.setScaling(this->odim.scaling); // needed?
371 }
372
373
374 template <class DT2>
375 inline
376 void copyEncoding(const PlainData<DT2> & srcData){
377 this->odim.importMap(srcData.odim);
378 data.setType(this->odim.type);
379 data.setScaling(this->odim.scaling); // needed?
380 }
381
382 inline
383 void setPhysicalRange(double min, double max){
384 //data.setPhysicalScale(min, max);
385 data.setPhysicalRange(min, max, true);
386 // data.setOptimalScale();
387 this->odim.scaling.assignSequence(data.getScaling());
388 // odim.scaling.scale = data.getScaling().scale; // needed?
389 // odim.scaling.offset = data.getScaling().offset; // needed?
390 }
391
392
393
395 inline
396 void setGeometry(size_t cols, size_t rows){
397 this->odim.setGeometry(cols, rows);
398 data.setGeometry(cols, rows);
399 }
400
402 inline
404 this->odim.setGeometry(geometry.getWidth(), geometry.getHeight());
405 data.setGeometry(geometry);
406 }
407
409
412 inline
413 void setGeometry(const odim_t & odim){
414 this->odim.setGeometry(odim.area);
415 data.setGeometry(odim.area);
416 // Note:
417 this->odim.resolution = odim.resolution;
418 }
419
421
424 template <class DT2>
425 inline
426 void copyGeometry(const PlainData<DT2> & srcData){
427 setGeometry(srcData.odim);
428 /*
429 this->odim.setGeometry(srcData.odim.area);
430 data.setGeometry(srcData.odim.area);
431 // Note:
432 this->odim.resolution = srcData.odim.resolution;
433 */
434 }
435
437 template <class T>
438 inline
439 void initialize(const T & type, size_t cols, size_t rows){
440 setEncoding(type);
441 setGeometry(cols, rows);
442 }
443
444 template <class T>
445 inline
446 void initialize(const T & type, const drain::image::AreaGeometry & geometry){
447 setEncoding(type);
448 setGeometry(geometry);
449 }
450
451 // NEW policy.
453 inline
455 setEncoding(odim.type);
456 setGeometry(odim.area);
457 }
458
460 template <class ...T>
461 inline
462 void initializeBest(const std::type_info & type, const T & ...args){
463 odim.setType(type);
464 initializeBest(args...);
465 }
466
468 template <class ...T>
469 inline
470 void initializeBest(const std::string & type, const T & ...args){
471 odim.setType(type);
472 initializeBest(args...);
473 }
474
476 template <class ...T>
477 inline
478 void initializeBest(const drain::image::AreaGeometry & geometry, const T & ...args){
479 odim.area.set(geometry);
480 initializeBest(args...);
481 }
482
484 template <class ...T>
485 inline
486 void initializeBest(const EncodingODIM & encoding, const T & ...args){
487 odim.updateFromMap(encoding); // importMap can NOT be used because non-EncodingODIM arg will have a larger map
488 odim.scaling.physRange.set(encoding.scaling.physRange);
489 initializeBest(args...);
490 }
491
492
493 // inline
494 void setNyquist(double nyquistSpeed){
495 odim.NI = nyquistSpeed;
496 odim.setRange(-odim.NI, +odim.NI);
497 data.setScaling(odim.scaling);
498 }
499
500
501 // Data array
502 image_t & data;
503
504 // Metadata structure
505 odim_t odim; // 2023/01 considered ...raising to TreeContainer
506
507 //drain::Legend legend;
508
509 // Possibly this should be somewhere else? (Too specific here?)
511 void createSimpleQualityData(drain::image::Image & qualityImage, double dataQuality=1.0, double undetectQuality=0.5, double nodataQuality=0.0) const;
512
513 // Possibly this should be somewhere else? (Too specific here?)
515 inline
516 void createSimpleQualityData(PlainData<DT> & qualityData, double dataQuality=1.0, double undetectQuality=0.5, double nodataQuality=0.0) const { //, double dataQuality=1.0, double nodataQuality=0.0) const {
517 qualityData.setEncoding(typeid(unsigned char));
518 createSimpleQualityData(qualityData.data, dataQuality, undetectQuality, nodataQuality);
519 qualityData.odim.scaling.set(qualityData.data.getScaling());
520 //qualityData.odim.scaling.scale = qualityData.data.getScaling().scale;
521 //qualityData.odim.scaling.offset = qualityData.data.getScaling().offset;
522 }
523
525 inline
527 ODIM::updateH5AttributeGroups<ODIMPathElem::DATA>(this->odim, this->tree);
528 DataTools::updateInternalAttributes(this->tree); // Needed? The subtree is small... But for quality field perhaps.
529 }
530
531
532protected:
533
534
535};
536
540template <typename DT> // PlainData<DT> & quality
541void PlainData<DT>::createSimpleQualityData(drain::image::Image & quality, double dataQuality, double undetectQuality, double nodataQuality) const {
542
543 quality.setPhysicalRange(0.0, 1.0, true);
544
545 const drain::ValueScaling & scaling = quality.getScaling();
546
547 const bool DATA = !std::isnan(dataQuality);
548 const bool UNDETECT = !std::isnan(undetectQuality);
549 const bool NODATA = !std::isnan(nodataQuality);
550
551 // Default ie. unset values are non_signaling_NAN's, but maybe more elegant to skip calculations:
552 const double dataCode = DATA ? scaling.inv(dataQuality) : 0.0;
553 const double undetectCode = UNDETECT ? scaling.inv(undetectQuality) : 0.0;
554 const double nodataCode = NODATA ? scaling.inv(nodataQuality) : 0.0;
555
556 quality.setGeometry(data.getWidth(), data.getHeight());
557
558 Image::iterator it = data.begin();
559 Image::iterator wit = quality.begin();
560 while (it != data.end()){
561 //if ((*it != odim.nodata) && (*it != odim.undetect))
562 if (UNDETECT && (*it == this->odim.undetect))
563 *wit = undetectCode;
564 else if (NODATA && (*it == this->odim.nodata))
565 *wit = nodataCode;
566 else if (DATA)
567 *wit = dataCode;
568 ++it;
569 ++wit;
570 }
571
572 /*
573 const double d = scaling.inv(dataQuality);
574 const double nd = scaling.inv(nodataQuality);
575 const double un = scaling.inv(undetectQuality);
576
577 quality.setGeometry(data.getWidth(), data.getHeight());
578
579 Image::iterator it = data.begin();
580 Image::iterator wit = quality.begin();
581 while (it != data.end()){
582 //if ((*it != odim.nodata) && (*it != odim.undetect))
583 if (*it == odim.nodata)
584 *wit = nd;
585 else if (*it == odim.undetect)
586 *wit = un;
587 else
588 *wit = d;
589 ++it;
590 ++wit;
591 }
592 */
593
594}
595
596
597
601template <typename DT>
602inline
603std::ostream & operator<<(std::ostream & ostr, const PlainData<DT> & d){
604 ostr << d.data << ", ODIM:\t ";
605 ostr << d.odim << '\n';
606 ostr << "props: " << d.data.properties << '\n';
607 return ostr;
608}
609
610
612
624template <class DT, ODIMPathElem::group_t G>
625class DataGroup : public TreeWrapper<typename DT::datatype_t>, public std::map<std::string, DT > { // typename T::datatype_t
626public:
627
628 typedef DataGroup<DT,G> datagroup_t; // this type
629 typedef DT data_t;
630 typedef typename DT::datatype_t datatype_t;
631 typedef std::map<std::string, DT > map_t;
632
633 // Experimental. Mainly for geometry (width, height) - but also for date+time or elangle?
634 /*
635 typedef typename DT::odim_t odim_t;
636 odim_t baseODIM;
637
638
639 inline
640 void setGeometry(size_t width, size_t height){
641 baseODIM.setGeometry(width, height);
642 }
643
644 inline
645 void setGeometry(const drain::image::AreaGeometry & geometry){
646 baseODIM.setGeometry(geometry);
647 }
648 */
649 //typename DT::odim_t odim;// 2023/01 experimental
650
651
653
654 // DataGroup(typename DT::tree_t & tree, const drain::RegExp & quantityRegExp = drain::RegExp()) :
655 // TreeWrapper<typename DT::datatype_t>(tree), odim(tree.data.image) {
656 DataGroup(typename DT::tree_t & tree, const QuantitySelector & slct = QuantitySelector()) :
657 TreeWrapper<typename DT::datatype_t>(tree) { //, baseODIM(ODIMPathElem::DATASET) {
658 init(tree, *this, slct);
659 }
660 //init(tree, *this, quantityRegExp);
661
662 /*
663 DataGroup(typename DT::tree_t & tree, const drain::RegExp & quantityRegExp = drain::RegExp()) :
664 TreeWrapper<typename DT::datatype_t>(tree) {
665 init(tree, *this, quantityRegExp);
666 }
667 */
668
669 // TreeWrapper<typename DT::datatype_t>(src.tree), odim(src.tree.data.image) {
670
671 DataGroup(const datagroup_t & src) :
672 TreeWrapper<typename DT::datatype_t>(src.tree) { // , baseODIM(ODIMPathElem::DATASET){
673 adapt(src, *this); // ALERT: includes all the quantities, even thoug src contained only some of them
674 }
675
676
677 virtual
678 ~DataGroup(){
679 /*
680 drain::Logger mout("DataGroup<" + ODIMPathElem::getKey(G)+">", __FUNCTION__);
681 switch (this->size()) {
682 case 0:
683 mout.debug3("no data<n> groups" );
684 break;
685 default:
686 mout.info("several Data groups, using: " , this->begin()->first );
687 // no break;
688 case 1:
689 mout.debug("updating from 1st data: " , this->begin()->first );
690 updateTree3(this->getFirstData().odim); // tree
691 }
692 */
693 };
694
695
696 bool has(const std::string & quantity) const {
697 return (this->find(quantity) != this->end());
698 }
699
700 inline
701 const data_t & getData(const char *quantity) const {
702 return getData(std::string(quantity));
703 //return getData(quantity.c_str());
704 }
705
706
707 const data_t & getData(const std::string & quantity) const {
708
709 //drain::Logger mout(__FILE__, __FUNCTION__); //
710 drain::Logger mout(__FUNCTION__, "DataGroup{" + ODIMPathElem::getKey(G) + "}");
711
712 typename datagroup_t::const_iterator it = this->find(quantity);
713
714 if (it != this->end()){
715 mout.debug3('[' , quantity , "]\t = " , it->first );
716 return it->second;
717 }
718 else {
719 mout.debug('[' , quantity , "] not found, returning empty" );
720 return getEmpty();
721 }
722 }
723
724 inline
725 data_t & getData(const char *quantity) {
726 return getData(std::string(quantity));
727 //return getData(quantity.c_str());
728 }
729
730
731 data_t & getData(const std::string & quantity) {
732
733 //drain::Logger mout(__FILE__, __FUNCTION__); //REPL "DataGroup." + ODIMPathElem::getKey(G), __FUNCTION__);
734 drain::Logger mout(__FUNCTION__, "DataGroup{" + ODIMPathElem::getKey(G) + "}");
735
736 //mout.warn("non-const " );
737 typename datagroup_t::iterator it;
738 #pragma omp critical //(h5insert)
739 {
740 it = this->find(quantity);
741 if (it != this->end()){
742 mout.debug3("found " , it->first );
743 }
744 else {
745 //mout.note("not found, creating new data array" );
746 ODIMPathElem child(G);
747 ODIMPathTools::getNextChild(this->tree, child);
748 mout.debug3("add: " , child , " [" , quantity , ']' );
749 it = this->insert(this->begin(), typename map_t::value_type(quantity, DT(this->getTree()[child], quantity))); // WAS [path]
750 // it->second.setGeometry(baseODIM.getGeometry());
751 }
752 }
753 return it->second;
754
755 }
756
757 //const data_t& getData(const drain::RegExp & regExp) const {
758 const data_t& getData(const QuantitySelector & slct) const {
759
760 //drain::Logger mout("DataGroup." + ODIMPathElem::getKey(G)+"(RegExp) {const}", __FUNCTION__);
761 drain::Logger mout(__FUNCTION__, "(KeySelector): ", "DataGroup{" + ODIMPathElem::getKey(G)+"}");
762
763 // NEW
764 for (const drain::StringMatcher & m: slct.getList()){
765 for (const auto & entry: *this){
766 if (m.test(entry.first)){
767 mout.debug("quantity " , entry.first , " matches " , slct);
768 return entry.second;
769 }
770 }
771 }
772
773 /* OLD
774 for (typename datagroup_t::const_iterator it = this->begin(); it != this->end(); ++it){
775 if (slct.testQuantity(it->first)){
776 mout.debug("quantity " , it->first , " matches " , slct);
777 return it->second;
778 }
779 }
780 */
781
782 mout.note("no quantity matched with " , slct);
783
784 return getEmpty();
785
786 }
787
789
794 data_t & create(const std::string & quantity) { // , const std::string & templateQuantity, const std::string & encodingParams) {
795 data_t & d = getData(quantity);
796 d.setGeometry(0, 0); // in case existed already
797 //getQuantityMap().setQuantityDefaults(d, templateQuantity, encodingParams);
798 return d;
799 }
800
801
802 data_t & getFirstData() {
803
804 for (auto & entry: *this){
805 if (!entry.second.isExcluded()){
806 return entry.second;
807 }
808 }
809
810 drain::Logger(__FILE__, __LINE__, "DataGroup{" + ODIMPathElem::getKey(G)+"}:", __FUNCTION__).error("no data");
811 //mout.error("no data" );
812 return this->getData(""); // empty?
813
814 /*
815 const typename datagroup_t::iterator it = this->begin();
816
817 if (it == this->end()){
818 //drain::Logger mout("DataGroup." + ODIMPathElem::getKey(G), __FUNCTION__);
819 drain::Logger mout(__FUNCTION__, "DataGroup{" + ODIMPathElem::getKey(G)+"}");
820
821 mout.error("no data" );
822 return this->getData("");
823 }
824 else
825 return it->second;
826 */
827 }
828
829 const data_t & getFirstData() const {
830
831 for (const auto & entry: *this){
832 if (!entry.second.isExcluded()){
833 return entry.second;
834 }
835 }
836
837 drain::Logger(__FILE__, __LINE__, "DataGroup{" + ODIMPathElem::getKey(G)+"}:", __FUNCTION__).error("no data");
838 return this->getData(""); // empty?
839
840
841 //drain::Logger mout("DataGroup." + ODIMPathElem::getKey(G) + " {const}", __FUNCTION__);
842 /*
843 drain::Logger mout(__FUNCTION__, "DataGroup{" + ODIMPathElem::getKey(G)+"}");
844
845 //mout.warn("const" );
846
847 typename datagroup_t::const_iterator it = this->begin();
848
849 if (it != this->end()){
850 mout.debug2("found: " , it->first );
851 return it->second;
852 }
853 else {
854 mout.note("not found, returning empty" );
855 return getEmpty();
856 }
857 */
858
859 }
860
861
862 // experimental
863 data_t & getLastData() {
864
865 const typename datagroup_t::reverse_iterator it = this->rend();
866
867 if (it == this->rbegin()){
868 drain::Logger mout(__FUNCTION__, "DataGroup{" + ODIMPathElem::getKey(G)+"}");
869 mout.error("no data" );
870 return this->getData("");
871 }
872 else
873 return it->second;
874
875 }
876
877 // experimental
878 const data_t & getLastData() const {
879
880 drain::Logger mout(__FUNCTION__, "DataGroup{" + ODIMPathElem::getKey(G) + "}-const");
881
882 //mout.warn("const" );
883
884 typename datagroup_t::const_reverse_iterator it = this->rend();
885
886 if (it != this->rbegin()){
887 mout.debug2("found: " , it->first );
888 return it->second;
889 }
890 else {
891 mout.note("not found, returning empty" );
892 return getEmpty();
893 }
894
895
896 }
897
898
899protected:
900
901 static
902 const data_t & getEmpty() {
903 static typename DT::tree_t t;
904 static data_t empty(t);
905 return empty;
906 }
907
909
914 static
915 typename DT::tree_t & init(typename DT::tree_t & t, datagroup_t & dst, const QuantitySelector & slct = QuantitySelector()){
916 // typename DT::tree_t & init(typename DT::tree_t & t, datagroup_t & dst, const drain::RegExp & quantityRegExp = drain::RegExp()){
917
918 //drain::Logger mout("DataGroup." + ODIMPathElem::getKey(G), __FUNCTION__);
919 drain::Logger mout(__FUNCTION__, "DataGroup{" + ODIMPathElem::getKey(G)+"}");
920
921 //const bool USE_REGEXP = quantityRegExp.isSet();
922 const bool USE_SELECTOR = slct.isSet();
923
924 // Number of potential groups for debug note at end
925 unsigned short counter = 0;
926
927 mout.debug3("collecting data items, selector='", slct, "'");
928 //if (USE_REGEXP)
929 // mout << ", regExp=" << quantityRegExp.toStr();
930 //mout << mout.endl;
931
932 // #pragma omp critical //(h5insert2)
933 {
934 // add UKMO
935
936 const std::string datasetQuantity = t[ODIMPathElem::WHAT].data.attributes.get("quantity", "");
937
938 //for (typename DT::tree_iter_t it=t.begin(); it!=t.end(); ++it){
939 for (auto & entry: t){
940
942 if (! (entry.first.is(G))){
943 //mout.warn("skip '" , it->first , "' \t group != " , G );
944 continue;
945 }
946
947 //const std::string dataQuantity = it->second["what"].data.attributes["quantity"];
948 //const std::string dataQuantity = it->second[ODIMPathElem::WHAT].data.attributes["quantity"];
949 const std::string dataQuantity = entry.second[ODIMPathElem::WHAT].data.attributes.get("quantity", ""); // otherways comes "null"
950
951 const std::string & quantity = !dataQuantity.empty() ? dataQuantity : datasetQuantity;
952
953 if (USE_SELECTOR){
954 ++counter; // candidate count
955 if (!slct.test(quantity)){
956 //if (!quantityRegExp.test(quantity)){
957 //if (it->second.hasChild("quality1"))
958 // mout.warn(it->first , "...rejecting, but has quality?" );
959 mout.debug3("rejected '" , entry.first , "' [" , quantity , "] !~" , slct ); // quantityRegExp.toStr()
960 continue;
961 }
962 }
963
964 /*
965 if (USE_REGEXP){
966 ++counter; // candidate count
967 if (!quantityRegExp.test(quantity)){
968 //if (it->second.hasChild("quality1"))
969 // mout.warn(it->first , "...rejecting, but has quality?" );
970 mout.debug3("rejected '" , entry.first , "' [" , quantity , "] !~" , quantityRegExp.toStr() );
971 continue;
972 }
973 }
974 */
975
976 mout.accept<LOG_DEBUG+2>("accept '", entry.first, "' [", quantity, ']' );
977
978 /*
979 mout.warn(entry.first, " 1st -> ", entry.second[ODIMPathElem::ARRAY].data.image);
980 mout.fail("type", drain::TypeName<DT>::get());
981 DT(entry.second, quantity);
982 mout.warn(entry.first, " 1bst-> ", entry.second[ODIMPathElem::ARRAY].data.image);
983 */
984
985 if (quantity.empty()){
986 //drain::Logger mout("DataSet", __FUNCTION__);
987 mout.info("quantities dataset:'", datasetQuantity, "', data:'", dataQuantity, "'");
988 mout.warn("undefined quantity in ", entry.first, ", using key=", entry.first );
989 // Assign by path component "data3"
990 dst.insert(typename map_t::value_type(entry.first, DT(entry.second, entry.first)));
991 //associate(dst, it->first, it->second);
992 }
993 else {
994 if (dst.find(quantity) != dst.end()){ // already created
995 //drain::Logger mout("DataSet", __FUNCTION__);
996 mout.warn("quantity '" , quantity , "' replaced same quantity at " , entry.first );
997 }
998 dst.insert(typename map_t::value_type(quantity, DT(entry.second, quantity)));
999 //typename datagroup_t::reverse_iterator rit = dst.rend();
1000 //mout.warn("last '" , "' [" , quantity , '=' , rit->first , ']' , rit->second );
1001 //dst[quantity] = T(it->second);
1002 // mout.warn(entry.first, " 2nd -> ", entry.second[ODIMPathElem::ARRAY].data.image);
1003 }
1004 }
1005 } // end pragma
1006
1007 if (USE_SELECTOR)
1008 mout.debug3("matched " , dst.size() , "(out of " , counter , ") data items with selector: " , slct , '/' );
1009 //mout.debug3("matched " , dst.size() , "(out of " , counter , ") data items with RegExp=/" , quantityRegExp.toStr() , '/' );
1010 else
1011 mout.debug3("collected " , dst.size() , " data items" );
1012
1013 return t;
1014 };
1015
1016
1017 static
1018 //typename D::tree_t & adapt(typename D::tree_t & t, datagroup_t & dst, const datagroup_t & src){
1019 typename DT::tree_t & adapt(const datagroup_t & src, datagroup_t & dst){
1020
1021 drain::Logger mout(__FUNCTION__, "DataGroup{" + ODIMPathElem::getKey(G)+"}");
1022
1023 // drain::Logger mout("DataGroup." + ODIMPathElem::getKey(G), __FUNCTION__);
1024 // drain::Logger mout(__FILE__, __FUNCTION__);
1025
1026 if (src.empty()){
1027 mout.debug3("src empty" );
1028 return src.tree;
1029 }
1030 else {
1031 for (const auto & entry: src){
1032 dst.insert(entry);
1033 }
1034 }
1035
1036 /*
1037 for (typename datagroup_t::const_iterator it=src.begin(); it!=src.end(); ++it){
1038 //dst.insert(typename map_t::value_type(it->first, D(it->second, it->first)));
1039 //dst.insert(typename map_t::value_type(it->first, D(it->second)));
1040 dst.insert(typename map_t::value_type(it->first, it->second));
1041 }
1042 */
1043 mout.debug3("adapted ", dst.size(), " data items; ", src.begin()->first, "...");
1044
1045 //return t
1046 return src.tree;
1047 };
1048
1049
1050};
1051
1052template <class DT, ODIMPathElem::group_t G>
1053std::ostream & operator<<(std::ostream & ostr, const DataGroup<DT,G> & d){
1054 // ostr << "-- dataGroup ";
1055 char separator = 0;
1057 for (typename DataGroup<DT,G>::const_iterator it = d.begin(); it != d.end(); ++it){
1058 if (separator)
1059 ostr << separator;
1060 else {
1061 separator = ',';
1062 g.setGeometry(it->second.data.getGeometry());
1063 }
1064 ostr << it->first << '[' << drain::Type::getTypeChar(it->second.data.getType()) << ']';
1065 }
1066 ostr << " ("<< g << ")";
1067 /*
1068 ostr << d.data << ", ODIM:\t ";
1069 ostr << d.odim << '\n';
1070 ostr << "props: " << d.data.properties << '\n';
1071 */
1072 return ostr;
1073}
1074
1075
1077
1082template <typename DT>
1084
1085public:
1086
1087 typedef PlainData<DT> plaindata_t;
1088
1090
1091
1092 inline
1093 QualityDataSupport(typename plaindata_t::tree_t & tree) : quality(tree) {};
1094
1095 inline
1096 QualityDataSupport(const QualityDataSupport<DT> & qds) : quality(qds.quality) {};
1097
1098 virtual inline
1100
1102 /*
1103 * \param quantity - quality quantity, "QIND" by default.
1104 * \return - \c data[i]/quality[j] for which \c quantity=[quantity]
1105 *
1106 */
1107 inline
1108 const plaindata_t & getQualityData(const std::string & quantity = "QIND") const {
1109 return this->quality.getData(quantity);
1110 }
1111
1113 /*
1114 * \param quantity - quality quantity, "QIND" by default.
1115 * \return - \c data[i]/quality[j] for which \c quantity=[quantity]
1116 *
1117 */
1118 /*
1119 inline
1120 const plaindata_t & getQualityData(const drain::RegExp & quantityRE) const {
1121 return this->quality.getData(quantityRE);
1122 }
1123 */
1124
1125
1127 /*
1128 * \param quantity - quality quantity, "QIND" by default.
1129 * \return - \c data[i]/quality[j] for which \c quantity=[quantity]
1130 *
1131 */
1132 inline
1133 plaindata_t & getQualityData(const std::string & quantity = "QIND") {
1134 return this->quality.getData(quantity);
1135 }
1136
1137
1138 inline
1139 bool hasQuality() const {
1140 return !this->quality.empty();
1141 }
1142
1143 inline
1144 bool hasQuality(const std::string & quantity) const {
1145 return this->quality.find(quantity) != this->quality.end();
1146 }
1147
1148 inline
1149 const qualitygroup_t & getQuality() const {
1150 return this->quality;
1151 }
1152
1153 inline
1154 qualitygroup_t & getQuality(){
1155 return this->quality;
1156 }
1157
1158 /* Well, needs quantity, primarily. So best place perhaps not here.
1159 static
1160 void createSimpleQualityData(PlainData<DT> & data, drain::image::Image & qualityImage, double dataQuality=1.0, double undetectQuality=0.5, double nodataQuality=0.0) const;
1161
1162 static inline
1163 void createSimpleQualityData(PlainData<DT> & data, PlainData<DT> & qualityData, double dataQuality=1.0, double undetectQuality=0.5, double nodataQuality=0.0) const { //, double dataQuality=1.0, double nodataQuality=0.0) const {
1164 qualityData.setEncoding(typeid(unsigned char));
1165 createSimpleQualityData(qualityData.data, dataQuality, undetectQuality, nodataQuality);
1166 qualityData.odim.scaling.set(qualityData.data.getScaling());
1167 //qualityData.odim.scaling.offset = qualityData.data.getScaling().offset;
1168 }
1169 */
1170
1171protected:
1172
1173 qualitygroup_t quality;
1174
1175};
1176
1177
1178
1179/*
1180 * Data<PolarSrc> will return
1181 *
1182 * Quality field:
1183 * - NONE: reference to dummy.
1184 * - LAST: reference to last existing quality[1].
1185 * - NEXT: reference to new quality[N+1], where N is the last
1186 */
1187
1189
1194template <typename DT>
1195class Data : public PlainData<DT>, public QualityDataSupport<DT> {
1196public:
1197
1198 typedef PlainData<DT> plaindata_t;
1199
1200 Data(typename DT::tree_t & tree) : PlainData<DT>(tree), QualityDataSupport<DT>(tree) {
1201 }
1202
1203 Data(typename DT::tree_t & tree, const std::string & quantity) : PlainData<DT>(tree, quantity), QualityDataSupport<DT>(tree) {
1204 }
1205
1206 Data(const Data<DT> & d) : PlainData<DT>(d), QualityDataSupport<DT>(d) { // was: d.tree, d.tree
1207 }
1208
1209 // Data(const Hi5Tree & src, const std::string & quantity = "^DBZH$");
1210 virtual ~Data(){};
1211
1212 // Experimental
1213 void swap(Data<DT> &d){ // TODO: also for plaindata?
1214
1215 drain::Logger mout("Data<DT>", __FUNCTION__);
1216 mout.experimental("Swapping...");
1217 this->tree.swap(d.tree);
1218
1219 typename DT::odim_t odim;
1220 odim.updateFromMap(this->odim);
1221 this->odim.updateFromMap(d.odim);
1222 d.odim.updateFromMap(odim);
1223
1224 this->updateTree2();
1225 d.updateTree2();
1226 }
1227
1228protected:
1229
1230};
1231
1232
1233
1234
1235
1236template <typename DT>
1237inline
1238std::ostream & operator<<(std::ostream & ostr, const Data<DT> & d){
1239 ostr << d.data << ", ODIM:\t "; // .getGeometry()
1240 ostr << d.odim; // << '\n';
1241 //ostr << d.data.properties << '\n';
1242 if (d.hasQuality()){
1243 //ostr << " (has quality field)";
1244 ostr << "+q(" << d.getQuality() << ')';
1245 /*
1246 const PlainData<T,D,M> & q = d.getQuality();
1247 ostr << '\t' << q.data.getGeometry() << '\t';
1248 ostr << q.odim << '\n';
1249 ostr << '\t' << q.data.properties << '\n';
1250 */
1251 }
1252 ostr << '\n';
1253 return ostr;
1254}
1255
1256
1258
1263template <typename DT>
1264class DataSet : public DataGroup<Data<DT>,ODIMPathElem::DATA>, public QualityDataSupport<DT> { // typename T::data_t
1265public:
1266
1267 typedef Data<DT> data_t;
1268 typedef PlainData<DT> plaindata_t;
1269 typedef typename DataGroup<data_t,ODIMPathElem::DATA>::datagroup_t datagroup_t; // ODIMPathElem::DATA>
1270 typedef typename datagroup_t::map_t map_t;
1271
1272
1273 inline
1275 //DataSet(typename data_t::tree_t & tree, const drain::RegExp & quantityRegExp = drain::RegExp()) :
1276 DataSet(typename data_t::tree_t & tree, const QuantitySelector & slct =QuantitySelector()) :
1277 //datagroup_t(tree, quantityRegExp), QualityDataSupport<DT>(tree)
1279 {
1280 }
1281
1282 DataSet(const DataSet<DT> & ds) : datagroup_t(ds), QualityDataSupport<DT>(ds) {
1283 }
1284
1285 ~DataSet(){
1286
1287 drain::Logger mout(__FILE__, __FUNCTION__);
1288
1289 switch (this->size()) {
1290 case 0:
1291 mout.debug3("no data<n> groups" );
1292 break;
1293 default:
1294 mout.info("several Data groups, using: " , this->begin()->first );
1295 // no break;
1296 case 1:
1297 mout.debug("updating from 1st data: " , this->begin()->first );
1298
1299
1300 const typename DT::odim_t & odim = this->getFirstData().odim;
1301
1302 /*
1303 // DEBUGGING 2024
1304 for (const auto & entry: this->baseODIM){
1305 if (odim[entry.first] == entry.second){
1306 mout.reject("BaseODIM differs: ", entry.first, ": ", entry.second, " vs ", odim[entry.first]);
1307 }
1308 }
1309 */
1310
1311 // updateTree3(this->getFirstData().odim);
1312 // mout.attention("start updateTree3");
1313 updateTree3(odim);
1314 // mout.attention("end updateTree3");
1315 }
1316
1317 }
1318
1319
1320 plaindata_t & getQualityData2(const std::string & quantity = "QIND", const std::string & dataQuantity = ""){ // , bool tmp = false
1321 if (dataQuantity.empty())
1322 return this->getQualityData(quantity);
1323 else {
1324 return this->getData(dataQuantity).getQualityData(quantity);
1325 }
1326 }
1327
1328
1329 const plaindata_t & getQualityData2(const std::string & quantity = "QIND", const std::string & dataQuantity = "") const { // , bool tmp = false
1330 if (dataQuantity.empty())
1331 return this->getQualityData(quantity);
1332 else {
1333 return this->getData(dataQuantity).getQualityData(quantity);
1334 }
1335 }
1336
1337
1338 inline
1339 void updateTree3(const typename DT::odim_t & odim){ //
1340 ODIM::updateH5AttributeGroups<ODIMPathElem::DATASET>(odim, this->tree);
1341 DataTools::updateInternalAttributes(this->tree); // TEST2019/09 // images, including DataSet.data, TODO: skip children
1342 //DataTools::updateInternalAttributes(this->tree, drain::FlexVariableMap()); // TEST2019/09 // images, including DataSet.data, TODO: skip children
1343 }
1344
1345 // TODO: consider this to destructor!
1346 inline
1347 void updateTree3(const typename DT::odim_t & odim) const { //
1348 std::cout << "updateTree3 const \n";
1349 //ODIM::updateH5AttributeGroups<ODIMPathElem::DATASET>(odim, tree);
1350 }
1351
1352
1353
1354protected:
1355
1356
1357
1359
1375};
1376
1378
1381template <typename DT>
1382std::ostream & operator<<(std::ostream & ostr, const DataSet<DT> & d){
1383 typedef DataSet<DT> dataset_t;
1384 ostr << "dataSet ";
1385 char separator = 0;
1387 for (typename dataset_t::const_iterator it = d.begin(); it != d.end(); ++it){
1388 if (separator)
1389 ostr << separator;
1390 else {
1391 separator = ',';
1392 g.setGeometry(it->second.data.getGeometry());
1393 }
1394 ostr << it->first << '[' << drain::Type::getTypeChar(it->second.data.getType()) << ']';
1395 if (it->second.hasQuality())
1396 ostr << "+Q(" << it->second.getQuality() << ')';
1397 }
1398 ostr << " ("<< g << ")";
1399 if (d.hasQuality())
1400 ostr << " +quality(" << d.getQuality() << ')';
1401 /*
1402 ostr << d.data << ", ODIM:\t ";
1403 ostr << d.odim << '\n';
1404 ostr << "props: " << d.data.properties << '\n';
1405 */
1406 return ostr;
1407}
1408
1409
1412// Becoming DEPRECATED
1413//template <typename DT>
1414//class DataSetMap : public std::map<double, DataSet<DT> > {
1415//};
1416
1417// TIMESTAMPED (what::date + what::time data) or path?
1418template <typename DT>
1419class DataSetMap : public std::map<std::string, DataSet<DT> > {
1420};
1421
1422
1423
1424template <typename DT>
1425class DataSetList : public std::list<DataSet<DT> > {
1426};
1427
1428
1429
1430
1431} // rack::
1432
1433namespace drain {
1434
1435template <class T, typename TI, typename D>
1436struct TypeName<rack::DataType<T,TI,D> > {
1437
1438 static const std::string & str(){
1439 static std::string name = drain::StringBuilder<>("DataType<", drain::TypeName<T>::str(), ',', drain::TypeName<TI>::str(), ',', drain::TypeName<D>::str(), ">");
1440 return name;
1441 }
1442
1443};
1444
1445}
1446
1447
1448#endif /* DATA_H_ */
1449
Definition CastableIterator.h:57
LogSourc e is the means for a function or any program segment to "connect" to a Log.
Definition Log.h:313
Logger & warn(const TT &... args)
Possible error, but execution can continue.
Definition Log.h:431
Logger & error(const TT &... args)
Echoes.
Definition Log.h:417
Logger & accept(const TT &... args)
Some input has been accepted, for example by a syntax.
Definition Log.h:583
void importMap(const std::map< std::string, S > &m)
Assign values from a map, overriding existing entries.
Definition SmartMap.h:255
Definition StringBuilder.h:58
bool test(const std::string &key, bool defaultResult=true) const
Check if key is accepted.
Definition StringMatcherList.h:253
General-purpose key matcher: tests string equality, or regExp, if defined as such.
Definition StringMatcher.h:58
Utilities related to std::type_info.
Definition Type.h:50
Linear scaling and physical range for image intensities.
Definition ValueScaling.h:64
drain::Range< double > physRange
Minimum and maximum physical value of the imaged quantity (not limited to corresponding to minCodeVal...
Definition ValueScaling.h:74
double inv(double y) const
Inverse scaling: given physically meaningful value y, returns the corresponding code value.
Definition ValueScaling.h:301
A map of Variables.
Definition VariableMap.h:61
Definition Geometry.h:145
const iterator & begin()
Returns iterator pointing to the first image element.
Definition ImageFrame.h:116
void setPhysicalRange(const Range< double > &range, bool rescale=false)
Sets the supported range for physical values and optionally adjusts the scaling for maximal resolutio...
Definition ImageFrame.h:103
Class for multi-channel digital images. Supports dynamic typing with base types (char,...
Definition Image.h:193
virtual void setGeometry(size_t width, size_t height, size_t imageChannels=1, size_t alphaChannels=0)
Resizes the image, keeps the current type.
Definition Image.h:97
A map of "data type" group_t (DATA or QUALITY) where the data can be retrieved using quantity keys (s...
Definition Data.h:625
data_t & create(const std::string &quantity)
Creates (or overrides) data array for quantity and scales it.
Definition Data.h:794
static DT::tree_t & init(typename DT::tree_t &t, datagroup_t &dst, const QuantitySelector &slct=QuantitySelector())
Given a dataset h5 subtree, like tree["dataset3"], collects all (or desired) quantities to a data obj...
Definition Data.h:915
DataGroup(typename DT::tree_t &tree, const QuantitySelector &slct=QuantitySelector())
Given a dataset subtree, like tree["dataset3"], constructs a data map of desired quantities.
Definition Data.h:656
Definition Data.h:1425
Definition Data.h:1419
A map of radar data, indexed by quantity code (DBZH, VRAD, etc).
Definition Data.h:1264
DataSet(typename data_t::tree_t &tree, const QuantitySelector &slct=QuantitySelector())
Given a dataset subtree, like tree["dataset3"], constructs a data map of desired quantities.
Definition Data.h:1276
static void updateInternalAttributes(Hi5Tree &src)
Copies values of what , where and how attributes to internal attributes down to data[n] groups.
Definition DataTools.cpp:186
Data structure consisting of plain data and an optional quality data.
Definition Data.h:1195
Structure for data storage type, scaling and marker codes. Does not contain quantity.
Definition EncodingODIM.h:75
std::string type
This is non-standard (not in ODIM), but a practical means of handling storage type of datasets.
Definition EncodingODIM.h:162
Definition ODIMPath.h:82
const std::string & getKey() const
Debugging/logging. Returns standard name. Does not check if type is OTHER.
Definition ODIMPath.h:357
static const group_t WHAT
Metadata group /what , at any depth.
Definition ODIMPath.h:127
static const group_t ARRAY
Data group "data", at deepest level, like /dataset4/data2/quality1/data.
Definition ODIMPath.h:112
static bool getNextChild(const Hi5Tree &tree, ODIMPathElem &child)
Derive a child with index one greater than the largest index encountered.
Definition ODIMPathTools.cpp:96
Essential class for storing radar data.
Definition Data.h:300
void setEncoding(const T &type, const std::string &values="")
Saves type and sets the type of the actual data array as well.
Definition Data.h:357
PlainData(const PlainData< DT2 > &d)
Copy constructor, also for referencing non-const as const.
Definition Data.h:337
void setGeometry(const odim_t &odim)
Copy dimensions of data array and resolution (rscale or xscale,yscale)
Definition Data.h:413
void initializeBest(const EncodingODIM &encoding, const T &...args)
Set encoding and other properties.
Definition Data.h:486
void setGeometry(size_t cols, size_t rows)
Sets dimensions of data array and metadata.
Definition Data.h:396
void copyGeometry(const PlainData< DT2 > &srcData)
Copy dimensions of data array and resolution (rscale or xscale,yscale)
Definition Data.h:426
void copyEncoding(const EncodingODIM &odim)
New, experimental.
Definition Data.h:367
void initializeBest(const std::string &type, const T &...args)
Set storage type and other properties.
Definition Data.h:470
void initializeBest(const drain::image::AreaGeometry &geometry, const T &...args)
Set width and height of the data array, and set other properties.
Definition Data.h:478
void createSimpleQualityData(PlainData< DT > &qualityData, double dataQuality=1.0, double undetectQuality=0.5, double nodataQuality=0.0) const
For this data, creates an on-off quality data.
Definition Data.h:516
void initializeBest(const std::type_info &type, const T &...args)
Set storage type and other properties.
Definition Data.h:462
void createSimpleQualityData(drain::image::Image &qualityImage, double dataQuality=1.0, double undetectQuality=0.5, double nodataQuality=0.0) const
For this data, creates an on-off quality data.
Definition Data.h:541
void updateTree2()
TODO: consider this to destructor.
Definition Data.h:526
void initializeBest()
Terminal step in initialisation: set actual data storage type and resize.
Definition Data.h:454
void setGeometry(const drain::image::AreaGeometry &geometry)
Sets dimensions of data array and metadata.
Definition Data.h:403
PlainData(typename DT::tree_t &tree, const std::string &quantity)
Constructor referring to HDF5 structure.
Definition Data.h:318
void initialize(const T &type, size_t cols, size_t rows)
Calls setEncoding() and setGeometry().
Definition Data.h:439
Base class providing quality support for Dataand DataSet
Definition Data.h:1083
const plaindata_t & getQualityData(const std::string &quantity="QIND") const
Finds associated quality data - maybe empty and unscaled.
Definition Data.h:1108
plaindata_t & getQualityData(const std::string &quantity="QIND")
Finds associated quality data - maybe empty and unscaled.
Definition Data.h:1133
Experimental structure, designed only for accessing root level metadata.
Definition Data.h:264
Base class for all kinds of radar data.
Definition Data.h:158
tree_t & tree
General HDF5 data structure.
Definition Data.h:221
Namespace for images and image processing tools.
Definition AccumulationArray.cpp:45
Definition DataSelector.cpp:1277
Definition DataSelector.cpp:44
Default implementation.
Definition Type.h:541
static const std::string name
Default implementation: name returned by std::type_info::name()
Definition Type.h:549
Container that couples together a tree structure and a data array (drain::image::Image),...
Definition Data.h:86
Writable data type.
Definition Data.h:120
Read-only data type.
Definition Data.h:110