WSL/SLF GitLab Repository

IOUtils.h 11.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/***********************************************************************************/
/*  Copyright 2009 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/>.
*/
#ifndef __IOUTILS_H__
#define __IOUTILS_H__

#include <meteoio/ConfigReader.h>
#include <meteoio/Coords.h>
#include <meteoio/Date.h>
#include <meteoio/IOExceptions.h>
#include <meteoio/MeteoData.h>

#include <sstream>
#include <string>
#include <map>
#include <vector>
#include <list>
#include <cstdlib>
#include <iostream>
#include <cstdio>
#include <fstream>
#include <dirent.h>
#include <sys/stat.h>
#include <cctype>
#include <limits>

#ifndef MAX
#define MAX(x,y)    (((x) < (y)) ? (y) : (x))
#endif
#ifndef MIN
#define MIN(x,y)    (((x) < (y)) ? (x) : (y))
#endif

#ifndef C_TO_K
#define C_TO_K( T ) ( T + 273.15 )	  // Celsius to Kelvin
#endif
#ifndef K_TO_C
#define K_TO_C( T ) ( T - 273.15 )	  // Kelvin to Celsius
#endif

namespace mio {

class MeteoData;
class Coords;
class ConfigReader;

namespace IOUtils {
62
63
	const unsigned int nothrow = 0;
	const unsigned int dothrow = 1;
64
65
66
67
	const double nodata = -999.0; ///<This is the internal nodata value
	//const double not_set = std::numeric_limits<double>::max()-2.;
	const unsigned int unodata = (unsigned int)-1;
	const int inodata = -999;
68
	const short int snodata = -999;
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
	const unsigned int npos    = (unsigned int)-1;  ///<npos is the out-of-range value

	const double earth_radius = 6371e3; ///<Earth radius in meters
	const double grid_epsilon = 5.; ///<What is an acceptable small distance on a grid, in meters
	const double lon_epsilon = grid_epsilon / earth_radius; ///<in degrees. Small angle for longitudes, so sin(x)=x
	const double lat_epsilon = lon_epsilon/2.; ///<in degrees. Small angle for latitudes. Since for longitudes it is for 360deg, it has to be 180deg for latitudes

	typedef enum NODATA_HANLDING {
		RAW_NODATA, ///< no special handling of nodata
		PARSE_NODATA ///< process nodata as "no data"
	} nodata_handling;

	/**
	* @brief Check whether two values are equal regarding a certain epsilon environment (within certain radius of each other)
	* @param val1
	* @param val2
	* @param epsilon is a radius around val1
	* @return true if val2 is within the radius around val1, false otherwise.
	*/
	bool checkEpsilonEquality(const double& val1, const double& val2, const double& epsilon);

	double pow2(const double& val);
	double pow3(const double& val);
	double pow4(const double& val);

	unsigned int seek(const Date& soughtdate, const std::vector<MeteoData>& vecM, const bool& exactmatch=true);

	/**
	* @brief Converts a compass bearing to a trigonometric angle
	* @param bearing compass bearing (0° on top, clockwise, in [0°, 360°[)
	* @return trigonometric angle (0° on the right, counterclockwise, in [0, 2PI[)
	*/
	double bearing_to_angle(const double& bearing);
	/**
	* @brief Converts a trigonometric angle to a compass bearing
	* @param angle trigonometric angle (0° on the right, counterclockwise, in [0, 2PI[)
	* @return bearing (0° on top, clockwise, in [0°, 360°[)
	*/
	double angle_to_bearing(const double& angle);

	void readDirectory(const std::string& path, std::list<std::string>& dirlist, const std::string& pattern = "");

	bool validFileName(const std::string& filename);

	bool fileExists(const std::string& filename);

	/**
	* @brief Removes trailing and leading whitespaces, tabs and newlines from a string. 
	* @param s The reference of the string to trim (in/out parameter)
	*/
	void trim(std::string &s);

121
122
	void stripComments(std::string& str);

123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
	char getEoln(std::istream& fin);

	void skipLines(std::istream& fin, const unsigned int& nbLines, const char& eoln='\n');

	/**
	* @brief read a string line, parse it and save it into a map object, that is passed by reference
	* @param in_line (const string&) string to parse
	* @param delimiter (const string&) delimiter to use for the parsing
	* @param out_map (map\<string,string\>&) map after parsing
	* @param keyprefix this string is prefixed before the key, defaults to no prefix: ""
	* @return (bool) true when line is empty
	*/
	bool readKeyValuePair(const std::string& in_line, 
					  const std::string& delimiter, 
					  std::map<std::string,std::string>& out_map,
					  const std::string& keyprefix="");

	void toUpper(std::string& str);
	unsigned int readLineToVec(const std::string& line_in, std::vector<std::string>& vecString);
	unsigned int readLineToVec(const std::string& line_in, std::vector<std::string>& vecString, const char& delim);
	void readKeyValueHeader(std::map<std::string, std::string>& headermap, 
				    std::istream& bs,
				    const unsigned int& linecount=1, 
				    const std::string& delimiter="=");


