WSL/SLF GitLab Repository

libsmet.h 14.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/***********************************************************************************/
/*  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 __LIBSMET_H__
#define __LIBSMET_H__

21
22
#include <meteoio/IOUtils.h> //HACK: move FileIndexer in a plugins specific file

23
#include <cmath>
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
62
63
64
65
#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <vector>
#include <set>
#include <map>
#include <fstream>
#include <algorithm>

#define SMET_STRINGIFY(x) #x
#define SMET_TOSTRING(x) SMET_STRINGIFY(x)
#define SMET_AT __FILE__ ":" SMET_TOSTRING(__LINE__)

namespace smet {

enum SMETType {ASCII, BINARY};
enum LocationType {WGS84, EPSG};

/**
 * @class SMETException
 * @brief A basic exception class adjusted for the needs of the SMET library
 * @author Thomas Egger
 */
class SMETException : public std::exception {
	public:
		SMETException(const std::string& message="SMETException occured", const std::string& position="");
		~SMETException() throw();
		const char* what() const throw();

	protected:
		std::string msg;
};

/**
 * @class SMETCommon
 * @brief A static class to provide basic operations and variables for the libsmet library
 * @author Thomas Egger
 */
class SMETCommon {
	public:
		static double convert_to_double(const std::string& in_string);
66
		static int convert_to_int(const std::string& in_string);
67
68
69
70
71
		static void stripComments(std::string& str);
		static char getEoln(std::istream& fin);
		static void trim(std::string& str);
		static void toUpper(std::string& str);
		static bool readKeyValuePair(const std::string& in_line, const std::string& delimiter,
72
		                             std::map<std::string,std::string>& out_map);
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

		static size_t readLineToVec(const std::string& line_in, std::vector<std::string>& vec_string);
		static bool is_decimal(const std::string& value);

		static std::set<std::string> all_optional_header_keys;
		static std::set<std::string> all_decimal_header_values;
		static std::set<std::string> all_mandatory_header_keys;
		static const std::string smet_version;

	private:
		static const bool __init;     ///<helper variable to enable the init of static collection data
		static bool initStaticData(); ///<initialize the static collections
};

/**
 * @class SMETReader
89
 * @brief The SMETReader class enables to read a SMET formatted file.
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
 *        Data and header info can be extracted through this class.
 *
 * @author Thomas Egger
 * @date   2011-07-18
 */
class SMETReader {
	public:
		/**
		 * @brief A constructor that will immediately parse the header of the SMET file
		 * @param[in] in_fname The filename of the SMET file
		 */
		SMETReader(const std::string& in_fname);
		~SMETReader();

		/**
		 * @brief Read the data in a SMET file for a given interval of time
		 *        if no timestamp is present in the file, the whole file is read
		 * @param[in] timestamp_start ISO formatted string, beginning of interval (inclusive)
		 * @param[in] timestamp_end ISO formatted string, end of interval (inclusive)
		 * @param[out] vec_timestamp A vector of string to hold the timestamp of each line
		 * @param[out] vec_data A vector of double holding all double values of all lines sequentially
		 */
112
		void read(const std::string& timestamp_start, const std::string& timestamp_end,
113
		          std::vector<std::string>& vec_timestamp, std::vector<double>& vec_data);
114
115
116
117
118
119
120
121
122
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

		/**
		 * @brief Read the data in a SMET file for a given interval of time
		 *        if no julian field is present in the file, the whole file is read
		 * @param[in] julian_start beginning of interval (inclusive), julian day
		 * @param[in] julian_end end of interval (inclusive), julian day
		 * @param[out] vec_data A vector of double holding all double values of all lines sequentially
		 */
		void read(const double& julian_start, const double& julian_end, std::vector<double>& vec_data);

		/**
		 * @brief Read all the data in a SMET file, if a timestamp is present
		 * @param[out] vec_timestamp A vector of string to hold the timestamp of each line
		 * @param[out] vec_data A vector of double holding all double values of all lines sequentially
		 */
		void read(std::vector<std::string>& vec_timestamp, std::vector<double>& vec_data);

		/**
		 * @brief Read all the data in a SMET file, if no timestamp is present
		 * @param[out] vec_data A vector of double holding all double values of all lines sequentially
		 */
		void read(std::vector<double>& vec_data);

