WSL/SLF GitLab Repository

IOHandler.cc 15.6 KB
Newer Older
1
/***********************************************************************************/
2
/*  Copyright 2009-2012 WSL Institute for Snow and Avalanche Research  SLF-DAVOS   */
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/***********************************************************************************/
/* 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>
20
21
22
23
24
25
26
27

using namespace std;

namespace mio {
 /**
 * @page plugins Plugins 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 loaded on demand (and also compiled only if requested at compile time). A plugin can therefore fail to load (for example if it does not exist) at run time.
 *
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
 * @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)
 * - SPECIALPTS, for a list of points that can be used for providing extra information at some specific location (extracting time series at a few selected points, etc)
 *
 * 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.
44
 *
45
 * @section available_plugins Available plugins
46
 * 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:
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
 * <center><table border="1">
 * <tr><th>Plugin keyword</th><th>Provides</th><th>Description</th><th>Extra requirements</th></tr>
 * <tr><td>\subpage a3d "A3D"</td><td>meteo, specialpts</td><td>original Alpine3D meteo files</td><td></td></tr>
 * <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>
 * <tr><td>\subpage cosmoxml "COSMOXML"</td><td>meteo</td><td>MeteoSwiss COSMO's postprocessing XML format</td><td><A HREF="http://libxmlplusplus.sourceforge.net/">libxml++</A></td></tr>
 * <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>
 * <tr><td>\subpage gsn "GSN"</td><td>meteo</td><td>connects to the Global Sensor Network web service interface</td><td><SUP>1</SUP></td></tr>
 * <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>
 * <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>
 * <tr><td>\subpage smetio "SMET"</td><td>meteo, specialpts</td><td>SMET data files</td><td></td></tr>
 * <tr><td>\subpage snowpack "SNOWPACK"</td><td>meteo</td><td>original SNOWPACK meteo files</td><td></td></tr>
 * </table></center>
 * <i><SUP>1</SUP>In order to rebuild the soap bindings for GSN, <A HREF="http://gsoap2.sourceforge.net/">gsoap</A> is required. This is only relevant to the plugin developers.</i>
65
 *
Mathias Bavay's avatar
Mathias Bavay committed
66
67
68
69
70
71
72
73
74
 * @section data_generators Data generators
 * It is also possible to duplicate a meteorological parameter as another meteorological parameter. This is done by specifying a COPY key, following the syntax
 * COPY::new_name = existing_parameter. For example:
 * @code
 * COPY::VW_avg = 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).
75
76
77
78
 */

void IOHandler::registerPlugins()
{
79
	//mapPlugins[io.ini KEY]= IOPlugin(library file name, class name, NULL, NULL);
80
#ifdef PLUGIN_SMETIO
81
	mapPlugins["SMET"]      = IOPlugin("SMETIO", NULL, &IOPlugin::createInstance<SMETIO>);
82
83
#endif
#ifdef PLUGIN_ARCIO
84
	mapPlugins["ARC"]       = IOPlugin("ARCIO", NULL, &IOPlugin::createInstance<ARCIO>);
85
86
#endif
#ifdef PLUGIN_A3DIO
87
	mapPlugins["A3D"]       = IOPlugin("A3DIO", NULL, &IOPlugin::createInstance<A3DIO>);
88
89
#endif
#ifdef PLUGIN_ARPSIO
90
	mapPlugins["ARPS"]      = IOPlugin("ARPSIO", NULL, &IOPlugin::createInstance<ARPSIO>);
91
92
#endif
#ifdef PLUGIN_GRASSIO
93
	mapPlugins["GRASS"]     = IOPlugin("GrassIO", NULL, &IOPlugin::createInstance<GrassIO>);
94
95
#endif
#ifdef PLUGIN_GEOTOPIO
96
	mapPlugins["GEOTOP"]    = IOPlugin("GeotopIO", NULL, &IOPlugin::createInstance<GeotopIO>);
97
98
#endif
#ifdef PLUGIN_SNIO
99
	mapPlugins["SNOWPACK"]  = IOPlugin("SNIO", NULL, &IOPlugin::createInstance<SNIO>);
100
101
#endif
#ifdef PLUGIN_PGMIO
102
	mapPlugins["PGM"]       = IOPlugin("PGMIO", NULL, &IOPlugin::createInstance<PGMIO>);
103
#endif
104
105
#ifdef PLUGIN_IMISIO
	mapPlugins["IMIS"]      = IOPlugin("ImisIO", NULL, &IOPlugin::createInstance<ImisIO>);
106
#endif
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#ifdef PLUGIN_GRIBIO
	mapPlugins["GRIB"]      = IOPlugin("GRIBIO", NULL, &IOPlugin::createInstance<GRIBIO>);
#endif
#ifdef PLUGIN_PNGIO
	mapPlugins["PNG"]       = IOPlugin("PNGIO", NULL, &IOPlugin::createInstance<PNGIO>);
#endif
#ifdef PLUGIN_BORMAIO
	mapPlugins["BORMA"]     = IOPlugin("BormaIO", NULL, &IOPlugin::createInstance<BormaIO>);
#endif
#ifdef PLUGIN_COSMOXMLIO
	mapPlugins["COSMOXML"]  = IOPlugin("CosmoXMLIO", NULL, &IOPlugin::createInstance<CosmoXMLIO>);
#endif
#ifdef PLUGIN_GSNIO
	mapPlugins["GSN"]       = IOPlugin("GSNIO", NULL, &IOPlugin::createInstance<GSNIO>);
121
122
123
124
125
#endif
}

