WSL/SLF GitLab Repository

IOHandler.cmake.cc 43.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/***********************************************************************************/
/*  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/>.
*/
18
19
#include <meteoio/IOHandler.h>
#include <meteoio/IOExceptions.h>
20
#include <meteoio/IOUtils.h>
21
#include <meteoio/MathOptim.h>
22
#include <meteoio/dataClasses/MeteoData.h> //needed for the merge strategies
23

24
#include <algorithm>
25
#include <fstream>
26

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

48
#include <meteoio/plugins/ALPUG.h>
49
50
51
52
53
54
55
56
57
58
59
60
61
#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

62
63
64
65
#ifdef PLUGIN_CNRMIO
#include <meteoio/plugins/CNRMIO.h>
#endif

66
67
68
69
70
71
72
73
#ifdef PLUGIN_COSMOXMLIO
#include <meteoio/plugins/CosmoXMLIO.h>
#endif

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

74
75
76
77
#ifdef PLUGIN_OSHDIO
#include <meteoio/plugins/OshdIO.h>
#endif

78
79
80
81
82
83
84
85
#ifdef PLUGIN_GRIBIO
#include <meteoio/plugins/GRIBIO.h>
#endif

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

86
87
88
89
#ifdef PLUGIN_NETCDFIO
#include <meteoio/plugins/NetCDFIO.h>
#endif

90
91
92
93
#ifdef PLUGIN_PNGIO
#include <meteoio/plugins/PNGIO.h>
#endif

Thomas Egger's avatar
Thomas Egger committed
94
95
96
97
#ifdef PLUGIN_PSQLIO
#include <meteoio/plugins/PSQLIO.h>
#endif

98
99
100
101
#ifdef PLUGIN_SASEIO
#include <meteoio/plugins/SASEIO.h>
#endif

102
103
104
105
using namespace std;