		/**
		 * @brief Get a string value for a header key in a SMET file
		 * @param[in] key A key in the header section of a SMET file
		 * @return The value for the key, if the key is not present return ""
		 */
		std::string get_header_value(const std::string& key) const;

		/**
		 * @brief Get a double value for a header key in a SMET file
		 * @param[in] key A key in the header section of a SMET file
		 * @return The value for the key, if the key is not present return nodata
		 */
		double get_header_doublevalue(const std::string& key) const;
150
151
152
153
154
155
156

		/**
		 * @brief Get an int value for a header key in a SMET file
		 * @param[in] key A key in the header section of a SMET file
		 * @return The value for the key, if the key is not present return (int)nodata
		 */
		int get_header_intvalue(const std::string& key) const;
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
192
193
194
195
196
197
198
199
200
201
		/**
		 * @brief Check whether timestamp is a part of the fields
		 * @return true if timestamp is a column in the SMET file, false otherwise
		 */
		bool contains_timestamp() const;

		/**
		 * @brief Get a name for a certain column in the SMET file
		 * @param[in] nr_of_field Column index (the column 'timestamp' is not counted)
		 * @return The string name of the column
		 */
		std::string get_field_name(const size_t& nr_of_field);

		/**
		 * @brief Check whether location information is written in the header
		 * @param[in] type Either smet::WGS84 or smet::EPSG
		 * @return true if type of location information is in header, false otherwise
		 */
		bool location_in_header(const LocationType& type) const;

		/**
		 * @brief Check whether location information is written in the data section
		 * @param[in] type Either smet::WGS84 or smet::EPSG
		 * @return true if type of location information is in data section, false otherwise
		 */
		bool location_in_data(const LocationType& type) const;

		/**
		 * @brief Get number of fields (=columns) in SMET file (timestamp excluded)
		 * @return A size_t value representing number of double valued columns
		 */
		size_t get_nr_of_fields() const;

		/**
		 * @brief Get the unit conversion (offset and multiplier) that are used for this SMET object
		 *        If the fields units_offset or units_multiplier are present in the header
		 *        they are returned, otherwise the default conversion vectors for the offset
		 *        (consisting of zeros) and for the multipliers (consisting of ones) are returned
		 * @param[out] offset A vector of doubles representing the offset for each column
		 * @param[out] multiplier A vector of doubles representing the multiplier for each column
		 */
		void get_units_conversion(std::vector<double>& offset, std::vector<double>& multiplier) const;

		/**
202
		 * @brief Set whether the values returned should be converted according to
203
204
205
206
207
		 *        unit_offset and multiplier or whether the user should get the raw data returned
		 * @param[in] in_mksa True if the user wants MKSA values returned, false otherwise (default)
		 */
		void convert_to_MKSA(const bool& in_mksa);

208
209
210
211
212
213
		/**
		 * @brief Retrieve the filename that this reader operates upon
		 * @return a std::string representing the filename
		 */
		std::string get_filename() const;

214
215
216
217
	private:
		void read_data_ascii(std::ifstream& fin, std::vector<std::string>& vec_timestamp, std::vector<double>& vec_data);
		void read_data_binary(std::ifstream& fin, std::vector<double>& vec_data);
		void cleanup(std::ifstream& fin) throw();
218
		void checkSignature(const std::vector<std::string>& vecSignature, bool& o_isAscii);
219
220
221
222
		void read_header(std::ifstream& fin);
		void process_header();

		std::streampos data_start_fpointer;
223
224
225
226
227

		std::vector<double> vec_offset;              //an offset for every column, except timestamp
		std::vector<double> vec_multiplier;          //a multiplier for every column, except timestamp
		std::vector<std::string> vec_fieldnames;     //holds the column names, except for timestamp column
		std::map< std::string, std::string > header; //holds the header
228
		mio::IOUtils::FileIndexer indexer; //in order to save file pointers
229
230
231
232
233

		std::string filename;
		std::string timestamp_start, timestamp_end; //the beginning and end date of the current timestamp_interval
		double nodata_value; //The nodata value as seen in the header section of the SMET file
		double julian_start, julian_end; //the beginning and end date of the current julian_interval
234
		static const size_t streampos_every_n_lines; //save current stream pos every n lines of data
235
236
237
238
239
240
241
242
		size_t nr_of_fields; //is always the number of fields minus the timestamp field, if present
		size_t timestamp_field, julian_field; //index of the timestamp and julian column, if present
		char location_wgs84, location_epsg, location_data_wgs84, location_data_epsg;
		char eoln; //end of line character for this file
		bool timestamp_present, julian_present;
		bool isAscii; //true if the file is in SMET ASCII format, false if it is in binary format
		bool mksa; //true if MKSA converted values have to be returned
		bool timestamp_interval, julian_interval; //true if data shall only be read for a time interval
243
244
245
246
};