//Copy constructor
#ifdef _POPC_
126
127
128
//IOHandler::IOHandler(const IOHandler& aio)
//           : cfg(aio.cfg), mapPlugins(), copy_parameter(), copy_name(), enable_copying(false)
//{
129
	//Nothing else so far //HACK for POPC
130
131
//}
#else
132
IOHandler::IOHandler(const IOHandler& aio)
133
           : cfg(aio.cfg), mapPlugins(), copy_parameter(), copy_name(), enable_copying(false)
134
135
136
137
138
139
{
	//Nothing else so far
	//TODO: Deal with the IOInterface* pointers, e.g. bormaio
}
#endif

140
IOHandler::IOHandler(const Config& cfgreader) : cfg(cfgreader), mapPlugins(), copy_parameter(), copy_name(), enable_copying(false)
141
142
{
	registerPlugins();
143
	parse_copy_config();
144
145
146
}

#ifdef _POPC_
147
IOHandler::~IOHandler(){
148
#else
149
IOHandler::~IOHandler() throw(){
150
151
#endif
	// Get rid of the objects
152
	std::map<std::string, IOPlugin>::iterator mapit;
153
	for (mapit = mapPlugins.begin(); mapit!=mapPlugins.end(); mapit++){
154
		IOInterface*& io = (mapit->second).io;
155
		if (io != NULL) {
156
			delete io;
157
158
159
160
161
162
163
164
165
166
			io = NULL;
		}
	}
}

IOInterface* IOHandler::getPlugin(const std::string& cfgkey, const std::string& cfgsection)
{
	std::string op_src="";
	cfg.getValue(cfgkey, cfgsection, op_src);

167
	std::map<std::string, IOPlugin>::iterator mapit = mapPlugins.find(op_src);
168
	if (mapit == mapPlugins.end())
169
		throw IOException("Cannot find plugin " + op_src + " as requested in file " + cfg.getSourceName() + ". Has it been activated through ccmake? Is it registered in IOHandler::registerPlugins?", AT);
170
	if ((mapit->second).io == NULL){
171
		(mapit->second).io = (mapit->second).creator_func(cfg);
172
	}
173
174

	//Now check if it is correctly loaded
175
	if ((mapit->second).io == NULL) {
176
		throw IOException("Requesting to read/write data with plugin '" + op_src + "', but plugin is not loaded", AT);
177
178
179
180
181
	}

	return (mapit->second).io;
}

182
void IOHandler::read2DGrid(Grid2DObject& grid_out, const std::string& i_filename)
183
184
{
	IOInterface *plugin = getPlugin("GRID2D", "Input");
185
186
187
188
189
190
191
	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);
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
}

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

207
void IOHandler::readStationData(const Date& date, STATION_TIMESERIE& vecStation)
208
209
210
211
212
{
	IOInterface *plugin = getPlugin("METEO", "Input");
	plugin->readStationData(date, vecStation);
}

213
void IOHandler::readMeteoData(const Date& date, METEO_TIMESERIE& vecMeteo)
214
215
{
	std::vector< std::vector<MeteoData> > meteoTmpBuffer;
216
	readMeteoData(date, date, meteoTmpBuffer);
217

218
219
220
221
222
	vecMeteo.clear();
	vecMeteo.reserve(meteoTmpBuffer.size());

	size_t emptycounter = 0;
	for (size_t ii=0; ii<meteoTmpBuffer.size(); ii++){//stations
223
		if (meteoTmpBuffer[ii].size() > 0){
224
225
226
227
228
229
230
231
232
233
234
235
			vecMeteo.push_back(meteoTmpBuffer[ii][0]);
		} else {
			emptycounter++;
		}
	}

	if (emptycounter == meteoTmpBuffer.size()){
		vecMeteo.clear();
	}
}

void IOHandler::readMeteoData(const Date& dateStart, const Date& dateEnd,
236
                              std::vector<METEO_TIMESERIE>& vecMeteo,
237
                              const size_t& stationindex)
