WSL/SLF GitLab Repository

libsmet.h 14.2 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
#include <cstdlib>
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
66
#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);
67
		static int convert_to_int(const std::string& in_string);
68
69
70
71
72
		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,
73
		                             std::map<std::string,std::string>& out_map);
74
75
76
77
78
79
80
81
82
83
84
85

		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
86
87
		static char* conversion_end;
		static double conversion_value;
88
89
90
91
};

/**
 * @class SMETReader
92
 * @brief The SMETReader class enables to read a SMET formatted file.
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
 *        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
		 */
115
		void read(const std::string& timestamp_start, const std::string& timestamp_end,
116
		          std::vector<std::string>& vec_timestamp, std::vector<double>& vec_data);
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
150
151
152

		/**
		 * @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;
153
154
155
156
157
158
159

		/**
		 * @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;
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
202
203
204
		/**
		 * @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;

		/**
205
		 * @brief Set whether the values returned should be converted according to
206
207
208
209
210
		 *        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);

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

217
218
219
220
	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();
221
		void checkSignature(const std::vector<std::string>& vecSignature, bool& o_isAscii);
222
223
224
225
		void read_header(std::ifstream& fin);
		void process_header();

		std::streampos data_start_fpointer;
226
227
228
229
230

		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
231
		mio::IOUtils::FileIndexer indexer; //in order to save file pointers
232
233
234
235
236

		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
237
		static const size_t streampos_every_n_lines; //save current stream pos every n lines of data
238
239
240
241
242
243
244
245
		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
246
247
248
249
};

/**
 * @class SMETWriter
250
 * @brief The SMETWriter class that enables to write a SMET formatted file.
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
 *        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
286
287
		 *            is aligned sequentially, not per line; Total size of the vector:
		 *            Total size of the vector: vec_timestamp.size() * nr_of_fields
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
		 *            (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)
		 */
305
		void set_precision(const std::vector<int>& vec_precision);
306
307
308
309
310
311
312

		/**
		 * @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)
		 */
313
		void set_width(const std::vector<int>& vec_width);
314
315
316
317
318
319
320
321
322
323
324
325

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

326
327
328
329
330
331
		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

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

}

#endif