namespace mio {
 /**
106
107
 * @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 
108
 * 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
109
 * compilation with cmake. A plugin can therefore fail to run if it has not been compiled.
110
 *
111
112
 * Please have a look at the support for \subpage coords "coordinate systems".
 * 
113
114
115
116
117
118
 * @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)
119
 * - 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)
120
121
122
123
124
125
126
127
128
129
 *
 * 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.
 *
130
 * @subsection available_plugins Available plugins
131
132
133
 * 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>
134
 * <tr><td>\subpage alpug "ALPUG"</td><td>meteo</td><td>data files generated by the %ALPUG meteo stations</td><td></td></tr>
135
 * <tr><td>\subpage a3d "A3D"</td><td>meteo, poi</td><td>original Alpine3D meteo files</td><td></td></tr>
136
137
138
 * <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>
139
 * <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>
140
 * <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>
141
142
143
 * <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>
144
 * <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>
145
 * <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>
146
 * <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>
147
 * <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>
148
149
 * <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>
150
 * <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>
151
 * <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>
152
 * <tr><td>\subpage smetio "SMET"</td><td>meteo, poi</td><td>SMET data files</td><td></td></tr>
153
154
155
 * <tr><td>\subpage snowpack "SNOWPACK"</td><td>meteo</td><td>original SNOWPACK meteo files</td><td></td></tr>
 * </table></center>
 *
156
157
158
159
 * In order to optimize the data retrieval, the raw data is buffered. This means that up to BUFFER_SIZE days of data will be read at once by the plugin
 * so subsequent reads will not have to get back to the data source (this key is in the [General] section). It is usually a good idea to configure BUFFER_SIZE
 * to the intended duration of the simulation (in days).
 *
160
 * @section data_manipulations Raw data editing
161
 * Before any filters, resampling algorithms or data generators are applied, it is possible to edit the original data:
162
 *     - rename certain parameters for all stations
163
164
 *     - exclude/keep certain parameters on a per station basis;
 *     - merge stations together;
165
166
 *     - make a copy of a certain parameter under a new parameter name for all stations;
 *     - create certain parameters based on some parametrizations.
167
 * 
168
 * @note Please note that the processing order is the following: the MOVE directives are processed first, then the EXCLUDE directives, 
169
170
 * then the KEEP directives, then the MERGE directives and finally the COPY directives. The CREATE directives only come after all the raw data
 * has been edited.
171
 * 
172
 * @subsection data_exclusion Data exclusion
173
 * 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) 
174
175
176
 * 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).
177
178
179
 * 
 * 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).
180
181
 *
 * @code
182
 * WFJ2::EXCLUDE = HS PSUM                       ;inline declaration of parameters exclusion
183
 * KLO3::KEEP = TA RH VW DW                      ;inline declaration of parameters to keep
184
185
 *
 * EXCLUDE_FILE = ../input/meteo/excludes.csv    ;parameters exclusions defined in a separate file
186
 * KEEP_FILE = ../input/meteo/keeps.csv          ;parameters to keep defined in a separate file
187
188
189
190
191
 * @endcode
 *
 * In the second example (relying on a separate file), the file "../input/meteo/excludes.csv" could look like this:
 * @code
 * WFJ2 TA RH
192
 * KLO3 HS PSUM
193
 * @endcode
194
195
196
197
198
199
 * 
 * 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
200
 *
201
 * @subsection data_merging Data merging
202
203
204
205
 * 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). 
206
207
 * 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)
208
 * 
209
 * @code
210
 * STATION1 = STB
211
 * STATION2 = WFJ2
212
 * STATION3 = WFJ1
213
 * STATION4 = DAV1
214
 * [...]
215
 * 
216
217
 * STB::EXCLUDE = ILWR PSUM
 * WFJ2::KEEP = PSUM ILWR RSWR
218
 * 
219
220
 * STB::MERGE = WFJ2 WFJ1
 * DAV1::MERGE = WFJ2
221
 * @endcode
222
 * In order to avoid circular dependencies, a station can NOT receive data from a station AND contribute data to another station. Otherwise, a 
223
 * station can be merged into multiple other stations. Moreover, the merging strategy can be controlled by setting the MERGE_STRATEGY key in
224
 * the [Input] section (by default it is "STRICT_MERGE", see MeteoData::Merge_Type).
225
 * 
226
227
 * @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 
228
 * is large enough to reach all the way to this new station (by setting General::BUFFER_SIZE at least to the number of days from
229
 * the start of the first station to the start of the second station)
230
 * 
231
232
233
234
235
236
237
238
239
 * @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.
 * 
240
241
242
243
244
245
246
247
248
249
 * @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).
 *
250
251
252
253
254
255
256
257
258
259
 * @subsection data_creation Data creation
 * Finally, it is possible to create new data based on some parametrizations. If the requested parameter does not exists, it will be created. Otherwise,
 * any pre-existing data is kept and only missing values in the original data set are filled with the generated values, keeping the original sampling rate. As
 * with all raw data editing, this takes place *before* any filtering/resampling/data generators. As the available algorithms are the same as for the
 * data generators, they are listed in the \ref generators_keywords "data generators section".
 * @code
 * P::create = STD_PRESS			#the pressure is filled with STD_PRESS if no measured values are available
 * ISWR_POT::create = clearSky_SW		#a new parameter "ISWR_POT" is created and filled with Clear Sky values
 * @endcode
 *
260
261
262
263
264
 * @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.
 * 
265
266
 */

267
IOInterface* IOHandler::getPlugin(const std::string& plugin_name) const
268
{
269
270
271
#ifdef PLUGIN_ALPUG
	if (plugin_name == "ALPUG") return new ALPUG(cfg);
#endif
272
#ifdef PLUGIN_ARCIO
273
	if (plugin_name == "ARC") return new ARCIO(cfg);
274
275
#endif
#ifdef PLUGIN_A3DIO
276
	if (plugin_name == "A3D") return new A3DIO(cfg);
277
278
#endif
#ifdef PLUGIN_ARPSIO
279
	if (plugin_name == "ARPS") return new ARPSIO(cfg);
280
281
#endif
#ifdef PLUGIN_GRASSIO
282
	if (plugin_name == "GRASS") return new GrassIO(cfg);
283
284
#endif
#ifdef PLUGIN_GEOTOPIO
285
	if (plugin_name == "GEOTOP") return new GeotopIO(cfg);
286
287
#endif
#ifdef PLUGIN_SMETIO
288
	if (plugin_name == "SMET") return new SMETIO(cfg);
289
290
#endif
#ifdef PLUGIN_SNIO
291
	if (plugin_name == "SNOWPACK") return new SNIO(cfg);
292
293
#endif
#ifdef PLUGIN_PGMIO
294
	if (plugin_name == "PGM") return new PGMIO(cfg);
295
296
#endif
#ifdef PLUGIN_IMISIO
297
	if (plugin_name == "IMIS") return new ImisIO(cfg);
298
#endif
299
300
301
#ifdef PLUGIN_OSHDIO
	if (plugin_name == "OSHD") return new OshdIO(cfg);
#endif
302
#ifdef PLUGIN_GRIBIO
303
	if (plugin_name == "GRIB") return new GRIBIO(cfg);
304
305
#endif
#ifdef PLUGIN_PNGIO
306
	if (plugin_name == "PNG") return new PNGIO(cfg);
307
308
#endif
#ifdef PLUGIN_BORMAIO
309
	if (plugin_name == "BORMA") return new BormaIO(cfg);
310
311
#endif
#ifdef PLUGIN_COSMOXMLIO
312
	if (plugin_name == "COSMOXML") return new CosmoXMLIO(cfg);
313
#endif
314
315
316
#ifdef PLUGIN_CNRMIO
	if (plugin_name == "CNRM") return new CNRMIO(cfg);
#endif
317
#ifdef PLUGIN_GSNIO
318
	if (plugin_name == "GSN") return new GSNIO(cfg);
319
#endif
320
#ifdef PLUGIN_NETCDFIO
321
	if (plugin_name == "NETCDF") return new NetCDFIO(cfg);
322
#endif
Thomas Egger's avatar
Thomas Egger committed
323
#ifdef PLUGIN_PSQLIO
324
	if (plugin_name == "PSQL") return new PSQLIO(cfg);
Thomas Egger's avatar
Thomas Egger committed
325
#endif
326
327
328
#ifdef PLUGIN_SASEIO
	if (plugin_name == "SASE") return new SASEIO(cfg);
#endif
329
330

	return NULL; //no plugin found
331
332
}

333
334
335
//this is actually an object factory
IOInterface* IOHandler::getPlugin(const std::string& cfgkey, const std::string& cfgsection)
{
336
	const std::string op_src = cfg.get(cfgkey, cfgsection);
337
338

	if (mapPlugins.find(op_src) == mapPlugins.end()) {
339
340
		IOInterface *ioPtr = getPlugin(op_src);
		if (ioPtr==NULL)
341
			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);
342
343
		else
			mapPlugins[op_src] = ioPtr;
344
345
346
347
348
	}

