WSL/SLF GitLab Repository

IOHandler.cmake.cc 41.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/***********************************************************************************/
/*  Copyright 2009-2012 WSL Institute for Snow and Avalanche Research  SLF-DAVOS   */
/***********************************************************************************/
/* This file is part of MeteoIO.
    MeteoIO is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    MeteoIO is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with MeteoIO.  If not, see <http://www.gnu.org/licenses/>.
*/

19
#include <meteoio/IOUtils.h>
20
#include <meteoio/MathOptim.h>
21
#include <meteoio/IOHandler.h>
22
#include <meteoio/dataClasses/MeteoData.h> //needed for the merge strategies
23

24
#cmakedefine PLUGIN_ALPUG
25
26
27
#cmakedefine PLUGIN_ARCIO
#cmakedefine PLUGIN_A3DIO
#cmakedefine PLUGIN_ARPSIO
28
#cmakedefine PLUGIN_CNRMIO
29
30
31
32
33
34
#cmakedefine PLUGIN_GRASSIO
#cmakedefine PLUGIN_GEOTOPIO
#cmakedefine PLUGIN_SMETIO
#cmakedefine PLUGIN_SNIO
#cmakedefine PLUGIN_PGMIO
#cmakedefine PLUGIN_IMISIO
35
#cmakedefine PLUGIN_OSHDIO
36
37
38
39
40
#cmakedefine PLUGIN_GRIBIO
#cmakedefine PLUGIN_PNGIO
#cmakedefine PLUGIN_BORMAIO
#cmakedefine PLUGIN_COSMOXMLIO
#cmakedefine PLUGIN_GSNIO
41
#cmakedefine PLUGIN_NETCDFIO
Thomas Egger's avatar
Thomas Egger committed
42
#cmakedefine PLUGIN_PSQLIO
43
#cmakedefine PLUGIN_SASEIO
44

45
#include <meteoio/plugins/ALPUG.h>
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <meteoio/plugins/ARCIO.h>
#include <meteoio/plugins/A3DIO.h>
#include <meteoio/plugins/ARPSIO.h>
#include <meteoio/plugins/GrassIO.h>
#include <meteoio/plugins/GeotopIO.h>
#include <meteoio/plugins/PGMIO.h>
#include <meteoio/plugins/SMETIO.h>
#include <meteoio/plugins/SNIO.h>

#ifdef PLUGIN_BORMAIO
#include <meteoio/plugins/BormaIO.h>
#endif

59
60
61
62
#ifdef PLUGIN_CNRMIO
#include <meteoio/plugins/CNRMIO.h>
#endif

63
64
65
66
67
68
69
70
#ifdef PLUGIN_COSMOXMLIO
#include <meteoio/plugins/CosmoXMLIO.h>
#endif

#ifdef PLUGIN_IMISIO
#include <meteoio/plugins/ImisIO.h>
#endif

71
72
73
74
#ifdef PLUGIN_OSHDIO
#include <meteoio/plugins/OshdIO.h>
#endif

75
76
77
78
79
80
81
82
#ifdef PLUGIN_GRIBIO
#include <meteoio/plugins/GRIBIO.h>
#endif

#ifdef PLUGIN_GSNIO
#include <meteoio/plugins/GSNIO.h>
#endif

83
84
85
86
#ifdef PLUGIN_NETCDFIO
#include <meteoio/plugins/NetCDFIO.h>
#endif

87
88
89
90
#ifdef PLUGIN_PNGIO
#include <meteoio/plugins/PNGIO.h>
#endif

Thomas Egger's avatar
Thomas Egger committed
91
92
93
94
#ifdef PLUGIN_PSQLIO
#include <meteoio/plugins/PSQLIO.h>
#endif

95
96
97
98
#ifdef PLUGIN_SASEIO
#include <meteoio/plugins/SASEIO.h>
#endif

99
100
101
102
using namespace std;

