DataConversionOp.h
1 /*
2 
3 MIT License
4 
5 Copyright (c) 2017 FMI Open Development / Markus Peura, first.last@fmi.fi
6 
7 Permission is hereby granted, free of charge, to any person obtaining a copy
8 of this software and associated documentation files (the "Software"), to deal
9 in the Software without restriction, including without limitation the rights
10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 copies of the Software, and to permit persons to whom the Software is
12 furnished to do so, subject to the following conditions:
13 
14 The above copyright notice and this permission notice shall be included in all
15 copies or substantial portions of the Software.
16 
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 SOFTWARE.
24 
25  */
26 /*
27 Part of Rack development has been done in the BALTRAD projects part-financed
28 by the European Union (European Regional Development Fund and European
29 Neighbourhood Partnership Instrument, Baltic Sea Region Programme 2007-2013)
30  */
31 
32 #ifndef DATACONVERSIONOP2_H_
33 #define DATACONVERSIONOP2_H_
34 
35 #include <list>
36 #include <map>
37 #include <set>
38 #include <string>
39 #include <typeinfo>
40 
41 #include <drain/Log.h>
42 #include <drain/RegExp.h>
43 #include <drain/Type.h>
44 #include <drain/TypeUtils.h>
45 #include <drain/util/StringMatcherList.h>
46 //#include "drain/util/LinearScaling.h"
47 #include "drain/util/SmartMap.h"
48 #include "drain/util/TreeOrdered.h"
49 #include "drain/util/ValueScaling.h"
50 #include "drain/util/VariableMap.h"
51 
52 #include "drain/image/Geometry.h"
53 #include "drain/image/Image.h"
54 #include "drain/image/ImageFrame.h"
55 
56 #include "data/Data.h"
57 #include "data/ODIM.h"
58 #include "data/ODIMPath.h"
59 #include "data/Quantity.h"
60 #include "data/QuantityMap.h"
61 //#include "drain/util/LinearScaling.h"
62 #include <hi5/Hi5.h>
63 #include "ProductOp.h"
64 
65 //#include "VolumeTraversalOp.h"
66 //#include "drain/utility>
67 
68 //#include "VolumeOpNew.h"
69 
70 
71 namespace rack {
72 
74 
81 template <class M>
82 class DataConversionOp: public ProductOp<M,M> {
83 
84 public:
85 
86  typedef SrcType<M const> src_t;
87  typedef DstType<M> dst_t;
88 
89  DataConversionOp(const std::string & type="C", double gain=1.0, double offset=0.0,
90  double undetect=0.0, double nodata=255.0, std::string copyGroupSuffix="") :
91  ProductOp<M, M>(__FUNCTION__, "Converts HDF5 data to use desired data type, scaling and encoding") {
92 
93  this->allowedEncoding.link("what:type", this->odim.type = type);
94  this->allowedEncoding.link("what:gain", this->odim.scaling.scale = gain);
95  this->allowedEncoding.link("what:offset", this->odim.scaling.offset = offset);
96  this->allowedEncoding.link("what:undetect", this->odim.undetect = undetect);
97  this->allowedEncoding.link("what:nodata", this->odim.nodata = nodata);
98 
99  //this->dataSelector.pathMatcher.setElems(ODIMPathElem::DATASET, ODIMPathElem::DATA);
100  this->dataSelector.setPathMatcher(ODIMPathElem::DATASET, ODIMPathElem::DATA);
101 
102  }
103 
104  virtual ~DataConversionOp(){};
105 
107  // static const Hi5Tree & getNormalizedDataOLD(const DataSet< src_t> & srcDataSet, DataSet<dst_t> & dstDataSet, const std::string & quantity){}:
108 
110  virtual
111  void processH5(const Hi5Tree &src, Hi5Tree &dst) const;
112 
113  virtual
114  void processDataSet(const DataSet<src_t> & srcSweep, DataSet<dst_t> & dstProduct) const;
115 
116 
117  inline
118  void processImage(const PlainData< src_t> & src, drain::image::Image & dst) const {
119  processImage(src.odim, src.data, this->odim, dst);
120  }
121 
123 
126  void processImage(const ODIM & odimSrc, const drain::image::ImageFrame & src, const ODIM & odimDst, drain::image::Image & dst) const;
127 
128  void processImage2023(const ODIM & srcOdim, const drain::image::ImageFrame & src, drain::image::Image & dst) const;
129 
130 
131  void traverseImageFrame(const ODIM & odimSrc, const drain::image::ImageFrame & src, const ODIM & odimDst, drain::image::ImageFrame & dst) const;
132 
133 
134  inline
135  void setGeometry(const M & srcODIM, PlainData<dst_t> & dstData) const {
136  // Does not change geometry.
137  }
138 
139 
140  static
141  PlainData< DstType<M> > & getNormalizedData(const DataSet<src_t> & srcDataSet, DataSet<dst_t> & dstDataSet, const std::string & quantity);
142 
143 
144 protected:
145 
147  std::string copyGroupSuffix;
148 
149 };
150 
151 
152 template <class M> //
154  const std::string & quantity) { // , const PlainData< src_t> & mika
155 
156  drain::Logger mout(__FILE__, __FUNCTION__);
157 
158  const std::string quantityExt = quantity+"_norm"; // std::string("~") +
159 
160  //typename DataSet< SrcType<M const> >::const_iterator it = normDataSet.find(quantityExt);
161  typename DataSet<dst_t >::iterator it = normDataSet.find(quantityExt);
162  if (it != normDataSet.end()){
163  mout.note("using cached data: ", quantityExt);
164  return it->second;
165  }
166  else {
167 
168  const PlainData<src_t> & srcData = srcDataSet.getData(quantity);
169  const EncodingODIM & odimNorm = getQuantityMap().get(quantity).get(); //[srcData.odim.type.at(0)];
170 
171  mout.info("converting and adding to cache: " , quantityExt , " odim: " , odimNorm );
172  PlainData<dst_t> & dstDataNew = normDataSet.getData(quantityExt);
173  dstDataNew.setExcluded();
174  DataConversionOp<M> op;
175  //op.odim.importMap(odimNorm);
176  // mout.warn("odimNorm: " , odimNorm );
177  // mout.warn("op.odim: " , op.odim );
178  dstDataNew.odim.importMap(srcData.odim);
179  dstDataNew.odim.importMap(odimNorm);
180  dstDataNew.odim.quantity = quantity;
181  //op.processData(srcData, dstDataNew);
182  op.processImage(srcData.odim, srcData.data, dstDataNew.odim, dstDataNew.data);
183  dstDataNew.odim.quantity = quantityExt;
184  dstDataNew.updateTree2(); // @?
185  mout.debug("obtained: " , dstDataNew );
186 
187  return dstDataNew;
188  }
189 
190  //return normDataSet.getData(quantityExt);
191 
192 }
193 
194 
195 
196 template <class M>
197 void DataConversionOp<M>::processH5(const Hi5Tree &src, Hi5Tree &dst) const {
198 
199  drain::Logger mout(__FILE__, __FUNCTION__);
200 
201  mout.debug3(*this );
202  mout.debug("DataSelector: ", this->dataSelector);
203 
205  ODIMPathList dataPaths;
206  this->dataSelector.getPaths(src, dataPaths); //, ODIMPathElem::DATA);
207 
208  mout.special("obtained ", dataPaths.size(), " paths.");
209 
210  // Parents are needed because converted data are stored in parallel.
211  std::set<ODIMPathElem> parents;
212 
213  //const drain::RegExp quantityRegExp(this->dataSelector.getQuantity());
214  const drain::KeySelector & slct = this->dataSelector.getQuantitySelector();
215 
216  mout.special("slct: ", slct);
217 
218  // copy
219  for (ODIMPath & path: dataPaths){
220 
221  if (path.front().is(ODIMPathElem::ROOT)){
222  // is this needed?
223  path.pop_front();
224  }
225  //const ODIMPathElem & parent = path.back();
226  const ODIMPathElem & parent = path.front();
227 
228  mout.special("handling: ", parent, " -> ", path);
229 
230  if (parents.find(parent) == parents.end()){
231  if (parent.getType() != ODIMPathElem::DATASET){
232  mout.note("non-dataset group: ", parent);
233  }
234  mout.note("now handling: ", parent);
235  parents.insert(parent);
236  // DataSet<src_t> srcDataSet(src(path), slct);
237  // DataSet<dst_t> dstDataSet(dst(path));
238  DataSet<src_t> srcDataSet(src[parent], slct);
239  DataSet<dst_t> dstDataSet(dst[parent]);
240  processDataSet(srcDataSet, dstDataSet);
241  }
242  else {
243  mout.note("already exists(?): ", parent); // ???
244  }
245 
246  }
247 }
248 
249 template <class M>
250 void DataConversionOp<M>::processDataSet(const DataSet<src_t> & srcSweep, DataSet<dst_t> & dstProduct) const {
251 
252  drain::Logger mout(__FILE__, __FUNCTION__);
253 
254  std::set<std::string> convertedQuantities;
255 
256  //const std::string extension("_X");
257 
258  mout.debug("number of layers (sub groups of DataSet): ", srcSweep.size());
259 
260  // Traverse quantities
261  //for (typename DataSet<src_t>::const_iterator it = srcSweep.begin(); it != srcSweep.end(); ++it){
262  for (const auto & entry: srcSweep){
263 
264  const std::string & quantity = entry.first;
265 
266  if (quantity.empty()){
267  mout.warn("empty quantity for data, skipping");
268  continue;
269  }
270 
271  // Rescaled quantity, temporary name
272  const std::string quantityTmp = quantity+'*';
273 
274  mout.debug("quantity: ", quantity);
275 
276  const Data<src_t> & srcData = entry.second;
277  Data<dst_t> & dstData = dstProduct.getData(quantityTmp); // todo: getNewData
278  //dstProduct.getData(quantity).setExcluded(true);
279  mout.attention<LOG_DEBUG>("srcData: ", entry.second);
280 
281  mout.debug2(EncodingODIM(this->odim));
282  //mout.toOStr() << "src " << (long int) &(srcData.data) << EncodingODIM(srcData.odim) << mout.endl;
283  //mout.warn("dst " , (long int) &(dstData.data) , EncodingODIM(dstData.odim) );
284 
285  const drain::Type t(this->odim.type);
286 
287  //const bool IN_PLACE = (&dstData.data == &srcData.data) && (t == srcData.data.getType());
288  const bool IN_PLACE = false;
289  if (IN_PLACE){
290 
291  if (ODIM::haveSimilarEncoding(srcData.odim, this->odim)){
292  mout.info("already similar encoding – no need to convert (1)");
293  continue; // to next quantity
294  }
295 
296  mout.unimplemented("in-place: not implemented");
297  mout.debug("in-place");
298  //processData(srcData, dstData);
299  //processImage(srcData.odim, srcData.data, dstData.odim, dstData.data);
300  //@ dstData.updateTree();
301 
302  }
303  else {
304 
305  mout.info("using tmp data – in-place computation not possible");
306 
307  // convertedQuantities.insert(entry.first);
308 
309  if (ODIM::haveSimilarEncoding(srcData.odim, this->odim)){
310  mout.info("already similar encoding – no need to convert (2)");
311  continue; // to next quantity
312  }
313 
314  convertedQuantities.insert(entry.first); // CHECK: was first abovw - ?
315 
316  //const M srcODIM(srcData.odim); // Copy, because src may be modified next
317  const M srcODIM(srcData.data); // Copy, because src may be modified next
318  mout.attention("srcData.odim: ", srcData.odim);
319  mout.attention("srcODIM: ", srcODIM);
320  dstData.odim.quantity = quantity;
321  dstData.odim.updateLenient(srcODIM); // <= dstData.odim.NI = srcData.odim.NI; // if Cart?
322  ProductBase::completeEncoding(dstData.odim, this->targetEncoding);
323  mout.special("Final encoding: ", (const EncodingODIM &)dstData.odim);
324  //processData(srcData, dstData2);
325  processImage(srcODIM, srcData.data, dstData.odim, dstData.data);
326 
327  }
328 
329  }
330 
332  mout.debug("Swap & mark for deletion: ", drain::sprinter(convertedQuantities));
333  for (const std::string & quantity: convertedQuantities){
334 
335  const std::string quantityTmp = quantity + '*';
336 
337  mout.special("Swapping quantity: ", quantity, " <-> ", quantityTmp);
338 
339  Data<dst_t> & dstDataOrig = dstProduct.getData(quantity);
340  Data<dst_t> & dstDataConv = dstProduct.getData(quantityTmp);
341 
342  dstDataOrig.swap(dstDataConv); // calls updateTree2 (consider what:quantity)
343 
344  dstDataConv.odim.quantity = quantityTmp;
345  dstDataConv.setExcluded(true);
346  }
347 
348 }
349 
350 template <class M>
351 void DataConversionOp<M>::processImage2023(const ODIM & srcOdim, const drain::image::ImageFrame & srcImage,
352  drain::image::Image & dstImage) const {
353 
354  // drain::Logger mout(__FILE__, __FUNCTION__);
355 
356  ODIM odim;
357  odim.updateFromCastableMap(srcOdim); // quantity, etc
358  ProductBase::completeEncoding(odim, this->targetEncoding);
359  processImage(srcOdim, srcImage, odim, dstImage);
360 
361  //op.processImage2(srcOdim, srcImage, ctx.targetEncoding, ctx.grayImage);
362 
363 }
364 
365 template <class M>
366 void DataConversionOp<M>::processImage(const ODIM & srcOdim, const drain::image::ImageFrame & srcImage, const ODIM & dstOdim, drain::image::Image & dstImage) const {
367 
368 
369  drain::Logger mout(__FILE__, __FUNCTION__);
370  mout.debug("type=", this->odim.type, ", geom=", srcImage.getGeometry() );
371 
372  // const drain::Type t(this->odim.type);
373  const drain::Type t(dstOdim.type);
374 
375  const drain::image::Geometry g(srcImage.getGeometry());
376 
377  mout.attention<LOG_DEBUG>("srcOdim ", srcOdim);
378  mout.attention<LOG_DEBUG>("srcImage:", srcImage);
379 
380  if (srcImage.hasOverlap(dstImage)){
381  if ((t.getType() != srcImage.getType()) || (g != dstImage.getGeometry())){
382  mout.warn("using temp image + swap");
384  tmp.setType(t);
385  tmp.setGeometry(g);
386  tmp.setScaling(dstOdim.scaling);
387  traverseImageFrame(srcOdim, srcImage, dstOdim, tmp);
388  dstImage.swap(tmp);
389  //dstImage.copyDeep(tmp);
390  return;
391  }
392  else {
393  mout.warn("same type and geometry, hence only rescaling (in-place)");
394  }
395  /*
396  if (t != srcImage.getType2()){
397  mout.error("trying to change type when dst==src" );
398  return;
399  }
400  if (g != dstImage.getGeometry()){
401  mout.error("trying to change geometry when dst==src" );
402  return;
403  }
404  */
405  }
406  else {
407  dstImage.setType(t);
408  dstImage.setGeometry(g);
409  //dstImage.setScaling(dstOdim.scaling.scale, dstOdim.scaling.offset);
410  dstImage.setScaling(dstOdim.scaling); // ok separate
411  mout.debug("dst:", dstImage);
412  traverseImageFrame(srcOdim, srcImage, dstOdim, dstImage);
413  }
414 
415  mout.attention<LOG_DEBUG>("dstOdim: ", dstOdim);
416  mout.attention<LOG_DEBUG>("dstImage: ", dstImage);
417 
418 
419 }
420 
421 template <class M>
423  const ODIM & dstOdim, drain::image::ImageFrame & dstImage) const {
424 
425  drain::Logger mout(__FILE__, __FUNCTION__);
426 
427  // mout.debug3("input name: " , src.getName() );
428  mout.debug2("src odim: ", EncodingODIM(srcOdim));
429  mout.debug3("src props:", srcImage.properties);
430  // std::cerr << src.properties << std::endl;
431 
432  mout.debug("dst:", dstImage);
433 
434  //dstImage.properties.updateFromCastableMap(srcImage.properties);
435  dstImage.properties.importCastableMap(srcImage.properties);
436  dstImage.properties.importCastableMap(dstOdim);
437  dstImage.setScaling(dstOdim.scaling);
438  dstImage.setCoordinatePolicy(srcImage.getCoordinatePolicy());
439 
440  // dst.odim.set(odim);
441  // mout.debug2("op odim: " , EncodingODIM(odim) );
442  mout.debug("dst odim: ", EncodingODIM(dstOdim));
443  mout.debug("dst props: ", dstImage.properties);
444  //std::cerr << dst.properties << std::endl;
445 
446  // const drain::ValueScaling scaling(srcOdim.scaling.scale, srcOdim.scaling.offset, dstOdim.scaling.scale, dstOdim.scaling.offset);
447  const drain::ValueScaling scaling(srcOdim.scaling, dstOdim.scaling); // 2023/04/21
448  mout.debug("scaling: ", scaling);
449 
450  typedef drain::typeLimiter<double> Limiter;
451  Limiter::value_t limit = drain::Type::call<Limiter>(dstOdim.type);
452 
453  Image::const_iterator s = srcImage.begin();
454  Image::iterator d = dstImage.begin();
455 
456  // Tailored long int check by writing and reading a pixel.
457  *d = dstOdim.nodata;
458  if (static_cast<double>(*d) != dstOdim.nodata){
459  mout.note("dstOdim.nodata=", dstOdim.nodata, " -> ", static_cast<double>(*d));
460  mout.note("type conversion ", srcOdim.type, " -> ", dstOdim.type);
461  mout.warn("type conversion ", dstOdim.type, " ~= ", drain::Type::getTypeChar(dstImage.getType()), " changed the value");
462  }
463  //mout.debug("dstOdim nodata long-int check " , dstOdim.nodata , " <> " , (long int)(*d = dstOdim.nodata) );
464 
465  mout.debug2("src: ", srcImage);
466  mout.debug2("dst: ", dstImage);
467  double x;
468  while (s != srcImage.end()){
469  x = *s;
470 
472  if (x == srcOdim.undetect)
473  *d = dstOdim.undetect;
474  else if (x == srcOdim.nodata)
475  *d = dstOdim.nodata;
476  else {
477  /*
478  x = srcOdim.scaleForward(x);
479  *d = limit(dstOdim.scaleInverse(x));
480  */
481  // x = dst.odim.scaleInverse(x);
482  // *d = dstImage.limit<double>( x );
483  // *d = limit( scaling.forward(x) );
484  *d = limit( scaling.inv(x) );
485 
486  }
487 
488  ++s;
489  ++d;
490  }
491 
492  mout.debug3("finished.");
493 
494 }
495 
496 
497 
498 }
499 
500 #endif /* DATACONVERSIONOP_H_ */
501 
502 // Rack
503 // REP // REP // REP // REP
LogSourc e is the means for a function or any program segment to "connect" to a Log.
Definition: Log.h:308
Logger & attention(const TT &... args)
Possible error, but execution can continue. Special type of Logger::warn().
Definition: Log.h:472
Logger & special(const TT &... args)
Other useful information.
Definition: Log.h:527
Logger & unimplemented(const TT &... args)
Feature to be done. Special type of Logger::note().
Definition: Log.h:507
Logger & note(const TT &... args)
For top-level information.
Definition: Log.h:485
Logger & warn(const TT &... args)
Possible error, but execution can continue.
Definition: Log.h:426
Logger & debug(const TT &... args)
Public, yet typically used "internally", when TIMING=true.
Definition: Log.h:676
Logger & debug2(const TT &... args)
Debug information.
Definition: Log.h:686
Definition: Path.h:112
void importCastableMap(const drain::SmartMap< T2 > &m)
Assign values from a map, possibly extending the map.
Definition: SmartMap.h:271
void updateFromCastableMap(const drain::SmartMap< T2 > &m)
Convenience.
Definition: SmartMap.h:301
Utilities related to std::type_info.
Definition: Type.h:51
const std::type_info & getType() const
Deprecated! Use cast (above) instead?
Definition: Type.h:107
Linear scaling and physical range for image intensities.
Definition: ValueScaling.h:64
double inv(double y) const
Inverse scaling: given physically meaningful value y, returns the corresponding code value.
Definition: ValueScaling.h:301
Definition: Geometry.h:145
Image with static geometry.
Definition: ImageFrame.h:67
const iterator & begin()
Returns iterator pointing to the first image element.
Definition: ImageFrame.h:119
const iterator & end()
Returns the iterator pointing to the element after the last element of the image buffer.
Definition: ImageFrame.h:125
FlexVariableMap properties
Container for user-defined KEY=VALUE metadata.
Definition: ImageFrame.h:369
bool hasOverlap(const ImageFrame &image) const
Checks if images have a common memory segment.
Definition: ImageFrame.h:341
const std::type_info & getType() const
Get the storage type.
Definition: ImageLike.h:100
const CoordinatePolicy & getCoordinatePolicy() const
Coord policy.
Definition: ImageLike.h:167
Class for multi-channel digital images. Supports dynamic typing with base types (char,...
Definition: Image.h:184
void setType(const std::type_info &type)
Sets the storage type of the image - typically unsigned char, unsigned int or float.
Definition: Image.h:268
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:95
Computes dot product of intensities of two images.
Definition: PixelVectorOp.h:364
Converts HDF5 data to use desired data type, scaling and encoding (ODIM gain, offset,...
Definition: DataConversionOp.h:82
virtual void processDataSet(const DataSet< src_t > &srcSweep, DataSet< dst_t > &dstProduct) const
Definition: DataConversionOp.h:250
virtual void processH5(const Hi5Tree &src, Hi5Tree &dst) const
Ensures data to be in standard type and scaling. Makes a converted copy if needed.
Definition: DataConversionOp.h:197
std::string copyGroupSuffix
Suffix for trailing path element ("/data") for storing the original.
Definition: DataConversionOp.h:147
void traverseImageFrame(const ODIM &odimSrc, const drain::image::ImageFrame &src, const ODIM &odimDst, drain::image::ImageFrame &dst) const
Definition: DataConversionOp.h:422
A map of radar data, indexed by quantity code (DBZH, VRAD, etc).
Definition: Data.h:1213
Data structure consisting of plain data and an optional quality data.
Definition: Data.h:1144
Structure for data storage type, scaling and marker codes. Does not contain quantity.
Definition: EncodingODIM.h:75
static bool haveSimilarEncoding(const EncodingODIM &odim1, const EncodingODIM &odim2)
Checks if data encoding is similar (storage type, gain, offset, undetect and nodata are the same).
Definition: EncodingODIM.h:167
std::string type
This is non-standard (not in ODIM), but a practical means of handling storage type of datasets.
Definition: EncodingODIM.h:146
double nodata
data[n]/what (obligatory)
Definition: EncodingODIM.h:151
Definition: ODIMPath.h:82
static const group_t DATASET
First level group, /dataset + digit .
Definition: ODIMPath.h:103
static const group_t ROOT
Definition: ODIMPath.h:99
static const group_t DATA
Second level group, /data + digit .
Definition: ODIMPath.h:106
ODIM metadata (quantity, gain, offset, undetect, nodata, date, time)
Definition: ODIM.h:79
Essential class for storing radar data.
Definition: Data.h:302
static void completeEncoding(ODIM &productODIM, const std::string &targetEncoding)
Modifies encoding. If type is changed, resets scaling first.
Definition: ProductBase.cpp:183
const EncodingODIM & get(char typecode='\0') const
Retrieve the scaling for a given storage type.
Definition: Quantity.h:108
Definition: DataSelector.cpp:44
Writable data type.
Definition: Data.h:122
Read-only data type.
Definition: Data.h:112