	return mapPlugins[op_src];
}

349
350
//Copy constructor
IOHandler::IOHandler(const IOHandler& aio)
351
           : IOInterface(), cfg(aio.cfg), dataCreator(aio.cfg), mapPlugins(aio.mapPlugins), excluded_params(aio.excluded_params), kept_params(aio.kept_params),
352
             merge_commands(aio.merge_commands), copy_commands(aio.copy_commands), move_commands(aio.move_commands),
353
             merged_stations(aio.merged_stations), merge_strategy(aio.merge_strategy), 
354
             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
355
{}
356

Mathias Bavay's avatar
Mathias Bavay committed
357
IOHandler::IOHandler(const Config& cfgreader)
358
           : IOInterface(), cfg(cfgreader), dataCreator(cfgreader), mapPlugins(), excluded_params(), kept_params(),
359
360
361
             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)
362
{
363
364
365
	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);
366
367
}

Mathias Bavay's avatar
Mathias Bavay committed
368
369
IOHandler::~IOHandler() throw()
{
370
	// Get rid of the objects
371
372
373
	std::map<std::string, IOInterface*>::iterator mapit;
	for (mapit = mapPlugins.begin(); mapit!=mapPlugins.end(); ++mapit) {
		delete mapit->second;
374
375
376
	}
}

377
IOHandler& IOHandler::operator=(const IOHandler& source) {
378
	if (this != &source) {
379
		dataCreator = source.dataCreator;
380
		mapPlugins = source.mapPlugins;
381
		excluded_params = source.excluded_params;
382
		kept_params = source.kept_params;
383
384
		merge_commands = source.merge_commands;
		merged_stations = source.merged_stations;
385
		copy_commands = source.copy_commands;
386
		move_commands = source.move_commands;
387
		merge_strategy = source.merge_strategy;
388
		copy_ready = source.copy_ready;
389
		move_ready = source.move_ready;
390
		excludes_ready = source.excludes_ready;
391
		keeps_ready = source.keeps_ready;
392
		merge_ready = source.merge_ready;
393
394
395
396
	}
	return *this;
}

