Loading...
Searching...
No Matches
DataConversionOp.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
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 <product/RadarProductOp.h>
64
65//#include "VolumeTraversalOp.h"
66//#include <drain/utility>
67
68//#include >VolumeOpNew.h"
69
70
71namespace rack {
72
74
81template <class M>
82class DataConversionOp: public RadarProductOp<M,M> {
83
84public:
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 RadarProductOp<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
144protected:
145
147 std::string copyGroupSuffix;
148
149};
150
151
152template <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
196template <class M>
197void 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 QuantitySelector & 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
249template <class M>
250void 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
350template <class M>
351void 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
365template <class M>
366void 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
421template <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_ */
Definition CastableIterator.h:57
LogSourc e is the means for a function or any program segment to "connect" to a Log.
Definition Log.h:312
Logger & warn(const TT &... args)
Possible error, but execution can continue.
Definition Log.h:430
Logger & debug(const TT &... args)
Debug information.
Definition Log.h:666
Logger & note(const TT &... args)
For top-level information.
Definition Log.h:489
Logger & special(const TT &... args)
Other useful information.
Definition Log.h:531
Logger & attention(const TT &... args)
Possible error, but execution can continue. Special type of Logger::warn().
Definition Log.h:476
Logger & unimplemented(const TT &... args)
Feature to be done. Special type of Logger::note().
Definition Log.h:511
Logger & debug2(const TT &... args)
Debug information.
Definition Log.h:676
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 CoordinatePolicy & getCoordinatePolicy() const
Coord policy.
Definition ImageLike.h:167
const std::type_info & getType() const
Get the storage type.
Definition ImageLike.h:100
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
Class for ensuring that variable of type D remains within limits of type S.
Definition TypeUtils.h:96
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
void setGeometry(const M &srcODIM, PlainData< dst_t > &dstData) const
Sets automagically the suitable dst parameters.
Definition DataConversionOp.h:135
A map of radar data, indexed by quantity code (DBZH, VRAD, etc).
Definition Data.h:1215
Data structure consisting of plain data and an optional quality data.
Definition Data.h:1146
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:152
double nodata
data[n]/what (obligatory)
Definition EncodingODIM.h:157
Definition ODIMPath.h:82
static const group_t DATASET
First level group, /dataset + digit .
Definition ODIMPath.h:106
static const group_t DATA
Second level group, /data + digit .
Definition ODIMPath.h:109
ODIM metadata (quantity, gain, offset, undetect, nodata, date, time)
Definition ODIM.h:79
Essential class for storing radar data.
Definition Data.h:300
drain::ReferenceMap allowedEncoding
Defines which encoding parameters can be changed by the user from command line.
Definition ProductBase.h:205
const EncodingODIM & get(char typecode='\0') const
Retrieve the scaling for a given storage type.
Definition Quantity.cpp:168
Polar and Cartesian products.
Definition RadarProductOp.h:78
M odim
The default data parameters for encoding output (the product).
Definition RadarProductOp.h:101
Definition DataSelector.cpp:44
Writable data type.
Definition Data.h:120
Read-only data type.
Definition Data.h:110