/**
 * @class SMETWriter
247
 * @brief The SMETWriter class that enables to write a SMET formatted file.
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
 *        The user constructs a SMETWriter class and fills in the header values
 *        Finally the user may call write(...) and pass the data to be written
 *
 * @author Thomas Egger
 * @date   2011-07-14
 */
class SMETWriter {
	public:
		/**
		 * @brief The constructor allows to set the filename, the type and whether the file should be gzipped
		 * @param[in] in_fname The filename of the SMET file to be written
		 * @param[in] in_type  The type of the SMET file, i.e. smet::ASCII or smet::BINARY (default: ASCII)
		 * @param[in] in_gzip  Whether the file should be zipped or not (default: false)
		 */
		SMETWriter(const std::string& in_fname, const SMETType& in_type=ASCII, const bool& in_gzip=false);
		~SMETWriter();

		/**
		 * @brief Set a key, value pair in the SMET header (both strings)
		 * @param[in] key A string key to set in the header (overwritten if already present)
		 * @param[in] value A string value associated with the key
		 */
		void set_header_value(const std::string& key, const std::string& value);

		/**
		 * @brief Set a double value for a string key in a SMET header
		 * @param[in] key A string key to set in the header (overwritten if already present)
		 * @param[in] value A double value to be converted into a string and stored in the header
		 */
		void set_header_value(const std::string& key, const double& value);

		/**
		 * @brief Write a SMET file, providing ASCII ISO formatted timestamps and data
		 * @param[in] vec_timestamp A vector with one ASCII date/time combined string for each line
		 * @param[in] data All the data to be written sequentially into the columns, the data
283
284
		 *            is aligned sequentially, not per line; Total size of the vector:
		 *            Total size of the vector: vec_timestamp.size() * nr_of_fields
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
		 *            (timestamp is not counted as field)
		 */
		void write(const std::vector<std::string>& vec_timestamp, const std::vector<double>& data);

		/**
		 * @brief Write a SMET file, providing a vector of doubles
		 * @param[in] data All the data to be written sequentially into the columns, the data
		 *            is aligned sequentially, not per line;
		 */
		void write(const std::vector<double>& data);

		/**
		 * @brief Set precision for each field (except timestamp), otherwise a default
		 *        precision of 3 is used for each column
		 * @param[in] vec_precision Set the precision for every column to be written
		 *            (timestamp is not counted if present)
		 */
302
		void set_precision(const std::vector<int>& vec_precision);
303
304
305
306
307
308
309

		/**
		 * @brief Set width for each field (except timestamp), otherwise a default
		 *        width of 8 is used for each column
		 * @param[in] vec_width Set the width for every column to be written
		 *            (timestamp is not counted if present)
		 */
310
		void set_width(const std::vector<int>& vec_width);
311
312
313
314
315
316
317
318
319
320
321
322

	private:
		void cleanup() throw();
		void write_header(); //only writes when all necessary header values are set
		void write_data_line_ascii(const std::string& timestamp, const std::vector<double>& data);
		void write_data_line_binary(const std::vector<double>& data);
		void write_signature();
		bool check_fields(const std::string& key, const std::string& value);
		void check_formatting();
		bool valid_header_pair(const std::string& key, const std::string& value);
		bool valid_header();

323
324
325
326
327
328
		std::vector<std::string> other_header_keys; //this vector is used to preserve the sequence of header keys
		std::vector<int> ascii_precision, ascii_width;
		std::map< std::string, std::string > header;
		std::set<std::string> mandatory_header_keys;
		std::ofstream fout; //Output file streams

329
		std::string filename;
330
		std::string nodata_string;
331
332
		SMETType smet_type;
		double nodata_value;
333
334
335
		size_t nr_of_fields, julian_field, timestamp_field;
		char location_wgs84, location_epsg;
		bool gzip;
336
337
338
339
340
341
342
343
		bool location_in_header, location_in_data_wgs84, location_in_data_epsg;
		bool timestamp_present, julian_present;
		bool file_is_binary;
};

}

#endif