397
398
399
400
401
402
403
404
405
406
407
408
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);
}

409
410
411
412
413
414
415
416
417
418
419
420
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);
}

421
422
423
424
425
426
427
428
429
430
431
432
433
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);
}

434
void IOHandler::readStationData(const Date& date, STATIONS_SET& vecStation)
435
436
437
{
	IOInterface *plugin = getPlugin("METEO", "Input");
	plugin->readStationData(date, vecStation);
438
439
440
	
	if (!merge_ready) create_merge_map(); 
	merge_stations(vecStation);
441
442
443
}

void IOHandler::readMeteoData(const Date& dateStart, const Date& dateEnd,
444
                              std::vector<METEO_SET>& vecMeteo)
445
446
{
	IOInterface *plugin = getPlugin("METEO", "Input");
447
	plugin->readMeteoData(dateStart, dateEnd, vecMeteo);
448
	
449
	checkTimestamps(vecMeteo);
450
	
451
452
453
	if (!move_ready) create_move_map();
	move_params(vecMeteo);
	
454
455
	if (!excludes_ready) create_exclude_map();
	exclude_params(vecMeteo);
456
457
458
	
	if (!keeps_ready) create_keep_map();
	keep_params(vecMeteo);
459
	
460
461
	if (!merge_ready) create_merge_map(); 
	merge_stations(vecMeteo);
462
	
463
	if (!copy_ready) create_copy_map();
464
	copy_params(vecMeteo);
465
466

	dataCreator.createParameters(vecMeteo);
467
}
Mathias Bavay's avatar
Mathias Bavay committed
468

469
void IOHandler::writeMeteoData(const std::vector<METEO_SET>& vecMeteo,
470
471
472
473
474
475
476
477
478
479
480
481
                               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);
}

482
483
484
void IOHandler::readPOI(std::vector<Coords>& pts) {
	IOInterface *plugin = getPlugin("POI", "Input");
	plugin->readPOI(pts);
485
486
487
488
489
490
491
492
493
494
495
496
497
498
}

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);
}

499
500
501
502
503
504
505
506
507
508
509
510
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);
}

511
512
513
/** 
 * @brief check that timestamps are unique and in increasing order
 * @param[in] vecVecMeteo all the data for all the stations
514
*/
515
void IOHandler::checkTimestamps(const std::vector<METEO_SET>& vecVecMeteo)
516
517
518
519
520
521
522
{
	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) {
523
			const Date current_date( vecVecMeteo[stat_idx][ii].date );
524
			if (current_date<=previous_date) {
525
				const StationData& station( vecVecMeteo[stat_idx][ii].meta );
526
527
528
529
				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);
530
531
532
533
534
535
			}
			previous_date = current_date;
		}
	}
}

536
537
538
539
void IOHandler::create_merge_map()
{
	merge_ready = true;
	
540
	std::vector<std::string> merge_keys;
541
542
543
544
545
	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;

Mathias Bavay's avatar
Mathias Bavay committed
546
		const std::string station( IOUtils::strToUpper(merge_keys[ii].substr(0,found)) );
547
548
549
550
551
		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) {
552
			IOUtils::toUpper( *it );
553
554
555
556
557
558
559
560
561
562
563
564
			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) {
565
		const std::string stationID( it_dest->first );
566
567
568
569
570
		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);
	}
}

