WSL/SLF GitLab Repository

GSNIO.cc 20.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/***********************************************************************************/
/*  Copyright 2009 EPFL                                                            */
/***********************************************************************************/
/* 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/>.
*/
#include "GSNIO.h"

20
21
22
23
#include <algorithm>
#include <sstream>
#include <iostream>

24
25
#include <curl/curl.h>

26
27
28
29
30
31
using namespace std;

namespace mio {
/**
 * @page gsn GSN
 * @section gsn_format Format
Thomas Egger's avatar
Thomas Egger committed
32
33
 * This plugin reads meteorological data from GSN (Global Sensor Network, see <a href="http://sourceforge.net/apps/trac/gsn/"> GSN home page</a>)
 * via the RESTful web service. To compile the plugin you need to have the <a href="http://curl.haxx.se/">CURL library</a> with its headers present.
34
35
 * @subsection gsn_fields Field mapping
 * The following GSN fields are read from GSN and mapped to MeteoData attributes:
36
37
38
39
 * <center><table border="0">
 * <tr><td>
 * <table border="1">
 * <tr><th>GSN attribute</th><th>MeteoData field</th></tr>
Thomas Egger's avatar
Thomas Egger committed
40
41
 * <tr><td>RELATIVE_HUMIDITY or AIR_HUMID</td><td>MeteoData::RH</td></tr>
 * <tr><td>AIR_TEMPERATURE or AIR_TEMP</td><td>MeteoData::TA</td></tr>
42
43
 * <tr><td>WIND_DIRECTION</td><td>MeteoData::DW</td></tr>
 * <tr><td>WIND_SPEED_MAX</td><td>MeteoData::VW_MAX</td></tr>
Thomas Egger's avatar
Thomas Egger committed
44
 * <tr><td>WIND_SPEED_SCALAR_AV or WIND_SPEED</td><td>MeteoData::VW</td></tr>
45
46
47
48
49
50
51
52
53
54
 * <tr><td>INCOMING_SHORTWAVE_RADIATION</td><td>MeteoData::ISWR</td></tr>
 * <tr><td>INCOMING_LONGWAVE_RADIATION</td><td>MeteoData::ILWR</td></tr>
 * <tr><td>OUTGOING_SHORTWAVE_RADIATION</td><td>MeteoData::RSWR</td></tr>
 * <tr><td>OUTGOING_LONGWAVE_RADIATION</td><td>equivalent MeteoData::TSS</td></tr>
 * <tr><td>SNOW_HEIGHT</td><td>MeteoData::HS</td></tr>
 * <tr><td>RAIN_METER</td><td>MeteoData::HNW</td></tr>
 * <tr><td>SURFACE_TEMP</td><td>MeteoData::TSS</td></tr>
 * <tr><td>SOLAR_RAD</td><td>MeteoData::ISWR</td></tr>
 * </table></td></tr>
 * </table></center>
55
 * Please keep in mind that the names in GSN have currently not been standardized. This means that any sensor that does
Thomas Egger's avatar
Thomas Egger committed
56
 * not use the above names will not be properly supported (fields will not be missing but might appear under a different name)!
57
58
 *
 * @section gsn_units Units
59
 * The units of measurements are sometimes listed in the response headers, they are then parsed by the plugin and if known,
Thomas Egger's avatar
Thomas Egger committed
60
61
62
 * like <b>°C</b> or <b>\%</b>, offsets and multipliers are set to convert the data to MKSA
 *
 * Otherwise the units are assumed to be the following:
63
64
65
66
67
 * - temperatures in celsius
 * - relative humidity in %
 * - wind speed in m/s
 * - precipitations in mm/h
 * - radiation in W/m²
68
 * - time is provided as a Unix timestamp, which is always in UTC
69
70
71
72
73
74
75
 *
 * @section gsn_keywords Keywords
 * This plugin uses the following keywords:
 * - COORDSYS: input coordinate system (see Coords) specified in the [Input] section
 * - COORDPARAM: extra input coordinates parameters (see Coords) specified in the [Input] section
 * - COORDSYS: output coordinate system (see Coords) specified in the [Output] section
 * - COORDPARAM: extra output coordinates parameters (see Coords) specified in the [Output] section
76
 * - GSN_URL: The URL of the RESTful web service e.g. http://planetdata.epfl.ch:22001/rest
77
78
 * - GSN_USER: The username to access the service (optional)
 * - GSN_PASS: The password to authenticate the USER (optional)
79
 * - STATION#: station code for the given number #, e. g. la_fouly_1034 (case sensitive!)
80
 *
81
82
 * If no STATION keys are given, the full list of ALL stations available to the user in GSN will be used!
 * This may result in a long download.
83
 *
84
85
86
87
88
89
90
91
 * @code
 * METEO	= GSN
 * GSN_URL	= http://montblanc.slf.ch:22001/rest
 * GSN_USER	= mylogin
 * GSN_PASS	= mypasswd
 * STATION1	= wind_tunnel_meteo
 * @endcode
 *
92
93
 */

94
const int GSNIO::http_timeout = 60; // seconds until connect time out for libcurl
95
const std::string GSNIO::sensors_endpoint = "sensors";
96
const std::string GSNIO::null_string = "null";
97

98
GSNIO::GSNIO(const std::string& configfile)
99
      : cfg(configfile), vecStationName(), multiplier(), offset(), vecMeta(), vecAllMeta(), coordin(),
100
        coordinparam(), coordout(), coordoutparam(), endpoint(), userid(), passwd(), default_timezone(1.)
101
{
102
103
104
105
	IOUtils::getProjectionParameters(cfg, coordin, coordinparam, coordout, coordoutparam);
	initGSNConnection();
}

106
GSNIO::GSNIO(const Config& cfgreader)
107
      : cfg(cfgreader), vecStationName(), multiplier(), offset(), vecMeta(), vecAllMeta(), coordin(),
108
        coordinparam(), coordout(), coordoutparam(), endpoint(), userid(), passwd(), default_timezone(1.)
109
110
111
112
113
114
115
{
	IOUtils::getProjectionParameters(cfg, coordin, coordinparam, coordout, coordoutparam);
	initGSNConnection();
}

GSNIO::~GSNIO() throw(){}

116
117
118
119
120
121
void GSNIO::initGSNConnection() {
	curl_global_init(CURL_GLOBAL_ALL);

	default_timezone = IOUtils::nodata;
	cfg.getValue("TIME_ZONE", "Input", default_timezone, IOUtils::nothrow);

122
123
124
	cfg.getValue("GSN_URL", "Input", endpoint);
	if (*endpoint.rbegin() != '/') endpoint += "/";
	cerr << "[i] Using GSN URL: " << endpoint << endl;
125

126
127
	cfg.getValue("GSN_USER", "Input", userid, IOUtils::nothrow);
	cfg.getValue("GSN_PASS", "Input", passwd, IOUtils::nothrow);
128
129
}

130
131
132
133
134
135
136
void GSNIO::read2DGrid(Grid2DObject&, const std::string&)
{
	//Nothing so far
	throw IOException("Nothing implemented here", AT);
}

void GSNIO::read2DGrid(Grid2DObject&, const MeteoGrids::Parameters&, const Date&)
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
{
	//Nothing so far
	throw IOException("Nothing implemented here", AT);
}

void GSNIO::readDEM(DEMObject&)
{
	//Nothing so far
	throw IOException("Nothing implemented here", AT);
}

void GSNIO::readLanduse(Grid2DObject&)
{
	//Nothing so far
	throw IOException("Nothing implemented here", AT);
}

void GSNIO::writeMeteoData(const std::vector< std::vector<MeteoData> >&,
155
                           const std::string&)
156
157
158
159
160
161
162
163
164
{
	//Nothing so far
	throw IOException("Nothing implemented here", AT);
}

void GSNIO::readStationData(const Date&, std::vector<StationData>& vecStation)
{
	vecStation.clear();

165
	if (vecMeta.empty())
166
		readMetaData();
167

168
	vecStation = vecMeta;
169
}
170

171
void GSNIO::readMetaData()
172
{
173
174
175
	vecMeta.clear();

	if (vecStationName.empty())
176
		cfg.getValues("STATION", "INPUT", vecStationName); //reads station names into vector<string> vecStationName
177
178
179
180
181
182
183
184

	//Get Meta Data for all stations first
	getAllStations();

	if (!vecStationName.empty()) { //if the user has specified a subset of stations
		for (size_t ii=0; ii<vecStationName.size(); ii++) {
			for (size_t jj=0; jj<vecAllMeta.size(); jj++) {
				if (vecAllMeta[jj].stationID == vecStationName[ii]) {
185
186
					vecMeta.push_back( vecAllMeta[jj] );
					break; //move on to next station
187
188
				}
			}
189
190
191
192

			if (vecMeta.size() != (ii+1)) { // could not find station in list of available stations
				throw NoAvailableDataException("Could not retrieve meta data for station " + vecStationName[ii], AT);
			}
193
194
195
196
197
198
		}
	} else { //otherwise use all available stations
		vecMeta = vecAllMeta;
	}
}

199
void GSNIO::save_station(const std::string& id, const std::string& name, const double& lat, const double& lon,
200
201
202
203
204
                         const double& alt, const double& slope_angle, const double& slope_azi)
{
	Coords current_coord(coordin, coordinparam);
	current_coord.setLatLon(lat, lon, alt);
	StationData sd(current_coord, id, name);
205

206
	if (slope_angle != IOUtils::nodata) {
207
208
209
210
211
212
213
214
215
216
217
218
219
220
		if ((slope_angle == 0.) && (slope_azi == IOUtils::nodata)) {
			sd.setSlope(slope_angle, 0.); //expostion: north assumed
		} else {
			sd.setSlope(slope_angle, slope_azi);
		}
	}

	vecAllMeta.push_back(sd);
}

void GSNIO::getAllStations()
{
	/**
	 * Retrieve all station names, that are available in the current GSN instance
221
	 * and which are accessible for the current user (see Input::GSN_USER)
222
	 */
223
224
225
226
227
228
229
	const string vsname_str("# vsname:");
	const string altitude_str("# altitude:");
	const string longitude_str("# longitude:");
	const string latitude_str("# latitude:");
	const string slope_str("# slope:");
	const string exposition_str("# exposition:");
	const string name_str("# name:");
230
231

	stringstream ss;
232
	string line;
233

234
	vecAllMeta.clear();
235

236
237
238
239
240
	const string auth_request = sensors_endpoint + "?username=" + userid + "&password=" + passwd;
	const string anon_request = sensors_endpoint;
	const string request = (!userid.empty())? auth_request : anon_request;
	if (curl_read(request, ss)) {
		string name, id, azi;
241
242
243
244
245
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
		double lat=0., lon=0., alt=0., slope_angle=IOUtils::nodata, slope_azi=IOUtils::nodata;
		unsigned int valid = 0;

		while (getline(ss, line)) {
			if (!line.compare(0, vsname_str.size(), vsname_str)) {
				if (valid == 15) { // Last station was valid: store StationData
					save_station(id, name, lat, lon, alt, slope_angle, slope_azi);
				}

				id = line.substr(vsname_str.size());
				IOUtils::trim(id);
				slope_angle = slope_azi = IOUtils::nodata;
				name  = azi = "";
				valid = 1;
			} else if (!line.compare(0, altitude_str.size(), altitude_str)) {
				IOUtils::convertString(alt, line.substr(altitude_str.size()));
				valid |= 2;
			} else if (!line.compare(0, latitude_str.size(), latitude_str)) {
				IOUtils::convertString(lat, line.substr(latitude_str.size()));
				valid |= 4;
			} else if (!line.compare(0, longitude_str.size(), longitude_str)) {
				IOUtils::convertString(lon, line.substr(longitude_str.size()));
				valid |= 8;
			} else if (!line.compare(0, name_str.size(), name_str)) { // optional
				name = line.substr(name_str.size());
				IOUtils::trim(name);
			} else if (!line.compare(0, slope_str.size(), slope_str)) { //optional
				IOUtils::convertString(slope_angle, line.substr(slope_str.size()));
			} else if (!line.compare(0, exposition_str.size(), exposition_str)) { //optional
				azi = line.substr(exposition_str.size());
				if (IOUtils::isNumeric(azi)) {
					IOUtils::convertString(slope_azi, azi);
				} else {
					slope_azi = IOUtils::bearing(azi);
				}
			}
		}

		if (valid == 15) { // Last station was valid: store StationData
			save_station(id, name, lat, lon, alt, slope_angle, slope_azi);
		}
	} else {
		throw IOException("Could not retrieve list of sensors", AT);
	}
}
286
287

void GSNIO::readMeteoData(const Date& dateStart, const Date& dateEnd,
288
                          std::vector< std::vector<MeteoData> >& vecMeteo,
Mathias Bavay's avatar
Mathias Bavay committed
289
                          const size_t& stationindex)
290
{
291
	if (vecMeta.empty())
292
		readMetaData();
293

294
	if (vecMeta.empty()) //if there are no stations -> return
295
296
		return;

297
	size_t indexStart=0, indexEnd=vecMeta.size();
298
299
300
301

	//The following part decides whether all the stations are rebuffered or just one station
	if (stationindex == IOUtils::npos){
		vecMeteo.clear();
302
		vecMeteo.insert(vecMeteo.begin(), vecMeta.size(), vector<MeteoData>());
303
	} else {
304
		if (stationindex < vecMeteo.size()){
305
306
307
308
309
310
311
			indexStart = stationindex;
			indexEnd   = stationindex+1;
		} else {
			throw IndexOutOfBoundsException("You tried to access a stationindex in readMeteoData that is out of bounds", AT);
		}
	}

312
	for (size_t ii=indexStart; ii<indexEnd; ii++){ //loop through stations
313
		readData(dateStart, dateEnd, vecMeteo[ii], ii);
314
	}
315

316
317
}

318
void GSNIO::readData(const Date& dateStart, const Date& dateEnd, std::vector<MeteoData>& vecMeteo, const size_t& stationindex)
319
{
320
321
	const string fields_str("# fields:");
	const string units_str("# units:");
322

323
	const string auth_request = sensors_endpoint + "/" + vecMeta[stationindex].stationID + "?from=" + dateStart.toString(Date::ISO) + ":00"
324
	                 + "&to=" + dateEnd.toString(Date::ISO) + ":00" + "&username=" + userid + "&password=" + passwd;
325
326
327
	const string anon_request = sensors_endpoint + "/" + vecMeta[stationindex].stationID + "?from=" + dateStart.toString(Date::ISO) + ":00"
	                 + "&to=" + dateEnd.toString(Date::ISO) + ":00";
	const string request = (!userid.empty())? auth_request : anon_request;
328
329

	stringstream ss;
330

331
	if (curl_read(request, ss)) {
332
333
		vector<size_t> index;
		bool olwr_present = false;
334
335

		MeteoData tmpmeteo;
336
		tmpmeteo.meta = vecMeta.at(stationindex);
337

338
		string line, fields, units;
339
		while (getline(ss, line)) { //parse header section
340

341
			if (line.size() && (line[0] != '#')) break;
342

343
344
345
346
			if (!line.compare(0, fields_str.size(), fields_str)) {
				fields = line.substr(fields_str.size());
			} else if (!line.compare(0, units_str.size(), units_str)) {
				units = line.substr(units_str.size()) + " "; // the extra space is important if no units are specified
347
			}
348
		}
349

Thomas Egger's avatar
Thomas Egger committed
350
351
352
353
		if (units.empty() || fields.empty()) {
			throw InvalidFormatException("Invalid header for station " + tmpmeteo.meta.stationID, AT);
		}

354
355
		map_parameters(fields, units, tmpmeteo, index);
		olwr_present = tmpmeteo.param_exists("OLWR");
356

Thomas Egger's avatar
Thomas Egger committed
357
		do { //parse data section, the first line should already be buffered
358
359
			parse_streamElement(line, index, olwr_present, vecMeteo, tmpmeteo);
		} while (getline(ss, line));
360
	} else {
361
		throw IOException("Could not retrieve data for station " + vecMeta[stationindex].stationID, AT);
362
	}
363
}
364

365
void GSNIO::map_parameters(const std::string& fields, const std::string& units, MeteoData& md, std::vector<size_t>& index)
366
{
Thomas Egger's avatar
Thomas Egger committed
367
	vector<string> field, unit;
368
	size_t timestamp_field = IOUtils::npos;
369
370
	multiplier.clear();
	offset.clear();
371

372
373
374
	IOUtils::readLineToVec(fields, field, ',');
	IOUtils::readLineToVec(units, unit, ',');

375
	if ((field.size() != unit.size()) || (field.size() < 2)) {
376
		throw InvalidFormatException("Fields and units are inconsistent for station " + md.meta.stationID, AT);
377
	}
378

379
	for (size_t ii=0; ii<field.size(); ii++) {
380
		const string field_name( IOUtils::strToUpper(field[ii]) );
381

382
		if (field_name == "RELATIVE_HUMIDITY" || field_name == "RH" || field_name == "AIR_HUMID" || field_name == "REL_HUMIDITY") {
383
			index.push_back(MeteoData::RH);
384
		} else if (field_name == "AIR_TEMPERATURE" || field_name == "TA" || field_name == "AIR_TEMP") {
385
			index.push_back(MeteoData::TA);
386
		} else if (field_name == "WIND_DIRECTION" || field_name == "DW") {
387
			index.push_back(MeteoData::DW);
388
		} else if (field_name == "WIND_SPEED_MAX" || field_name == "VW_MAX") {
389
			index.push_back(MeteoData::VW_MAX);
390
		} else if (field_name == "WIND_SPEED_SCALAR_AV" || field_name == "VW" || field_name == "WIND_SPEED") {
391
			index.push_back(MeteoData::VW);
392
		} else if (field_name == "INCOMING_SHORTWAVE_RADIATION" || field_name == "ISWR" || field_name == "SOLAR_RAD") {
393
			index.push_back(MeteoData::ISWR);
394
		} else if (field_name == "INCOMING_LONGWAVE_RADIATION" || field_name == "ILWR") {
395
			index.push_back(MeteoData::ILWR);
396
		} else if (field_name == "OUTGOING_SHORTWAVE_RADIATION" || field_name == "RSWR") {
397
			index.push_back(MeteoData::RSWR);
398
		} else if (field_name == "OUTGOING_LONGWAVE_RADIATION" || field_name == "RLWR") { //is used to calculate TSS
399
400
			md.addParameter("OLWR");
			index.push_back(md.getParameterIndex("OLWR"));
401
		} else if (field_name == "SNOW_HEIGHT" || field_name == "HS1") {
402
			index.push_back(MeteoData::HS);
403
		} else if (field_name == "RAIN_METER" || field_name == "PINT") {
404
			index.push_back(MeteoData::HNW);
405
		} else if (field_name == "SURFACE_TEMP" || field_name == "TSS" || field_name == "SNOW_SURFACE_TEMPERATURE") {
406
			index.push_back(MeteoData::TSS);
407
408
		} else if (field_name == "ATM_PRESSURE" || field_name == "P") {
			index.push_back(MeteoData::P);
409
410
411
412
413
		} else if (field_name == "TIMESTAMP") {
			timestamp_field = ii;
			index.push_back(IOUtils::npos);
		} else if (field_name == "TIME") {
			index.push_back(IOUtils::npos);
414
415
		} else { //this is an extra parameter
			md.addParameter(field_name);
416
			const size_t parindex = md.getParameterIndex(field_name);
417
418
			index.push_back(parindex);

419
420
			//For the parameters unknown to MeteoIO we can store the units qualification °C, %, etc
			//and make it possible for the values to be converted to MKSA in the convertUnits procedure
421
			string name( unit[ii] );
422
423
424
425
426
427
			IOUtils::trim(name);

			if (name == "%") {
				multiplier[parindex] = 0.01;
			} else if (name.size() == 2 && (int)((unsigned char)name[0]) == 176 && name[1] == 'C') { //in °C, UTF8
				offset[parindex] = 273.15;
428
			}
429
430
		}
	}
431
432
433
434
435
436

	if (timestamp_field != IOUtils::npos) { //store timestamp index at index[0]
		index[0] = timestamp_field;
	} else {
		throw InvalidFormatException("No timestamp field for station " + md.meta.stationID, AT);
	}
437
}
438

439
void GSNIO::parse_streamElement(const std::string& line, const std::vector<size_t>& index, const bool& olwr_present, std::vector<MeteoData>& vecMeteo, MeteoData& tmpmeteo) const
440
{
441
442
443
444
	static vector<string> data;
	static double timestamp;
	static const size_t timestamp_index = index[0];

445
446
	const size_t size = IOUtils::readLineToVec(line, data, ',');
	if (size < 2) return; // Malformed for sure, retire gracefully, no exception thrown
447

448
	//The timestamp index is stored in index[0]
449
	IOUtils::convertString(timestamp, data[timestamp_index]);
450
451
	tmpmeteo.date.setUnixDate((time_t)(floor(timestamp/1000.0)));
	tmpmeteo.date.setTimeZone(default_timezone);
452

453
	for (size_t jj=2; jj<size; jj++) {
454
455
		const string& value = data[jj];
		if (value != GSNIO::null_string){
456
457
458
			IOUtils::convertString(tmpmeteo(index[jj]), value);
		} else {
			tmpmeteo(index[jj]) = IOUtils::nodata;
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
		}
	}

	convertUnits(tmpmeteo);
	if ((olwr_present) && (tmpmeteo(MeteoData::TSS) == IOUtils::nodata))
		tmpmeteo(MeteoData::TSS) = olwr_to_tss(tmpmeteo("OLWR"));

	vecMeteo.push_back(tmpmeteo);
	tmpmeteo(MeteoData::TSS) = IOUtils::nodata; //if tss has been set, then it needs to be reset manually
}

double GSNIO::olwr_to_tss(const double& olwr) {
	const double ea = 1.;
	if (olwr == IOUtils::nodata)
		return IOUtils::nodata;

	return pow( olwr / ( ea * Cst::stefan_boltzmann ), 0.25);
}

void GSNIO::readAssimilationData(const Date&, Grid2DObject&)
{
	//Nothing so far
	throw IOException("Nothing implemented here", AT);
}

void GSNIO::readPOI(std::vector<Coords>&)
{
	//Nothing so far
	throw IOException("Nothing implemented here", AT);
}

void GSNIO::write2DGrid(const Grid2DObject&, const std::string&)
{
	//Nothing so far
	throw IOException("Nothing implemented here", AT);
}

void GSNIO::write2DGrid(const Grid2DObject&, const MeteoGrids::Parameters&, const Date&)
{
	//Nothing so far
	throw IOException("Nothing implemented here", AT);
}

502
void GSNIO::convertUnits(MeteoData& meteo) const
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
{
	//converts C to Kelvin, converts RH to [0,1]
	double& ta = meteo(MeteoData::TA);
	if (ta != IOUtils::nodata)
		ta = C_TO_K(ta);

	double& tsg = meteo(MeteoData::TSG);
	if (tsg != IOUtils::nodata)
		tsg = C_TO_K(tsg);

	double& tss = meteo(MeteoData::TSS);
	if (tss != IOUtils::nodata)
		tss = C_TO_K(tss);

	double& rh = meteo(MeteoData::RH);
	if (rh != IOUtils::nodata)
		rh /= 100.;

	double& hs = meteo(MeteoData::HS);
	if (hs != IOUtils::nodata)
		hs /= 100.;
524
525

	// For all parameters that have either an offset or an multiplier to bring to MKSA
526
	map<size_t, double>::const_iterator it;
527
528
529
530
531
532
533
534
535
	for (it = multiplier.begin(); it != multiplier.end(); it++) {
		double& tmp = meteo(it->first);
		if (tmp != IOUtils::nodata) tmp *= it->second;
	}

	for (it = offset.begin(); it != offset.end(); it++) {
		double& tmp = meteo(it->first);
		if (tmp != IOUtils::nodata) tmp += it->second;
	}
536
537
538
539
}

size_t GSNIO::data_write(void* buf, size_t size, size_t nmemb, void* userp)
{
Thomas Egger's avatar
Thomas Egger committed
540
541
	if (userp) {
		ostream& os = *static_cast<ostream*>(userp);
542
		const streamsize len = size * nmemb;
543

Thomas Egger's avatar
Thomas Egger committed
544
		if (os.write(static_cast<char*>(buf), len)) return len;
545
546
547
548
549
	}

	return 0;
}

550
bool GSNIO::curl_read(const std::string& url_query, std::ostream& os)
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
{
	CURLcode code(CURLE_FAILED_INIT);
	CURL* curl = curl_easy_init();

	const string url = endpoint + url_query;

	if (curl) {
		if(CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &data_write))
		   && CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L))
		   && CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L))
		   && CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_FILE, &os))
		   && CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_TIMEOUT, GSNIO::http_timeout))
		   && CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_URL, url.c_str())))
		{
			code = curl_easy_perform(curl);
		}
		curl_easy_cleanup(curl);
	}

570
	if(code!=CURLE_OK)
571
		std::cout << "[E] " << curl_easy_strerror(code) << "\t";
572
573

	return (code==CURLE_OK);
574
575
576
}

} //namespace