Loading...
Searching...
No Matches
VolumeOp.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 * ProductOp.h
33 *
34 * Created on: Mar 7, 2011
35 * Author: mpeura
36 */
37
38#ifndef RACKOP_H_
39#define RACKOP_H_
40
41
42#include <drain/util/ReferenceMap.h>
43
44#include "data/DataSelector.h"
45#include "data/Data.h"
46#include "data/ODIM.h"
47#include "data/ODIMPathTools.h"
48#include "data/Quantity.h" // NEW
49
50#include "hi5/Hi5.h"
51#include "hi5/Hi5Write.h" // debugging
52
53
54#include "radar/PolarSector.h"
55#include "RadarProductOp.h"
56
57namespace rack {
58
59using namespace drain::image;
60
62
64
80// TODO: generalize for Cart
81template <class M>
82class VolumeOp : public RadarProductOp<const PolarODIM, M> {
83
84public:
85
86 VolumeOp(const std::string & name, const std::string &description="") : RadarProductOp<const PolarODIM, M>(name, description){
87 };
88
89 virtual inline
90 ~VolumeOp(){};
91
92
94
101 virtual
102 Hi5Tree & processVolume(const Hi5Tree &srcRoot, Hi5Tree &dstRoot) const final;
103
104 virtual inline
105 void setPolarSelector(const PolarSelector & ps){
106 // polarSelector = ps;
107 if (ps.azimuth.range.empty() && ps.radius.range.empty()){
108 drain::Logger mout(__FILE__, __FUNCTION__);
109 mout.info(this->getName(), ": no support for polar selector");
110 }
111 }
112
113protected:
114
115 // PolarSelector polarSelector;
116
117
118};
119
120
121
122template <class M>
123Hi5Tree & VolumeOp<M>::processVolume(const Hi5Tree &srcRoot, Hi5Tree &dstRoot) const{
124
125 drain::Logger mout(__FILE__, __FUNCTION__);
126
127 mout.debug("start" );
128 mout.debug3(*this );
129 mout.debug2("DataSelector: " , this->dataSelector );
130
131 // Step 1: collect sweeps (/datasetN/)
133
135 mout.debug3("collect the applicable paths" );
136 ODIMPathList dataPaths; // Down to ../dataN/ level, eg. /dataset5/data4
137 //this->dataSelector.getPaths(src, dataPaths, ODIMPathElem::DATASET); // RE2
138
139 this->dataSelector.getPaths(srcRoot, dataPaths);
140
141 if (dataPaths.empty()){
142 if (!this->dataSelector.getQuantitySelector().test("HGHT")){
143 mout.experimental<LOG_INFO>("no dataset's selected, but height=[HGHT] requested, continuing");
144 }
145 else {
146 mout.warn("no dataset's selected");
147 }
148 }
149 else {
150 mout.debug3("populate the dataset map, paths=" , dataPaths.size() );
151 }
152
153 ODIM dstODIM;
154
155 for (ODIMPath & path: dataPaths){
156
157 if (!path.front().is(ODIMPathElem::DATASET)){
158 path.pop_front();
159 if (path.empty()){
160 mout.warn("odd 1st path elem (..), with selector: ", this->dataSelector);
161 continue;
162 }
163 }
164
165 const ODIMPathElem & parentElem = path.front();
166
167 if (!parentElem.is(ODIMPathElem::DATASET)){
168 mout.warn("path does not start with /dataset.. :", path, ", with selector: ", this->dataSelector);
169 continue;
170 }
171
172 // mout.debug3("elangles (this far> " , elangles );
173 const Hi5Tree & srcDataSet = srcRoot(parentElem);
174
175 // const double elangle = srcDataSet[ODIMPathElem::WHERE].data.attributes["elangle"]; // PATH
176 // mout.deprecating("no more testing ", parent, ", elangle=", elangle, ':', srcDataSet.data.dataSet);
177
178
179 const ODIM srcODIM(srcDataSet.data.image);
180 std::string datetime = srcODIM.startdate + srcODIM.starttime;
181
182 dstODIM.updateLenient(srcODIM);
183 mout.attention<LOG_DEBUG+1>("start=", dstODIM.startdate, '+', dstODIM.starttime, " nominal<", datetime, "> end==", dstODIM.enddate, '+', dstODIM.endtime);
184
185 if (sweeps.find(datetime) == sweeps.end()){
186 mout.debug2("adding time=", datetime, ':', parentElem);
187 // Consider removing RegExp form datasets?
188 //sweeps.insert(DataSetMap<PolarSrc>::value_type(datetime, DataSet<PolarSrc>(srcDataSet, drain::RegExp(this->dataSelector.getQuantity()))));
189 sweeps.insert(DataSetMap<PolarSrc>::value_type(datetime, DataSet<PolarSrc>(srcDataSet, this->dataSelector.getQuantitySelector())));
190 }
191 else {
192 // mout.warn("datetime =", datetime, " already added?, skipping ", parent);
193 }
194
195 /*
196 if (sweeps.find(elangle) == sweeps.end()){
197 mout.debug3("add " , elangle , ':' , parent , " quantity RegExp:" , this->dataSelector.quantity );
198 sweeps.insert(DataSetMap<PolarSrc>::value_type(elangle, DataSet<PolarSrc>(srcDataSet, drain::RegExp(this->dataSelector.quantity) ))); // Something like: sweeps[elangle] = src[parent] .
199 // elangles << elangle;
200 //mout.warn("add " , DataSet<PolarSrc>(src(parent), drain::RegExp(this->dataSelector.quantity) ) );
201 }
202 else {
203 mout.note("elange =" , elangle , " already added, skipping " , parent );
204 }
205 */
206 }
207
208 //mout.note("first elange =" , sweeps.begin()->first , " DS =" , sweeps.begin()->second );
209 //mout.note("first qty =" , sweeps.begin()->second.begin()->first , " D =" , sweeps.begin()->second.getFirstData() );
210
211
212 ODIMPathElem dataSetPathElem(ODIMPathElem::DATASET); // ,1) // 2024/11
213 //if (!DataTools::getNextDescendant(dst, ProductBase::appendResults.getType(), dataSetPath))
214
215 if (ProductBase::appendResults.is(ODIMPathElem::DATASET)){
216 if (ProductBase::appendResults.getIndex()){
217 dataSetPathElem.index = ProductBase::appendResults.getIndex();
218 }
219 else {
220 ODIMPathTools::getNextChild(dstRoot, dataSetPathElem);
221 }
222 }
223 else if (ProductBase::appendResults.is(ODIMPathElem::DATA)){
224 //mout.info("appending to next available data group in " , dataSetPath ); // ALWAYS dataset1, then ?
225 }
226 else if (ProductBase::appendResults.is(ODIMPathElem::ROOT)){
227 if (!dstRoot.empty() && (&srcRoot != &dstRoot)){ // latter is ANDRE test... (kludge)
228 mout.info("clearing previous result, use --append [data|dataset] to avoid");
229 dstRoot.clear();
230 }
231 }
232 else {
233 dataSetPathElem = ProductBase::appendResults;
234 mout.warn("non-standard path location '", dataSetPathElem, "', consider --help append ");
235 }
236 //++dataSetPath.index;
237
238 //mout.warn("FAILED: " , dataSetPath );
239 //dataSetPath.push_back(ODIMPathElem(ODIMPathElem::DATASET, 1));
240
241 mout.debug("storing product in path: ", dataSetPathElem);
242
243 //Hi5Tree & dstProduct = dst[dataSetPath];
244 /* /// WARNING: Root odim has to be modified explicitly, otherwise remains empty.
245 RootData<DstType<M> > root(dst);
246 drain::VariableMap & whatRoot = root.getWhat();
247 whatRoot["object"] = this->odim.object;
248 whatRoot["version"] = this->odim.version;
249 */
250
251
253 Hi5Tree & dstDataSet = dstRoot[dataSetPathElem];
254 { // SCOPE
255 DataSet<DstType<M> > dstProductDataset(dstDataSet); // PATH
256 this->computeSingleProduct(sweeps, dstProductDataset);
257
258 drain::VariableMap & how = dstProductDataset.getHow();
259 ProductBase::setRackVersion(how);
260 if (!dstProductDataset.empty()){
262 how["angles"] = dstProductDataset.getFirstData().odim.angles; // FIX: this may be some tmp data, not main [odim.quantity]
263 }
264 }
265
266 /* keep for debugging
267 for (const auto & entry: dstDataSet){
268 mout.warn(entry.first, ": ", entry.second[ODIMPathElem::ARRAY].data.image.getProperties());
269 }
270 */
271
272
273 // Copy metadata from the input volume (note that dst may have been cleared above)
274 mout.revised<LOG_DEBUG>("check DataSet metadata");
275 // drain::TreeUtils::dump(dstRoot, std::cout, DataTools::treeToStream);
276
277 // Root level
278 for (const ODIMPathElem::group_t group: {ODIMPathElem::WHAT, ODIMPathElem::WHERE, ODIMPathElem::HOW}){
279 dstRoot[group].data.attributes = srcRoot[group].data.attributes;
280 }
281 drain::VariableMap & rootWhat = dstRoot[ODIMPathElem::WHAT].data.attributes; // dstProduct["what"].data.attributes;
283 rootWhat["object"] = this->odim.object; // ?
284 rootWhat["version"] = this->odim.version;
285
286 dstDataSet[ODIMPathElem::WHAT].data.attributes["product"] = this->odim.product;
287 dstDataSet[ODIMPathElem::WHAT].data.attributes["startdate"] = dstODIM.startdate;
288 dstDataSet[ODIMPathElem::WHAT].data.attributes["starttime"] = dstODIM.starttime;
289 dstDataSet[ODIMPathElem::WHAT].data.attributes["enddate"] = dstODIM.enddate;
290 dstDataSet[ODIMPathElem::WHAT].data.attributes["endtime"] = dstODIM.endtime;
291
292 // PRODPAR ... complicated! Fix at Sprinter level, getKeyList()
293 if (ODIM::versionFlagger.isSet(ODIM::RACK_EXTENSIONS)){
294
295 dstDataSet[ODIMPathElem::WHAT].data.attributes["product2"] = dstODIM.product;
296
297 // keep scope, even if condition removed
298 std::stringstream sstr;
299 char sep = 0;
300 const drain::ReferenceMap & params = this->getParameters();
301 for (const std::string & key: params.getKeyList()){
302 if (sep){
303 sstr << sep;
304 }
305 else {
306 sep = ',';
307 }
308 sstr << key << '=' << params[key]; // TODO: escape \" ?
309 }
310 dstDataSet[ODIMPathElem::WHAT].data.attributes["rack_prodpar"] = sstr.str();
311 //dstDataSet[ODIMPathElem::WHAT].data.attributes["prodpar"] = drain::sprinter(this->getParameters(), drain::Sprinter::cmdLineLayout);
312 }
313
314 return dstDataSet;
315
316}
317
318
319} // namespace rack
320
321
322#endif
virtual const std::string & getName() const
Return the name of an instance.
Definition BeanLike.h:82
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 & debug(const TT &... args)
Debug information.
Definition Log.h:667
Logger & attention(const TT &... args)
Possible error, but execution can continue. Special type of Logger::warn().
Definition Log.h:477
Logger & debug2(const TT &... args)
Debug information.
Definition Log.h:677
Definition Path.h:137
Definition ReferenceMap.h:215
virtual const keylist_t & getKeyList() const
Derived versions may produce an ordered set of keys.
Definition SmartMap.h:200
A map of Variables.
Definition VariableMap.h:61
Definition Data.h:1433
A map of radar data, indexed by quantity code (DBZH, VRAD, etc).
Definition Data.h:1278
Definition ODIMPath.h:82
bool is(group_t g) const
Abbreviation of (group == NONE)
Definition ODIMPath.h:294
unsigned int group_t
In H5, "groups" correspond to directories or folders in file system.
Definition ODIMPath.h:92
ODIM metadata (quantity, gain, offset, undetect, nodata, date, time)
Definition ODIM.h:79
virtual void updateLenient(const ODIM &odim)
Updates object, quantity, product and time information.
Definition ODIM.cpp:461
Polar and Cartesian products.
Definition RadarProductOp.h:78
Base class for radar data processors.
Definition VolumeOp.h:82
virtual Hi5Tree & processVolume(const Hi5Tree &srcRoot, Hi5Tree &dstRoot) const final
Traverse through given volume and create new, processed data (volume or polar product).
Definition VolumeOp.h:123
Namespace for images and image processing tools.
Definition AccumulationArray.cpp:45
Definition DataSelector.cpp:44
Definition PolarSector.h:110