571
//merge stations that have identical names
572
void IOHandler::merge_stations(STATIONS_SET& vecStation) const
573
{
574
575
	if (merge_commands.empty()) return;
	
576
	for (size_t ii=0; ii<vecStation.size(); ii++) {
577
		const std::string toStationID( IOUtils::strToUpper( vecStation[ii].stationID ) );
578
579
580
		//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;
		
581
		const std::map< string, vector<string> >::const_iterator it = merge_commands.find( toStationID );
582
583
		if (it == merge_commands.end()) continue; //no merge commands for this station

584
		const std::vector<std::string> merge_from( it->second );
585
		for (std::vector<std::string>::const_iterator it_set=merge_from.begin(); it_set != merge_from.end(); ++it_set) {
Mathias Bavay's avatar
Mathias Bavay committed
586
			const std::string fromStationID( *it_set );
587
			
588
			bool found = false;
589
			for (size_t jj=0; jj<vecStation.size(); jj++) {
Mathias Bavay's avatar
Mathias Bavay committed
590
				const std::string curr_station( IOUtils::strToUpper(vecStation[jj].stationID) );
591
592
593
594
				if (curr_station==fromStationID) {
					vecStation[ii].merge( vecStation[jj] );
					found = true;
				}
595
			}
596
			if (!found)
597
				throw InvalidArgumentException("Station ID '"+fromStationID+"' not found when merging toward station '"+toStationID+"'. Consider increasing BUFFER_SIZE!", AT);
598
599
		}
	}
600
601
602
	
	//remove the stations that have been merged into other ones
	for (size_t ii=0; ii<vecStation.size(); ii++) {
Mathias Bavay's avatar
Mathias Bavay committed
603
604
		const std::string stationID( IOUtils::strToUpper( vecStation[ii].stationID ) );
		const std::vector<std::string>::const_iterator it = std::find(merged_stations.begin(), merged_stations.end(), stationID);
605
606
607
608
609
610
		if ( it!=merged_stations.end() ) {
			std::swap( vecStation[ii], vecStation.back() );
			vecStation.pop_back();
			ii--; //in case we have multiple identical stations ID
		}
	}
611
612
613
}

//in this implementation, we consider that the station name does NOT change over time
614
void IOHandler::merge_stations(std::vector<METEO_SET>& vecVecMeteo) const
615
{
616
617
	if (merge_commands.empty()) return;
	
618
	for (size_t ii=0; ii<vecVecMeteo.size(); ii++) { //loop over the stations
619
		if (vecVecMeteo[ii].empty())  continue;
620
		const std::string toStationID( IOUtils::strToUpper(vecVecMeteo[ii][0].meta.stationID) );
621
622
623
		//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;
		
624
		const std::map< std::string, std::vector<std::string> >::const_iterator it = merge_commands.find( toStationID );
625
626
		if (it == merge_commands.end()) continue; //no merge commands for this station

627
		const std::vector<std::string> merge_from( it->second );
628
		for (std::vector<std::string>::const_iterator it_set=merge_from.begin(); it_set != merge_from.end(); ++it_set) {
629
			const std::string fromStationID( *it_set );
630
631
			
			bool found = false;
632
			for (size_t jj=0; jj<vecVecMeteo.size(); jj++) { //loop over the available stations in the current dataset
633
				if (vecVecMeteo[jj].empty()) continue;
634
				const std::string curr_station( IOUtils::strToUpper(vecVecMeteo[jj][0].meta.stationID) );
635
				if (curr_station==fromStationID) {
636
					MeteoData::mergeTimeSeries(vecVecMeteo[ii], vecVecMeteo[jj], static_cast<MeteoData::Merge_Type>(merge_strategy)); //merge timeseries for the two stations
637
638
					found = true;
				}
639
			}
640
641
			if (!found)
				throw InvalidArgumentException("Station ID '"+fromStationID+"' not found when merging toward station '"+toStationID+"'", AT);
642
643
		}
	}
644
645
646
	
	//remove the stations that have been merged into other ones
	for (size_t ii=0; ii<vecVecMeteo.size(); ii++) {
647
		if (vecVecMeteo[ii].empty())  continue;
648
649
		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);
650
651
652
653
654
655
		if ( it!=merged_stations.end() ) {
			std::swap( vecVecMeteo[ii], vecVecMeteo.back() );
			vecVecMeteo.pop_back();
			ii--; //in case we have multiple identical stations ID
		}
	}
656
657
}

658
659
void IOHandler::create_exclude_map()
{
660
	excludes_ready = true;
Mathias Bavay's avatar
Mathias Bavay committed
661
	const std::string exclude_file = cfg.get("EXCLUDE_FILE", "Input", IOUtils::nothrow);
662

663
664
	if (!exclude_file.empty()) {
		//if this is a relative path, prefix the path with the current path
665
		const std::string prefix = ( FileUtils::isAbsolutePath(exclude_file) )? "" : cfg.getConfigRootDir()+"/";
Mathias Bavay's avatar
Mathias Bavay committed
666
667
		const std::string path( FileUtils::getPath(prefix+exclude_file, true) );  //clean & resolve path
		const std::string filename( path + "/" + FileUtils::getFilename(exclude_file) );
668

669
		if (!FileUtils::fileExists(filename)) throw AccessException(filename, AT); //prevent invalid filenames
670
		std::ifstream fin(filename.c_str(), std::ifstream::in);
671
		if (fin.fail()) throw AccessException(filename, AT);
672

673
		try {
674
			const char eoln = FileUtils::getEoln(fin); //get the end of line character for the file
675

676
677
			vector<string> tmpvec;
			string line;
678

679
680
681
682
			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, ' ');
683

684
				if (ncols > 1) {
685
					for (vector<string>::iterator it = tmpvec.begin()+1; it != tmpvec.end(); ++it) {
686
						IOUtils::toUpper( *it );
687
					}
688

689
					const std::set<std::string> tmpset(tmpvec.begin()+1, tmpvec.end());
690
					excluded_params[ IOUtils::strToUpper(tmpvec[0]) ] = tmpset;
691
692
				}
			}
693
694
695
		} catch (const std::exception&) {
			fin.close();
			throw;
696
		}