	/** 
	* @brief Convert a string to the requested type (template function). 
	* @tparam T   [in] The type wanted for the return value (template type parameter). 
	* @param t   [out] The value converted to the requested type. 
	* @param str   [in] The input string to convert; trailling whitespaces are ignored, 
	*              comment after non-string values are allowed, but multiple values are not allowed. 
	* @param f   [in] The radix for reading numbers, such as std::dec or std::oct; default is std::dec.
	* @return true if everything went fine, false otherwise
	*/
	template <class T> bool convertString(T& t, const std::string& str, std::ios_base& (*f)(std::ios_base&) = std::dec) {
		std::string s = str; 
		trim(s); //delete trailing and leading whitespaces and tabs
		if (s.size() == 0) {
			t = static_cast<T> (nodata);
			return true;
		} else {
			std::istringstream iss(s);
			iss.setf(std::ios::fixed);
			iss.precision(std::numeric_limits<double>::digits10); //try to read values with maximum precision
			iss >> f >> t; //Convert first part of stream with the formatter (e.g. std::dec, std::oct)
			if (iss.fail()) {
				//Conversion failed
				return false;
			}
			std::string tmp="";
			getline(iss,  tmp); //get rest of line, if any
			trim(tmp);
			if ((tmp.length() > 0) && tmp[0] != '#' && tmp[0] != ';') {
				//if line holds more than one value it's invalid
				return false;
			}
			return true;
		}
	}
	// fully specialized template functions (implementation must not be in header)
	template<> bool convertString<std::string>(std::string& t, const std::string& str, std::ios_base& (*f)(std::ios_base&));
	template<> bool convertString<bool>(bool& t, const std::string& str, std::ios_base& (*f)(std::ios_base&));
	template<> bool convertString<Date>(Date& t, const std::string& str, std::ios_base& (*f)(std::ios_base&));
	template<> bool convertString<Coords>(Coords& t, const std::string& str, std::ios_base& (*f)(std::ios_base&));

	/**
	* @brief Returns, with the requested type, the value associated to a key (template function).
	* @tparam T   [in] The type wanted for the return value (template type parameter).
192
193
194
195
	* @param[in]  properties  A map containing all the parameters.
	* @param[in]  key         The key of the parameter to retrieve.
	* @param[out] t           The value associated to the key, converted to the requested type
	* @param[in]  options     Extra options, by default IOUtils::dothrow
196
197
	*/
	template <class T> void getValueForKey(const std::map<std::string,std::string>& properties,
198
								    const std::string& key, T& t, const unsigned int& options=IOUtils::dothrow){
199
200
201
202
		if (key == "") {
			throw InvalidArgumentException("Empty key", AT);
		}

203
204
205
206
207
208
209
210
211
212
213
		//const std::string value = (const_cast<std::map<std::string,std::string>&>(properties))[key];
		//if (value == ""){} //The alternative way

		std::map<std::string, std::string>::const_iterator it;
		it = properties.find(key);

		if (it == properties.end()){
			if (options == IOUtils::nothrow)
				return;
			else 
				throw UnknownValueException("No value for key " + key, AT);
214
		}
215
		const std::string value = it->second;
216
217
218
219
220
221
222
223
224

		if(!convertString<T>(t, value, std::dec)) {
			throw ConversionFailedException(value, AT);
		}
	}

	/**
	* @brief Returns, with the requested type, the value associated to a key (template function).
	* @tparam T           [in] The type wanted for the return value (template type parameter).
225
226
	* @param[in]  properties  A map containing all the parameters.
	* @param[in]  key         The key of the parameter to retrieve.
227
	* @param[out] vecT        The vector of values associated to the key, each value is converted to the requested type
228
	* @param[in]  options     Extra options, by default IOUtils::dothrow
229
230
	*/
	template <class T> void getValueForKey(const std::map<std::string,std::string>& properties, 
231
					    const std::string& key, std::vector<T>& vecT, const unsigned int& options=IOUtils::dothrow){
232
233
234
235
		if (key == "") {
			throw InvalidArgumentException("Empty key", AT);
		}

236
237
238
239
240
241
242
243
		std::map<std::string, std::string>::const_iterator it;
		it = properties.find(key);

		if (it == properties.end()){
			if (options == IOUtils::nothrow)
				return;
			else 
				throw UnknownValueException("No value for key " + key, AT);
244
		}
245
		const std::string value = it->second;
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295

		//split value string 
		std::vector<std::string> vecUnconvertedValues;
		unsigned int counter = readLineToVec(value, vecUnconvertedValues);

		for (unsigned int ii=0; ii<counter; ii++){
			T myvar;
			if(!convertString<T>(myvar, vecUnconvertedValues.at(ii), std::dec)){
				throw ConversionFailedException(vecUnconvertedValues.at(ii), AT);
			}
			vecT.push_back(myvar);
		}
	}

	/**
	* @brief Standardize a given value to use MeteoIO's internal nodata value (if applicable)
	* @tparam T           [in] The type wanted for the return value (template type parameter).
	* @param[in] value  the value to check/convert
	* @param[in] plugin_nodata plugin-specific nodata value
	* @return checked/converted value
	*/
	template <class T> T standardizeNodata(const T& value, const double& plugin_nodata) {
		if(value==plugin_nodata) return static_cast<T> (nodata);
		else return value;
	}

	/**
	* @brief A function that parses a ConfigReader object for COORSYS, COORDPARAM keywords in [Input] and [Output]
	*        section and sets the respective strings to the values of those keywords
	* @param[in] cfg  A ConfigReader object
	* @param[out] coordin The coordinate system to be used for input data
	* @param[out] coordinparam The coordinate system parameters to be used for output data
	* @param[out] coordout The coordinate system to be used for output data
	* @param[out] coordoutparam The coordinate system parameters to be used for output data
	*/
	void getProjectionParameters(const ConfigReader& cfg, std::string& coordin, std::string& coordinparam, 
						    std::string& coordout, std::string& coordoutparam);

	/**
	* @brief A function that parses a ConfigReader object for the TZ keyword and returns the timezone
	* @param[in] cfg  A ConfigReader object
	* @param[out] tz_in value to be used for the input timezone
	* @param[out] tz_out value to be used for the output timezone
	*/
	void getTimeZoneParameters(const ConfigReader& cfg, double& tz_in, double& tz_out);

} //end namespace IOUtils
} //end namespace mio

#endif