namespace mio {
 /**
103
104
105
106
 * @page data_sources Data input overview
 * The data access is handled by a system of plugins. They all offer the same interface, meaning that a plugin can transparently be replaced by another one. Since they 
 * might rely on third party libraries for accessing the data, they have been created as plugins, that is they are  only compiled if requested when configuring the 
 * compilation with cmake. A plugin can therefore fail to run if it has not been compiled.
107
 *
108
109
 * Please have a look at the support for \subpage coords "coordinate systems".
 * 
110
111
112
113
114
115
 * @section available_categories Data sources categories
 * Several data sources categories have been defined that can be provided by a different plugin. Each data source category is defined by a specific key in the configuration file (usually, io.ini):
 * - METEO, for meteorological time series
 * - DEM, for Digital Elevation Maps
 * - LANDUSE, for land cover information
 * - GRID2D, for generic 2D grids (they can contain meteo fields and be recognized as such or arbitrary gridded data)
116
 * - POI, for a list of Points Of Interest that can be used for providing extra information at some specific location (extracting time series at a few selected points, etc)
117
118
119
120
121
122
123
124
125
126
 *
 * A plugin is "connected" to a given data source category simply by giving its keyword as value for the data source key:
 * @code
 * METEO = SMET
 * DEM = ARC
 * @endcode
 * Each plugin might have its own specific options, meaning that it might require its own keywords. Please check in each plugin documentation the supported options and keys (see links below).
 * Moreover, a given plugin might only support a given category for read or write (for example, PNG: there is no easy and safe way to interpret a given color as a given numeric value without knowing its color scale, so reading a png has been disabled).
 * Finally, the plugins usually don't implement all these categories (for example, ArcGIS file format only describes 2D grids, so the ARC plugin will only deal with 2D grids), so please check what a given plugin implements before connecting it to a specific data source category.
 *
127
 * @subsection available_plugins Available plugins
128
129
130
 * So far the following plugins have been implemented (by keyword for the io.ini key/value config file). Please read the documentation for each plugin in order to know the plugin-specific keywords:
 * <center><table border="1">
 * <tr><th>Plugin keyword</th><th>Provides</th><th>Description</th><th>Extra requirements</th></tr>
131
 * <tr><td>\subpage alpug "ALPUG"</td><td>meteo</td><td>data files generated by the %ALPUG meteo stations</td><td></td></tr>
132
 * <tr><td>\subpage a3d "A3D"</td><td>meteo, poi</td><td>original Alpine3D meteo files</td><td></td></tr>
133
134
135
 * <tr><td>\subpage arc "ARC"</td><td>dem, landuse, grid2d</td><td>ESRI/ARC ascii grid files</td><td></td></tr>
 * <tr><td>\subpage arps "ARPS"</td><td>dem, grid2d</td><td>ARPS ascii formatted grids</td><td></td></tr>
 * <tr><td>\subpage borma "BORMA"</td><td>meteo</td><td>Borma xml meteo files</td><td><A HREF="http://libxmlplusplus.sourceforge.net/">libxml++</A></td></tr>
136
 * <tr><td>\subpage cnrm "CNRM"</td><td>dem, grid2d, meteo</td><td>NetCDF meteorological timeseries following the <A HREF="http://www.cnrm.meteo.fr/?lang=en">CNRM</A> schema</td><td><A HREF="http://www.unidata.ucar.edu/downloads/netcdf/index.jsp">NetCDF-C library</A></td></tr>
137
 * <tr><td>\subpage cosmoxml "COSMOXML"</td><td>meteo</td><td>MeteoSwiss COSMO's postprocessing XML format</td><td><A HREF="http://xmlsoft.org/">libxml2</A></td></tr>
138
139
140
 * <tr><td>\subpage geotop "GEOTOP"</td><td>meteo</td><td>GeoTop meteo files</td><td></td></tr>
 * <tr><td>\subpage grass "GRASS"</td><td>dem, landuse, grid2d</td><td>Grass grid files</td><td></td></tr>
 * <tr><td>\subpage gribio "GRIB"</td><td>meteo, dem, grid2d</td><td>GRIB meteo grid files</td><td><A HREF="http://www.ecmwf.int/products/data/software/grib_api.html">grib-api</A></td></tr>
141
 * <tr><td>\subpage gsn "GSN"</td><td>meteo</td><td>connects to the Global Sensor Network web service interface</td><td><A HREF="http://curl.haxx.se/libcurl/">libcurl</A></td></tr>
142
 * <tr><td>\subpage imis "IMIS"</td><td>meteo</td><td>connects to the IMIS database</td><td><A HREF="http://docs.oracle.com/cd/B12037_01/appdev.101/b10778/introduction.htm">Oracle's OCCI library</A></td></tr>
143
 * <tr><td>\subpage netcdf "NETCDF"</td><td>dem, grid2d</td><td>NetCDF grids</td><td><A HREF="http://www.unidata.ucar.edu/downloads/netcdf/index.jsp">NetCDF-C library</A></td></tr>
144
 * <tr><td>\subpage oshd "OSHD"</td><td>meteo</td><td>OSHD generated binary Matlab files</td><td><A HREF="https://sourceforge.net/projects/matio">libmatio</A></td></tr>
145
146
 * <tr><td>\subpage pgmio "PGM"</td><td>dem, grid2d</td><td>PGM grid files</td><td></td></tr>
 * <tr><td>\subpage pngio "PNG"</td><td>dem, grid2d</td><td>PNG grid files</td><td><A HREF="http://www.libpng.org/pub/png/libpng.html">libpng</A></td></tr>
147
 * <tr><td>\subpage psqlio "PSQL"</td><td>meteo</td><td>connects to PostgreSQL database</td><td><A HREF="http://www.postgresql.org/">PostgreSQL</A>'s libpq</td></tr>
148
 * <tr><td>\subpage sase "SASE"</td><td>meteo</td><td>connects to the SASE database</td><td><A HREF="https://dev.mysql.com/doc/refman/5.0/en/c-api.html">MySQL's C API</A></td></tr>
149
 * <tr><td>\subpage smetio "SMET"</td><td>meteo, poi</td><td>SMET data files</td><td></td></tr>
150
151
152
 * <tr><td>\subpage snowpack "SNOWPACK"</td><td>meteo</td><td>original SNOWPACK meteo files</td><td></td></tr>
 * </table></center>
 *
153
 * @section data_manipulations Raw data editing
154
 * Before any filters, resampling algorithms or data generators are applied, it is possible to edit the original data:
155
 *     - rename certain parameters for all stations
156
157
158
159
 *     - exclude/keep certain parameters on a per station basis;
 *     - merge stations together;
 *     - make a copy of a certain parameter under a new parameter name for all stations.
 * 
160
161
 * @note Please note that the processing order is the following: the MOVE directives are processed first, then the EXCLUDE directives, 
 * then the KEEP directives, then the MERGE directives and finally the COPY directives.
162
 * 
163
 * @subsection data_exclusion Data exclusion
164
 * It is possible to exclude specific parameters from given stations (on a per station basis). This is either done by using the station ID (or the '*' wildcard) 
165
166
167
 * followed by "::exclude" as key with a space delimited list of \ref meteoparam "meteorological parameters" to exclude for the station as key.
 * Another possibility is to provide a file containing one station ID per line followed by a space delimited list of \ref meteoparam "meteorological parameters"
 * to exclude for the station (the path to the file can be a relative path and will be properly resolved).
168
169
170
 * 
 * The exact opposite can also be done, excluding ALL parameters except the ones declared with the "::keep" statement (or a file containing one station ID
 * per line followed by a space delimited list of \ref meteoparam "meteorological parameters" to keep for the station).
171
172
 *
 * @code
173
 * WFJ2::EXCLUDE = HS PSUM                       ;inline declaration of parameters exclusion
174
 * KLO3::KEEP = TA RH VW DW                      ;inline declaration of parameters to keep
175
176
 *
 * EXCLUDE_FILE = ../input/meteo/excludes.csv    ;parameters exclusions defined in a separate file
177
 * KEEP_FILE = ../input/meteo/keeps.csv          ;parameters to keep defined in a separate file
178
179
180
181
182
 * @endcode
 *
 * In the second example (relying on a separate file), the file "../input/meteo/excludes.csv" could look like this:
 * @code
 * WFJ2 TA RH
183
 * KLO3 HS PSUM
184
 * @endcode
185
186
187
188
189
190
 * 
 * Another example relying on wildcards (the kept/excluded parameters lists are additive):
 * @code
 * *::KEEP = TA RH                               ;all stations will keep TA and RH and reject the other parameters
 * WFJ2::KEEP = HS PSUM                          ;WFJ2 will keep TA and RH as defined above but also HS and PSUM
 * @endcode
191
 *
192
 * @subsection data_merging Data merging
193
194
195
196
 * It is possible to merge different data sets together, with a syntax similar to the Exclude/Keep syntax. This merging occurs <b>after</b> any 
 * EXCLUDE/KEEP commands. This is useful, for example, to provide measurements from different stations that actually share the 
 * same measurement location or to build "composite" station from multiple real stations (in this case, using EXCLUDE and/or KEEP 
 * commands to fine tune how the composite station(s) is/are built). 
197
198
 * Please note that the order of declaration defines the priority (ie the first station that has a value for a given parameter has priority). Please also
 * note that only common timestamps will be merged! (ie if the stations have different sampling rates, it might end up that no merge gets performed)
199
 * 
200
 * @code
201
 * STATION1 = STB
202
 * STATION2 = WFJ2
203
 * STATION3 = WFJ1
204
 * STATION4 = DAV1
205
 * [...]
206
 * 
207
208
 * STB::EXCLUDE = ILWR PSUM
 * WFJ2::KEEP = PSUM ILWR RSWR
209
 * 
210
211
 * STB::MERGE = WFJ2 WFJ1
 * DAV1::MERGE = WFJ2
212
 * @endcode
213
 * In order to avoid circular dependencies, a station can NOT receive data from a station AND contribute data to another station. Otherwise, a 
214
 * station can be merged into multiple other stations. Moreover, the merging strategy can be controlled by setting the MERGE_STRATEGY key in
215
 * the [Input] section (by default it is "STRICT_MERGE", see MeteoData::Merge_Type).
216
 * 
217
218
219
220
 * @note One limitation when handling "extra" parameters (ie parameters that are not in the default \ref meteoparam) is that these extra 
 * parameters must be known from the begining. So if station2 appears later in time with extra parameters, make sure that the buffer size 
 * is large enough to reach all the way to this new station (by setting General::BUFF_CHUNK_SIZE at least to the number of days from 
 * the start of the first station to the start of the second station)
221
 * 
222
223
224
225
226
227
228
229
230
 * @subsection data_move Data renaming
 * It is possible to rename a meteorological parameter thanks to the MOVE key. This key can take multiple source names that will be processed in the
 * order of declaration. The syntax is new_name::MOVE = {*space delimited list of original names*}. Original names that are not found in the current
 * dataset will silently be ignored, so it is safe to provide a list that contain many possible names:
 * @code
 * TA::MOVE = air_temp air_temperature temperature_air
 * @endcode
 * This can be used to rename non-standard parameter names into standard ones.
 * 
231
232
233
234
235
236
237
238
239
240
 * @subsection data_copy Data copy
 * It is also possible to duplicate a meteorological parameter as another meteorological parameter. This is done by specifying a COPY key, following the syntax
 * new_name::COPY = existing_parameter. For example:
 * @code
 * VW_avg::COPY = VW
 * @endcode
 * This creates a new parameter VW_avg that starts as an exact copy of the raw data of VW, for each station. This newly created parameter is
 * then processed as any other meteorological parameter (thus going through filtering, generic processing, spatial interpolations). This only current
 * limitation is that the parameter providing the raw data must be defined for all stations (even if filled with nodata, this is good enough).
 *
241
242
243
244
245
 * @section virtual_stations_section Virtual stations
 * It is possible to use spatially interpolated meteorological fields or time series of 2D grids to extract meteorological time series for a set of points.
 * This is handled as "virtual stations" since the data will seem to originate from points where no station is present. This is described in the 
 * \subpage virtual_stations "virtual stations" page.
 * 
246
247
 */

248
IOInterface* IOHandler::getPlugin(const std::string& plugin_name) const
249
{
250
251
252
#ifdef PLUGIN_ALPUG
	if (plugin_name == "ALPUG") return new ALPUG(cfg);
#endif
253
#ifdef PLUGIN_ARCIO
254
	if (plugin_name == "ARC") return new ARCIO(cfg);
255
256
#endif
#ifdef PLUGIN_A3DIO
257
	if (plugin_name == "A3D") return new A3DIO(cfg);
258
259
#endif
#ifdef PLUGIN_ARPSIO
260
	if (plugin_name == "ARPS") return new ARPSIO(cfg);
261
262
#endif
#ifdef PLUGIN_GRASSIO
263
	if (plugin_name == "GRASS") return new GrassIO(cfg);
264
265
#endif
#ifdef PLUGIN_GEOTOPIO
266
	if (plugin_name == "GEOTOP") return new GeotopIO(cfg);
267
268
#endif
#ifdef PLUGIN_SMETIO
269
	if (plugin_name == "SMET") return new SMETIO(cfg);
270
271
#endif
#ifdef PLUGIN_SNIO
272
	if (plugin_name == "SNOWPACK") return new SNIO(cfg);
273
274
#endif
#ifdef PLUGIN_PGMIO
275
	if (plugin_name == "PGM") return new PGMIO(cfg);
276
277
#endif
#ifdef PLUGIN_IMISIO
278
	if (plugin_name == "IMIS") return new ImisIO(cfg);
279
#endif
280
281
282
#ifdef PLUGIN_OSHDIO
	if (plugin_name == "OSHD") return new OshdIO(cfg);
#endif
283
#ifdef PLUGIN_GRIBIO
284
	if (plugin_name == "GRIB") return new GRIBIO(cfg);
285
286
#endif
#ifdef PLUGIN_PNGIO
287
	if (plugin_name == "PNG") return new PNGIO(cfg);
288
289
#endif
#ifdef PLUGIN_BORMAIO
290
	if (plugin_name == "BORMA") return new BormaIO(cfg);
291
292
#endif
#ifdef PLUGIN_COSMOXMLIO
293
	if (plugin_name == "COSMOXML") return new CosmoXMLIO(cfg);
294
#endif
295
296
297
#ifdef PLUGIN_CNRMIO
	if (plugin_name == "CNRM") return new CNRMIO(cfg);
#endif
298
#ifdef PLUGIN_GSNIO
299
	if (plugin_name == "GSN") return new GSNIO(cfg);
300
#endif
301
#ifdef PLUGIN_NETCDFIO
302
	if (plugin_name == "NETCDF") return new NetCDFIO(cfg);
303
#endif
Thomas Egger's avatar
Thomas Egger committed
304
#ifdef PLUGIN_PSQLIO
305
	if (plugin_name == "PSQL") return new PSQLIO(cfg);
Thomas Egger's avatar
Thomas Egger committed
306
#endif
307
308
309
#ifdef PLUGIN_SASEIO
	if (plugin_name == "SASE") return new SASEIO(cfg);
#endif
310
311

	return NULL; //no plugin found
312
313
}

314
315
316
//this is actually an object factory
IOInterface* IOHandler::getPlugin(const std::string& cfgkey, const std::string& cfgsection)
{
317
	const std::string op_src = cfg.get(cfgkey, cfgsection);
318
319

	if (mapPlugins.find(op_src) == mapPlugins.end()) {
320
321
		IOInterface *ioPtr = getPlugin(op_src);
		if (ioPtr==NULL)
322
			throw IOException("Cannot find plugin " + op_src + " as requested in file " + cfg.getSourceName() + ". Has it been activated through ccmake? Is it declared in IOHandler::getPlugin?", AT);
323
324
		else
			mapPlugins[op_src] = ioPtr;
325
326
327
328
329
	}

	return mapPlugins[op_src];
}

330
331
//Copy constructor
IOHandler::IOHandler(const IOHandler& aio)
332
           : IOInterface(), cfg(aio.cfg), mapPlugins(aio.mapPlugins), excluded_params(aio.excluded_params), kept_params(aio.kept_params), 
333
             merge_commands(aio.merge_commands), copy_commands(aio.copy_commands), move_commands(aio.move_commands),
334
             merged_stations(aio.merged_stations), merge_strategy(aio.merge_strategy), 
335
             copy_ready(aio.copy_ready), move_ready(aio.move_ready), excludes_ready(aio.excludes_ready), keeps_ready(aio.keeps_ready), merge_ready(aio.merge_ready)
Mathias Bavay's avatar
Mathias Bavay committed
336
{}
337

Mathias Bavay's avatar
Mathias Bavay committed
338
IOHandler::IOHandler(const Config& cfgreader)
339
           : IOInterface(), cfg(cfgreader), mapPlugins(), excluded_params(), kept_params(), 
340
341
342
             merge_commands(), copy_commands(), move_commands(), 
             merged_stations(), merge_strategy(MeteoData::STRICT_MERGE), 
             copy_ready(false), move_ready(false), excludes_ready(false), keeps_ready(false), merge_ready(false)
343
{
344
345
346
	const std::string merge_strategy_str = cfg.get("MERGE_STRATEGY", "Input", IOUtils::nothrow);
	if (!merge_strategy_str.empty())
		merge_strategy = MeteoData::getMergeType(merge_strategy_str);
347
348
}

Mathias Bavay's avatar
Mathias Bavay committed
349
350
IOHandler::~IOHandler() throw()
{
351
	// Get rid of the objects
352
353
354
	std::map<std::string, IOInterface*>::iterator mapit;
	for (mapit = mapPlugins.begin(); mapit!=mapPlugins.end(); ++mapit) {
		delete mapit->second;
355
356
357
	}
}

358
IOHandler& IOHandler::operator=(const IOHandler& source) {
359
	if (this != &source) {
360
		mapPlugins = source.mapPlugins;
361
		excluded_params = source.excluded_params;
362
		kept_params = source.kept_params;
363
364
		merge_commands = source.merge_commands;
		merged_stations = source.merged_stations;
365
		copy_commands = source.copy_commands;
366
		move_commands = source.move_commands;
367
		merge_strategy = source.merge_strategy;
368
		copy_ready = source.copy_ready;
369
		move_ready = source.move_ready;
370
		excludes_ready = source.excludes_ready;
371
		keeps_ready = source.keeps_ready;
372
		merge_ready = source.merge_ready;
373
374
375
376
	}
	return *this;
}

377
378
379
380
381
382
383
384
385
386
387
388
void IOHandler::read2DGrid(Grid2DObject& grid_out, const std::string& i_filename)
{
	IOInterface *plugin = getPlugin("GRID2D", "Input");
	plugin->read2DGrid(grid_out, i_filename);
}

void IOHandler::read2DGrid(Grid2DObject& grid_out, const MeteoGrids::Parameters& parameter, const Date& date)
{
	IOInterface *plugin = getPlugin("GRID2D", "Input");
	plugin->read2DGrid(grid_out, parameter, date);
}

389
390
391
392
393
394
395
396
397
398
399
400
void IOHandler::read3DGrid(Grid3DObject& grid_out, const std::string& i_filename)
{
	IOInterface *plugin = getPlugin("GRID3D", "Input");
	plugin->read3DGrid(grid_out, i_filename);
}

void IOHandler::read3DGrid(Grid3DObject& grid_out, const MeteoGrids::Parameters& parameter, const Date& date)
{
	IOInterface *plugin = getPlugin("GRID3D", "Input");
	plugin->read3DGrid(grid_out, parameter, date);
}

401
402
403
404
405
406
407
408
409
410
411
412
413
void IOHandler::readDEM(DEMObject& dem_out)
{
	IOInterface *plugin = getPlugin("DEM", "Input");
	plugin->readDEM(dem_out);
	dem_out.update();
}

void IOHandler::readLanduse(Grid2DObject& landuse_out)
{
	IOInterface *plugin = getPlugin("LANDUSE", "Input");
	plugin->readLanduse(landuse_out);
}

414
void IOHandler::readStationData(const Date& date, STATIONS_SET& vecStation)
415
416
417
{
	IOInterface *plugin = getPlugin("METEO", "Input");
	plugin->readStationData(date, vecStation);
418
419
420
	
	if (!merge_ready) create_merge_map(); 
	merge_stations(vecStation);
421
422
423
}

void IOHandler::readMeteoData(const Date& dateStart, const Date& dateEnd,
424
                              std::vector<METEO_SET>& vecMeteo,
425
426
427
428
                              const size_t& stationindex)
{
	IOInterface *plugin = getPlugin("METEO", "Input");
	plugin->readMeteoData(dateStart, dateEnd, vecMeteo, stationindex);
429
	
430
	checkTimestamps(vecMeteo);
431
	
432
433
434
	if (!move_ready) create_move_map();
	move_params(vecMeteo);
	
435
436
	if (!excludes_ready) create_exclude_map();
	exclude_params(vecMeteo);
437
438
439
	
	if (!keeps_ready) create_keep_map();
	keep_params(vecMeteo);
440
	
441
442
	if (!merge_ready) create_merge_map(); 
	merge_stations(vecMeteo);
443
	
444
	if (!copy_ready) create_copy_map();
445
	copy_params(vecMeteo);
446
}
Mathias Bavay's avatar
Mathias Bavay committed
447

448
void IOHandler::writeMeteoData(const std::vector<METEO_SET>& vecMeteo,
449
450
451
452
453
454
455
456
457
458
459
460
                               const std::string& name)
{
	IOInterface *plugin = getPlugin("METEO", "Output");
	plugin->writeMeteoData(vecMeteo, name);
}

void IOHandler::readAssimilationData(const Date& date_in, Grid2DObject& da_out)
{
	IOInterface *plugin = getPlugin("DA", "Input");
	plugin->readAssimilationData(date_in, da_out);
}

461
462
463
void IOHandler::readPOI(std::vector<Coords>& pts) {
	IOInterface *plugin = getPlugin("POI", "Input");
	plugin->readPOI(pts);
464
465
466
467
468
469
470
471
472
473
474
475
476
477
}

void IOHandler::write2DGrid(const Grid2DObject& grid_in, const std::string& name)
{
	IOInterface *plugin = getPlugin("GRID2D", "Output");
	plugin->write2DGrid(grid_in, name);
}

void IOHandler::write2DGrid(const Grid2DObject& grid_in, const MeteoGrids::Parameters& parameter, const Date& date)
{
	IOInterface *plugin = getPlugin("GRID2D", "Output");
	plugin->write2DGrid(grid_in, parameter, date);
}

478
479
480
481
482
483
484
485
486
487
488
489
void IOHandler::write3DGrid(const Grid3DObject& grid_out, const std::string& options)
{
	IOInterface *plugin = getPlugin("GRID3D", "Output");
	plugin->write3DGrid(grid_out, options);
}

void IOHandler::write3DGrid(const Grid3DObject& grid_out, const MeteoGrids::Parameters& parameter, const Date& date)
{
	IOInterface *plugin = getPlugin("GRID3D", "Output");
	plugin->write3DGrid(grid_out, parameter, date);
}

490
491
492
/** 
 * @brief check that timestamps are unique and in increasing order
 * @param[in] vecVecMeteo all the data for all the stations
493
*/
494
void IOHandler::checkTimestamps(const std::vector<METEO_SET>& vecVecMeteo)
495
496
497
498
499
500
501
502
503
504
{
	for (size_t stat_idx=0; stat_idx<vecVecMeteo.size(); ++stat_idx) { //for each station
		const size_t nr_timestamps = vecVecMeteo[stat_idx].size();
		if (nr_timestamps==0) continue;

		Date previous_date( vecVecMeteo[stat_idx].front().date );
		for (size_t ii=1; ii<nr_timestamps; ++ii) {
			const Date& current_date = vecVecMeteo[stat_idx][ii].date;
			if (current_date<=previous_date) {
				const StationData& station = vecVecMeteo[stat_idx][ii].meta;
505
506
507
508
				if (current_date==previous_date)
					throw IOException("Error for station \""+station.stationName+"\" ("+station.stationID+") at time "+current_date.toString(Date::ISO)+": timestamps must be unique!", AT);
				else
					throw IOException("Error for station \""+station.stationName+"\" ("+station.stationID+"): jumping from "+previous_date.toString(Date::ISO)+" to "+current_date.toString(Date::ISO), AT);
509
510
511
512
513
514
			}
			previous_date = current_date;
		}
	}
}

515
516
517
518
519
520
521
522
523
524
void IOHandler::create_merge_map()
{
	merge_ready = true;
	
	vector<string> merge_keys;
	const size_t nrOfStations = cfg.findKeys(merge_keys, "::MERGE", "Input", true);
	for (size_t ii=0; ii<nrOfStations; ++ii) {
		const size_t found = merge_keys[ii].find_first_of(":");
		if (found==std::string::npos) continue;

525
		const string station( IOUtils::strToUpper(merge_keys[ii].substr(0,found)) );
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
		std::vector<std::string> vecString;
		cfg.getValue(merge_keys[ii], "Input", vecString);
		if (vecString.empty()) throw InvalidArgumentException("Empty value for key \""+merge_keys[ii]+"\"", AT);
		
		for (vector<string>::iterator it = vecString.begin(); it != vecString.end(); ++it) {
			IOUtils::toUpper(*it);
			const std::vector<std::string>::const_iterator vec_it = find (merged_stations.begin(), merged_stations.end(), *it);
			if (vec_it==merged_stations.end()) merged_stations.push_back( *it ); //this station will be merged into another one
		}
		merge_commands[ station ] = vecString;
	}
	
	//sort the merged_stations vector so searches will be faster
	std::sort(merged_stations.begin(), merged_stations.end());
	
	//make sure there is no "chain merge": station A merging station B and station C merging station A
	std::map< std::string, std::vector<std::string> >::iterator it_dest;
	for(it_dest=merge_commands.begin(); it_dest!=merge_commands.end(); ++it_dest) {
		const string &stationID = it_dest->first;
		if (std::binary_search(merged_stations.begin(), merged_stations.end(), stationID))
			throw InvalidArgumentException("\'chain merge\' detected for station \'"+stationID+"\', this is not supported (see documentation)", AT);
	}
}

550
//merge stations that have identical names
551
void IOHandler::merge_stations(STATIONS_SET& vecStation) const
552
{
553
554
	if (merge_commands.empty()) return;
	
555
	for (size_t ii=0; ii<vecStation.size(); ii++) {
556
		const std::string toStationID( IOUtils::strToUpper( vecStation[ii].stationID ) );
557
558
559
		//we do not support "chain merge": station A merging station B and station C merging station A
		if ( std::find(merged_stations.begin(), merged_stations.end(), toStationID)!=merged_stations.end() ) continue;
		
560
		const std::map< string, vector<string> >::const_iterator it = merge_commands.find( toStationID );
561
562
		if (it == merge_commands.end()) continue; //no merge commands for this station

563
564
565
		const std::vector<std::string> &merge_from( it->second );
		for (std::vector<std::string>::const_iterator it_set=merge_from.begin(); it_set != merge_from.end(); ++it_set) {
			const string fromStationID( *it_set );
566
			
567
			bool found = false;
568
			for (size_t jj=0; jj<vecStation.size(); jj++) {
569
570
571
572
573
				const string curr_station( IOUtils::strToUpper(vecStation[jj].stationID) );
				if (curr_station==fromStationID) {
					vecStation[ii].merge( vecStation[jj] );
					found = true;
				}
574
			}
575
			if (!found)
576
				throw InvalidArgumentException("Station ID '"+fromStationID+"' not found when merging toward station '"+toStationID+"'. Consider increasing BUFF_CHUNK_SIZE!", AT);
577
578
		}
	}
579
580
581
	
	//remove the stations that have been merged into other ones
	for (size_t ii=0; ii<vecStation.size(); ii++) {
582
583
		const string stationID( IOUtils::strToUpper( vecStation[ii].stationID ) );
		const vector<string>::const_iterator it = std::find(merged_stations.begin(), merged_stations.end(), stationID);
584
585
586
587
588
589
		if ( it!=merged_stations.end() ) {
			std::swap( vecStation[ii], vecStation.back() );
			vecStation.pop_back();
			ii--; //in case we have multiple identical stations ID
		}
	}
590
591
592
}

//in this implementation, we consider that the station name does NOT change over time
593
void IOHandler::merge_stations(std::vector<METEO_SET>& vecVecMeteo) const
594
{
595
596
	if (merge_commands.empty()) return;
	
597
	for (size_t ii=0; ii<vecVecMeteo.size(); ii++) { //loop over the stations
598
		if (vecVecMeteo[ii].empty())  continue;
599
		const std::string toStationID( IOUtils::strToUpper(vecVecMeteo[ii][0].meta.stationID) );
600
601
602
		//we do not support "chain merge": station A merging station B and station C merging station A
		if ( std::find(merged_stations.begin(), merged_stations.end(), toStationID)!=merged_stations.end() ) continue;
		
603
		const std::map< std::string, std::vector<std::string> >::const_iterator it = merge_commands.find( toStationID );
604
605
		if (it == merge_commands.end()) continue; //no merge commands for this station

606
607
		const std::vector<std::string> &merge_from( it->second );
		for (std::vector<std::string>::const_iterator it_set=merge_from.begin(); it_set != merge_from.end(); ++it_set) {
608
			const std::string fromStationID( *it_set );
609
610
			
			bool found = false;
611
			for (size_t jj=0; jj<vecVecMeteo.size(); jj++) { //loop over the available stations in the current dataset
612
				if (vecVecMeteo[jj].empty()) continue;
613
				const std::string curr_station( IOUtils::strToUpper(vecVecMeteo[jj][0].meta.stationID) );
614
				if (curr_station==fromStationID) {
615
					MeteoData::mergeTimeSeries(vecVecMeteo[ii], vecVecMeteo[jj], static_cast<MeteoData::Merge_Type>(merge_strategy)); //merge timeseries for the two stations
616
617
					found = true;
				}
618
			}
619
620
			if (!found)
				throw InvalidArgumentException("Station ID '"+fromStationID+"' not found when merging toward station '"+toStationID+"'", AT);
621
622
		}
	}
623
624
625
	
	//remove the stations that have been merged into other ones
	for (size_t ii=0; ii<vecVecMeteo.size(); ii++) {
626
		if (vecVecMeteo[ii].empty())  continue;
627
628
		const std::string stationID( IOUtils::strToUpper(vecVecMeteo[ii][0].meta.stationID) );
		const std::vector<std::string>::const_iterator it = std::find(merged_stations.begin(), merged_stations.end(), stationID);
629
630
631
632
633
634
		if ( it!=merged_stations.end() ) {
			std::swap( vecVecMeteo[ii], vecVecMeteo.back() );
			vecVecMeteo.pop_back();
			ii--; //in case we have multiple identical stations ID
		}
	}
635
636
}

637
638
void IOHandler::create_exclude_map()
{
639
	excludes_ready = true;
640
	const string exclude_file = cfg.get("EXCLUDE_FILE", "Input", IOUtils::nothrow);
641

642
643
	if (!exclude_file.empty()) {
		//if this is a relative path, prefix the path with the current path
644
645
646
		const std::string prefix = ( FileUtils::isAbsolutePath(exclude_file) )? "" : cfg.getConfigRootDir()+"/";
		const std::string path = FileUtils::getPath(prefix+exclude_file, true);  //clean & resolve path
		const std::string filename = path + "/" + FileUtils::getFilename(exclude_file);
647

648
		if (!FileUtils::fileExists(filename)) throw AccessException(filename, AT); //prevent invalid filenames
649
		std::ifstream fin(filename.c_str(), std::ifstream::in);
650
		if (fin.fail()) throw AccessException(filename, AT);
651

652
		try {
653
			const char eoln = FileUtils::getEoln(fin); //get the end of line character for the file
654

655
656
			vector<string> tmpvec;
			string line;
657

658
659
660
661
			while (!fin.eof()) { //Go through file
				getline(fin, line, eoln); //read complete line meta information
				IOUtils::stripComments(line);
				const size_t ncols = IOUtils::readLineToVec(line, tmpvec, ' ');
662

663
				if (ncols > 1) {
664
					for (vector<string>::iterator it = tmpvec.begin()+1; it != tmpvec.end(); ++it) {
665
666
						IOUtils::toUpper(*it);
					}
667

668
					const std::set<std::string> tmpset(tmpvec.begin()+1, tmpvec.end());
669
					excluded_params[ tmpvec[0] ] = tmpset;
670
671
				}
			}
672
673
674
		} catch (const std::exception&) {
			fin.close();
			throw;
675
		}
676

677
678
679
		fin.close();
	}

680
681
682
683
684
685
	vector<string> exclude_keys;
	const size_t nrOfStations = cfg.findKeys(exclude_keys, "::EXCLUDE", "Input", true);
	for (size_t ii=0; ii<nrOfStations; ++ii) {
		const size_t found = exclude_keys[ii].find_first_of(":");
		if (found==std::string::npos) continue;

686
		const std::string station( IOUtils::strToUpper(exclude_keys[ii].substr(0,found)) );
687
688
689
		std::vector<std::string> vecString;
		cfg.getValue(exclude_keys[ii], "Input", vecString);
		if (vecString.empty()) throw InvalidArgumentException("Empty value for key \""+exclude_keys[ii]+"\"", AT);
690
		for (vector<string>::iterator it = vecString.begin(); it != vecString.end(); ++it) {
691
692
693
			IOUtils::toUpper(*it);
		}

694
		const std::set<std::string> tmpset(vecString.begin(), vecString.end());
695
696
		excluded_params[ station ] = tmpset;
	}
697
698
699
700
701
702
703
704
705
706
707
708
709
710
	
	//Handle "*" wildcard: add the params to all other declared stations
	map< string, set<string> >::const_iterator it_station = excluded_params.find("*");
	if (it_station!=excluded_params.end()) {
		const std::set<std::string> wildcard( excluded_params["*"] );
		for (it_station=excluded_params.begin(); it_station!=excluded_params.end(); ++it_station) {
			std::set<std::string> params( it_station->second );
			
			for (std::set<std::string>::iterator it=wildcard.begin(); it!=wildcard.end(); ++it)
				params.insert( *it ); //merging: keep in mind that a set can not contain duplicates
			
			excluded_params[ it_station->first ] = params;
		}
	}
711
712
}

713
714
715
void IOHandler::create_keep_map()
{
	keeps_ready = true;
716
	const string keep_file = cfg.get("KEEP_FILE", "Input", IOUtils::nothrow);
717
718
719

	if (!keep_file.empty()) {
		//if this is a relative path, prefix the path with the current path
720
721
722
		const std::string prefix = ( FileUtils::isAbsolutePath(keep_file) )? "" : cfg.getConfigRootDir()+"/";
		const std::string path = FileUtils::getPath(prefix+keep_file, true);  //clean & resolve path
		const std::string filename = path + "/" + FileUtils::getFilename(keep_file);
723

724
		if (!FileUtils::fileExists(filename)) throw AccessException(filename, AT); //prevent invalid filenames
725
		std::ifstream fin(filename.c_str(), std::ifstream::in);
726
		if (fin.fail()) throw AccessException(filename, AT);
727
728

		try {
729
			const char eoln = FileUtils::getEoln(fin); //get the end of line character for the file
730
731
732
733
734
735
736
737
738
739

			vector<string> tmpvec;
			string line;

			while (!fin.eof()) { //Go through file
				getline(fin, line, eoln); //read complete line meta information
				IOUtils::stripComments(line);
				const size_t ncols = IOUtils::readLineToVec(line, tmpvec, ' ');

				if (ncols > 1) {
740
					for (vector<string>::iterator it = tmpvec.begin()+1; it != tmpvec.end(); ++it) {
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
						IOUtils::toUpper(*it);
					}

					const set<string> tmpset(tmpvec.begin()+1, tmpvec.end());
					kept_params[ tmpvec[0] ] = tmpset;
				}
			}
		} catch (const std::exception&) {
			fin.close();
			throw;
		}

		fin.close();
	}

	vector<string> keep_keys;
	const size_t nrOfStations = cfg.findKeys(keep_keys, "::KEEP", "Input", true);
	for (size_t ii=0; ii<nrOfStations; ++ii) {
		const size_t found = keep_keys[ii].find_first_of(":");
		if (found==std::string::npos) continue;

762
		const std::string station( IOUtils::strToUpper(keep_keys[ii].substr(0,found)) );
763
764
765
		std::vector<std::string> vecString;
		cfg.getValue(keep_keys[ii], "Input", vecString);
		if (vecString.empty()) throw InvalidArgumentException("Empty value for key \""+keep_keys[ii]+"\"", AT);
766
		for (vector<string>::iterator it = vecString.begin(); it != vecString.end(); ++it) {
767
768
769
			IOUtils::toUpper(*it);
		}

770
		const std::set<std::string> tmpset(vecString.begin(), vecString.end());
771
772
		kept_params[ station ] = tmpset;
	}
773
774
775
776
777
778
779
780
781
782
783
784
785
786
	
	//Handle "*" wildcard: add the params to all other declared stations
	map< string, set<string> >::const_iterator it_station = kept_params.find("*");
	if (it_station!=kept_params.end()) {
		const std::set<std::string> wildcard( kept_params["*"] );
		for (it_station=kept_params.begin(); it_station!=kept_params.end(); ++it_station) {
			std::set<std::string> params( it_station->second );
			
			for (std::set<std::string>::iterator it=wildcard.begin(); it!=wildcard.end(); ++it)
				params.insert( *it ); //merging: keep in mind that a set can not contain duplicates
			
			kept_params[ it_station->first ] = params;
		}
	}
787
788
}

789
/**
790
* @brief reset to nodata the parameters marked as EXCLUDE on a per station basis
791
*/
792
793
794
795
void IOHandler::exclude_params(std::vector<METEO_SET>& vecVecMeteo) const
{
	if (excluded_params.empty()) return;

796
	for (size_t station=0; station<vecVecMeteo.size(); ++station) { //loop over the stations
797
		if (vecVecMeteo[station].empty()) continue;
798
799
800
801
802
803
		const std::string stationID( IOUtils::strToUpper(vecVecMeteo[station][0].meta.stationID) );
		map< string, set<string> >::const_iterator it = excluded_params.find(stationID);
		if (it == excluded_params.end()) {
			it = excluded_params.find("*"); //fallback: is there a wildcard like "*::KEEP"?
			if (it == excluded_params.end()) continue;
		}
804
805
806

		const set<string> excluded = it->second;

807
		for (size_t ii=0; ii<vecVecMeteo[station].size(); ++ii) { //loop over the timesteps
808
809
			for (std::set<std::string>::const_iterator it_set=excluded.begin(); it_set != excluded.end(); ++it_set) {
				const string param( *it_set );
810
811
812
813
814
815
816
				if (vecVecMeteo[station][ii].param_exists(param))
					vecVecMeteo[station][ii](param) = IOUtils::nodata;
			}
		}
	}
}

817
818
819
820
821
822
823
824
825
826
/**
* @brief only keep the parameters marked as KEEP on a per station basis
*/
void IOHandler::keep_params(std::vector<METEO_SET>& vecVecMeteo) const
{
	if (kept_params.empty()) return;

	for (size_t station=0; station<vecVecMeteo.size(); ++station) { //loop over the stations
		if (vecVecMeteo[station].empty()) continue;
		
827
828
829
830
831
832
		const std::string stationID( IOUtils::strToUpper(vecVecMeteo[station][0].meta.stationID) );
		map< string, set<string> >::const_iterator it = kept_params.find(stationID);
		if (it == kept_params.end()) {
			it = kept_params.find("*"); //fallback: is there a wildcard like "*::KEEP"?
			if (it == kept_params.end()) continue;
		}
833
834
835
836
837
838
839
840

		const set<string> kept = it->second;
		
		for (size_t ii=0; ii<vecVecMeteo[station].size(); ++ii) {
			MeteoData& md_ref = vecVecMeteo[station][ii];
			MeteoData md( md_ref );
			md.reset(); //delete all meteo fields
			
841
842
			for (std::set<std::string>::const_iterator it_set=kept.begin(); it_set != kept.end(); ++it_set) { //loop over the parameters to keep
				const string param( *it_set);
843
844
845
846
847
848
849
850
851
852
				if (!md.param_exists(param)) continue;
				 md(param) = md_ref(param);
			}
			
			//copy back the new object into vecVecMeteo
			md_ref = md;
		}
	}
}

853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
/**
* Parse [Input] section for potential parameters that the user wants
* renamed (as '%%::MOVE = %%')
*/
void IOHandler::create_move_map()
{
	vector<string> move_keys;
	const size_t nrOfMatches = cfg.findKeys(move_keys, "::MOVE", "Input", true); //search anywhere in key

	for (size_t ii=0; ii<nrOfMatches; ++ii) {
		const string dest_param = move_keys[ii].substr( 0, move_keys[ii].find_first_of(":") );
		std::vector<std::string> vecString;
		cfg.getValue(move_keys[ii], "Input", vecString); //multiple source can be provided
		
		if (vecString.empty()) throw InvalidArgumentException("Empty value for key \""+move_keys[ii]+"\"", AT);
		for (vector<string>::iterator it = vecString.begin(); it != vecString.end(); ++it) {
			IOUtils::toUpper(*it);
		}
		
		const std::set<std::string> tmpset(vecString.begin(), vecString.end());
		move_commands[ dest_param ] = tmpset;
	}
	
	move_ready = true;
}

/**
* This procedure runs through the MeteoData objects in vecMeteo and according to user
* configuration renames a certain present meteo parameter to another one, named by the
* user in the [Input] section of the io.ini, e.g.
* [Input]
* TA::MOVE = air_temp air_temperature
* means that TA will be the name of a new parameter in MeteoData with the copied value
* of the original parameter air_temp or air_temperature
*/
void IOHandler::move_params(std::vector< METEO_SET >& vecMeteo) const
{
	if (move_commands.empty()) return; //Nothing configured

	for (size_t station=0; station<vecMeteo.size(); ++station) { //for each station
		if (vecMeteo[station].empty()) continue;
		
		std::map< std::string, std::set<std::string> >::const_iterator param = move_commands.begin();
		for (; param!=move_commands.end(); ++param) { //loop over all the MOVE commands
			const std::string dest_param( param->first );
			const std::set<std::string> src( param->second );
			
			for (std::set<std::string>::const_iterator it_set=src.begin(); it_set != src.end(); ++it_set) { //loop over the parameters to move
				const std::string src_param( *it_set );
				const size_t src_index = vecMeteo[station].front().getParameterIndex( *it_set );
				if (src_index == IOUtils::npos) continue; //no such parameter for this station, skipping

				for (size_t jj=0; jj<vecMeteo[station].size(); ++jj) {
					const size_t dest_index = vecMeteo[station][jj].addParameter( dest_param ); //either add or just return the proper index
					vecMeteo[station][jj]( dest_index ) = vecMeteo[station][jj]( src_index );
					vecMeteo[station][jj]( src_index ) = IOUtils::nodata; 
				}
			}
		}
	}
}


916
917
918
919
/**
* Parse [Input] section for potential parameters that the user wants
* duplicated (as '%%::COPY = %%')
*/
920
void IOHandler::create_copy_map()
921
922
{
	vector<string> copy_keys;
923
	const size_t nrOfMatches = cfg.findKeys(copy_keys, "::COPY", "Input", true); //search anywhere in key
924

925
	for (size_t ii=0; ii<nrOfMatches; ++ii) {
926
927
928
929
		const string dest_param = copy_keys[ii].substr( 0, copy_keys[ii].find_first_of(":") );
		const string src_param = cfg.get(copy_keys[ii], "Input");
		if (!dest_param.empty() && !src_param.empty())
			copy_commands[ dest_param ] = src_param;
930
	}
931
932
	
	copy_ready = true;
933
934
}

935
936
937
938
939
/**
* This procedure runs through the MeteoData objects in vecMeteo and according to user
* configuration copies a certain present meteo parameter to another one, named by the
* user in the [Input] section of the io.ini, e.g.
* [Input]
940
* TA2::COPY = TA
941
942
943
* means that TA2 will be the name of a new parameter in MeteoData with the copied value
* of the meteo parameter MeteoData::TA
*/
944
void IOHandler::copy_params(std::vector< METEO_SET >& vecMeteo) const
945
{
946
	if (copy_commands.empty()) return; //Nothing configured
947

948
949
950
951
952
953
954
955
956
	for (size_t station=0; station<vecMeteo.size(); ++station) { //for each station
		if (vecMeteo[station].empty()) continue;
		
		std::map< std::string, std::string >::const_iterator param = copy_commands.begin();
		for (; param!=copy_commands.end(); ++param) {
			const size_t src_index = vecMeteo[station].front().getParameterIndex(param->second);
			if (src_index == IOUtils::npos) {
				const std::string stationID( vecMeteo[station].front().meta.stationID );
				throw InvalidArgumentException("Station "+stationID+" has no parameter '"+param->second+"' to copy", AT);
957
			}
958
			
959
			const std::string dest( param->first );
960
961
962
			for (size_t jj=0; jj<vecMeteo[station].size(); ++jj) { //for each MeteoData object of one station
				const size_t dest_index = vecMeteo[station][jj].addParameter( dest );
				vecMeteo[station][jj]( dest_index ) = vecMeteo[station][jj]( src_index );
963
964
965
966
967
			}
		}
	}
}

968
const std::string IOHandler::toString() const
969
{
970
	std::ostringstream os;
971
972
973
974
	os << "<IOHandler>\n";
	os << "Config& cfg = " << hex << &cfg << dec << "\n";

	os << "<mapPlugins>\n";
975
976
977
	std::map<std::string, IOInterface*>::const_iterator it1;
	for (it1=mapPlugins.begin(); it1 != mapPlugins.end(); ++it1) {
		os << setw(10) << it1->first << " = " << hex <<  it1->second << dec << "\n";
978
979
	}
	os << "</mapPlugins>\n";
980

981
982
983
984
985
986
987
988
989
990
991
	if (!excluded_params.empty()) {
		os << "<excluded_params>\n";
		std::map< std::string, std::set<std::string> >::const_iterator it_exc;
		for (it_exc=excluded_params.begin(); it_exc != excluded_params.end(); ++it_exc) {
			os << setw(10) << it_exc->first << " = ";
			std::set<std::string>::const_iterator it_set;
			for (it_set=(it_exc->second).begin(); it_set != (it_exc->second).end(); ++it_set)
				os << *it_set << " ";
			os << "\n";
		}
		os << "</excluded_params>\n";
992
	}
993
	
994
995
996
997
998
999
1000
	if (!merge_commands.empty()) {
		os << "<merge_commands>\n";
		std::map< std::string, std::vector<std::string> >::const_iterator it_merge;
		for (it_merge=merge_commands.begin(); it_merge != merge_commands.end(); ++it_merge) {
			os << setw(10) << it_merge->first << " <- ";
			for (size_t ii=0; ii<it_merge->second.size(); ++ii)
				os << it_merge->second[ii] << " ";
For faster browsing, not all history is shown. View entire blame