697

698
699
700
		fin.close();
	}

Mathias Bavay's avatar
Mathias Bavay committed
701
	std::vector<std::string> exclude_keys;
702
703
704
705
706
	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;

707
		const std::string station( IOUtils::strToUpper(exclude_keys[ii].substr(0,found)) );
708
709
710
		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);
711
		for (vector<string>::iterator it = vecString.begin(); it != vecString.end(); ++it) {
712
			IOUtils::toUpper( *it );
713
714
		}

715
		const std::set<std::string> tmpset(vecString.begin(), vecString.end());
716
717
		excluded_params[ station ] = tmpset;
	}
718
719
	
	//Handle "*" wildcard: add the params to all other declared stations
Mathias Bavay's avatar
Mathias Bavay committed
720
	std::map< std::string, std::set<std::string> >::const_iterator it_station = excluded_params.find("*");
721
722
723
724
725
726
727
728
729
730
731
	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;
		}
	}
732
733
}

734
735
736
void IOHandler::create_keep_map()
{
	keeps_ready = true;
Mathias Bavay's avatar
Mathias Bavay committed
737
	const std::string keep_file = cfg.get("KEEP_FILE", "Input", IOUtils::nothrow);
738
739
740

	if (!keep_file.empty()) {
		//if this is a relative path, prefix the path with the current path
741
		const std::string prefix = ( FileUtils::isAbsolutePath(keep_file) )? "" : cfg.getConfigRootDir()+"/";
Mathias Bavay's avatar
Mathias Bavay committed
742
743
		const std::string path( FileUtils::getPath(prefix+keep_file, true) );  //clean & resolve path
		const std::string filename( path + "/" + FileUtils::getFilename(keep_file) );
744

745
		if (!FileUtils::fileExists(filename)) throw AccessException(filename, AT); //prevent invalid filenames
746
		std::ifstream fin(filename.c_str(), std::ifstream::in);
747
		if (fin.fail()) throw AccessException(filename, AT);
748
749

		try {
750
			const char eoln = FileUtils::getEoln(fin); //get the end of line character for the file
751
752
753
754
755
756
757
758
759
760

			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) {
761
					for (vector<string>::iterator it = tmpvec.begin()+1; it != tmpvec.end(); ++it) {
762
						IOUtils::toUpper( *it );
763
764
765
					}

					const set<string> tmpset(tmpvec.begin()+1, tmpvec.end());
766
					kept_params[ IOUtils::strToUpper(tmpvec[0]) ] = tmpset;
767
768
769
770
771
772
773
774
775
776
				}
			}
		} catch (const std::exception&) {
			fin.close();
			throw;
		}

		fin.close();
	}

Mathias Bavay's avatar
Mathias Bavay committed
777
	std::vector<std::string> keep_keys;
778
779
780
781
782
	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;

783
		const std::string station( IOUtils::strToUpper(keep_keys[ii].substr(0,found)) );
784
785
786
		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);
787
		for (vector<string>::iterator it = vecString.begin(); it != vecString.end(); ++it) {
788
			IOUtils::toUpper( *it );
789
790
		}

791
		const std::set<std::string> tmpset(vecString.begin(), vecString.end());
792
793
		kept_params[ station ] = tmpset;
	}
794
795
	
	//Handle "*" wildcard: add the params to all other declared stations
Mathias Bavay's avatar
Mathias Bavay committed
796
	std::map< std::string, std::set<std::string> >::const_iterator it_station = kept_params.find("*");
797
798
799
800
801
802
803
804
805
806
807
	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;
		}
	}
808
809
}