238
239
{
	IOInterface *plugin = getPlugin("METEO", "Input");
240
	plugin->readMeteoData(dateStart, dateEnd, vecMeteo, stationindex);
241
242

	copy_parameters(stationindex, vecMeteo);
243
}
244
245
246
247
#ifdef _POPC_
void IOHandler::writeMeteoData(std::vector<METEO_TIMESERIE>& vecMeteo,
                               const std::string& name)
#else
248
void IOHandler::writeMeteoData(const std::vector<METEO_TIMESERIE>& vecMeteo,
249
                               const std::string& name)
250
#endif
251
252
{
	IOInterface *plugin = getPlugin("METEO", "Output");
253
	plugin->writeMeteoData(vecMeteo, name);
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
}

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

void IOHandler::readSpecialPoints(std::vector<Coords>& pts) {
	IOInterface *plugin = getPlugin("SPECIALPTS", "Input");
	plugin->readSpecialPoints(pts);
}

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

273
274
275
276
277
278
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);
}

279
280
281
void IOHandler::parse_copy_config()
{
	/**
282
	 * Parse [Input] section for potential parameters that the user wants
283
284
285
	 * duplicated (starting with 'COPY::')
	 */
	vector<string> copy_keys;
286
	const size_t nrOfMatches = cfg.findKeys(copy_keys, "COPY::", "Input");
287
288
289

	for (size_t ii=0; ii<nrOfMatches; ii++) {
		string initial_name = "";
290
		const string name_of_copy = copy_keys[ii].substr(6);
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
		cfg.getValue(copy_keys[ii], "Input", initial_name);

		if ((name_of_copy.length() > 0) && (initial_name.length() > 0)){
			copy_parameter.push_back(initial_name);
			copy_name.push_back(name_of_copy);
			enable_copying = true;
		}
	}
}

void IOHandler::copy_parameters(const size_t& stationindex, std::vector< METEO_TIMESERIE >& vecMeteo) const
{
	/**
	 * 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]
	 * COPY::TA2 = TA
	 * means that TA2 will be the name of a new parameter in MeteoData with the copied value
	 * of the meteo parameter MeteoData::TA
	 */
	if (!enable_copying) return; //Nothing configured
313

314
315
316
317
318
319
320
	size_t station_start=0, station_end=vecMeteo.size();
	if (stationindex != IOUtils::npos) {
		if (stationindex < vecMeteo.size()) {
			station_start = stationindex;
			station_end   = stationindex+1;
		} else {
			throw IndexOutOfBoundsException("Accessing stationindex in readMeteoData that is out of bounds", AT);
321
		}
322
323
	}

324
	const size_t nr_of_params = copy_parameter.size();
325
326
327
328
329
330
331
	vector<size_t> indices; //will hold the indices of the parameters to be copied

	for (size_t ii=station_start; ii<station_end; ii++) { //for each station
		for (size_t jj=0; jj<vecMeteo[ii].size(); jj++) { //for each MeteoData object of one station

			if (jj==0) { //buffer the index numbers
				for (size_t kk=0; kk<nr_of_params; kk++) {
332
					const size_t param_index = vecMeteo[ii][jj].getParameterIndex(copy_parameter[kk]);
333
334
335
336
337
338
					if (param_index == IOUtils::npos) {
						std::stringstream ss;
						ss << "At " << vecMeteo[ii][jj].date.toString(Date::ISO) << ", station " << vecMeteo[ii][jj].meta.stationID;
						ss << " has no parameter \"" << copy_parameter[kk] << "\" to copy!\n";
						throw InvalidArgumentException(ss.str(), AT);
					}
339
340
341
342
343
344

					indices.push_back(param_index);
				}
			}

			for (size_t kk=0; kk<nr_of_params; kk++) {
345
346
				const size_t newparam = vecMeteo[ii][jj].addParameter(copy_name[kk]);
				vecMeteo[ii][jj](newparam) = vecMeteo[ii][jj](indices[kk]);
347
348
349
350
351
352
			}
		}
		indices.clear(); //may change for every station
	}
}

353
#ifndef _POPC_
354
355
std::ostream& operator<<(std::ostream& os, const IOHandler& data)
{
356
357
358
359
360
361
362
363
364
365
366
367
	os << data.toString();
	return os;
}
#endif

#ifdef _POPC_
std::string IOHandler::toString()
#else
std::string IOHandler::toString() const
#endif
{
	std::stringstream os;
368
	os << "<IOHandler>\n";
369
	os << "Config& cfg = " << hex << &cfg << dec << "\n";
370
371
372

	os << "<mapPlugins>\n";
	os << setw(10) << "Keyword" << " = " << IOPlugin::header << "\n";
373
	std::map<std::string, IOPlugin>::const_iterator it1;
374
	for (it1=mapPlugins.begin(); it1 != mapPlugins.end(); it1++){
375
376
377
378
		os << setw(10) << it1->first << " = " <<  it1->second;
	}
	os << "</mapPlugins>\n";
	os << "</IOHandler>\n";
379
	return os.str();
380
381
}

382
} //end namespace