810
/**
811
* @brief reset to nodata the parameters marked as EXCLUDE on a per station basis
812
*/
813
814
815
816
void IOHandler::exclude_params(std::vector<METEO_SET>& vecVecMeteo) const
{
	if (excluded_params.empty()) return;

817
	for (size_t station=0; station<vecVecMeteo.size(); ++station) { //loop over the stations
818
		if (vecVecMeteo[station].empty()) continue;
819
		const std::string stationID( IOUtils::strToUpper(vecVecMeteo[station][0].meta.stationID) );
Mathias Bavay's avatar
Mathias Bavay committed
820
		std::map< std::string, std::set<std::string> >::const_iterator it = excluded_params.find(stationID);
821
822
823
824
		if (it == excluded_params.end()) {
			it = excluded_params.find("*"); //fallback: is there a wildcard like "*::KEEP"?
			if (it == excluded_params.end()) continue;
		}
825

Mathias Bavay's avatar
Mathias Bavay committed
826
		const std::set<std::string> excluded( it->second );
827

828
		for (size_t ii=0; ii<vecVecMeteo[station].size(); ++ii) { //loop over the timesteps
829
			for (std::set<std::string>::const_iterator it_set=excluded.begin(); it_set != excluded.end(); ++it_set) {
Mathias Bavay's avatar
Mathias Bavay committed
830
				const std::string param( *it_set );
831
832
833
834
835
836
837
				if (vecVecMeteo[station][ii].param_exists(param))
					vecVecMeteo[station][ii](param) = IOUtils::nodata;
			}
		}
	}
}

838
839
840
841
842
843
844
845
846
847
/**
* @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;
		
848
		const std::string stationID( IOUtils::strToUpper(vecVecMeteo[station][0].meta.stationID) );
Mathias Bavay's avatar
Mathias Bavay committed
849
		std::map< std::string, std::set<std::string> >::const_iterator it = kept_params.find(stationID);
850
851
852
853
		if (it == kept_params.end()) {
			it = kept_params.find("*"); //fallback: is there a wildcard like "*::KEEP"?
			if (it == kept_params.end()) continue;
		}
854

Mathias Bavay's avatar
Mathias Bavay committed
855
		const std::set<std::string> kept( it->second );
856
857
		
		for (size_t ii=0; ii<vecVecMeteo[station].size(); ++ii) {
858
			MeteoData& md_ref( vecVecMeteo[station][ii] );
859
860
861
			MeteoData md( md_ref );
			md.reset(); //delete all meteo fields
			
862
			for (std::set<std::string>::const_iterator it_set=kept.begin(); it_set != kept.end(); ++it_set) { //loop over the parameters to keep
Mathias Bavay's avatar
Mathias Bavay committed
863
				const std::string param( *it_set);
864
865
866
867
868
869
870
871
872
873
				if (!md.param_exists(param)) continue;
				 md(param) = md_ref(param);
			}
			
			//copy back the new object into vecVecMeteo
			md_ref = md;
		}
	}
}

874
875
876
877
878
879
/**
* Parse [Input] section for potential parameters that the user wants
* renamed (as '%%::MOVE = %%')
*/
void IOHandler::create_move_map()
{
Mathias Bavay's avatar
Mathias Bavay committed
880
	std::vector<std::string> move_keys;
881
882
883
	const size_t nrOfMatches = cfg.findKeys(move_keys, "::MOVE", "Input", true); //search anywhere in key

	for (size_t ii=0; ii<nrOfMatches; ++ii) {
Mathias Bavay's avatar
Mathias Bavay committed
884
		const std::string dest_param( move_keys[ii].substr( 0, move_keys[ii].find_first_of(":") ) );
885
886
887
888
889
		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) {
890
			IOUtils::toUpper( *it );
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
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
		}
		
		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; 
				}
			}
		}
	}
}


937
938
939
940
/**
* Parse [Input] section for potential parameters that the user wants
* duplicated (as '%%::COPY = %%')
*/
941
void IOHandler::create_copy_map()
942
{
Mathias Bavay's avatar
Mathias Bavay committed
943
	std::vector<std::string> copy_keys;
944
	const size_t nrOfMatches = cfg.findKeys(copy_keys, "::COPY", "Input", true); //search anywhere in key
945

946
	for (size_t ii=0; ii<nrOfMatches; ++ii) {
Mathias Bavay's avatar
Mathias Bavay committed
947
948